示例#1
0
/*
============
GetFirstBlockingObstacle
============
*/
bool GetFirstBlockingObstacle( const obstacle_t *obstacles, int numObstacles, int skipObstacle, const idVec2 &startPos, const idVec2 &delta, float &blockingScale, int &blockingObstacle, int &blockingEdgeNum ) {
	int i, edgeNums[2];
	float dist, scale1, scale2;
	idVec2 bounds[2];

	// get bounds for the current movement delta
	bounds[0] = startPos - idVec2( CM_BOX_EPSILON, CM_BOX_EPSILON );
	bounds[1] = startPos + idVec2( CM_BOX_EPSILON, CM_BOX_EPSILON );
	bounds[FLOATSIGNBITNOTSET(delta.x)].x += delta.x;
	bounds[FLOATSIGNBITNOTSET(delta.y)].y += delta.y;

	// test for obstacles blocking the path
	blockingScale = idMath::INFINITY;
	dist = delta.Length();
	for ( i = 0; i < numObstacles; i++ ) {
		if ( i == skipObstacle ) {
			continue;
		}
		if ( bounds[0].x > obstacles[i].bounds[1].x || bounds[0].y > obstacles[i].bounds[1].y ||
				bounds[1].x < obstacles[i].bounds[0].x || bounds[1].y < obstacles[i].bounds[0].y ) {
			continue;
		}
		if ( obstacles[i].winding.RayIntersection( startPos, delta, scale1, scale2, edgeNums ) ) {
			if ( scale1 < blockingScale && scale1 * dist > -0.01f && scale2 * dist > 0.01f ) {
				blockingScale = scale1;
				blockingObstacle = i;
				blockingEdgeNum = edgeNums[0];
			}
		}
	}
	return ( blockingScale < 1.0f );
}
/*
========================
idSWFSpriteInstance::SetMoveToScale
========================
*/
bool idSWFSpriteInstance::UpdateMoveToScale( float speed ) {

	if ( parent == NULL ) {
		return false;
	}
	swfDisplayEntry_t * thisDisplayEntry = parent->FindDisplayEntry( depth );
	if ( thisDisplayEntry == NULL || thisDisplayEntry->spriteInstance != this ) {
		idLib::Warning( "SetMoveToScale: Couldn't find our display entry in our parents display list" );
		return false;
	}

	swfMatrix_t & matrix = thisDisplayEntry->matrix;
	float xscale = matrix.Scale( idVec2( 1.0f, 0.0f ) ).Length() * 100.0f; 
	float yscale = matrix.Scale( idVec2( 0.0f, 1.0f ) ).Length() * 100.0f;
	
	float toX = xscale;
	if ( moveToXScale >= 0.0f ) {
		toX = moveToXScale * 100.0f;
	}

	float toY = yscale;
	if ( moveToYScale >= 0.0f ) {
		toY = moveToYScale * 100.0f;
	}

	int rXTo = idMath::Ftoi( toX + 0.5f );
	int rYTo = idMath::Ftoi( toY + 0.5f );
	int rXScale = idMath::Ftoi( xscale + 0.5f );
	int rYScale = idMath::Ftoi( yscale + 0.5f );

	if ( rXTo == rXScale && rYTo == rYScale ) {
		return false;
	}

	float newXScale = xscale;
	float newYScale = yscale;

	if ( rXTo != rXScale && toX >= 0.0f ) {
		if ( toX < xscale ) {
			newXScale -= speed;
			newXScale = idMath::ClampFloat( toX, 100.0f, newXScale );
		} else if ( toX > xscale ) {
			newXScale += speed;
			newXScale = idMath::ClampFloat( 0.0f, toX, newXScale );
		}
	}

	if ( rYTo != rYScale && toY >= 0.0f ) {
		if ( toY < yscale ) {
			newYScale -= speed;
			newYScale = idMath::ClampFloat( toY, 100.0f, newYScale );
		} else if ( toY > yscale ) {
			newYScale += speed;
			newYScale = idMath::ClampFloat( 0.0f, toY, newYScale );
		}
	}

	SetScale( newXScale, newYScale );
	return true;
}
/*
========================
idSWFSpriteInstance::SetScale
========================
*/
void idSWFSpriteInstance::SetScale( float x, float y ) {
	if ( parent == NULL ) {
		return;
	}
	swfDisplayEntry_t * thisDisplayEntry = parent->FindDisplayEntry( depth );
	if ( thisDisplayEntry == NULL || thisDisplayEntry->spriteInstance != this ) {
		idLib::Warning( "scale: Couldn't find our display entry in our parents display list" );
		return;
	}

	float newScale = x / 100.0f;
	// this is done funky to maintain the current rotation
	idVec2 currentScale = thisDisplayEntry->matrix.Scale( idVec2( 1.0f, 0.0f ) );
	if ( currentScale.Normalize() == 0.0f ) {
		thisDisplayEntry->matrix.xx = newScale;
		thisDisplayEntry->matrix.yx = 0.0f;
	} else {
		thisDisplayEntry->matrix.xx = currentScale.x * newScale;
		thisDisplayEntry->matrix.yx = currentScale.y * newScale;
	}
	
	newScale = y / 100.0f;
	// this is done funky to maintain the current rotation
	currentScale = thisDisplayEntry->matrix.Scale( idVec2( 0.0f, 1.0f ) );
	if ( currentScale.Normalize() == 0.0f ) {
		thisDisplayEntry->matrix.yy = newScale;
		thisDisplayEntry->matrix.xy = 0.0f;
	} else {
		thisDisplayEntry->matrix.yy = currentScale.y * newScale;
		thisDisplayEntry->matrix.xy = currentScale.x * newScale;
	}
}
示例#4
0
/*
============
OptimizePath
============
*/
int OptimizePath( const pathNode_t *root, const pathNode_t *leafNode, const obstacle_t *obstacles, int numObstacles, idVec2 optimizedPath[MAX_OBSTACLE_PATH] ) {
	int i, numPathPoints, edgeNums[2];
	const pathNode_t *curNode, *nextNode;
	idVec2 curPos, curDelta, bounds[2];
	float scale1, scale2, curLength;

	optimizedPath[0] = root->pos;
	numPathPoints = 1;

	for ( nextNode = curNode = root; curNode != leafNode; curNode = nextNode ) {

		for ( nextNode = leafNode; nextNode->parent != curNode; nextNode = nextNode->parent ) {

			// can only take shortcuts when going from one object to another
			if ( nextNode->obstacle == curNode->obstacle ) {
				continue;
			}

			curPos = curNode->pos;
			curDelta = nextNode->pos - curPos;
			curLength = curDelta.Length();

			// get bounds for the current movement delta
			bounds[0] = curPos - idVec2( CM_BOX_EPSILON, CM_BOX_EPSILON );
			bounds[1] = curPos + idVec2( CM_BOX_EPSILON, CM_BOX_EPSILON );
			bounds[FLOATSIGNBITNOTSET(curDelta.x)].x += curDelta.x;
			bounds[FLOATSIGNBITNOTSET(curDelta.y)].y += curDelta.y;

			// test if the shortcut intersects with any obstacles
			for ( i = 0; i < numObstacles; i++ ) {
				if ( bounds[0].x > obstacles[i].bounds[1].x || bounds[0].y > obstacles[i].bounds[1].y ||
						bounds[1].x < obstacles[i].bounds[0].x || bounds[1].y < obstacles[i].bounds[0].y ) {
					continue;
				}
				if ( obstacles[i].winding.RayIntersection( curPos, curDelta, scale1, scale2, edgeNums ) ) {
					if ( scale1 >= 0.0f && scale1 <= 1.0f && ( i != nextNode->obstacle || scale1 * curLength < curLength - 0.5f ) ) {
						break;
					}
					if ( scale2 >= 0.0f && scale2 <= 1.0f && ( i != nextNode->obstacle || scale2 * curLength < curLength - 0.5f ) ) {
						break;
					}
				}
			}
			if ( i >= numObstacles ) {
				break;
			}
		}

		// store the next position along the optimized path
		optimizedPath[numPathPoints++] = nextNode->pos;
	}

	return numPathPoints;
}
/*
========================
idSWFSpriteInstance::SetRotation
========================
*/
void idSWFSpriteInstance::SetRotation( float rot ) {
	if ( parent == NULL ) {
		return;
	}
	swfDisplayEntry_t * thisDisplayEntry = parent->FindDisplayEntry( depth );
	if ( thisDisplayEntry == NULL || thisDisplayEntry->spriteInstance != this ) {
		idLib::Warning( "_rotation: Couldn't find our display entry in our parents display list" );
		return;
	}

	swfMatrix_t & matrix = thisDisplayEntry->matrix;
	float xscale = matrix.Scale( idVec2( 1.0f, 0.0f ) ).Length(); 
	float yscale = matrix.Scale( idVec2( 0.0f, 1.0f ) ).Length(); 

	float s, c;
	idMath::SinCos( DEG2RAD( rot ), s, c ); 
	matrix.xx = c * xscale;
	matrix.yx = s * xscale;
	matrix.xy = -s * yscale;
	matrix.yy = c * yscale;
}
示例#6
0
/*
================
sdAdEntity::UpdateImpression
================
*/
void sdAdEntity::UpdateImpression( impressionInfo_t& impression, const renderView_t& view, const sdBounds2D& viewPort ) {
	assert( adSurface );

	sdWorldToScreenConverter converter( view );

	sdBounds2D	screenBounds;
	idVec2		screenCenter;

	const idBounds& surfaceBounds	= adSurface->geometry->bounds;
	idVec3 sufaceCenter				= renderEntity.origin + ( surfaceBounds.GetCenter() * renderEntity.axis );

	impression.screenWidth	= viewPort.GetWidth();
	impression.screenHeight	= viewPort.GetHeight();

	converter.SetExtents( idVec2( impression.screenWidth, impression.screenHeight ) );
	converter.Transform( surfaceBounds, renderEntity.axis, renderEntity.origin, screenBounds );
	converter.Transform( sufaceCenter, screenCenter );

	sdBounds2D clippedImpressionBounds;
	idFrustum f;
	f.SetAxis( view.viewaxis );
	f.SetOrigin( view.vieworg );
	float dNear = 0.0f;
	float dFar	= MAX_WORLD_SIZE;
	float dLeft = idMath::Tan( DEG2RAD( view.fov_x * 0.5f ) ) * dFar;
	float dUp	= idMath::Tan( DEG2RAD( view.fov_y * 0.5f ) ) * dFar;
	f.SetSize( dNear, dFar, dLeft, dUp );
	sdWorldToScreenConverter::TransformClipped( surfaceBounds, renderEntity.axis, renderEntity.origin, clippedImpressionBounds, f, idVec2( impression.screenWidth, impression.screenHeight ) );
	impression.size		= ( clippedImpressionBounds.GetMaxs() - clippedImpressionBounds.GetMins() ).Length();

	// Angle calculation	
	idVec3 objectNormal = adSurfaceNormal * renderEntity.axis;

	impression.angle	= objectNormal * -view.viewaxis[ 0 ];

	// Offscreen determination
	impression.inView	= true;

	// If the center point is off screen, and if either of the two bounding points are offscreen
	// then the entire object is considered to be offscreen
	if ( !viewPort.ContainsPoint( screenCenter ) ) {
		if ( !viewPort.ContainsPoint( screenBounds.GetMins() ) || !viewPort.ContainsPoint( screenBounds.GetMaxs() ) ) {
			impression.inView = false;
		}
	}

	lastImpression = impression;
}
示例#7
0
void rvGEWorkspace::UpdateCursor ( float x, float y )
{
	idVec2						point;
	rvGESelectionMgr::EHitTest	type;

	// First convert the worspace coord to a window coord
	point = WorkspaceToWindow ( idVec2( x, y ) );

	// See if it hits anything
	type = mSelections.HitTest ( point.x, point.y );

	// If it hits something then use it to update the cursor
	if ( rvGESelectionMgr::HT_NONE != type )
	{
		UpdateCursor ( type );
	}
	else
	{
		SetCursor ( LoadCursor ( NULL, MAKEINTRESOURCE(IDC_ARROW ) ) );
	}
}
示例#8
0
bool MeshLoaderB3D::ReadVrts() {
	const int max_tex_coords = 3;
	int flags, tex_coord_sets, tex_coord_set_size;

	_file->ReadInt(flags);
	_file->ReadInt(tex_coord_sets);
	_file->ReadInt(tex_coord_set_size);

	if (tex_coord_sets >= max_tex_coords || tex_coord_set_size >= 4) // Something is wrong 
	{
		Sys_Printf("tex_coord_sets or tex_coord_set_size too big");
		return false;
	}

	//------ Allocate Memory, for speed -----------//

	int sizeOfVertex = 3;
	bool hasNormal = false;
	bool hasVertexColors = false;
	if (flags & 1) {
		hasNormal = true;
		sizeOfVertex += 3;
	}
	if (flags & 2) {
		sizeOfVertex += 4;
		hasVertexColors=true;
	}

	sizeOfVertex += tex_coord_sets*tex_coord_set_size;
	unsigned int size = _stack[_stackIndex-1] - _file->Tell();
	//106407 16800
	unsigned int numVertex = size / sizeof(float) ;
	numVertex /= sizeOfVertex;

	srfTriangles_t* tri = R_AllocStaticTriSurf();
	R_AllocStaticTriSurfVerts(tri, numVertex);
	tri->numVerts = numVertex;

	int idx = 0;
	while( CheckSize()) {
		float color[4]={1.0f, 1.0f, 1.0f, 1.0f};
		_file->ReadVec3(tri->verts[idx].xyz);

		if (flags & 1) {
			_file->ReadVec3(tri->verts[idx].normal);
		}
		if (flags & 2) {
			_file->ReadFloat(color[0]);
			_file->ReadFloat(color[1]);
			_file->ReadFloat(color[2]);
			_file->ReadFloat(color[3]);
		}
		float u, v;
		for (int i = 0; i < tex_coord_sets; ++i) {
			//for (int j = 0; j < tex_coord_set_size; ++j)
			{
				_file->ReadFloat(u);
				_file->ReadFloat(v);
				//v = 1 - v;
			}
		}

		tri->verts[idx].st = idVec2(u, v);
		idx++;
	}

	modelSurface_t modelSurf;
	modelSurf.id = _surfaces.Size();
	modelSurf.shader = declManager->FindMaterial("textures/ninja/ninja");
	modelSurf.geometry = tri;
	_surfaces.Append(modelSurf);

	return true;
}
示例#9
0
/*
============
GetObstacles
============
*/
int GetObstacles( const idPhysics *physics, const idAAS *aas, const idEntity *ignore, int areaNum, const idVec3 &startPos, const idVec3 &seekPos, obstacle_t *obstacles, int maxObstacles, idBounds &clipBounds ) {
	int i, j, numListedClipModels, numObstacles, numVerts, clipMask, blockingObstacle, blockingEdgeNum;
	int wallEdges[MAX_AAS_WALL_EDGES], numWallEdges, verts[2], lastVerts[2], nextVerts[2];
	float stepHeight, headHeight, blockingScale, min, max;
	idVec3 seekDelta, silVerts[32], start, end, nextStart, nextEnd;
	idVec2 expBounds[2], edgeDir, edgeNormal, nextEdgeDir, nextEdgeNormal, lastEdgeNormal;
	idVec2 obDelta;
	idPhysics *obPhys;
	idBox box;
	idEntity *obEnt;
	idClipModel *clipModel;
	idClipModel *clipModelList[ MAX_GENTITIES ];

	numObstacles = 0;

	seekDelta = seekPos - startPos;
	expBounds[0] = physics->GetBounds()[0].ToVec2() - idVec2( CM_BOX_EPSILON, CM_BOX_EPSILON );
	expBounds[1] = physics->GetBounds()[1].ToVec2() + idVec2( CM_BOX_EPSILON, CM_BOX_EPSILON );

	physics->GetAbsBounds().AxisProjection( -physics->GetGravityNormal(), stepHeight, headHeight );
	stepHeight += aas->GetSettings()->maxStepHeight;

	// clip bounds for the obstacle search space
	clipBounds[0] = clipBounds[1] = startPos;
	clipBounds.AddPoint( seekPos );
	clipBounds.ExpandSelf( MAX_OBSTACLE_RADIUS );
	clipMask = physics->GetClipMask();

	// find all obstacles touching the clip bounds
	numListedClipModels = gameLocal.clip.ClipModelsTouchingBounds( clipBounds, clipMask, clipModelList, MAX_GENTITIES );

	for ( i = 0; i < numListedClipModels && numObstacles < MAX_OBSTACLES; i++ ) {
		clipModel = clipModelList[i];
		obEnt = clipModel->GetEntity();

		if ( !clipModel->IsTraceModel() ) {
			continue;
		}

		if ( obEnt->IsType( idActor::Type ) ) {
			obPhys = obEnt->GetPhysics();
			// ignore myself, my enemy, and dead bodies
			if ( ( obPhys == physics ) || ( obEnt == ignore ) || ( obEnt->health <= 0 ) ) {
				continue;
			}
			// if the actor is moving
			idVec3 v1 = obPhys->GetLinearVelocity();
			if ( v1.LengthSqr() > Square( 10.0f ) ) {
				idVec3 v2 = physics->GetLinearVelocity();
				if ( v2.LengthSqr() > Square( 10.0f ) ) {
					// if moving in about the same direction
					if ( v1 * v2 > 0.0f ) {
						continue;
					}
				}
			}
		} else if ( obEnt->IsType( idMoveable::Type ) ) {
			// moveables are considered obstacles
		} else {
			// ignore everything else
			continue;
		}

		// check if we can step over the object
		clipModel->GetAbsBounds().AxisProjection( -physics->GetGravityNormal(), min, max );
		if ( max < stepHeight || min > headHeight ) {
			// can step over this one
			continue;
		}

		// project a box containing the obstacle onto the floor plane
		box = idBox( clipModel->GetBounds(), clipModel->GetOrigin(), clipModel->GetAxis() );
		numVerts = box.GetParallelProjectionSilhouetteVerts( physics->GetGravityNormal(), silVerts );

		// create a 2D winding for the obstacle;
		obstacle_t &obstacle = obstacles[numObstacles++];
		obstacle.winding.Clear();
		for ( j = 0; j < numVerts; j++ ) {
			obstacle.winding.AddPoint( silVerts[j].ToVec2() );
		}

		if ( ai_showObstacleAvoidance.GetBool() ) {
			for ( j = 0; j < numVerts; j++ ) {
				silVerts[j].z = startPos.z;
			}
			for ( j = 0; j < numVerts; j++ ) {
				gameRenderWorld->DebugArrow( colorWhite, silVerts[j], silVerts[(j+1)%numVerts], 4 );
			}
		}

		// expand the 2D winding for collision with a 2D box
		obstacle.winding.ExpandForAxialBox( expBounds );
		obstacle.winding.GetBounds( obstacle.bounds );
		obstacle.entity = obEnt;
	}

	// if there are no dynamic obstacles the path should be through valid AAS space
	if ( numObstacles == 0 ) {
		return 0;
	}

	// if the current path doesn't intersect any dynamic obstacles the path should be through valid AAS space
	if ( PointInsideObstacle( obstacles, numObstacles, startPos.ToVec2() ) == -1 ) {
		if ( !GetFirstBlockingObstacle( obstacles, numObstacles, -1, startPos.ToVec2(), seekDelta.ToVec2(), blockingScale, blockingObstacle, blockingEdgeNum ) ) {
			return 0;
		}
	}

	// create obstacles for AAS walls
	if ( aas ) {
		float halfBoundsSize = ( expBounds[ 1 ].x - expBounds[ 0 ].x ) * 0.5f;

		numWallEdges = aas->GetWallEdges( areaNum, clipBounds, TFL_WALK, wallEdges, MAX_AAS_WALL_EDGES );
		aas->SortWallEdges( wallEdges, numWallEdges );

		lastVerts[0] = lastVerts[1] = 0;
		lastEdgeNormal.Zero();
		nextVerts[0] = nextVerts[1] = 0;
		for ( i = 0; i < numWallEdges && numObstacles < MAX_OBSTACLES; i++ ) {
            aas->GetEdge( wallEdges[i], start, end );
			aas->GetEdgeVertexNumbers( wallEdges[i], verts );
			edgeDir = end.ToVec2() - start.ToVec2();
			edgeDir.Normalize();
			edgeNormal.x = edgeDir.y;
			edgeNormal.y = -edgeDir.x;
			if ( i < numWallEdges-1 ) {
				aas->GetEdge( wallEdges[i+1], nextStart, nextEnd );
				aas->GetEdgeVertexNumbers( wallEdges[i+1], nextVerts );
				nextEdgeDir = nextEnd.ToVec2() - nextStart.ToVec2();
				nextEdgeDir.Normalize();
				nextEdgeNormal.x = nextEdgeDir.y;
				nextEdgeNormal.y = -nextEdgeDir.x;
			}

			obstacle_t &obstacle = obstacles[numObstacles++];
			obstacle.winding.Clear();
			obstacle.winding.AddPoint( end.ToVec2() );
			obstacle.winding.AddPoint( start.ToVec2() );
			obstacle.winding.AddPoint( start.ToVec2() - edgeDir - edgeNormal * halfBoundsSize );
			obstacle.winding.AddPoint( end.ToVec2() + edgeDir - edgeNormal * halfBoundsSize );
			if ( lastVerts[1] == verts[0] ) {
				obstacle.winding[2] -= lastEdgeNormal * halfBoundsSize;
			} else {
				obstacle.winding[1] -= edgeDir;
			}
			if ( verts[1] == nextVerts[0] ) {
				obstacle.winding[3] -= nextEdgeNormal * halfBoundsSize;
			} else {
				obstacle.winding[0] += edgeDir;
			}
			obstacle.winding.GetBounds( obstacle.bounds );
			obstacle.entity = NULL;

			memcpy( lastVerts, verts, sizeof( lastVerts ) );
			lastEdgeNormal = edgeNormal;
		}
	}

	// show obstacles
	if ( ai_showObstacleAvoidance.GetBool() ) {
		for ( i = 0; i < numObstacles; i++ ) {
			obstacle_t &obstacle = obstacles[i];
			for ( j = 0; j < obstacle.winding.GetNumPoints(); j++ ) {
				silVerts[j].ToVec2() = obstacle.winding[j];
				silVerts[j].z = startPos.z;
			}
			for ( j = 0; j < obstacle.winding.GetNumPoints(); j++ ) {
				gameRenderWorld->DebugArrow( colorGreen, silVerts[j], silVerts[(j+1)%obstacle.winding.GetNumPoints()], 4 );
			}
		}
	}

	return numObstacles;
}
/*
=============================
idGameBustOutWindow::UpdateBall
=============================
*/
void idGameBustOutWindow::UpdateBall( void ) {
	int ballnum,i,j;
	bool playSoundBounce = false;
	bool playSoundBrick = false;
	static int bounceChannel = 1;

	if ( ballsInPlay == 0 ) {
		return;
	}

	for ( ballnum = 0; ballnum < balls.Num(); ballnum++ ) {
		BOEntity *ball = balls[ballnum];

		// Check for ball going below screen, lost ball
		if ( ball->position.y > 480.f ) {
			ball->removed = true;
			continue;
		}

		// Check world collision
		if ( ball->position.y < 20 && ball->velocity.y < 0 ) {
			ball->velocity.y = -ball->velocity.y;

			// Increase ball speed when it hits ceiling
			if ( !ballHitCeiling ) {
				ballSpeed *= 1.25f;
				ballHitCeiling = true;
			}
			playSoundBounce = true;
		}

		if ( ball->position.x > 608 && ball->velocity.x > 0 ) {
			ball->velocity.x = -ball->velocity.x;
			playSoundBounce = true;
		} else if ( ball->position.x < 8 && ball->velocity.x < 0 ) {
			ball->velocity.x = -ball->velocity.x;
			playSoundBounce = true;
		}

		// Check for Paddle collision
		idVec2 ballCenter = ball->position + idVec2( BALL_RADIUS, BALL_RADIUS );
		collideDir_t collision = paddle->checkCollision( ballCenter, ball->velocity );

		if ( collision == COLLIDE_UP ) {
			if ( ball->velocity.y > 0 ) {
				idVec2	paddleVec( paddleVelocity*2, 0 );
				float	centerX;

				if ( bigPaddleTime > gui->GetTime() ) {
					centerX = paddle->x + 80.f;
				} else {
					centerX = paddle->x + 48.f;
				}

				ball->velocity.y = -ball->velocity.y;

				paddleVec.x += (ball->position.x - centerX) * 2;

				ball->velocity += paddleVec;
				ball->velocity.NormalizeFast();
				ball->velocity *= ballSpeed;

				playSoundBounce = true;
			}
		} else if ( collision == COLLIDE_LEFT || collision == COLLIDE_RIGHT ) {
			if ( ball->velocity.y > 0 ) {
				ball->velocity.x = -ball->velocity.x;
				playSoundBounce = true;
			}
		}

		collision = COLLIDE_NONE;

		// Check for collision with bricks
		for ( i=0; i<BOARD_ROWS; i++ ) {
			int num = board[i].Num();

			for ( j=0; j<num; j++ ) {
				BOBrick *brick = (board[i])[j];

				collision = brick->checkCollision( ballCenter, ball->velocity );
				if ( collision ) {
					// Now break the brick if there was a collision
					brick->isBroken = true;
					brick->ent->fadeOut = true;

					if ( brick->powerup > POWERUP_NONE ) {
						CreatePowerup( brick );
					}

					numBricks--;
					gameScore += 100;
					updateScore = true;

					// Go ahead an forcibly remove the last brick, no fade
					if ( numBricks == 0 ) {
						brick->ent->removed = true;
					}
					board[i].Remove( brick );
					break;
				}
			}

			if ( collision ) {
				playSoundBrick = true;
				break;
			}
		}

		if ( collision == COLLIDE_DOWN || collision == COLLIDE_UP ) {
			ball->velocity.y *= -1;
		} else if ( collision == COLLIDE_LEFT || collision == COLLIDE_RIGHT ) {
			ball->velocity.x *= -1;
		}

		if ( playSoundBounce ) {
			session->sw->PlayShaderDirectly( "arcade_ballbounce", bounceChannel );
		} else if ( playSoundBrick ) {
			session->sw->PlayShaderDirectly( "arcade_brickhit", bounceChannel );
		}

		if ( playSoundBounce || playSoundBrick ) {
			bounceChannel++;
			if ( bounceChannel == 4 ) {
				bounceChannel = 1;
			}
		}
	}

	// Check to see if any balls were removed from play
	for ( ballnum=0; ballnum<balls.Num(); ballnum++ ) {
		if ( balls[ballnum]->removed ) {
			ballsInPlay--;
			balls.RemoveIndex( ballnum );
		}
	}

	// If all the balls were removed, update the game accordingly
	if ( ballsInPlay == 0 ) {
		if ( ballsRemaining == 0 ) {
			gameOver = true;

			// Game Over sound
			session->sw->PlayShaderDirectly( "arcade_sadsound", S_UNIQUE_CHANNEL );
		} else {
			ballsRemaining--;

			// Ball was lost, but game is not over
			session->sw->PlayShaderDirectly( "arcade_missedball", S_UNIQUE_CHANNEL );
		}

		ClearPowerups();
		updateScore = true;
	}
}
示例#11
0
/*
==============
sdDeployMaskEditSession::UpdateProjection
==============
*/
void sdDeployMaskEditSession::UpdateProjection( const idVec3& position ) {
	if ( !decalMaterial ) {
		return;
	}

	if ( decalHandle == -1 ) {
		decalHandle = gameLocal.RegisterLoggedDecal( decalMaterial );
	}
	gameLocal.ResetLoggedDecal( decalHandle );

	gameDecalInfo_t* decalInfo = gameLocal.GetLoggedDecal( decalHandle );

	sdDeployMaskInstance* mask = GetMask( position );
	const sdHeightMapInstance* heightMap = GetHeightMap( position );

	if ( mask != NULL && mask->IsValid() && heightMap != NULL ) {
		sdDeployMask::extents_t extents;
		GetExtents( position, *mask, extents );

		float depth = 512.0f;

		int maxX, maxY;
		mask->GetDimensions( maxX, maxY );

		sdDeployMask::extents_t expandedExtents;

		expandedExtents.minx = Max( 0, extents.minx - 2 );
		expandedExtents.miny = Max( 0, extents.miny - 2 );

		expandedExtents.maxx = Min( maxX, extents.maxx + 2 );
		expandedExtents.maxy = Min( maxY, extents.maxy + 2 );

		idList< const idMaterial* > megaTextureMaterials;
		const idStrList& megaTextureMaterialNames = gameLocal.GetMapInfo().GetMegatextureMaterials();
		for ( int i = 0; i < megaTextureMaterialNames.Num(); i++ ) {
			megaTextureMaterials.Append( declHolder.FindMaterial( megaTextureMaterialNames[ i ] ) );
		}

		idFixedWinding winding;
		
		int spawnID = WORLD_SPAWN_ID;

		for ( int i = expandedExtents.minx; i <= expandedExtents.maxx; i++ ) {
			for ( int j = expandedExtents.miny; j <= expandedExtents.maxy; j++ ) {
				gameDecalInfo_t* info = decalInfo;
				if ( !info ) {
					continue;
				}

				sdDeployMask::extents_t localExtents;
				localExtents.minx = i;
				localExtents.maxx = i;
				localExtents.miny = j;
				localExtents.maxy = j;

				idBounds bounds;
				mask->GetBounds( localExtents, bounds, heightMap );

				idVec3 top = bounds.GetCenter();
				top[ 2 ] = bounds.GetMaxs()[ 2 ];				

				deployResult_t localResult = mask->IsValid( localExtents );

				idVec4 localColor;
				switch ( localResult ) {
					case DR_CLEAR:
						localColor = colorGreen;
						break;
					default:
					case DR_FAILED:
						localColor = colorDkRed;
						break;
				}

				if ( !( ( i >= extents.minx ) && ( i <= extents.maxx ) && ( j >= extents.miny ) && ( j <= extents.maxy ) ) ) {
					localColor.x *= 0.3f;
					localColor.y *= 0.3f;
					localColor.z *= 0.3f;
				}

				winding.Clear();
				winding += idVec5( idVec3( bounds.GetMins()[ 0 ], bounds.GetMins()[ 1 ], bounds.GetMins()[ 2 ] - depth ), idVec2( 0.0f, 0.0f ) );
				winding += idVec5( idVec3( bounds.GetMins()[ 0 ], bounds.GetMaxs()[ 1 ], bounds.GetMins()[ 2 ] - depth ), idVec2( 0.0f, 1.0f ) );
				winding += idVec5( idVec3( bounds.GetMaxs()[ 0 ], bounds.GetMaxs()[ 1 ], bounds.GetMins()[ 2 ] - depth ), idVec2( 1.0f, 1.0f ) );
				winding += idVec5( idVec3( bounds.GetMaxs()[ 0 ], bounds.GetMins()[ 1 ], bounds.GetMins()[ 2 ] - depth ), idVec2( 1.0f, 0.0f ) );

				gameRenderWorld->AddToProjectedDecal( winding, top + idVec3( 0, 0, 64.f + depth ), true, localColor, info->renderEntity.hModel, spawnID, megaTextureMaterials.Begin(), megaTextureMaterials.Num() );
			}
		}
	}
}
示例#12
0
/*
===================
idSWF::HitTest
===================
*/
idSWFScriptObject* idSWF::HitTest( idSWFSpriteInstance* spriteInstance, const swfRenderState_t& renderState, int x, int y, idSWFScriptObject* parentObject )
{

	if( spriteInstance->parent != NULL )
	{
		swfDisplayEntry_t* thisDisplayEntry = spriteInstance->parent->FindDisplayEntry( spriteInstance->depth );
		if( thisDisplayEntry->cxf.mul.w + thisDisplayEntry->cxf.add.w < 0.001f )
		{
			return NULL;
		}
	}
	
	if( !spriteInstance->isVisible )
	{
		return NULL;
	}
	
	if( spriteInstance->scriptObject->HasValidProperty( "onRelease" )
			|| spriteInstance->scriptObject->HasValidProperty( "onPress" )
			|| spriteInstance->scriptObject->HasValidProperty( "onRollOver" )
			|| spriteInstance->scriptObject->HasValidProperty( "onRollOut" )
			|| spriteInstance->scriptObject->HasValidProperty( "onDrag" )
	  )
	{
		parentObject = spriteInstance->scriptObject;
	}
	
	// rather than returning the first object we find, we actually want to return the last object we find
	idSWFScriptObject* returnObject = NULL;
	
	float xOffset = spriteInstance->xOffset;
	float yOffset = spriteInstance->yOffset;
	
	for( int i = 0; i < spriteInstance->displayList.Num(); i++ )
	{
		const swfDisplayEntry_t& display = spriteInstance->displayList[i];
		idSWFDictionaryEntry* entry = FindDictionaryEntry( display.characterID );
		if( entry == NULL )
		{
			continue;
		}
		swfRenderState_t renderState2;
		renderState2.matrix = display.matrix.Multiply( renderState.matrix );
		renderState2.ratio = display.ratio;
		
		if( entry->type == SWF_DICT_SPRITE )
		{
			idSWFScriptObject* object = HitTest( display.spriteInstance, renderState2, x, y, parentObject );
			if( object != NULL && object->Get( "_visible" ).ToBool() )
			{
				returnObject = object;
			}
		}
		else if( entry->type == SWF_DICT_SHAPE && ( parentObject != NULL ) )
		{
			idSWFShape* shape = entry->shape;
			for( int i = 0; i < shape->fillDraws.Num(); i++ )
			{
				const idSWFShapeDrawFill& fill = shape->fillDraws[i];
				for( int j = 0; j < fill.indices.Num(); j += 3 )
				{
					idVec2 xy1 = renderState2.matrix.Transform( fill.startVerts[fill.indices[j + 0]] );
					idVec2 xy2 = renderState2.matrix.Transform( fill.startVerts[fill.indices[j + 1]] );
					idVec2 xy3 = renderState2.matrix.Transform( fill.startVerts[fill.indices[j + 2]] );
					
					idMat3 edgeEquations;
					edgeEquations[0].Set( xy1.x + xOffset, xy1.y + yOffset, 1.0f );
					edgeEquations[1].Set( xy2.x + xOffset, xy2.y + yOffset, 1.0f );
					edgeEquations[2].Set( xy3.x + xOffset, xy3.y + yOffset, 1.0f );
					edgeEquations.InverseSelf();
					
					idVec3 p( x, y, 1.0f );
					idVec3 signs = p * edgeEquations;
					
					bool bx = signs.x > 0;
					bool by = signs.y > 0;
					bool bz = signs.z > 0;
					if( bx == by && bx == bz )
					{
						// point inside
						returnObject = parentObject;
					}
				}
			}
		}
		else if( entry->type == SWF_DICT_MORPH )
		{
			// FIXME: this should be roughly the same as SWF_DICT_SHAPE
		}
		else if( entry->type == SWF_DICT_TEXT )
		{
			// FIXME: this should be roughly the same as SWF_DICT_SHAPE
		}
		else if( entry->type == SWF_DICT_EDITTEXT )
		{
			idSWFScriptObject* editObject = NULL;
			
			if( display.textInstance->scriptObject.HasProperty( "onRelease" ) || display.textInstance->scriptObject.HasProperty( "onPress" ) )
			{
				// if the edit box itself can be clicked, then we want to return it when it's clicked on
				editObject = &display.textInstance->scriptObject;
			}
			else if( parentObject != NULL )
			{
				// otherwise, we want to return the parent object
				editObject = parentObject;
			}
			
			if( editObject == NULL )
			{
				continue;
			}
			
			if( display.textInstance->text.IsEmpty() )
			{
				continue;
			}
			
			const idSWFEditText* shape = entry->edittext;
			const idSWFEditText* text = display.textInstance->GetEditText();
			float textLength = display.textInstance->GetTextLength();
			
			float lengthDiff = fabs( shape->bounds.br.x - shape->bounds.tl.x ) - textLength;
			
			idVec3 tl;
			idVec3 tr;
			idVec3 br;
			idVec3 bl;
			
			float xOffset = spriteInstance->xOffset;
			float yOffset = spriteInstance->yOffset;
			
			float topOffset = 0.0f;
			
			if( text->align == SWF_ET_ALIGN_LEFT )
			{
				tl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x  + xOffset, shape->bounds.tl.y + topOffset + yOffset ) );
				tr.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x - lengthDiff + xOffset, shape->bounds.tl.y + topOffset + yOffset ) );
				br.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x - lengthDiff + xOffset, shape->bounds.br.y + topOffset + yOffset ) );
				bl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x + xOffset, shape->bounds.br.y + topOffset + yOffset ) );
			}
			else if( text->align == SWF_ET_ALIGN_RIGHT )
			{
				tl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x + lengthDiff + xOffset, shape->bounds.tl.y + topOffset + yOffset ) );
				tr.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x + xOffset, shape->bounds.tl.y + topOffset + yOffset ) );
				br.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x + xOffset, shape->bounds.br.y + topOffset + yOffset ) );
				bl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x + lengthDiff + xOffset, shape->bounds.br.y + topOffset + yOffset ) );
			}
			else if( text->align == SWF_ET_ALIGN_CENTER )
			{
				float middle = ( ( shape->bounds.br.x + xOffset ) + ( shape->bounds.tl.x + xOffset ) ) / 2.0f;
				tl.ToVec2() = renderState2.matrix.Transform( idVec2( middle - ( textLength / 2.0f ), shape->bounds.tl.y + topOffset + yOffset ) );
				tr.ToVec2() = renderState2.matrix.Transform( idVec2( middle + ( textLength / 2.0f ), shape->bounds.tl.y + topOffset + yOffset ) );
				br.ToVec2() = renderState2.matrix.Transform( idVec2( middle + ( textLength / 2.0f ), shape->bounds.br.y + topOffset + yOffset ) );
				bl.ToVec2() = renderState2.matrix.Transform( idVec2( middle - ( textLength / 2.0f ), shape->bounds.br.y + topOffset + yOffset ) );
			}
			else
			{
				tl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x + xOffset, shape->bounds.tl.y + topOffset + yOffset ) );
				tr.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x + xOffset, shape->bounds.tl.y + topOffset + yOffset ) );
				br.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.br.x + xOffset, shape->bounds.br.y + topOffset + yOffset ) );
				bl.ToVec2() = renderState2.matrix.Transform( idVec2( shape->bounds.tl.x + xOffset, shape->bounds.br.y + topOffset + yOffset ) );
			}
			
			tl.z = 1.0f;
			tr.z = 1.0f;
			br.z = 1.0f;
			bl.z = 1.0f;
			
			idMat3 edgeEquations;
			edgeEquations[0] = tl;
			edgeEquations[1] = tr;
			edgeEquations[2] = br;
			edgeEquations.InverseSelf();
			
			idVec3 p( x, y, 1.0f );
			idVec3 signs = p * edgeEquations;
			
			bool bx = signs.x > 0;
			bool by = signs.y > 0;
			bool bz = signs.z > 0;
			if( bx == by && bx == bz )
			{
				// point inside top right triangle
				returnObject = editObject;
			}
			
			edgeEquations[0] = tl;
			edgeEquations[1] = br;
			edgeEquations[2] = bl;
			edgeEquations.InverseSelf();
			signs = p * edgeEquations;
			
			bx = signs.x > 0;
			by = signs.y > 0;
			bz = signs.z > 0;
			if( bx == by && bx == bz )
			{
				// point inside bottom left triangle
				returnObject = editObject;
			}
		}
	}
	return returnObject;
}
示例#13
0
/*
==============
sdDeployMenu::Update
==============
*/
void sdDeployMenu::Update( void ) {
	sdHudModule::Update();

	idPlayer* localPlayer = gameLocal.GetLocalPlayer();

	if ( !deployableObject || !localPlayer ) {
		return;
	}

	deployResult_t result = localPlayer->GetDeployResult( position, deployableObject );

	if ( result != DR_OUT_OF_RANGE ) {

		if ( deployableRenderEntityHandle < 0 ) {
			deployableRenderEntityHandle = gameRenderWorld->AddEntityDef( &deployableRenderEntity );
		}

		idVec4 color;
		switch ( deployableState ) {
			case DR_CLEAR:
				color = colorGreen;
				break;
			case DR_WARNING:
				color = colorOrange;
				break;
			case DR_CONDITION_FAILED:
				color = colorWhite;
				break;
			default:
			case DR_FAILED:
				color = colorDkRed;
				break;
		}

		if ( decalHandle == -1 ) {
			decalHandle = gameLocal.RegisterLoggedDecal( decalMaterial );
			lastExpandedExtents.minx = -1;
		}
		if ( decalHandleOuter == -1 ) {
			decalHandleOuter = gameLocal.RegisterLoggedDecal( decalMaterialOuter );
			lastExpandedExtents.minx = -1;
		}
		if ( decalHandleArrows == -1 ) {
			decalHandleArrows = gameLocal.RegisterLoggedDecal( decalMaterialArrows );
		}
		gameLocal.ResetLoggedDecal( decalHandleArrows );

		gameDecalInfo_t* decalInfo = gameLocal.GetLoggedDecal( decalHandle );
		gameDecalInfo_t* decalInfoOuter = gameLocal.GetLoggedDecal( decalHandleOuter );
		gameDecalInfo_t* decalInfoArrows = gameLocal.GetLoggedDecal( decalHandleArrows );

		if ( !deployableObject->AllowRotation() ) {
			rotation = 0.0f;
		}

		idMat3 rotationStart;
		idAngles::YawToMat3( rotation, rotationStart );

		const sdPlayZone* playZone = gameLocal.GetPlayZone( position, sdPlayZone::PZF_DEPLOYMENT );

		const sdPlayZone* playZoneHeight = gameLocal.GetPlayZone( position, sdPlayZone::PZF_HEIGHTMAP );
		const sdHeightMapInstance* heightMap = NULL;
		if ( playZoneHeight ) {
			heightMap = &playZoneHeight->GetHeightMap();
		}

		const sdDeployMaskInstance* mask = NULL;

		if ( playZone ) {
			mask = playZone->GetMask( deployableObject->GetDeploymentMask() );
		}

		if ( mask && mask->IsValid() && heightMap && heightMap->IsValid() ) {
			idBounds bounds( position );
			bounds.ExpandSelf( deployableObject->GetObjectSize() );

			sdDeployMask::extents_t extents;
			mask->CoordsForBounds( bounds, extents );

			idBounds mainBounds;
			mask->GetBounds( extents, mainBounds, heightMap );

			float depth = 512.0f;
			idVec3 top;

			idBounds moveArrowBounds = mainBounds;
			idBounds rotateArrowBounds = mainBounds;

			// straight arrows
			moveArrowBounds.GetMaxs().y = moveArrowBounds.GetCenter().y;
			moveArrowBounds.GetMins().y += 0.25f * ( mainBounds.GetMaxs().y - mainBounds.GetMins().y );
			moveArrowBounds.GetMaxs().y += 0.25f * ( mainBounds.GetMaxs().y - mainBounds.GetMins().y );
			moveArrowBounds.GetMins().x = moveArrowBounds.GetCenter().x;
			{
				idVec3 center = moveArrowBounds.GetCenter();
				moveArrowBounds.TranslateSelf( -center );
				for ( int index = 0; index < 3; index++ ) {
					moveArrowBounds.GetMins()[ index ] *= 0.25f;
					moveArrowBounds.GetMaxs()[ index ] *= 0.25f;
				}
				moveArrowBounds.TranslateSelf( center );
			}

			// rotate arrows
			rotateArrowBounds.GetMins().x = rotateArrowBounds.GetCenter().x;
			{
				idVec3 center = rotateArrowBounds.GetCenter();
				rotateArrowBounds.TranslateSelf( -center );
				for ( int index = 0; index < 3; index++ ) {
					rotateArrowBounds.GetMins()[ index ] *= 0.2f;
					rotateArrowBounds.GetMaxs()[ index ] *= 0.2f;
				}
				rotateArrowBounds.TranslateSelf( center );
			}

			top = mainBounds.GetCenter();
			top[ 2 ] = mainBounds.GetMaxs()[ 2 ];

			idList< const idMaterial* > megaTextureMaterials;
			const idStrList& megaTextureMaterialNames = gameLocal.GetMapInfo().GetMegatextureMaterials();
			for ( int i = 0; i < megaTextureMaterialNames.Num(); i++ ) {
				megaTextureMaterials.Append( declHolder.FindMaterial( megaTextureMaterialNames[ i ] ) );
			}

			idFixedWinding winding;

			int spawnID = WORLD_SPAWN_ID;

			if ( GetDeployMode() ) {
				// rotate mode

				idMat3 rotationOffset;
				idAngles::YawToMat3( 120.0f, rotationOffset );

				// forward arrow
				winding += idVec5( idVec3( moveArrowBounds.GetMins().x, moveArrowBounds.GetMins().y, moveArrowBounds.GetMins().z - depth ), idVec2( 0.5f, 0.0f ) );
				winding += idVec5( idVec3( moveArrowBounds.GetMins().x, moveArrowBounds.GetMaxs().y, moveArrowBounds.GetMins().z - depth ), idVec2( 0.5f, 0.5f ) );
				winding += idVec5( idVec3( moveArrowBounds.GetMaxs().x, moveArrowBounds.GetMaxs().y, moveArrowBounds.GetMins().z - depth ), idVec2( 1.0f, 0.5f ) );
				winding += idVec5( idVec3( moveArrowBounds.GetMaxs().x, moveArrowBounds.GetMins().y, moveArrowBounds.GetMins().z - depth ), idVec2( 1.0f, 0.0f ) );

				winding.Rotate( mainBounds.GetCenter(), rotationStart );
				idVec3 offset = rotationStart[ 0 ] * ( 16.f * idMath::Sin( ( gameLocal.time / 500.f ) ) );
				for ( int i = 0; i < winding.GetNumPoints(); i++ ) {
					winding[ i ].ToVec3() += offset;
				}
				gameRenderWorld->AddToProjectedDecal( winding, top + idVec3( 0.0f, 0.0f, 64.0f + depth ), true, idVec4( 0.87f, 0.59f, 0.f, 1.f ), decalInfoArrows->renderEntity.hModel, spawnID, megaTextureMaterials.Begin(), megaTextureMaterials.Num() );

				// rotate arrows
				winding.Clear();
				winding += idVec5( idVec3( rotateArrowBounds.GetMins().x, rotateArrowBounds.GetMins().y, rotateArrowBounds.GetMins().z - depth ), idVec2( 0.0f, 0.0f ) );
				winding += idVec5( idVec3( rotateArrowBounds.GetMins().x, rotateArrowBounds.GetMaxs().y, rotateArrowBounds.GetMins().z - depth ), idVec2( 0.0f, 1.0f ) );
				winding += idVec5( idVec3( rotateArrowBounds.GetMaxs().x, rotateArrowBounds.GetMaxs().y, rotateArrowBounds.GetMins().z - depth ), idVec2( 0.5f, 1.0f ) );
				winding += idVec5( idVec3( rotateArrowBounds.GetMaxs().x, rotateArrowBounds.GetMins().y, rotateArrowBounds.GetMins().z - depth ), idVec2( 0.5f, 0.0f ) );

				winding.Rotate( mainBounds.GetCenter(), rotationStart * rotationOffset );
				gameRenderWorld->AddToProjectedDecal( winding, top + idVec3( 0.0f, 0.0f, 64.0f + depth ), true, colorWhite, decalInfoArrows->renderEntity.hModel, spawnID, megaTextureMaterials.Begin(), megaTextureMaterials.Num() );

				winding.Rotate( mainBounds.GetCenter(), rotationOffset );
				gameRenderWorld->AddToProjectedDecal( winding, top + idVec3( 0.0f, 0.0f, 64.0f + depth ), true, colorWhite, decalInfoArrows->renderEntity.hModel, spawnID, megaTextureMaterials.Begin(), megaTextureMaterials.Num() );
			} else {
				// move mode

				idMat3 rotationOffset;
				idAngles::YawToMat3( 90.0f, rotationOffset );

				winding += idVec5( idVec3( moveArrowBounds.GetMins().x, moveArrowBounds.GetMins().y, moveArrowBounds.GetMins().z - depth ), idVec2( 0.5f, 0.0f ) );
				winding += idVec5( idVec3( moveArrowBounds.GetMins().x, moveArrowBounds.GetMaxs().y, moveArrowBounds.GetMins().z - depth ), idVec2( 0.5f, 0.5f ) );
				winding += idVec5( idVec3( moveArrowBounds.GetMaxs().x, moveArrowBounds.GetMaxs().y, moveArrowBounds.GetMins().z - depth ), idVec2( 1.0f, 0.5f ) );
				winding += idVec5( idVec3( moveArrowBounds.GetMaxs().x, moveArrowBounds.GetMins().y, moveArrowBounds.GetMins().z - depth ), idVec2( 1.0f, 0.0f ) );

				gameRenderWorld->AddToProjectedDecal( winding, top + idVec3( 0.0f, 0.0f, 64.0f + depth ), true, colorWhite, decalInfoArrows->renderEntity.hModel, spawnID, megaTextureMaterials.Begin(), megaTextureMaterials.Num() );

				winding.Rotate( mainBounds.GetCenter(), rotationOffset );
				gameRenderWorld->AddToProjectedDecal( winding, top + idVec3( 0.0f, 0.0f, 64.0f + depth ), true, colorWhite, decalInfoArrows->renderEntity.hModel, spawnID, megaTextureMaterials.Begin(), megaTextureMaterials.Num() );

				winding.Rotate( mainBounds.GetCenter(), rotationOffset );
				gameRenderWorld->AddToProjectedDecal( winding, top + idVec3( 0.0f, 0.0f, 64.0f + depth ), true, colorWhite, decalInfoArrows->renderEntity.hModel, spawnID, megaTextureMaterials.Begin(), megaTextureMaterials.Num() );

				winding.Rotate( mainBounds.GetCenter(), rotationOffset );
				gameRenderWorld->AddToProjectedDecal( winding, top + idVec3( 0.0f, 0.0f, 64.0f + depth ), true, colorWhite, decalInfoArrows->renderEntity.hModel, spawnID, megaTextureMaterials.Begin(), megaTextureMaterials.Num() );
			}

			// the grid
			int maxX, maxY;
			mask->GetDimensions( maxX, maxY );

			sdDeployMask::extents_t expandedExtents;

			expandedExtents.minx = Max( 0, extents.minx - 2 );
			expandedExtents.miny = Max( 0, extents.miny - 2 );

			expandedExtents.maxx = Min( maxX, extents.maxx + 2 );
			expandedExtents.maxy = Min( maxY, extents.maxy + 2 );

			int w = expandedExtents.maxx - expandedExtents.minx + 1;
			int h = expandedExtents.maxx - expandedExtents.minx + 1;
			int terrIdx = 0;
			bool regen = false;

			if ( ( w * h ) > sizeof( lastTerritory ) / sizeof( lastTerritory[0] ) ) {
				common->Warning( "test territory size too large" );
				return;
			}

			for ( int i = expandedExtents.minx; i <= expandedExtents.maxx; i++ ) {
				for ( int j = expandedExtents.miny; j <= expandedExtents.maxy; j++ ) {
					gameDecalInfo_t* info = ( ( i >= extents.minx ) && ( i <= extents.maxx ) && ( j >= extents.miny ) && ( j <= extents.maxy ) ) ? decalInfo : decalInfoOuter;
					if ( !info ) {
						continue;
					}

					sdDeployMask::extents_t localExtents;
					localExtents.minx = i;
					localExtents.maxx = i;
					localExtents.miny = j;
					localExtents.maxy = j;

					mask->GetBounds( localExtents, bounds, heightMap );

					top = bounds.GetCenter();
					top[ 2 ] = bounds.GetMaxs()[ 2 ];

					deployResult_t localResult = localPlayer->CheckBoundsForDeployment( localExtents, *mask, deployableObject, playZoneHeight );

					bool hasTerritory;
					if ( localResult == DR_CLEAR ) {
						hasTerritory = gameLocal.TerritoryForPoint( top, localPlayer->GetGameTeam(), true ) != NULL;
					} else {
						hasTerritory = false;
					}
					if ( hasTerritory != lastTerritory[ terrIdx ] ) {
						regen = true;
					}
					lastTerritory[ terrIdx++ ] = hasTerritory;
				}
			}

			if ( expandedExtents.minx != lastExpandedExtents.minx || expandedExtents.maxx != lastExpandedExtents.maxx ||
				expandedExtents.miny != lastExpandedExtents.miny || expandedExtents.maxy != lastExpandedExtents.maxy || regen ) {
				gameLocal.ResetLoggedDecal( decalHandle );
				gameLocal.ResetLoggedDecal( decalHandleOuter );

				lastExpandedExtents = expandedExtents;

				terrIdx = 0;
				for ( int i = expandedExtents.minx; i <= expandedExtents.maxx; i++ ) {
					for ( int j = expandedExtents.miny; j <= expandedExtents.maxy; j++ ) {
						gameDecalInfo_t* info = ( ( i >= extents.minx ) && ( i <= extents.maxx ) && ( j >= extents.miny ) && ( j <= extents.maxy ) ) ? decalInfo : decalInfoOuter;
						if ( !info ) {
							continue;
						}

						sdDeployMask::extents_t localExtents;
						localExtents.minx = i;
						localExtents.maxx = i;
						localExtents.miny = j;
						localExtents.maxy = j;

						mask->GetBounds( localExtents, bounds, heightMap );

						top = bounds.GetCenter();
						top[ 2 ] = bounds.GetMaxs()[ 2 ];

						//bool hasTerritory = gameLocal.TerritoryForPoint( top, localPlayer->GetGameTeam(), true ) != NULL;
						deployResult_t localResult = localPlayer->CheckBoundsForDeployment( localExtents, *mask, deployableObject, playZoneHeight );

						idVec4 localColor;
						switch ( localResult ) {
							case DR_CLEAR:
								if ( !lastTerritory[ terrIdx ] ) {
									localColor = colorOrange;
								} else {
									localColor = colorGreen;
								}
								break;
							case DR_WARNING:
								localColor = colorOrange;
								break;
							case DR_FAILED:
								localColor = colorDkRed;
								break;
						}

						winding.Clear();
						winding += idVec5( idVec3( bounds.GetMins()[ 0 ], bounds.GetMins()[ 1 ], bounds.GetMins()[ 2 ] - depth ), idVec2( 0.0f, 0.0f ) );
						winding += idVec5( idVec3( bounds.GetMins()[ 0 ], bounds.GetMaxs()[ 1 ], bounds.GetMins()[ 2 ] - depth ), idVec2( 0.0f, 1.0f ) );
						winding += idVec5( idVec3( bounds.GetMaxs()[ 0 ], bounds.GetMaxs()[ 1 ], bounds.GetMins()[ 2 ] - depth ), idVec2( 1.0f, 1.0f ) );
						winding += idVec5( idVec3( bounds.GetMaxs()[ 0 ], bounds.GetMins()[ 1 ], bounds.GetMins()[ 2 ] - depth ), idVec2( 1.0f, 0.0f ) );

	//					gameRenderWorld->DebugBounds( localColor, bounds );
	//					gameRenderWorld->DrawText( va( "%i %i", i, j ), top, 0.5, colorWhite, gameLocal.GetLocalPlayer()->GetRenderView()->viewaxis );

						gameRenderWorld->AddToProjectedDecal( winding, top + idVec3( 0, 0, 64.f + depth ), true, localColor, info->renderEntity.hModel, spawnID, megaTextureMaterials.Begin(), megaTextureMaterials.Num() );
						terrIdx++;
					}
				}
			}
		} else {
			gameLocal.ResetLoggedDecal( decalHandle );
			gameLocal.ResetLoggedDecal( decalHandleOuter );
			lastExpandedExtents.minx = -1;
		}

		idVec3 modelPos = position;
		if ( playZoneHeight ) {
			const sdHeightMapInstance& heightMap = playZoneHeight->GetHeightMap();
			if ( heightMap.IsValid() ) {
				modelPos.z = heightMap.GetHeight( modelPos );
			}
		}

		sdDeployRequest::UpdateRenderEntity( deployableRenderEntity, color, modelPos );
		deployableRenderEntity.axis = rotationStart;
		gameRenderWorld->UpdateEntityDef( deployableRenderEntityHandle, &deployableRenderEntity );
	} else {
		if ( deployableRenderEntityHandle >= 0 ) {
			gameRenderWorld->FreeEntityDef( deployableRenderEntityHandle );
			deployableRenderEntityHandle = -1;
		}
		FreeDecals();
	}
}