void Screenport::SetUI( const Rectangle2I* clip )	
{
	if ( clip && clip->Area() > 1 ) {
		clipInUI2D = Rectangle2F( (float)clip->min.x, (float)clip->min.y, (float)clip->max.x, (float)clip->max.y );
	}
	else {
		clipInUI2D = Rectangle2F( 0, 0, UIWidth(), UIHeight() );
	}
	GLASSERT( clipInUI2D.IsValid() );
	GLASSERT( clipInUI2D.min.x >= 0 && clipInUI2D.max.x <= UIWidth() );
	GLASSERT( clipInUI2D.min.y >= 0 && clipInUI2D.max.y <= UIHeight() );

	Rectangle2F scissor;
	UIToWindow( clipInUI2D, &scissor );

	Rectangle2I clean;
	CleanScissor( scissor, &clean );
	GPUShader::SetScissor(  clean.min.x, clean.min.y, clean.Width(), clean.Height() );

	//view2D.SetIdentity();
	
	projection2D.SetIdentity();
	projection2D.SetOrtho( 0, screenWidth, screenHeight, 0, -1, 1 );
	GPUShader::SetOrthoTransform( (int)screenWidth, (int)screenHeight, Rotation()*90 );
	uiMode = true;
}
Exemple #2
0
KrEngine::KrEngine( SDL_Surface* _screen )
	: paintInfo( _screen )
{
	Rectangle2I bounds;
	bounds.Set( 0, 0, _screen->w - 1, _screen->h - 1 );

	Init( _screen, 1, &bounds, 0 );
}
Rectangle2I MapSpatialComponent::PorchPos() const
{
	Rectangle2I v;
	v.Set( 0, 0, 0, 0 );
	if ( !hasPorch ) return v;

	float yRotation = YRotation(parentChit->Rotation());
	v = CalcPorchPos(bounds.min, bounds.Width(), yRotation);
	return v;
}
Exemple #4
0
void MapScene::ItemTapped(const gamui::UIItem* item)
{
	Vector2I sector = { 0, 0 };
	Vector2I v = data->destSector;
	CoreScript* cs = CoreScript::GetCore(v);
	CoreScript* homeCore = lumosChitBag->GetHomeCore();

	if (item == &okay) {
		data->destSector.Zero();
		lumosGame->PopScene();
	}
	else if (item == &gridTravel) {
		lumosGame->PopScene();
	}
	else if (item == &viewButton) {
		data->view = true;
		lumosGame->PopScene();
	}
	else if (item == &mapImage) {
		float x = 0, y = 0;
		gamui2D.GetRelativeTap(&x, &y);
		sector.x = int(x * float(NUM_SECTORS));
		sector.y = int(y * float(NUM_SECTORS));
		data->destSector = sector;
		DrawMap();
		EnableButtons();
	}
	else if (item == &mapImage2) {
		float x = 0, y = 0;
		gamui2D.GetRelativeTap(&x, &y);
		Rectangle2I b = MapBounds2();
		sector.x = b.min.x + int(x * float(b.Width()));
		sector.y = b.min.y + int(y * float(b.Height()));
		data->destSector = sector;
		DrawMap();
		EnableButtons();
	}
	else if (item == &warButton) {
		Team::Instance()->War(cs, homeCore, true, &lumosChitBag->GetSim()->GetCachedWeb());
		DrawMap();
		EnableButtons();
	}
	else if (item == &peaceButton) {
		int cost = Team::Instance()->Peace(cs, homeCore, false, &lumosChitBag->GetSim()->GetCachedWeb());
		Wallet* wallet = homeCore->ParentChit()->GetWallet();
		if (wallet->HasGold(cost)) {
			ReserveBank::Instance()->GetWallet()->Deposit(wallet, cost);
			Team::Instance()->Peace(cs, homeCore, true, &lumosChitBag->GetSim()->GetCachedWeb());
			DrawMap();
			EnableButtons();
		}
	}
}
void MapSpatialComponent::OnRemove()
{
	Context()->chitBag->RemoveFromBuildingHash(this, bounds.min.x, bounds.min.y);
	
	WorldMap* worldMap = Context()->worldMap;
	LumosChitBag* chitBag = Context()->chitBag;

	super::OnRemove();

	// Since we are removed from the HashTable, this
	// won't be found by UpdateGridLayer()
	Rectangle2I b = bounds;
	b.Outset(1);
	worldMap->UpdateBlock(bounds);
	UpdateGridLayer(worldMap, chitBag, b);
}
void Visibility::InvalidateAll( const Rectangle2I& bounds )
{
	if ( !bounds.IsValid() )
		return;

	Rectangle2I vis;

	for( int i=0; i<MAX_UNITS; ++i ) {
		if ( units[i].IsAlive() ) {
			units[i].CalcVisBounds( &vis );
			if ( bounds.Intersect( vis ) ) {
				current[i] = false;
				if ( units[i].Team() == TERRAN_TEAM ) {
					fogInvalid = true;
				}
			}
		}
	}
}
Exemple #7
0
Rectangle2I MapScene::MapBounds2()
{
	Vector2I subOrigin = data->destSector;
	if (subOrigin.IsZero()) {
		subOrigin = lumosChitBag->GetHomeSector();
	}
	if (subOrigin.IsZero()) {
		subOrigin.Set(NUM_SECTORS / 2, NUM_SECTORS / 2);
	}

	if ( subOrigin.x < MAP2_RAD )					subOrigin.x = MAP2_RAD;
	if ( subOrigin.y < MAP2_RAD )					subOrigin.y = MAP2_RAD;
	if ( subOrigin.x >= NUM_SECTORS - MAP2_RAD )	subOrigin.x = NUM_SECTORS - MAP2_RAD - 1;
	if ( subOrigin.y >= NUM_SECTORS - MAP2_RAD )	subOrigin.y = NUM_SECTORS - MAP2_RAD - 1;
	Rectangle2I subBounds;
	subBounds.min = subBounds.max = subOrigin;
	subBounds.Outset(MAP2_RAD);

	return subBounds;
}
void Visibility::CalcUnitVisibility( int unitID )
{
	//unit = units;	// debugging: 1st unit only
	GLRELASSERT( unitID >= 0 && unitID < MAX_UNITS );
	const Unit* unit = &units[unitID];

	Vector2I pos = unit->MapPos();

	// Clear out the old settings.
	// Walk the area in range around the unit and cast rays.
	visibilityMap.ClearPlane( unitID );
	visibilityProcessed.ClearAll();

	Rectangle2I mapBounds = map->Bounds();

	// Can always see yourself.
	visibilityMap.Set( pos.x, pos.y, unitID );
	visibilityProcessed.Set( pos.x, pos.y, 0 );

	const int MAX_SIGHT_SQUARED = MAX_EYESIGHT_RANGE*MAX_EYESIGHT_RANGE;

	for( int r=MAX_EYESIGHT_RANGE; r>0; --r ) {
		Vector2I p = { pos.x-r, pos.y-r };
		static const Vector2I delta[4] = { { 1,0 }, {0,1}, {-1,0}, {0,-1} };

		for( int k=0; k<4; ++k ) {
			for( int i=0; i<r*2; ++i ) {
				if (    mapBounds.Contains( p )
					 && !visibilityProcessed.IsSet( p.x, p.y )
					 && (p-pos).LengthSquared() <= MAX_SIGHT_SQUARED ) 
				{
					CalcVisibilityRay( unitID, p, pos );
				}
				p += delta[k];
			}
		}
	}
}
Exemple #9
0
void KrOglTexture::SetTexture(	const KrRGBA* pixels, 
								const Rectangle2I& pixelCoords  )
{
	glBindTexture( GL_TEXTURE_2D, textureId );

	/* sigh. Still have to flip bytes. */
	int byteSize = pixelCoords.Width() * pixelCoords.Height() * 4;
	int colorSize = pixelCoords.Width() * pixelCoords.Height();

	U8* data = new U8[ byteSize ];
	
	GLASSERT( data  );
	if ( !data ) return;

	memcpy( data, pixels, byteSize );

	#ifdef KYRA_FLIP_COLORS
		U8* p = data;
		for ( int i=0; i<colorSize; ++i )
		{
			Swap( &p[0], &p[2] );
			p += 4;
		}
	#endif

	glTexSubImage2D(GL_TEXTURE_2D, 
					0,						// no mip mapping
					pixelCoords.min.x,		// coords on the texture
					pixelCoords.min.y,
					pixelCoords.Width(),	// coords on the source -- must be equal
					pixelCoords.Height(),
					GL_RGBA,				// format
					GL_UNSIGNED_BYTE,
					data );

	GLASSERT( glGetError() == GL_NO_ERROR );
	delete [] data;
}
void Screenport::SetPerspective( const grinliz::Rectangle2I* clip )
{
	uiMode = false;

	if ( clip && clip->Area() > 1 ) {
		clipInUI3D = Rectangle2F( (float)clip->min.x, (float)clip->min.y, (float)clip->max.x, (float)clip->max.y );
	}
	else {
		clipInUI3D = Rectangle2F( 0, 0, UIWidth(), UIHeight() );
	}
	GLASSERT( clipInUI3D.IsValid() );
	GLASSERT( clipInUI3D.min.x >= 0 && clipInUI3D.max.x <= UIWidth() );
	GLASSERT( clipInUI3D.min.y >= 0 && clipInUI3D.max.y <= UIHeight() );
	
	Rectangle2F scissor;
	UIToWindow( clipInUI3D,  &scissor );

	Rectangle2I clean;
	CleanScissor( scissor, &clean );
	GPUShader::SetScissor(  clean.min.x, clean.min.y, clean.Width(), clean.Height() );

	GLASSERT( uiMode == false );
	GLASSERT( EL_NEAR > 0.0f );
	GLASSERT( EL_FAR > EL_NEAR );

	frustum.zNear = EL_NEAR;
	frustum.zFar  = EL_FAR;

	// Convert from the FOV to the half angle.
	float theta = ToRadian( EL_FOV * 0.5f );
	float tanTheta = tanf( theta );
	float halfLongSide = tanTheta * frustum.zNear;

	// left, right, top, & bottom are on the near clipping
	// plane. (Not an obvious point to my mind.)

	// Also, the 3D camera applies the rotation.
	
	if ( Rotation() & 1 ) {
		float ratio = (float)clipInUI3D.Height() / (float)clipInUI3D.Width();
		
		// frustum is in original screen coordinates.
		frustum.top		=  halfLongSide;
		frustum.bottom	= -halfLongSide;
		
		frustum.left	= -ratio * halfLongSide;
		frustum.right	=  ratio * halfLongSide;
	}
	else {
		// Since FOV is specified as the 1/2 width, the ratio
		// is the height/width (different than gluPerspective)
		float ratio = (float)clipInUI3D.Height() / (float)clipInUI3D.Width();

		frustum.top		= ratio * halfLongSide;
		frustum.bottom	= -frustum.top;

		frustum.left	= -halfLongSide;
		frustum.right	=  halfLongSide;
	}
	
	Matrix4 rot;
	rot.SetZRotation( (float)(-90 * Rotation()) );
	
	// In normalized coordinates.
	projection3D.SetFrustum( frustum.left, frustum.right, frustum.bottom, frustum.top, frustum.zNear, frustum.zFar );
	projection3D = projection3D * rot;
	GPUShader::SetPerspectiveTransform( frustum.left, frustum.right,
										frustum.bottom, frustum.top,	
										frustum.zNear, frustum.zFar,
										(90*Rotation()) );
}
bool PitMorph::DoSequence( TimeClock* timeClock )
{
	const float SPEED = 4.0f  * ( TERRAIN_HEIGHT / 10.0f );
	// m term in y = mx+b
	const float SLOPE = 1.0f;
	
	bool last = false;
	Lilith3D* lilith = Lilith3D::Instance();
	TerrainMesh* tmesh = lilith->GetTerrainMesh();
	float height = tmesh->Height( terrainX, terrainY ) - timeClock->CalcVelocity( SPEED );
	height = Clamp( height, TERRAIN_MIN, TERRAIN_MAX );

	// essentially the x-intecept:
	// y = mx + b, b=height
	// x = -height / m
	float radius = ( TERRAIN_MAX - height ) / SLOPE;
	int   irad = int( radius ) + 1;

	Rectangle2I bounds;
	bounds.Set( terrainX - irad, terrainY - irad, terrainX + irad, terrainY + irad );
	Rectangle2I constrain;
	constrain.Set( 0, 0, lilith3d::VERTEXSIZE-1, lilith3d::VERTEXSIZE-1 );
	bounds.DoIntersection( constrain );

	// Do the center first.
	if ( height <= altitude )
		last = true;

	if ( tmesh->Height( terrainX, terrainY ) > altitude )
	{
		tmesh->StartHeightChange();
		tmesh->SetHeight( terrainX, terrainY, height );

		for( int j=bounds.min.y; j<=bounds.max.y; ++j )
		{
			for( int i=bounds.min.x; i<=bounds.max.x; ++i )
			{
				if ( i != terrainX || j != terrainY )
				{
					float len = Length( float( terrainX-i ), float( terrainY-j ), 0.0f );
					float setHeight = len*SLOPE + height;
					if ( setHeight < tmesh->Height( i, j ) )
						tmesh->SetHeight( i, j, setHeight );
				}
			}
		}
		tmesh->EndHeightChange();
	}

	for(	Publisher< PitMorphListener >::const_iterator it = publish.begin();
			it != publish.end();
			++it )
	{
		if ( last )
			(*it)->PitDone( this, terrainX, terrainY, height );
		else
			(*it)->PitSink( this, terrainX, terrainY, height );
	}

	return last;
}
bool VolcanoMorph::DoSequence( TimeClock* timeClock )
{
	const float SPEED = 4.0f  * ( TERRAIN_HEIGHT / 10.0f );
	// m term in y = mx+b
	const float SLOPE = -1.3f;
	const float SMOOTH_RADIUS = 3.0f;
	bool last = false;

	Lilith3D* lilith = Lilith3D::Instance();
	TerrainMesh* tmesh = lilith->GetTerrainMesh();

	float height = tmesh->Height( terrainX, terrainY ) + timeClock->CalcVelocity( SPEED );
	height = Clamp( height, TERRAIN_MIN, TERRAIN_MAX );

	// essentially the x-intecept:
	// y = mx + b, b=height
	// x = -height / m
	float radius = -( height - TERRAIN_MIN ) / SLOPE;
	int   irad = int( radius ) + 1;

	Rectangle2I bounds;
	bounds.Set( terrainX - irad, terrainY - irad, terrainX + irad, terrainY + irad );
	Rectangle2I constrain;
	constrain.Set( 0, 0, lilith3d::VERTEXSIZE-1, lilith3d::VERTEXSIZE-1 );

	bounds.DoIntersection( constrain );

	// Do the center first.
	if ( height >= altitude )
		last = true;

	if ( tmesh->Height( terrainX, terrainY ) < altitude )
	{
		tmesh->StartHeightChange();
		tmesh->SetHeight( terrainX, terrainY, height );

		for( int j=bounds.min.y; j<=bounds.max.y; ++j )
		{
			for( int i=bounds.min.x; i<=bounds.max.x; ++i )
			{
				if ( i != terrainX || j != terrainY )
				{
					float len = Length( float( terrainX-i ), float( terrainY-j ) );
					float setHeight = len*SLOPE + height;
					
					if ( len < SMOOTH_RADIUS )
						setHeight += fuzzHeight[ (j*lilith3d::VERTEXSIZE+i) % FUZZ_SIZE ];

					if ( setHeight > tmesh->Height( i, j ) )
						tmesh->SetHeight( i, j, setHeight );
				}
			}
		}
		tmesh->EndHeightChange();
	}

	// Inform listeners of changes.
	for( Publisher< VolcanoMorphListener >::const_iterator it = publish.begin();
		 it != publish.end();
		 ++it )
	{
		if ( !last )
			(*it)->VolcanoGrow( this, terrainX, terrainY, height );
		else
			(*it)->VolcanoDone( this, terrainX, terrainY, height );
	}
	
	// And now the particles and lights!
	const float FUZZ = 4.0f;
	const float FUZZMORE = 8.0f;
	float alt = tmesh->Height( terrainX, terrainY );

	GravParticles* grav = lilith->GetGravParticles();
	TimeClock* tc = Lilith3D::GetTimeClock();

	while ( tc->Msec() > lastParticleTime )
	{
		lastParticleTime += 50;
		Vector3F loc = { (float)terrainX, (float)terrainY, alt };
		Vector3F vel = {	-FUZZ / 2 + rand.FRand( FUZZ ),	// velocity
							-FUZZ / 2 + rand.FRand( FUZZ ),
							SPEED * 1.3f };
		Color3F col = {	0.8f + rand.FRand(0.2f), 			// coloring
							0.6f + rand.FRand(0.2f), 
							0.0f };

		grav->Create( loc, vel, col,
					  GravParticles::GRAVITY,
					  1.0f,
					  GravParticles::FLAME0 );

		for( int i=0; i<3; ++i )
		{
			Vector3F vel2 = {	-FUZZMORE / 2 + rand.FRand( FUZZMORE ),
								-FUZZMORE / 2 + rand.FRand( FUZZMORE ),
								SPEED * 0.8f };
			Color3F col2 = { 1.0f, 0.0f, 0.0f };
			grav->Create( loc, vel2, col2, 
						  GravParticles::GRAVITY,
						  GravParticles::MEDIUM );
		}
	}				  
//	glowLight->SetPos( (float)terrainX, (float)terrainY, alt );
	if ( decal ) {
		decal->SetAlpha( 1.0f );
		const float FADEOUT = 0.8f * altitude;
		if ( alt > FADEOUT ) {
			decal->SetAlpha( Interpolate( FADEOUT, 1.0f, altitude, 0.0f, alt ));
		}
	}
	return last;
}
Exemple #13
0
void KrEngine::Draw( bool updateRect, std::vector< Rectangle2I >* _rectangles )
{
	std::vector< Rectangle2I > rectArrayOnStack;

	#if defined( DRAWDEBUG_RLE ) || defined( DRAWDEBUG_BLTRECTS )
		debugFrameCounter++;
	#endif

//	GLOUTPUT( "Engine::Draw Walk\n" );
	tree->Walk();

		// We either use the passed in rectangles,
		// or the one here on the stack. Set the pointer
		// rectArray to the right thing.
	std::vector< Rectangle2I >* rectArray = ( _rectangles ) ? _rectangles : &rectArrayOnStack;
	rectArray->resize(0);
	
	if ( !paintInfo.openGL )
	{
		// Queue up the rectangles that will be used to blit to screen:

		if ( needFullScreenUpdate )
		{
			needFullScreenUpdate = false;			
			Rectangle2I rect;
			rect.Set( 0, 0, screen->w-1, screen->h-1 );
			rectArray->push_back( rect );
		}
		else
		{
			for ( int i=0; i<nWindows; ++i )
			{
				for ( int j=0; j<dirtyRectangle[i].NumRect(); ++j )
				{
					rectArray->push_back( dirtyRectangle[i].Rect(j) );
				}
			}
		}	
	}
	
	if ( paintInfo.openGL )
	{
		#ifdef KYRA_SUPPORT_OPENGL

		// OpenGL drawing
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		for( int j=0; j<nWindows; ++j )
		{
			if ( fillBackground[j] )
			{
				glBindTexture( GL_TEXTURE_2D, 0 );
				glColor4f( backgroundColor[j].Redf(), backgroundColor[j].Greenf(), backgroundColor[j].Bluef(), 1.0f );
				glBegin( GL_QUADS );
				{
					glVertex3i( screenBounds[j].min.x, screenBounds[j].min.y, 0 );
					glVertex3i( screenBounds[j].min.x + screenBounds[j].Width(), screenBounds[j].min.y, 0 );
					glVertex3i( screenBounds[j].min.x + screenBounds[j].Width(), screenBounds[j].min.y + screenBounds[j].Height(), 0 );
					glVertex3i( screenBounds[j].min.x, screenBounds[j].min.y + screenBounds[j].Height(), 0 );
				}
				glEnd();
			}

			bool clipping = ( screenBounds[j] != windowBounds );

			if ( clipping )
			{
				glEnable(GL_CLIP_PLANE0);
				glEnable(GL_CLIP_PLANE1);
				glEnable(GL_CLIP_PLANE2);
				glEnable(GL_CLIP_PLANE3);

				double plane0[4] = { 1.0, 0.0, 0.0, -screenBounds[j].min.x };
				double plane1[4] = { -1.0, 0.0, 0.0, (screenBounds[j].min.x + screenBounds[j].Width() ) };
				double plane2[4] = { 0.0, 1.0, 0.0, -screenBounds[j].min.y };
				double plane3[4] = { 0.0, -1.0, 0.0, (screenBounds[j].min.y + screenBounds[j].Height() ) };

				glClipPlane( GL_CLIP_PLANE0, plane0 );
				glClipPlane( GL_CLIP_PLANE1, plane1 );
				glClipPlane( GL_CLIP_PLANE2, plane2 );
				glClipPlane( GL_CLIP_PLANE3, plane3 );
			}

			tree->DrawWalk( screenBounds[j], &paintInfo, j );

			if ( clipping )
			{
				glDisable(GL_CLIP_PLANE0);
				glDisable(GL_CLIP_PLANE1);
				glDisable(GL_CLIP_PLANE2);
				glDisable(GL_CLIP_PLANE3);
			}
			dirtyRectangle[j].Clear();
		}
		UpdateScreen( 0 );
		#else
			// No openGl support, but openGl surface used
			GLASSERT( 0 );
		#endif
	}
	else
	{
		// Bitmap drawing.

		// Draw the background, if necessary. Then
		// do a draw walk for every DR.
		for( int win=0; win<nWindows; ++win  )
		{
			for( int i=0; i<dirtyRectangle[win].NumRect(); ++i )
			{
				const Rectangle2I& rect = dirtyRectangle[win].Rect( i );

				// Draw the background.
				//GLASSERT( fillBackground[j] );
				if ( fillBackground[win] )
				{
					SDL_Rect sdlrect = { rect.min.x, rect.min.y, rect.Width(), rect.Height() };
					U32 sdlColor = SDL_MapRGB( screen->format,	backgroundColor[win].c.red,
																backgroundColor[win].c.green,
																backgroundColor[win].c.blue );
					//GLASSERT( sdlColor == 0 );
					SDL_FillRect( screen, &sdlrect, sdlColor );
				}
				tree->DrawWalk( rect, &paintInfo, win );

				/*
				#ifdef DRAWDEBUG_BLTRECTS
					KrPainter painter( &paintInfo );
					painter.DrawBox( rect.xmin, rect.ymin, rect.Width(), rect.Height(), 200, 0, 0 );
				#endif
				*/
			}

			#ifdef DRAWDEBUG_BLTRECTS
				dirtyRectangle[win].DrawRects( screen );
			#endif
			dirtyRectangle[win].Clear();
		}

		// The windows and DRs have been walked. Now transfer to physical screen.
		if ( updateRect )
		{
			// Use the composite list of rectangles.
			UpdateScreen( rectArray );
		}
	}
}
Exemple #14
0
Chit* LumosChitBag::NewBuilding(const Vector2I& pos, const char* name, int team)
{
	const ChitContext* context = Context();
	Chit* chit = NewChit();

	const GameItem& rootItem = ItemDefDB::Instance()->Get(name);
	GameItem* item = rootItem.Clone();

	// Hack...how to do this better??
	if (item->IResourceName() == "pyramid0") {
		CStr<32> str;
		str.Format("pyramid%d", random.Rand(3));
		item->SetResource(str.c_str());
	}
	if (item->IResourceName() == ISC::kiosk) {
		switch (random.Rand(4)) {
			case 0: item->SetResource("kiosk.n");	break;
			case 1: item->SetResource("kiosk.m");	break;
			case 2: item->SetResource("kiosk.s");	break;
			default:item->SetResource("kiosk.c");	break;
		}
	}

	int size = 1;
	rootItem.keyValues.Get(ISC::size, &size);
	int porch = 0;
	rootItem.keyValues.Get(ISC::porch, &porch);
	const int circuit = 0;

	// Should be pre-cleared. But recover from weird situations by clearing.
	// Note that water is a real problem.
	Rectangle2I r;
	r.Set(pos.x, pos.y, pos.x + size - 1, pos.y + size - 1);
	for (Rectangle2IIterator it(r); !it.Done(); it.Next()) {
		const WorldGrid& wg = context->worldMap->GetWorldGrid(it.Pos());
		GLASSERT(wg.IsLand());
		(void)wg;
		context->worldMap->SetRock(it.Pos().x, it.Pos().y, 0, false, 0);
		context->worldMap->SetPlant(it.Pos().x, it.Pos().y, 0, 0);
	}

	MapSpatialComponent* msc = new MapSpatialComponent();
	msc->SetBuilding(size, porch != 0, circuit);
	msc->SetBlocks((rootItem.flags & GameItem::PATH_NON_BLOCKING) ? false : true);
	chit->Add(msc);
	MapSpatialComponent::SetMapPosition(chit, pos.x, pos.y);

	chit->Add(new RenderComponent(item->ResourceName()));
	chit->Add(new HealthComponent());
	AddItem(item, chit, context->engine, team, 0);

	IString script = rootItem.keyValues.GetIString("script");
	if (!script.empty()) {
		Component* s = ComponentFactory::Factory(script.c_str(), &chitContext);
		GLASSERT(s);
		chit->Add(s);
	}

	IString proc = rootItem.keyValues.GetIString("procedural");
	if (!proc.empty()) {
		ProcRenderInfo info;
		AssignProcedural(chit->GetItem(), &info);
		chit->GetRenderComponent()->SetProcedural(0, info);
	}

	IString consumes = rootItem.keyValues.GetIString(ISC::zone);
	if (!consumes.empty()) {
		Component* s = ComponentFactory::Factory("EvalBuildingScript", &chitContext);
		GLASSERT(s);
		chit->Add(s);
	}

	IString nameGen = rootItem.keyValues.GetIString( "nameGen");
	if ( !nameGen.empty() ) {
		IString p = Context()->chitBag->NameGen(nameGen.c_str(), chit->random.Rand());
		chit->GetItem()->SetProperName( p );
	}

#if 0	// debugging
	SectorPort sp;
	sp.sector.Set( pos.x/SECTOR_SIZE, pos.y/SECTOR_SIZE );
	sp.port = 1;
	worldMap->SetRandomPort( sp );
#endif

	context->engine->particleSystem->EmitPD( ISC::constructiondone, ToWorld3F( pos ), V3F_UP, 0 );

	if (XenoAudio::Instance()) {
		Vector3F pos3 = ToWorld3F(pos);
		XenoAudio::Instance()->PlayVariation(ISC::rezWAV, random.Rand(), &pos3);
	}

	return chit;
}
void MapSpatialComponent::SyncWithSpatial()
{
	if (!parentChit) return;
	Vector3F pos = parentChit->Position();
	if (pos.IsZero()) {
		GLASSERT(bounds.min.IsZero());
		return;
	}

	Rectangle2I oldBounds = bounds;

	// Position is in the center!
	if (size == 1) {
		bounds.min = ToWorld2I(pos);
	}
	else if (size == 2) {
		bounds.min = ToWorld2I(pos);
		bounds.min.x -= 1;
		bounds.min.y -= 1;
	}
	else {
		GLASSERT(0);
	}

	bounds.max.x = bounds.min.x + size - 1;
	bounds.max.y = bounds.min.y + size - 1;

	if (oldBounds != bounds) {
		// We have a new position, update in the hash tables:
		Context()->chitBag->RemoveFromBuildingHash(this, oldBounds.min.x, oldBounds.min.y);
		Context()->chitBag->AddToBuildingHash(this, bounds.min.x, bounds.min.y);
	}
	// And the pather.
	if (!oldBounds.min.IsZero()) {
		Context()->worldMap->UpdateBlock(oldBounds);
	}
	Context()->worldMap->UpdateBlock(bounds);

	// Compute a new porch type:
	EvalBuildingScript* ebs = 0;
	if (hasPorch) {
		ebs = (EvalBuildingScript*)parentChit->GetComponent("EvalBuildingScript");
		const GameItem* item = parentChit->GetItem();
		hasPorch = WorldGrid::BASE_PORCH;
		if (ebs && item) {
			double eval = ebs->EvalIndustrial(false);	// sets "Reachable". Must be called first.
			if (ebs->Reachable()) {
				double consumes = item->GetBuildingIndustrial();
				if (consumes) {
					double dot = eval * consumes;
					int q = int((1.0 + dot) * 2.0 + 0.5);
					// q=0, no porch. q=1 default.
					hasPorch = WorldGrid::BASE_PORCH + 1 + q;
					GLASSERT(hasPorch > 1 && hasPorch < WorldGrid::NUM_PORCH);
				}
			}
			else {
				hasPorch = WorldGrid::PORCH_UNREACHABLE;
			}
		}

	}
	// And the porches / circuits: (rotation doesn't change bounds);
	Rectangle2I oldOutset = oldBounds, outset = bounds;
	oldOutset.Outset(1);
	outset.Outset(1);

	if (!oldBounds.min.IsZero()) {
		UpdateGridLayer(Context()->worldMap, Context()->chitBag, oldOutset);
	}
	UpdateGridLayer(Context()->worldMap, Context()->chitBag, outset);
}
void Engine::Draw()
{
	GRINLIZ_PERFTRACK;

	// -------- Camera & Frustum -------- //
	screenport->SetView( camera.ViewMatrix() );	// Draw the camera

#ifdef DEBUG
	{
		Vector3F at;
		CameraLookingAt( &at );
		//GLOUTPUT(( "View set. Camera at (%.1f,%.1f,%.1f) looking at (%.1f,%.1f,%.1f)\n",
		//	camera.PosWC().x, camera.PosWC().y, camera.PosWC().z,
		//	at.x, at.y, at.z ));
		if ( map ) {
			Rectangle2I b = map->Bounds();
			b.Outset( 2 );
			if ( !b.Contains( (int)at.x, (int)at.z ) ) {
				GLASSERT( 0 );	// looking at nothing.
			}
		}
	}
#endif
				

	// Compute the frustum planes and query the tree.
	Plane planes[6];
	CalcFrustumPlanes( planes );

	Model* modelRoot = spaceTree->Query( planes, 6, 0, Model::MODEL_INVISIBLE, false );
	
	Color4F ambient, diffuse;
	Vector4F dir;
	CalcLights( ( !map || map->DayTime() ) ? DAY_TIME : NIGHT_TIME, &ambient, &dir, &diffuse );

	LightShader lightShader( ambient, dir, diffuse, false );
	LightShader blendLightShader( ambient, dir, diffuse, true );	// Some tiles use alpha - for instance the "splat" image

	LightShader mapItemShader( ambient, dir, diffuse, false );
	LightShader mapBlendItemShader( ambient, dir, diffuse, true );

	Rectangle2I mapBounds( 0, 0, EL_MAP_SIZE-1, EL_MAP_SIZE-1 );
	if ( map ) {
		mapBounds = map->Bounds();
	}

	// ------------ Process the models into the render queue -----------
	{
		GLASSERT( renderQueue->Empty() );
		const grinliz::BitArray<Map::SIZE, Map::SIZE, 1>* fogOfWar = (map) ? &map->GetFogOfWar() : 0;

		for( Model* model=modelRoot; model; model=model->next ) {
			if ( model->IsFlagSet( Model::MODEL_METADATA ) && !enableMeta )
				continue;

			if ( model->IsFlagSet(  Model::MODEL_OWNED_BY_MAP ) ) {
				model->Queue(	renderQueue, 
								&mapItemShader,
								&mapBlendItemShader,
								0 );
			}
			else {
				Vector3F pos = model->AABB().Center();
				int x = LRintf( pos.x - 0.5f );
				int y = LRintf( pos.z - 0.5f );

#ifdef EL_SHOW_ALL_UNITS
				{
#else
				if ( mapBounds.Contains( x, y ) && (!fogOfWar || fogOfWar->IsSet( x, y ) ) ) {
#endif
					model->Queue( renderQueue, &lightShader, &blendLightShader, 0 );
				}
			}
		}
	}


	// ----------- Render Passess ---------- //
	Color4F color;

	if ( map ) {
		// If the map is enabled, we draw the basic map plane lighted. Then draw the model shadows.
		// The shadows are the tricky part: one matrix is used to transform the vertices to the ground
		// plane, and the other matrix is used to transform the vertices to texture coordinates.
		// Shaders make this much, much, much easier.

		// -------- Ground plane lighted -------- //
#ifdef ENGINE_RENDER_MAP
		map->GenerateSeenUnseen();
		map->DrawSeen();
#endif

		// -------- Shadow casters/ground plane ---------- //
		// Set up the planar projection matrix, with a little z offset
		// to help with z resolution fighting.
		const float SHADOW_START_HEIGHT = 80.0f;
		const float SHADOW_END_HEIGHT   = SHADOW_START_HEIGHT + 5.0f;
		float shadowAmount = 1.0f;
		if ( camera.PosWC().y > SHADOW_START_HEIGHT ) {
			shadowAmount = 1.0f - ( camera.PosWC().y - SHADOW_START_HEIGHT ) / ( SHADOW_END_HEIGHT - SHADOW_START_HEIGHT );
		}
		if ( shadowAmount > 0.0f ) {
#ifdef ENGINE_RENDER_SHADOWS
			CompositingShader shadowShader;
			shadowShader.SetTexture0( map->BackgroundTexture() );
			shadowShader.SetTexture1( map->LightMapTexture() );

			// The shadow matrix pushes in a depth. Its the depth<0 that allows the GL_LESS
			// test for the shadow write, below.
			PushShadowSwizzleMatrix( &shadowShader );

			// Just computes how dark the shadow is.
			LightGroundPlane( map->DayTime() ? DAY_TIME : NIGHT_TIME, IN_SHADOW, shadowAmount, &color );
			shadowShader.SetColor( color );

			renderQueue->Submit(	&shadowShader,
									RenderQueue::MODE_PLANAR_SHADOW,
									0,
									Model::MODEL_NO_SHADOW );

			shadowShader.PopMatrix( GPUShader::MODELVIEW_MATRIX );
			shadowShader.PopTextureMatrix( 3 );
		}
#endif
		{
			LightGroundPlane( map->DayTime() ? DAY_TIME : NIGHT_TIME, OPEN_LIGHT, 0, &color );

			float ave = 0.5f*((color.r + color.g + color.b)*0.333f);
			Color4F c = { ave, ave, ave, 1.0f };
#ifdef ENGINE_RENDER_MAP
			map->DrawPastSeen( c );
#endif
		}

#ifdef ENGINE_RENDER_MAP
		map->DrawOverlay( Map::LAYER_UNDER_LOW );
		map->DrawUnseen();
		map->DrawOverlay( Map::LAYER_UNDER_HIGH );
#endif
	}

	// -------- Models ---------- //
#ifdef ENGINE_RENDER_MODELS
	{
		if ( iMap ) {
			mapItemShader.SetTexture1( iMap->LightFogMapTexture() );
			mapBlendItemShader.SetTexture1( iMap->LightFogMapTexture() );
			
			PushLightSwizzleMatrix( &mapItemShader );

			renderQueue->Submit( 0, 0, Model::MODEL_OWNED_BY_MAP, 0 );
			lightShader.PopTextureMatrix( 2 );
		}
		// Render everything NOT in the map.
		renderQueue->Submit( 0, 0, 0, Model::MODEL_OWNED_BY_MAP );
	}
#endif

	if ( map )
		map->DrawOverlay( Map::LAYER_OVER );
	renderQueue->Clear();
}


void Engine::CalcLights( DayNight dayNight, Color4F* ambient, Vector4F* dir, Color4F* diffuse )
{
	ambient->Set( AMBIENT, AMBIENT, AMBIENT, 1.0f );
	diffuse->Set( DIFFUSE, DIFFUSE, DIFFUSE, 1.0f );
	if ( dayNight == NIGHT_TIME ) {
		diffuse->r *= EL_NIGHT_RED;
		diffuse->g *= EL_NIGHT_GREEN;
		diffuse->b *= EL_NIGHT_BLUE;
	}
	dir->Set( lightDirection.x, lightDirection.y, lightDirection.z, 0 );	// '0' in last term is parallel
}
Exemple #17
0
void PlantScript::DoTick(U32 delta)
{
	// We need process at a steady rate so that
	// the time between ticks is constant.
	// This is performance regressive, so something
	// to keep an eye on.
	static const int MAP2 = MAX_MAP_SIZE*MAX_MAP_SIZE;
	static const int DELTA = 100*1000;	// How frequenty to tick a given plant
	static const int N_PER_MSEC = MAP2 / DELTA;
	static const int GROWTH_CHANCE = 8;
	static const int PRIME = 1553;

	static const float SHADE_EFFECT = 0.7f;

	int n = N_PER_MSEC * delta;
	Weather* weather = Weather::Instance();

	WorldMap* worldMap = context->worldMap;
	Rectangle2I bounds = worldMap->Bounds();
	bounds.Outset(-1);	// edge of map: don't want to tap over the edge.

	const Vector3F& light = context->engine->lighting.direction;
	const float		norm = Max(fabs(light.x), fabs(light.z));
	Vector2I		lightTap = { int(LRintf(light.x / norm)), int(LRintf(light.z / norm)) };
	Census*			census = &context->chitBag->census;

	for (int i = 0; i < n; ++i) {
		index += PRIME;

		int x = IndexToMapX(index);
		int y = IndexToMapY(index);

		const WorldGrid& wg = worldMap->GetWorldGrid(x, y);
		if (!wg.Plant()) continue;

		Vector2I pos2i = { x, y };
		Vector2F pos2f = ToWorld2F(pos2i);

		// --- Light Tap --- //
		const float	height = PlantScript::PlantRes(wg.Plant() - 1, wg.PlantStage())->AABB().SizeY();
		const float	rainBase		= weather->RainFraction(pos2f.x, pos2f.y);
		const float sunBase			= (1.0f - rainBase);
		const float temperatureBase	= weather->Temperature(pos2f.x, pos2f.y);

		float rain			= rainBase;
		float sun			= sunBase;
		float temperature	= temperatureBase;
		float growth		= 1.0f;

		Vector2I tap = pos2i + lightTap;

		// Check for something between us and the light.
		const WorldGrid& wgTap = worldMap->GetWorldGrid(tap);
		float tapHeight = float(wgTap.RockHeight());
		if (wgTap.PlantStage()) {
			tapHeight = PlantScript::PlantRes(wgTap.Plant() - 1, wgTap.PlantStage())->AABB().SizeY();
		}

		if (tapHeight > (height + 0.1f)) {
			// in shade
			sun *= SHADE_EFFECT;
			temperature *= SHADE_EFFECT;
		}

		// ---- Adjacent --- //
		static const int NADJ = 4;
		static const Vector2I check[NADJ] = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
		int same = 0;
		for (int i = 0; i<NADJ; ++i) {
			tap = pos2i + check[i];
			const WorldGrid& wgAdj = worldMap->GetWorldGrid(tap.x, tap.y);
			if (wgAdj.Plant() == wg.Plant()) {
				++same;
			}

			if (wgAdj.RockHeight()) {
				// Water or rock runoff increase water.
				rain += 0.25f * rainBase;
			}
			if (wgAdj.IsFluid()) {
				rain += 0.25f;	// just a lot of water.
			}
			if (wgAdj.IsWater()) {
				rain += 0.25f;	// more water
				temperature = Mean(0.5f, temperature); // moderate temperature
			}
		}
		// Nutrient depletion? Too packed in?
		if (same == NADJ) {
			growth *= 0.25f;
		}

		// Are we under water?
		float fluidHeight = wg.FluidHeight();
		if (fluidHeight > 0.01f) {
			// Any amount of water goes to rain 100%
			rain = 1.0f;
			// not sure what to do with temp...assume a little cooler?
			temperature *= 0.8f;

			// blocks light...
			float sizeY = PlantScript::PlantRes(wg.Plant() - 1, wg.PlantStage())->AABB().SizeY();
			if (fluidHeight > sizeY)
				sun = 0;
			else if (fluidHeight > sizeY * 0.5f)
				sun = sun * (1.0f - fluidHeight / sizeY);
		}

		rain = Clamp(rain, 0.0f, 1.0f);
		temperature = Clamp(temperature, 0.0f, 1.0f);
		sun = Clamp(sun, 0.0f, 1.0f);

		// ------- calc ------- //
		Vector3F actual = { sun, rain, temperature };
		Vector3F optimal = { 0.5f, 0.5f, 0.5f };

		const GameItem* item = PlantScript::PlantDef(wg.Plant() - 1);
		item->keyValues.Get(ISC::sun, &optimal.x);
		item->keyValues.Get(ISC::rain, &optimal.y);
		item->keyValues.Get(ISC::temp, &optimal.z);

		float distance = (optimal - actual).Length();
		distance = distance / growth;

		const float GROW = Lerp(0.2f, 0.1f, (float)wg.PlantStage() / (float)(MAX_PLANT_STAGES - 1));
		const float DIE = 0.4f;
		float seconds = float(DELTA) / 1000.0f;

		if (distance < GROW) {
			// Heal.
			float hp = HP_PER_SECOND*seconds;
			DamageDesc heal( -hp, 0 );
			worldMap->VoxelHit(pos2i, heal);

			// Grow
			int nStage = wg.IsFlower() ? PLANT_BLOCKING_STAGE : MAX_PLANT_STAGES;

			if (wg.HPFraction() > 0.8f) {
				if (wg.PlantStage() < (nStage - 1)) {
					int hp = wg.HP();
					worldMap->SetPlant(pos2i.x, pos2i.y, wg.Plant(), wg.PlantStage() + 1);
					worldMap->SetWorldGridHP(pos2i.x, pos2i.y, hp);
				}
				if (random.Rand(GROWTH_CHANCE) < wg.PlantStage()) {
					// Number range reflects wind direction.
					int dx = -1 + random.Rand(4);	// [-1,2]
					int dy = -1 + random.Rand(3);	// [-1,1]

					// Remember that create plant will favor creating
					// existing plants, so we don't need to specify
					// what to create.
					Sim* sim = context->chitBag->GetSim();
					GLASSERT(sim);
					sim->CreatePlant(pos2i.x + dx, pos2i.y + dy, -1);
				}
			}
			int stage = wg.PlantStage();	// 0-3
			CoreScript* cs = CoreScript::GetCore(ToSector(pos2i));
			// Totally "what feels right in world gen" constant in the random.Rand()
			if ((census->wildFruit < MAX_WILD_FRUIT) && cs && (!cs->InUse()) && int(random.Rand(200)) < (stage*stage)) {
				context->chitBag->NewWildFruit(pos2i);
			}
		}
		else if (distance > DIE) {
			DamageDesc dd(HP_PER_SECOND * seconds, 0);
			worldMap->VoxelHit(pos2i, dd);
			if (wg.HP() == 0) {
				worldMap->SetPlant(pos2i.x, pos2i.y, 0, 0);
			}
		}
	}
}
double EvalBuildingScript::EvalIndustrial( bool debugLog )
{
	Chit* building = ParentChit();

	int hitB = 0, hitIBuilding = 0, hitNBuilding = 0, hitWater = 0, hitPlant = 0, hitRock = 0, hitWaterfall = 0;
	int hitShrub = 0;	// doesn't terminate a ray.


	if (lastEval == 0 || (Context()->chitBag->AbsTime() - lastEval) > 2000) {
		lastEval = Context()->chitBag->AbsTime();

		GameItem* item = building->GetItem();
		GLASSERT(item);
		reachable = true;

		IString consume = item->keyValues.GetIString(ISC::zone);
		if (consume.empty()) {
			eval = 0;
			return eval;
		}
		MapSpatialComponent* msc = GET_SUB_COMPONENT(building, SpatialComponent, MapSpatialComponent);
		GLASSERT(msc);
		if (!msc) {
			eval = 0;
			return eval;
		}
		Rectangle2I porch = msc->PorchPos();

		static const int RAD = 4;

		Rectangle2I bounds = porch;
		if (porch.min.x == 0) {	// shouldn't happen, but be sure.
			GLASSERT(0);
			eval = 0;
			return eval;	
		}

		bounds.Outset(RAD);
		Vector2I sector = ToSector(building->Position());

		WorldMap* worldMap = Context()->worldMap;
		Rectangle2I mapBounds = worldMap->Bounds();
		if (!mapBounds.Contains(bounds)) {
			eval = 0;
			return eval;	// not worth dealing with edge of world
		}

		// Check if we can go from the core to the porch.
		// And make sure the core is inUse!
		CoreScript* cs = CoreScript::GetCore(ToSector(porch.min));
		if (!cs || !cs->InUse())
			reachable = false;
		if (reachable) {
			const SectorData& sd = worldMap->GetSectorData(ToSector(porch.min));
			reachable = worldMap->CalcPath(ToWorld2F(sd.core), ToWorld2F(porch.min), 0, 0, false);
		}

		CChitArray arr;
		BuildingFilter buildingFilter;

		const FluidSim* fluidSim = Context()->physicsSims->GetFluidSim(sector);
		bool hasWaterfalls = fluidSim->NumWaterfalls() > 0;

		LumosChitBag* chitBag = Context()->chitBag;
		Rectangle2IEdgeIterator it(bounds);

		while (!it.Done()) {
			Vector2I pos = { it.Pos().x >= porch.max.x ? porch.max.x : porch.min.x,
							 it.Pos().y >= porch.max.y ? porch.max.y : porch.min.y };

			LineWalk walk(pos.x, pos.y, it.Pos().x, it.Pos().y);
			walk.Step();	// ignore where we are standing.

			while ( !walk.Done() ) {	// non-intuitive iterator. See linewalk docs.
				// - building
				// - plant
				// - ice
				// - rock
				// - waterfall
				// - water

				// Buildings. Can be 2x2. Extend out beyond current check.
				bool hitBuilding = false;
				Vector2I p = walk.P();
				// Don't count self as a hit, but stops the ray cast.
				// Also, use a larger radius because buildings can be 2x2
				chitBag->QuerySpatialHash(&arr, ToWorld2F(p), 0.8f, 0, &buildingFilter);
				for (int i = 0; i < arr.Size(); ++i) {
					if (arr[i] != building) {
						MapSpatialComponent* buildingMSC = GET_SUB_COMPONENT(arr[i], SpatialComponent, MapSpatialComponent);
						GLASSERT(buildingMSC);
						if (buildingMSC->Bounds().Contains(p)) {
							hitBuilding = true;
							double thisSys = arr[i]->GetItem()->GetBuildingIndustrial();

							hitB++;
							if (thisSys <= -0.5) hitNBuilding++;
							if (thisSys >= 0.5)  hitIBuilding++;

							break;
						}
					}
				}
				if (hitBuilding) break;
				
				const WorldGrid& wg = worldMap->GetWorldGrid(p.x, p.y);
				if (wg.Plant()) {
//					int type = wg.Plant() - 1;
					int stage = wg.PlantStage();

					if (stage >= 2) {
						++hitPlant;
						break;
					}
					else {
						hitShrub++;
					}
				}

				if (wg.RockHeight()) {
					++hitRock;
					break;
				}
				if (wg.IsWater()) {
					++hitWater;
					break;
				}

				Rectangle2I wb;
				wb.min = wb.max = p;
				if (hasWaterfalls && fluidSim->ContainsWaterfalls(wb)) {
					++hitWaterfall;
					break;
				}
				walk.Step();
			}
			it.Next();
		}

		// Note rock/ice isn't counted either way.
		int natural = hitNBuilding
			+ hitWater
			+ hitPlant
			+ 10 * hitWaterfall
			+ hitShrub / 4;				// small plants don't add to rRays, so divide is okay.
		int industrial = hitIBuilding;
		int nRays = hitNBuilding + hitWater + hitPlant + hitWaterfall + hitIBuilding;

		eval = 0;
		if (nRays) {
			// With this system, that one ray (say from a distillery to plant) can be
			// hugely impactful. This may need tweaking:
			if (nRays < 2)
				nRays = 2;
			eval = double(industrial - natural) / double(nRays);
		}
		eval = Clamp(eval, -1.0, 1.0);

		if (debugLog) {
			Vector2I pos = ToWorld2I(building->Position());
			GLOUTPUT(("Building %s at %d,%d eval=%.2f nRays=%d \n  hit: Build=%d (I=%d N=%d) water=%d plant=%d rock=%d\n",
				building->GetItem()->Name(),
				pos.x, pos.y,
				eval,
				nRays,
				hitB, hitIBuilding, hitNBuilding, hitWater, hitPlant, hitRock));
			(void)pos;
		}
	}
	return eval;
}