bool SoulDestroyer::SearchForRandomPosition()
{
    Vector3 toSpawnPoint = ( m_pos - m_spawnPoint );
    toSpawnPoint.y = 0.0;
    double distToSpawnPoint = toSpawnPoint.Mag();
    double chanceOfReturn = ( distToSpawnPoint / m_roamRange );
    if( chanceOfReturn >= 1.0 || syncfrand(1.0) <= chanceOfReturn )
    {
        // We have strayed too far from our spawn point
        // So head back there now       
        Vector3 targetPos = m_spawnPoint;
        targetPos.y = g_app->m_location->m_landscape.m_heightMap->GetValue( targetPos.x, targetPos.z );
        targetPos.y += 100.0 + syncsfrand( 100.0 );
        
        Vector3 returnVector = ( targetPos - m_pos );
        returnVector.SetLength( 160.0 );
        m_targetPos = m_pos + returnVector;
    }
    else
    {
        double distance = 160.0;
        double angle = syncsfrand(2.0 * M_PI);

        m_targetPos = m_pos + Vector3( iv_sin(angle) * distance,
                                       0.0,
                                       iv_cos(angle) * distance );  
        m_targetPos.y = g_app->m_location->m_landscape.m_heightMap->GetValue( m_targetPos.x, m_targetPos.z );
        m_targetPos.y += (100.0 + syncsfrand( 100.0 ));        
    }
    
    return true;
}
Exemplo n.º 2
0
/**
*  @brief
*    Initializes a particle
*/
void PGLeaf::InitParticle(Particle &cParticle) const
{
	// Initialize all particles to a random but valued state
	cParticle.vPos.x = (Math::GetRandNegFloat()*Radius) + GetTransform().GetPosition().x;
	cParticle.vPos.z = (Math::GetRandNegFloat()*Radius) + GetTransform().GetPosition().z;
	cParticle.vPos.y = (Math::GetRandFloat()*Height) + GetTransform().GetPosition().y;
	Vector3 vN(Math::GetRandNegFloat(), Math::GetRandNegFloat(), Math::GetRandNegFloat());
	vN.SetLength(1.0f);
	if (!cParticle.pRot)
		cParticle.pRot = new Quaternion();
	EulerAngles::ToQuaternion(static_cast<float>(vN.x*Math::RadToDeg), static_cast<float>(vN.y*Math::RadToDeg), static_cast<float>(vN.z*Math::RadToDeg), *cParticle.pRot);
	cParticle.bDistorted  = true;
	cParticle.fCustom1    = cParticle.fSize = LeafSize + LeafSizeVariation*Math::GetRandNegFloat();
	cParticle.vVelocity.x = Math::GetRandNegFloat();
	cParticle.vVelocity.y = Math::GetRandNegFloat();
	cParticle.vVelocity.z = Math::GetRandNegFloat();
	cParticle.nCustom1    = ((static_cast<int>(Math::GetRandNegFloat()*255.0f*2.0f))<<8)|0xff;
	Vector3 vD = Vector3::UnitY.CrossProduct(vN);
	vD.SetLength(cParticle.fSize);
	cParticle.vDistortion.x = vD.x;
	cParticle.vDistortion.y = vD.y;
	cParticle.vDistortion.z = vD.z;
	cParticle.vFixPos.x = cParticle.vColor[0] = 0.8f;
	cParticle.vFixPos.y = cParticle.vColor[1] = 0.5f+Math::GetRandFloat()*0.5f;
	cParticle.vFixPos.z = cParticle.vColor[2] = 0.5f+Math::GetRandFloat()*0.5f;
	cParticle.vColor[3] = 1.0f;
}
bool SmokeMarker::Advance()
{
    for( int i = 0; i < 3; ++i )
    {
        Vector3 vel = g_upVector;
        vel.x += sfrand(0.4f);
        vel.z += sfrand(0.4f);
        vel.SetLength( 50.0f );

        float size = 200.0f + frand(100.0f);

        g_app->m_particleSystem->CreateParticle( m_pos, vel, Particle::TypeMissileTrail, size );
    }

    return false;
}
void AntHill::Damage ( float _damage )
{
    Building::Damage( _damage );

    if( m_health > 0 )
    {
        int healthBandBefore = int(m_health / 20.0f);
        m_health += _damage;
        int healthBandAfter = int(m_health / 20.0f);

        if( healthBandAfter != healthBandBefore )
        {
            Matrix34 mat( m_front, g_upVector, m_pos );
            g_explosionManager.AddExplosion( m_shape, mat, 1.0f - (float)m_health/100.0f );
            g_app->m_soundSystem->TriggerBuildingEvent( this, "Damage" );
        }

        if( m_health <= 0 )
        {
            Matrix34 mat( m_front, g_upVector, m_pos );
            g_explosionManager.AddExplosion( m_shape, mat );

            int numSpirits = m_numAntsInside + m_numSpiritsInside;
            for( int i = 0; i < numSpirits; ++i )
            {
                Vector3 pos = m_pos;
	            float radius = syncfrand(20.0f);
	            float theta = syncfrand(M_PI * 2);
    	        pos.x += radius * sinf(theta);
	            pos.z += radius * cosf(theta);

                Vector3 vel = ( pos - m_pos );
                vel.SetLength( syncfrand(50.0f) );

                g_app->m_location->SpawnSpirit( pos, vel, m_id.GetTeamId(), WorldObjectId() );
            }

            g_app->m_soundSystem->TriggerBuildingEvent( this, "Explode" );
            m_health = 0;
        }
    }
}
void Tripod::AdvanceAttack()
{
	START_PROFILE(g_app->m_profiler, "AdvanceAttack");


	// Consider mode switch
	float timeInAttack = g_gameTime - m_modeStartTime;
	if (timeInAttack > ATTACK_DURATION)
	{
		m_mode = ModePostAttack;
		m_modeStartTime = g_gameTime;
		END_PROFILE(g_app->m_profiler, "AdvanceAttack");
		return;
	}


//	m_up = CalcAttackUpVector();
	Vector3 right = m_up ^ g_upVector;
//	m_front = m_up ^ right;
//	m_front.Normalise();

	if (timeInAttack > 1.0f)
	{
		int t2 = (int)(timeInAttack * 10.0f);
		if (t2 & 1)
		{
			Vector3 toEnemy = m_attackTarget - m_pos;
			toEnemy.Normalise();
			float const speed = 80.0f;
			right.SetLength(4.0f);
			Vector3 pos = m_pos + right;
			toEnemy.x += syncsfrand(0.1f);
			toEnemy.z += syncsfrand(0.1f);
			g_app->m_location->FireLaser(pos + toEnemy * 5.0f, toEnemy * speed, m_id.GetTeamId());
			pos = m_pos - right;
			g_app->m_location->FireLaser(pos + toEnemy * 5.0f, toEnemy * speed, m_id.GetTeamId());
		}
	}

	END_PROFILE(g_app->m_profiler, "AdvanceAttack");
}
bool SoulDestroyer::AdvanceToTargetPosition()
{
    double amountToTurn = SERVER_ADVANCE_PERIOD * 2.0;
    Vector3 targetDir = (m_targetPos - m_pos).Normalise();
    if( !m_targetEntity.IsValid() )
    {
        Vector3 right1 = m_front ^ m_up;    
        targetDir.RotateAround( right1 * iv_sin(GetNetworkTime() * 6.0) * 1.5 );
    }

    // Look ahead to see if we're about to hit the ground
    Vector3 forwardPos = m_pos + targetDir * 50.0;
    double landHeight = g_app->m_location->m_landscape.m_heightMap->GetValue(forwardPos.x, forwardPos.z);
    if( forwardPos.y <= landHeight )
    {
        targetDir = g_upVector;
    }

    Vector3 actualDir = m_front * (1.0 - amountToTurn) + targetDir * amountToTurn;
    actualDir.Normalise();
    double speed = m_stats[StatSpeed];
    speed = 130.0;
    
    Vector3 oldPos = m_pos;
    Vector3 newPos = m_pos + actualDir * speed * SERVER_ADVANCE_PERIOD;
    landHeight = g_app->m_location->m_landscape.m_heightMap->GetValue( newPos.x, newPos.z );
    //newPos.y = max( newPos.y, landHeight );
    
    Vector3 moved = newPos - oldPos;
    if( moved.Mag() > speed * SERVER_ADVANCE_PERIOD ) moved.SetLength( speed * SERVER_ADVANCE_PERIOD );
    newPos = m_pos + moved;

    m_pos = newPos;       
    m_vel = ( m_pos - oldPos ) / SERVER_ADVANCE_PERIOD;
    m_front = actualDir;
                
    Vector3 right = m_front ^ g_upVector;
    m_up = right ^ m_front;
    
    return ( m_pos - m_targetPos ).Mag() < 40.0;
}
void LaserFence::Spark()
{
    Vector3 sparkPos = m_pos;
    sparkPos.y += frand( m_scale*50.0f );

    LaserFence *nextFence = (LaserFence *) g_app->m_location->GetBuilding( m_nextLaserFenceId );

    int numSparks = 5.0f + frand(5.0f);
    for( int i = 0; i < numSparks; ++i )
    {
        Vector3 particleVel;
        if( nextFence ) particleVel = ( m_pos - nextFence->m_pos ) ^ g_upVector;
        else            particleVel = Vector3( sfrand(10.0f), sfrand(5.0f), sfrand(10.0f) );

        particleVel.SetLength( 40.0f+frand(20.0f) );
        particleVel += Vector3( frand() * 20.0f, sfrand() * 20.0f, sfrand() * 20.0f );
        float size = 25.0f + frand(25.0f);
        g_app->m_particleSystem->CreateParticle( sparkPos, particleVel, Particle::TypeSpark, size );
    }

    g_app->m_soundSystem->TriggerBuildingEvent( this, "Spark" );
}
Exemplo n.º 8
0
/**
*  @brief
*    Called when the scene node needs to be updated
*/
void PGLeaf::OnUpdate()
{
	// If this scene node wasn't drawn at the last frame, we can skip some update stuff
	if ((GetFlags() & ForceUpdate) || m_bUpdate) {
		m_bUpdate = false;

		// If there are free particles, create new particles
		Particle *pParticle = AddParticle();
		while (pParticle) {
			InitParticle(*pParticle);

			// Next particle, please
			pParticle = AddParticle();
		}

		{ // Update particles
			float fTimeDiff = Timing::GetInstance()->GetTimeDifference();
			Iterator<Particle> cIterator = GetParticleIterator();
			while (cIterator.HasNext()) {
				Particle &cParticle = cIterator.Next();

				// Update forces
				// One: gravity
				cParticle.vVelocity[1] -= fTimeDiff*(cParticle.vPos[1]-FloorHeight)/5;

				// Two wind (dot wind vector normal)
				Vector3 vRot;
				EulerAngles::FromQuaternion(*cParticle.pRot, vRot.x, vRot.y, vRot.z);
				vRot.x *= static_cast<float>(Math::RadToDeg);
				vRot.y *= static_cast<float>(Math::RadToDeg);
				vRot.z *= static_cast<float>(Math::RadToDeg);
				cParticle.vVelocity += Wind.Get()*fTimeDiff*Math::Abs(vRot.DotProduct(Wind.Get()));

				// Four collision (just check whether leaf would end up on wrong side)
				if (cParticle.vPos.y + cParticle.vVelocity.y*fTimeDiff < FloorHeight) {
					// Random bouncy and some friction
					cParticle.vVelocity.y *= -1.0;
					cParticle.vVelocity.z *= Math::GetRandNegFloat();
					cParticle.vVelocity.x *= Math::GetRandNegFloat();
					cParticle.nCustom1     = ((static_cast<int>(Math::GetRandNegFloat()*255.0f*8.0f))<<8) | (cParticle.nCustom1&0xff);
				}

				// Five check whether particle has left area and can die... if it is outside, let it die
				float  fDistx		 = GetTransform().GetPosition().x - cParticle.vPos.x;
				float  fDisty		 = GetTransform().GetPosition().z - cParticle.vPos.z;
				float  fDistToCenter = fDistx*fDistx + fDisty*fDisty;
				uint32 nStart		 = cParticle.nCustom1 & 0xff;
				if (nStart < 0xff) { // It is being spawned
					nStart += static_cast<uint32>(fTimeDiff*512.0f + 0.5f);
					if (nStart > 0xff)
						nStart = 0xff; // Clamp
					cParticle.nCustom1 = (cParticle.nCustom1&0xffffff00)|nStart; // Write to lower 8 bits
				}

				if (fDistToCenter > Radius*Radius) {
					// Start dying... set alpha according to how far it is away
					fDistToCenter = 255 - (Math::Sqrt(fDistToCenter) - Radius)*3.0f;
					cParticle.fSize = static_cast<float>(static_cast<int>(fDistToCenter*(cParticle.nCustom1 & 0xff))>>8)/255*cParticle.fCustom1;
					if (cParticle.fSize < 0.2f)
						InitParticle(cParticle);
				} else {
					cParticle.fSize = static_cast<float>(cParticle.nCustom1 &0xff)/255*cParticle.fCustom1;
				}

				// Update position
				cParticle.vPos += cParticle.vVelocity*fTimeDiff*cParticle.fSize/2;

				// Update spin
				Vector3 vAxis = cParticle.vVelocity;
				vAxis.SetLength(1);

				// Transform normal and distortion
				vAxis *= ((static_cast<float>(cParticle.nCustom1>>8))/255.0f)*fTimeDiff/10;
				Quaternion qRotInc;
				EulerAngles::ToQuaternion(static_cast<float>(vAxis.x*Math::DegToRad), static_cast<float>(vAxis.y*Math::DegToRad), static_cast<float>(vAxis.z*Math::DegToRad), qRotInc);
				*cParticle.pRot *= qRotInc;

				Vector3 d = cParticle.vDistortion;
				d.SetLength(cParticle.fSize);
				cParticle.vDistortion = (*cParticle.pRot)*d;

				// Update color
				EulerAngles::FromQuaternion(*cParticle.pRot, vRot.x, vRot.y, vRot.z);
				vRot.x *= static_cast<float>(Math::RadToDeg);
				vRot.y *= static_cast<float>(Math::RadToDeg);
				vRot.z *= static_cast<float>(Math::RadToDeg);
				vAxis = vRot.Normalize();
				fDistx  = vAxis.x+vAxis.y*2+vAxis.z*3;
				if (fDistx > 1.0f)
					fDistx = 1.0f;
				if (fDistx < 0.5f)
					fDistx = 0.5f;
				cParticle.vColor[0] = cParticle.vFixPos.x*fDistx;
				cParticle.vColor[1] = cParticle.vFixPos.y*fDistx;
				cParticle.vColor[2] = cParticle.vFixPos.z*fDistx;
			}
void PowerBuilding::RenderAlphas ( float _predictionTime )
{
    Building::RenderAlphas( _predictionTime );

    Building *powerLink = g_app->m_location->GetBuilding( m_powerLink );
    if( powerLink )
    {
        //
        // Render the power line itself
        PowerBuilding *powerBuilding = (PowerBuilding *) powerLink;

        Vector3 ourPos = GetPowerLocation();
        Vector3 theirPos = powerBuilding->GetPowerLocation();

        Vector3 camToOurPos = g_app->m_camera->GetPos() - ourPos;
        Vector3 ourPosRight = camToOurPos ^ ( theirPos - ourPos );
        ourPosRight.SetLength( 2.0f );

        Vector3 camToTheirPos = g_app->m_camera->GetPos() - theirPos;
        Vector3 theirPosRight = camToTheirPos ^ ( theirPos - ourPos );
        theirPosRight.SetLength( 2.0f );

        glDisable   ( GL_CULL_FACE );
        glEnable    ( GL_BLEND );
        glBlendFunc ( GL_SRC_ALPHA, GL_ONE );
        glDepthMask ( false );
        glColor4f   ( 0.9f, 0.9f, 0.5f, 1.0f );

        glEnable        ( GL_TEXTURE_2D );
        glBindTexture   ( GL_TEXTURE_2D, g_app->m_resource->GetTexture( "textures/laser.bmp" ) );
        glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
        glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );

        glBegin( GL_QUADS );
            glTexCoord2f(0.1f, 0);      glVertex3fv( (ourPos - ourPosRight).GetData() );
            glTexCoord2f(0.1f, 1);      glVertex3fv( (ourPos + ourPosRight).GetData() );
            glTexCoord2f(0.9f, 1);      glVertex3fv( (theirPos + theirPosRight).GetData() );
            glTexCoord2f(0.9f, 0);      glVertex3fv( (theirPos - theirPosRight).GetData() );
        glEnd();

        //
        // Render any surges

        glEnable        ( GL_TEXTURE_2D );
        glBindTexture   ( GL_TEXTURE_2D, g_app->m_resource->GetTexture( "textures/starburst.bmp" ) );

        float surgeSize = 25.0f;
        glColor4f( 0.5f, 0.5f, 1.0f, 1.0f );
        Vector3 camUp = g_app->m_camera->GetUp() * surgeSize;
        Vector3 camRight = g_app->m_camera->GetRight() * surgeSize;
        glBegin( GL_QUADS );
        for( int i = 0; i < m_surges.Size(); ++i )
        {
            float thisSurge = m_surges[i];
            thisSurge += _predictionTime * 2;
            if( thisSurge < 0.0f ) thisSurge = 0.0f;
            if( thisSurge > 1.0f ) thisSurge = 1.0f;
            Vector3 thisSurgePos = ourPos + (theirPos-ourPos) * thisSurge;
            glTexCoord2i( 0, 0 );       glVertex3fv( (thisSurgePos - camUp - camRight).GetData() );
            glTexCoord2i( 1, 0 );       glVertex3fv( (thisSurgePos - camUp + camRight).GetData() );
            glTexCoord2i( 1, 1 );       glVertex3fv( (thisSurgePos + camUp + camRight).GetData() );
            glTexCoord2i( 0, 1 );       glVertex3fv( (thisSurgePos + camUp - camRight).GetData() );
        }
        glEnd();

        glDisable   ( GL_TEXTURE_2D );
        glDepthMask ( true );
        glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
        glDisable   ( GL_BLEND );
        glEnable    ( GL_CULL_FACE );
    }
}
bool DustBall::Advance()
{
    double speed = DUSTBALL_SPEED;

    if( s_vortexPos != g_zeroVector )
    {
        /*double speed = DUSTBALL_SPEED / 5.0;
        speed /= (m_pos.y / 1000.0);
        speed = max( speed, 100.0 );
        speed = min( speed, 500.0 );
        Vector3 thisVortexPos = s_vortexPos;
        thisVortexPos.y = m_pos.y;
        Vector3 oldPos = m_pos;
        double amountToTurn = SERVER_ADVANCE_PERIOD * (1.0 / (m_pos.y / 1000.0));
        Vector3 targetDir = (thisVortexPos - m_pos).Normalise();
        Vector3 actualDir = m_vel.Normalise() * (1.0 - amountToTurn) + targetDir * amountToTurn;
        actualDir.Normalise();

        Vector3 newPos = m_pos + actualDir * speed * SERVER_ADVANCE_PERIOD;

        Vector3 moved = newPos - oldPos;
        if( moved.Mag() > speed * SERVER_ADVANCE_PERIOD ) moved.SetLength( speed * SERVER_ADVANCE_PERIOD );
        newPos = m_pos + moved;*/

        Vector3 vortexPos = s_vortexPos;

        double timeIndex = GetNetworkTime() + m_id.GetUniqueId() * 20;
        double radius = (300.0 * (m_pos.y / 1000.0) );

        Vector3 targetPos = vortexPos;
        targetPos.x += iv_cos( timeIndex ) * radius;
        targetPos.z += iv_sin( timeIndex ) * radius;
        targetPos.y = m_pos.y;

        Vector3 oldPos = m_pos;
        Vector3 actualDir = (targetPos - m_pos).Normalise();

        Vector3 newPos = m_pos + (actualDir * speed * SERVER_ADVANCE_PERIOD);
        Vector3 moved = newPos - oldPos;
        if( moved.Mag() > speed * SERVER_ADVANCE_PERIOD ) moved.SetLength( speed * SERVER_ADVANCE_PERIOD );
        
        m_pos = newPos;       
        m_vel = ( m_pos - oldPos ) / SERVER_ADVANCE_PERIOD;
    }
    else
    {
        Vector3 oldPos = m_pos;
        Vector3 target = m_pos + g_app->m_location->m_windDirection * 2000.0;

        double amountToTurn = SERVER_ADVANCE_PERIOD * 3.0;
        Vector3 targetDir = (target - m_pos).Normalise();
        Vector3 actualDir = m_vel.Normalise() * (1.0 - amountToTurn) + targetDir * amountToTurn;
        actualDir.Normalise();

        Vector3 newPos = m_pos + actualDir * speed * SERVER_ADVANCE_PERIOD;

        Vector3 moved = newPos - oldPos;
        if( moved.Mag() > speed * SERVER_ADVANCE_PERIOD ) moved.SetLength( speed * SERVER_ADVANCE_PERIOD );
        newPos = m_pos + moved;

        m_pos = newPos;       
        m_vel = ( m_pos - oldPos ) / SERVER_ADVANCE_PERIOD;
    }

    if( m_pos.x > g_app->m_location->m_landscape.GetWorldSizeX() ||
        m_pos.z > g_app->m_location->m_landscape.GetWorldSizeZ() ||
        m_pos.x < 0.0 ||
        m_pos.z < 0.0 )
    {
        return true;
    }

    return false;
}
void SoulDestroyer::Attack( Vector3 const &_pos )
{
    int numFound;
    g_app->m_location->m_entityGrid->GetEnemies( s_neighbours, _pos.x, _pos.z, SOULDESTROYER_DAMAGERANGE, &numFound, m_id.GetTeamId() );

    for( int i = 0; i < numFound; ++i )
    {
        WorldObjectId id = s_neighbours[i];
        Entity *entity = (Entity *) g_app->m_location->GetEntity( id );        
        bool killed = false;

        Vector3 pushVector = ( entity->m_pos - _pos );
        double distance = pushVector.Mag();       
        if( distance < SOULDESTROYER_DAMAGERANGE )
        {
            g_app->m_soundSystem->TriggerEntityEvent( this, "Attack" );

            pushVector.SetLength( SOULDESTROYER_DAMAGERANGE - distance );
                                        
            g_app->m_location->m_entityGrid->RemoveObject( id, entity->m_pos.x, entity->m_pos.z, entity->m_radius );
            entity->m_pos += pushVector;
            g_app->m_location->m_entityGrid->AddObject( id, entity->m_pos.x, entity->m_pos.z, entity->m_radius );
            
            bool dead = entity->m_dead;
            entity->ChangeHealth( (SOULDESTROYER_DAMAGERANGE - distance) * -50.0 );            
            if( !dead && entity->m_dead ) killed = true;
        }

        if( killed )
        {
            // Eat the spirit
            int spiritIndex = g_app->m_location->GetSpirit( id );
            if( spiritIndex != -1 )
            {
                g_app->m_location->m_spirits.RemoveData( spiritIndex );
                if( m_spirits.NumUsed() < SOULDESTROYER_MAXSPIRITS )
                {
                    m_spirits.PutData( (double) GetHighResTime() );
                }
                else
                {
                    // Doesnt need to be sync safe
                    int index = AppRandom() % SOULDESTROYER_MAXSPIRITS;
                    m_spirits.PutData( (double) GetHighResTime(), index );
                }
            }
            
			if(entity->m_type == TypeDarwinian )
            {
				// Create a zombie
				Zombie *zombie = new Zombie();
				zombie->m_pos = entity->m_pos;
				zombie->m_front = entity->m_front;
				zombie->m_up = g_upVector;
				zombie->m_up.RotateAround( zombie->m_front * syncsfrand(1) );
				zombie->m_vel = m_vel * 0.5;
				zombie->m_vel.y = 20.0 + syncfrand(25.0);
				int index = g_app->m_location->m_effects.PutData( zombie );
				zombie->m_id.Set( id.GetTeamId(), UNIT_EFFECTS, index, -1 );
				zombie->m_id.GenerateUniqueId();
			}
        }
    } 
}
Vector3 Entity::PushFromObstructions( Vector3 const &pos, bool killem )
{
    Vector3 result = pos;    
    if( m_onGround )
    {
        result.y = g_app->m_location->m_landscape.m_heightMap->GetValue( result.x, result.z );
    }

    Matrix34 transform( m_front, g_upVector, result );

    //
    // Push from Water

    if( result.y <= 1.0 )
    {        
        double pushAngle = syncsfrand(1.0);
        double distance = 0.0;
        while( distance < 50.0 )
        {
            double angle = distance * pushAngle * M_PI;
            Vector3 offset( iv_cos(angle) * distance, 0.0, iv_sin(angle) * distance );
            Vector3 newPos = result + offset;
            double height = g_app->m_location->m_landscape.m_heightMap->GetValue( newPos.x, newPos.z );
            if( height > 1.0 )
            {
                result = newPos;
                result.y = height;
                break;
            }
            distance += 1.0;
        }
    }
    

    //
    // Push from buildings

    LList<int> *buildings = g_app->m_location->m_obstructionGrid->GetBuildings( result.x, result.z );

    for( int b = 0; b < buildings->Size(); ++b )
    {
        int buildingId = buildings->GetData(b);
        Building *building = g_app->m_location->GetBuilding( buildingId );
        if( building )
        {        
            bool hit = false;
            if( m_shape && building->DoesShapeHit( m_shape, transform ) ) hit = true;
            if( (!m_shape || m_type == TypeOfficer ) && building->DoesSphereHit( result, 1.0 ) ) hit = true;
            // cheap hack, but no point overriding the entire function for this one line
            if( !hit )
            {
                Vector3 oldPos = m_pos - m_vel * SERVER_ADVANCE_PERIOD;
                if( building->DoesRayHit( oldPos, m_front, (m_pos - oldPos).Mag() ) ) hit = true;
            }

            if( hit )
            {            
                if( building->m_type == Building::TypeLaserFence && killem &&
                    ((LaserFence *) building)->IsEnabled())
                {
                    if( !g_app->m_location->IsFriend(building->m_id.GetTeamId(), m_id.GetTeamId() ) )
                    {
                        ChangeHealth( -9999 );
                        ((LaserFence *) building)->Electrocute( m_pos );
                    }
                }
                else
                {               
                    Vector3 pushForce = (building->m_pos - result);
                    pushForce.y = 0.0f;
                    pushForce.SetLength(4.0f);
                    while( building->DoesSphereHit( result, 2.0f ) )
                    {
                        result -= pushForce;   
                        //result.y = g_app->m_location->m_landscape.m_heightMap->GetValue( result.x, result.z );
                    }
                }
            }
        }
    }

    return result;
}
void Nuke::RenderHistory( double _predictionTime )
{
    if( m_history.Size() > 0 && m_id.GetTeamId() != 255 )
    {
        glBindTexture( GL_TEXTURE_2D, g_app->m_resource->GetTexture( "textures/laser.bmp" ) );

        Vector3 predictedPos = m_pos + m_vel * SERVER_ADVANCE_PERIOD;
        Vector3 lastPos = predictedPos;

        RGBAColour colour = g_app->m_location->m_teams[m_id.GetTeamId()]->m_colour;
        if( m_id.GetTeamId() == g_app->m_location->GetMonsterTeamId() )
        {
            glBlendFunc     ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR );
            colour.Set( 255, 255, 255, 0 );
        }

        for( int i = 0; i < m_history.Size(); ++i )
        {
            Vector3 historyPos, thisPos;
			thisPos = historyPos = *m_history[i];

            Vector3 diff = thisPos - lastPos;
            lastPos += diff * 0.1f;
            if( m_id.GetTeamId() != g_app->m_location->GetMonsterTeamId() )
            {
                colour.a = 255 - 255 * (float) i / (float) m_history.Size();
            }

            glColor4ubv( colour.GetData() );

            Vector3 lengthVector = (thisPos - lastPos).Normalise();
	        lengthVector.SetLength((thisPos - lastPos).Mag());
            Vector3 fromPos = lastPos;
            Vector3 toPos = thisPos;//lastPos - lengthVector;

            Vector3 midPoint        = fromPos + (toPos - fromPos)/2.0f;
            Vector3 camToMidPoint   = g_app->m_camera->GetPos() - midPoint;
            float   camDistSqd      = camToMidPoint.MagSquared();
            Vector3 rightAngle      = (camToMidPoint ^ ( midPoint - toPos )).Normalise();
            
            //rightAngle *= 0.8f;
            rightAngle.SetLength(5.0f);

            glBegin( GL_QUADS );
                glTexCoord2i(0,0);      glVertex3dv( (fromPos - rightAngle).GetData() );
                glTexCoord2i(0,1);      glVertex3dv( (fromPos + rightAngle).GetData() );
                glTexCoord2i(1,1);      glVertex3dv( (toPos + rightAngle).GetData() );                
                glTexCoord2i(1,0);      glVertex3dv( (toPos - rightAngle).GetData() );                     

                glTexCoord2i(0,0);      glVertex3dv( (fromPos - rightAngle).GetData() );
                glTexCoord2i(0,1);      glVertex3dv( (fromPos + rightAngle).GetData() );
                glTexCoord2i(1,1);      glVertex3dv( (toPos + rightAngle).GetData() );                
                glTexCoord2i(1,0);      glVertex3dv( (toPos - rightAngle).GetData() );                     

                glTexCoord2i(0,0);      glVertex3dv( (fromPos - rightAngle).GetData() );
                glTexCoord2i(0,1);      glVertex3dv( (fromPos + rightAngle).GetData() );
                glTexCoord2i(1,1);      glVertex3dv( (toPos + rightAngle).GetData() );                
                glTexCoord2i(1,0);      glVertex3dv( (toPos - rightAngle).GetData() );                     
            glEnd();

            lastPos = historyPos;
        }
    }
}
void Nuke::Render( double _predictionTime )
{
    if( m_renderMarker )
    {
        RenderGroundMarker();
    }
    RenderSub( _predictionTime );
    if( m_exploded )
    {
        //RenderDeaths();
    }
    else
    {
        if( !m_launched ) return;

        Vector3 front = m_front;
        front.RotateAroundY( M_PI / 2.0f );
        Vector3 predictedPos = m_pos + m_vel * _predictionTime;;
        Vector3 entityUp = m_front;
        Vector3 entityRight (front ^ entityUp);

        Vector3 lengthVector = m_vel;
        lengthVector.SetLength( 10.0f );
        Vector3 fromPos = predictedPos;
        Vector3 toPos = predictedPos - lengthVector;

        Vector3 midPoint        = fromPos + (toPos - fromPos)/2.0f;
        Vector3 camToMidPoint   = g_app->m_camera->GetPos() - midPoint;
        float   camDistSqd      = camToMidPoint.MagSquared();
        Vector3 rightAngle      = (camToMidPoint ^ ( midPoint - toPos )).Normalise();

        entityRight = rightAngle;

        float size = 10.0f;  
        size *= (1.0f + 0.03f * (( m_id.GetIndex() * m_id.GetUniqueId() ) % 10));
        entityRight *= size;
        entityUp *= size * 2.0f;

        glDepthMask     ( false );
        glEnable        ( GL_BLEND );
        glBlendFunc     ( GL_SRC_ALPHA, GL_ONE );
        glEnable        ( GL_TEXTURE_2D );
        glBindTexture( GL_TEXTURE_2D, g_app->m_resource->GetTexture( "sprites/nuke.bmp" ) );

        RGBAColour colour = g_app->m_location->m_teams[m_id.GetTeamId()]->m_colour;
        if( m_id.GetTeamId() == g_app->m_location->GetMonsterTeamId() )
        {
            glBlendFunc     ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR );
            colour.Set( 255, 255, 255, 0 );
        }
        glColor4ubv(colour.GetData());

        glBegin(GL_QUADS);
            glTexCoord2i(0, 1);     glVertex3dv( (predictedPos - entityRight + entityUp).GetData() );
            glTexCoord2i(1, 1);     glVertex3dv( (predictedPos + entityRight + entityUp).GetData() );
            glTexCoord2i(1, 0);     glVertex3dv( (predictedPos + entityRight).GetData() );
            glTexCoord2i(0, 0);     glVertex3dv( (predictedPos - entityRight).GetData() );
        glEnd();

        RenderHistory( _predictionTime );

        glShadeModel    ( GL_FLAT );
        glDisable       ( GL_TEXTURE_2D );
        glDepthMask     ( true );
    }
}