void CTransportUnit::KillUnit(bool selfDestruct,bool reclaimed, CUnit *attacker)
{
	list<TransportedUnit>::iterator ti;
	for (ti = transported.begin(); ti != transported.end(); ++ti) {
		ti->unit->transporter = 0;
		ti->unit->DeleteDeathDependence(this);
		if (!unitDef->releaseHeld) {
			if (!selfDestruct) {
				ti->unit->DoDamage(DamageArray()*1000000,0,ZeroVector);	//dont want it to leave a corpse
			}
			ti->unit->KillUnit(selfDestruct,reclaimed,attacker);
		}
		else {
			ti->unit->stunned = (ti->unit->paralyzeDamage > ti->unit->health);
			if (CGroundMoveType* mt = dynamic_cast<CGroundMoveType*>(ti->unit->moveType)) {
				mt->StartSkidding();
				mt->StartFlying();
			}
			ti->unit->speed = speed;

			luaCallIns.UnitUnloaded(ti->unit, this);
		}
	}

	CUnit::KillUnit(selfDestruct,reclaimed,attacker);
}
예제 #2
0
void CPieceProjectile::Collision()
{
	if (speed.SqLength() > Square(gs->randFloat() * 5 + 1) && pos.y > radius + 2) {
		float3 norm = ground->GetNormal(pos.x, pos.z);
		float ns = speed.dot(norm);
		speed -= norm * ns * 1.6f;
		pos += norm * 0.1f;
	} else {
		if (flags & PF_Explode) {
			helper->Explosion(pos, DamageArray(50), 5, 0, 10, owner(), false, 1.0f, false, false, 0, 0, ZeroVector, -1);
		}
		if (flags & PF_Smoke) {
			if (flags & PF_NoCEGTrail) {
				float3 dir = speed;
				dir.Normalize();

				CSmokeTrailProjectile* tp =
					new CSmokeTrailProjectile(pos, oldSmoke, dir, oldSmokeDir, owner(),
					false, true, 7, Smoke_Time, 0.5f, drawTrail, 0, projectileDrawer->smoketrailtex);
				tp->creationTime += (8 - ((age) & 7));
			}
		}

		CProjectile::Collision();
		oldSmoke = pos;
	}
}
void CFireProjectile::Update(void)
{
	ttl--;
	if(ttl>0){
		if(ph->particleSaturation<0.8 || (ph->particleSaturation<1 && (gs->frameNum & 1))){	//this area must be unsynced
			SubParticle sub;
			sub.age=0;
			sub.maxSize=(0.7+gu->usRandFloat()*0.3)*particleSize;
			sub.posDif=gu->usRandVector()*emitRadius;
			sub.pos=emitPos;
			sub.pos.y+=sub.posDif.y;
			sub.posDif.y=0;
			sub.rotSpeed=(gu->usRandFloat()-0.5)*4;
			sub.smokeType=gu->usRandInt()%6;
			subParticles.push_front(sub);

			sub.maxSize=(0.7+gu->usRandFloat()*0.3)*particleSize;
			sub.posDif=gu->usRandVector()*emitRadius;
			sub.pos=emitPos;
			sub.pos.y+=sub.posDif.y-radius*0.3;
			sub.posDif.y=0;
			sub.rotSpeed=(gu->usRandFloat()-0.5)*4;
			subParticles2.push_front(sub);
		}
		if(!(ttl&31)){		//this area must be synced
			std::vector<CFeature*> f=qf->GetFeaturesExact(emitPos+wind.curWind*0.7,emitRadius*2);
			for(std::vector<CFeature*>::iterator fi=f.begin();fi!=f.end();++fi){
				if(gs->randFloat()>0.8)
					(*fi)->StartFire();
			}
			std::vector<CUnit*> units=qf->GetUnitsExact(emitPos+wind.curWind*0.7,emitRadius*2);
			for(std::vector<CUnit*>::iterator ui=units.begin();ui!=units.end();++ui){
				(*ui)->DoDamage(DamageArray()*30,0,ZeroVector);
			}
		}
	}

	for(std::list<SubParticle>::iterator pi=subParticles.begin();pi!=subParticles.end();++pi){
		pi->age+=ageSpeed;
		if(pi->age>1){
			subParticles.pop_back();
			break;
		}
		pi->pos+=speed + wind.curWind*pi->age*0.05 + pi->posDif*0.1;
		pi->posDif*=0.9;
	}
	for(std::list<SubParticle>::iterator pi=subParticles2.begin();pi!=subParticles2.end();++pi){
		pi->age+=ageSpeed*1.5;
		if(pi->age>1){
			subParticles2.pop_back();
			break;
		}
		pi->pos+=speed*0.7+pi->posDif*0.1;
		pi->posDif*=0.9;
	}

	if(subParticles.empty() && ttl<=0)
		deleteMe=true;
}
void CTransportUnit::KillUnit(bool selfDestruct,bool reclaimed)
{
	for(list<TransportedUnit>::iterator ti=transported.begin();ti!=transported.end();++ti){
		ti->unit->inTransport=false;
		if(!selfDestruct)
			ti->unit->DoDamage(DamageArray()*1000000,0,ZeroVector);	//dont want it to leave a corpse
		ti->unit->KillUnit(selfDestruct,reclaimed);
	}
	CUnit::KillUnit(selfDestruct,reclaimed);
}
void CPieceProjectile::Collision(CUnit* unit)
{
	if(unit==owner)
		return;
	if (flags & PP_Explode) {
		helper->Explosion(pos,DamageArray()*50,5,0,10,owner,false,1.0f, false, 0, unit, ZeroVector, -1);
	}
	if(flags & PP_Smoke){
		float3 dir=speed;
		dir.Normalize();
		CSmokeTrailProjectile* tp=SAFE_NEW CSmokeTrailProjectile(pos,oldSmoke,dir,oldSmokeDir,owner,false,true,7,Smoke_Time,0.5f,drawTrail);
		tp->creationTime+=8-((age)&7);
	}
	CProjectile::Collision(unit);
	oldSmoke=pos;
}
void CTransportUnit::KillUnit(bool selfDestruct, bool reclaimed, CUnit* attacker, bool)
{
	std::list<TransportedUnit>::iterator ti;
	for (ti = transported.begin(); ti != transported.end(); ++ti) {
		CUnit* u = ti->unit;

		u->transporter = 0;
		u->DeleteDeathDependence(this);

		// prevent a position teleport on the next movetype update if
		// the transport died in a place that the unit being carried
		// could not get to on its own
		if (!u->pos.IsInBounds()) {
			u->KillUnit(false, false, 0x0, false);
			continue;
		} else {
			const float gh = ground->GetHeight2(u->pos.x, u->pos.z);

			if (gh < -u->unitDef->maxWaterDepth || gh > -u->unitDef->minWaterDepth) {
				// note: should also check movedef restraints?
				u->KillUnit(false, false, 0x0, false);
				continue;
			}
		}


		if (!unitDef->releaseHeld) {
			if (!selfDestruct) {
				// we don't want it to leave a corpse
				u->DoDamage(DamageArray() * 1000000, 0, ZeroVector);
			}
			u->KillUnit(selfDestruct, reclaimed, attacker);
		} else {
			u->stunned = (u->paralyzeDamage > u->health);
			if (CGroundMoveType* mt = dynamic_cast<CGroundMoveType*>(u->moveType)) {
				mt->StartFlying();
			}
			u->speed = speed;

			eventHandler.UnitUnloaded(u, this);
		}
	}

	CUnit::KillUnit(selfDestruct, reclaimed, attacker);
}
예제 #7
0
void CPieceProjectile::Collision()
{
	if (speed.SqLength() > Square(gs->randFloat() * 5 + 1) && pos.y > radius + 2) {
		float3 norm = ground->GetNormal(pos.x, pos.z);
		float ns = speed.dot(norm);
		speed -= norm * ns * 1.6f;
		pos += norm * 0.1f;
	} else {
		if (flags & PF_Explode) {
			CGameHelper::ExplosionParams params = {
				pos,
				ZeroVector,
				DamageArray(50),
				NULL,              // weaponDef
				owner(),
				NULL,              // hitUnit
				NULL,              // hitFeature
				5.0f,              // areaOfEffect
				0.0f,              // edgeEffectiveness
				10.0f,             // explosionSpeed
				1.0f,              // gfxMod
				false,             // impactOnly
				false,             // ignoreOwner
				true               // damageGround
			};

			helper->Explosion(params);
		}
		if (flags & PF_Smoke) {
			if (flags & PF_NoCEGTrail) {
				float3 dir = speed;
				dir.Normalize();

				CSmokeTrailProjectile* tp =
					new CSmokeTrailProjectile(pos, oldSmoke, dir, oldSmokeDir, owner(),
					false, true, 7, Smoke_Time, 0.5f, drawTrail, 0, projectileDrawer->smoketrailtex);
				tp->creationTime += (8 - ((age) & 7));
			}
		}

		CProjectile::Collision();
		oldSmoke = pos;
	}
}
void CPieceProjectile::Collision()
{
	if(speed.Length()>gs->randFloat()*5+1 && pos.y>radius+2){
		float3 norm=ground->GetNormal(pos.x,pos.z);
		float ns=speed.dot(norm);
		speed-=norm*ns*1.6f;
		pos+=norm*0.1f;
	} else {
		if (flags & PP_Explode) {
			helper->Explosion(pos,DamageArray()*50,5,0,10,owner,false,1.0f,false,0,0,ZeroVector, -1);
		}
		if(flags & PP_Smoke){
			float3 dir=speed;
			dir.Normalize();
			CSmokeTrailProjectile* tp=SAFE_NEW CSmokeTrailProjectile(pos,oldSmoke,dir,oldSmokeDir,owner,false,true,7,Smoke_Time,0.5f,drawTrail);
			tp->creationTime+=8-((age)&7);
		}
		CProjectile::Collision();
		oldSmoke=pos;
	}
}
예제 #9
0
void CPieceProjectile::Collision(CUnit* unit)
{
	if (unit == owner()) {
		return;
	}
	if (flags & PF_Explode) {
		CGameHelper::ExplosionParams params = {
			pos,
			ZeroVector,
			DamageArray(50),
			NULL,                                            // weaponDef
			owner(),
			unit,                                            // hitUnit
			NULL,                                            // hitFeature
			5.0f,                                            // areaOfEffect
			0.0f,                                            // edgeEffectiveness
			10.0f,                                           // explosionSpeed
			1.0f,                                            // gfxMod
			false,                                           // impactOnly
			false,                                           // ignoreOwner
			true                                             // damageGround
		};

		helper->Explosion(params);
	}
	if (flags & PF_Smoke) {
		if (flags & PF_NoCEGTrail) {
			float3 dir = speed;
			dir.Normalize();

			CSmokeTrailProjectile* tp =
				new CSmokeTrailProjectile(pos, oldSmoke, dir, oldSmokeDir, owner(),
				false, true, 7, Smoke_Time, 0.5f, drawTrail, 0, projectileDrawer->smoketrailtex);
			tp->creationTime += (8 - ((age) & 7));
		}
	}

	CProjectile::Collision(unit);
	oldSmoke = pos;
}
예제 #10
0
void CPieceProjectile::Collision(CUnit* unit)
{
	if (unit == owner())
		return;
	if (flags & PF_Explode) {
		helper->Explosion(pos, DamageArray() * 50, 5, 0, 10, owner(), false, 1.0f, false, false, 0, unit, ZeroVector, -1);
	}
	if (flags & PF_Smoke) {
		if (flags & PF_NoCEGTrail) {
			float3 dir = speed;
			dir.Normalize();

			CSmokeTrailProjectile* tp =
				new CSmokeTrailProjectile(pos, oldSmoke, dir, oldSmokeDir, owner(),
				false, true, 7, Smoke_Time, 0.5f, drawTrail, 0, &ph->smoketrailtex);
			tp->creationTime += (8 - ((age) & 7));
		}
	}

	CProjectile::Collision(unit);
	oldSmoke = pos;
}
예제 #11
0
void CFireProjectile::Update()
{
	ttl--;
	if (ttl > 0) {
		if (projectileHandler->particleSaturation < 0.8f || (projectileHandler->particleSaturation < 1 && (gs->frameNum & 1))) {
			//! unsynced code
			SubParticle sub;
			sub.age = 0;
			sub.maxSize = (0.7f + gu->RandFloat()*0.3f) * particleSize;
			sub.posDif = gu->RandVector() * emitRadius;
			sub.pos = emitPos;
			sub.pos.y += sub.posDif.y;
			sub.posDif.y = 0;
			sub.rotSpeed = (gu->RandFloat() - 0.5f) * 4;
			sub.smokeType = gu->RandInt() % projectileDrawer->smoketex.size();
			subParticles.push_front(sub);

			sub.maxSize = (0.7f + gu->RandFloat()*0.3f) * particleSize;
			sub.posDif = gu->RandVector() * emitRadius;
			sub.pos = emitPos;
			sub.pos.y += sub.posDif.y - radius*0.3f;
			sub.posDif.y = 0;
			sub.rotSpeed=(gu->RandFloat() - 0.5f) * 4;
			subParticles2.push_front(sub);
		}
		if (!(ttl & 31)) {
			//! synced code
			const std::vector<CFeature*>& features = quadField->GetFeaturesExact(emitPos + wind.GetCurrentWind() * 0.7f, emitRadius * 2);
			const std::vector<CUnit*>& units = quadField->GetUnitsExact(emitPos + wind.GetCurrentWind() * 0.7f, emitRadius * 2);

			for (std::vector<CFeature*>::const_iterator fi = features.begin(); fi != features.end(); ++fi) {
				if (gs->randFloat() > 0.8f) {
					(*fi)->StartFire();
				}
			}

			for (std::vector<CUnit*>::const_iterator ui = units.begin(); ui != units.end(); ++ui) {
				(*ui)->DoDamage(DamageArray(30), ZeroVector, NULL, -CSolidObject::DAMAGE_EXTSOURCE_FIRE, -1);
			}
		}
	}

	for(part_list_type::iterator pi=subParticles.begin();pi!=subParticles.end();++pi){
		pi->age+=ageSpeed;
		if(pi->age>1){
			subParticles.pop_back();
			break;
		}
		pi->pos+=speed + wind.GetCurrentWind()*pi->age*0.05f + pi->posDif*0.1f;
		pi->posDif*=0.9f;
	}
	for(part_list_type::iterator pi=subParticles2.begin();pi!=subParticles2.end();++pi){
		pi->age+=ageSpeed*1.5f;
		if(pi->age>1){
			subParticles2.pop_back();
			break;
		}
		pi->pos+=speed*0.7f+pi->posDif*0.1f;
		pi->posDif*=0.9f;
	}

	if (subParticles.empty() && (ttl <= 0)) {
		deleteMe = true;
	}
}
예제 #12
0
void CAirMoveType::Update(void)
{
	float3 &pos=owner->pos;

	//This is only set to false after the plane has finished constructing
	if (useHeading){
		useHeading = false;
		SetState(AIRCRAFT_TAKEOFF);
	}

	if(owner->stunned){
		UpdateAirPhysics(0,lastAileronPos,lastElevatorPos,0,ZeroVector);
		goto EndNormalControl;
	}
#ifdef DIRECT_CONTROL_ALLOWED
	if(owner->directControl && !(aircraftState==AIRCRAFT_CRASHING)){
		SetState(AIRCRAFT_FLYING);
		DirectControlStruct* dc=owner->directControl;
		
		inefficientAttackTime=0;
		if(dc->forward || dc->back || dc->left || dc->right){
			float aileron=0;
			float elevator=0;
			if(dc->forward)
				elevator-=1;
			if(dc->back)
				elevator+=1;
			if(dc->right)
				aileron+=1;
			if(dc->left)
				aileron-=1;
			UpdateAirPhysics(0,aileron,elevator,1,owner->frontdir);
			maneuver=0;
			goto EndNormalControl;		//ok so goto is bad i know
		}
	}
#endif

	if(reservedPad){
		CUnit* unit=reservedPad->unit;
		float3 relPos=unit->localmodel->GetPiecePos(reservedPad->piece);
	float3 pos=unit->pos + unit->frontdir*relPos.z + unit->updir*relPos.y + unit->rightdir*relPos.x;
		if(padStatus==0){
			if(aircraftState!=AIRCRAFT_FLYING && aircraftState!=AIRCRAFT_TAKEOFF)
				SetState(AIRCRAFT_FLYING);

			goalPos=pos;

			if(pos.distance(owner->pos)<400){
				padStatus=1;
			}
//			geometricObjects->AddLine(owner->pos,pos,1,0,1);
		} else if(padStatus==1){
			if(aircraftState!=AIRCRAFT_LANDING)
				SetState(AIRCRAFT_LANDING);

			goalPos=pos;
			reservedLandingPos=pos;

			if(owner->pos.distance(pos)<3 || aircraftState==AIRCRAFT_LANDED){
				padStatus=2;
			}
//			geometricObjects->AddLine(owner->pos,pos,10,0,1);
		} else {
			if(aircraftState!=AIRCRAFT_LANDED)
				SetState(AIRCRAFT_LANDED);
			
			owner->pos=pos;

			owner->AddBuildPower(20,unit);


			if(owner->health>=owner->maxHealth-1){
				airBaseHandler->LeaveLandingPad(reservedPad);
				reservedPad=0;
				padStatus=0;
				goalPos=oldGoalPos;
				SetState(AIRCRAFT_TAKEOFF);
			}
		}
	}

	switch(aircraftState){
	case AIRCRAFT_FLYING:
#ifdef DEBUG_AIRCRAFT
	if(selectedUnits.selectedUnits.find(this)!=selectedUnits.selectedUnits.end()){
		info->AddLine("Flying %i %i %.1f %i",moveState,fireState,inefficientAttackTime,(int)isFighter);
	}
#endif
		owner->restTime=0;
		if(owner->userTarget || owner->userAttackGround){
			inefficientAttackTime=min(inefficientAttackTime,(float)gs->frameNum-owner->lastFireWeapon);
			if(owner->userTarget){
				goalPos=owner->userTarget->pos;
			} else {
				goalPos=owner->userAttackPos;
			}
			if(maneuver){
				UpdateManeuver();
				inefficientAttackTime=0;
			} else if(isFighter && goalPos.distance(pos)<owner->maxRange*4){
				inefficientAttackTime++;
				UpdateFighterAttack();
			}else{
				inefficientAttackTime=0;
				UpdateAttack();
			}
		}else{
			inefficientAttackTime=0;
			UpdateFlying(wantedHeight,1);
		}
		break;
	case AIRCRAFT_LANDED:
		inefficientAttackTime=0;
		UpdateLanded();
		break;
	case AIRCRAFT_LANDING:
		inefficientAttackTime=0;
		UpdateLanding();
		break;
	case AIRCRAFT_CRASHING:
		owner->crashing=true;
		UpdateAirPhysics(crashRudder,crashAileron,crashElevator,0,owner->frontdir);
		new CSmokeProjectile(owner->midPos,gs->randVector()*0.08,100+gs->randFloat()*50,5,0.2,owner,0.4);
		if(!(gs->frameNum&3) && max(0.f,ground->GetApproximateHeight(pos.x,pos.z))+5+owner->radius>pos.y)
			owner->KillUnit(true,false,0);
		break;
	case AIRCRAFT_TAKEOFF:
		UpdateTakeOff(wantedHeight);
	default:
		break;
	}
EndNormalControl:
	if(pos!=oldpos){
		oldpos=pos;
		if(aircraftState==AIRCRAFT_FLYING || aircraftState==AIRCRAFT_CRASHING){
			vector<CUnit*> nearUnits=qf->GetUnitsExact(pos,owner->radius+6);
			vector<CUnit*>::iterator ui;
			for(ui=nearUnits.begin();ui!=nearUnits.end();++ui){
				float sqDist=(pos-(*ui)->pos).SqLength();
				float totRad=owner->radius+(*ui)->radius;
				if(sqDist<totRad*totRad && sqDist!=0){
					float dist=sqrt(sqDist);
					float3 dif=pos-(*ui)->pos;
					dif/=dist;
					if((*ui)->immobile){
						pos-=dif*(dist-totRad);
						owner->midPos=pos+owner->frontdir*owner->relMidPos.z + owner->updir*owner->relMidPos.y + owner->rightdir*owner->relMidPos.x;	
						owner->speed*=0.99f;
						float damage=(((*ui)->speed-owner->speed)*0.1).SqLength();
						owner->DoDamage(DamageArray()*damage,0,ZeroVector);
						(*ui)->DoDamage(DamageArray()*damage,0,ZeroVector);
					} else {
						float part=owner->mass/(owner->mass+(*ui)->mass);
						pos-=dif*(dist-totRad)*(1-part);
						owner->midPos=pos+owner->frontdir*owner->relMidPos.z + owner->updir*owner->relMidPos.y + owner->rightdir*owner->relMidPos.x;	
						CUnit* u=(CUnit*)(*ui);
						u->pos+=dif*(dist-totRad)*(part);
						u->midPos=u->pos+u->frontdir*u->relMidPos.z + u->updir*u->relMidPos.y + u->rightdir*u->relMidPos.x;	
						float damage=(((*ui)->speed-owner->speed)*0.1).SqLength();
						owner->DoDamage(DamageArray()*damage,0,ZeroVector);
						(*ui)->DoDamage(DamageArray()*damage,0,ZeroVector);
						owner->speed*=0.99f;
					}
				}
			}
		}
		if(pos.x<0){
			pos.x+=1.5;
			owner->midPos.x+=1.5;
		}else if(pos.x>float3::maxxpos){
			pos.x-=1.5;
			owner->midPos.x-=1.5;
		}

		if(pos.z<0){
			pos.z+=1.5;
			owner->midPos.z+=1.5;
		}else if(pos.z>float3::maxzpos){
			pos.z-=1.5;
			owner->midPos.z-=1.5;
		}
	}
#ifdef DEBUG_AIRCRAFT
	if(lastColWarningType==1){
		int g=geometricObjects->AddLine(owner->pos,lastColWarning->pos,10,1,1);
		geometricObjects->SetColor(g,0.2,1,0.2,0.6);
	} else if(lastColWarningType==2){
		int g=geometricObjects->AddLine(owner->pos,lastColWarning->pos,10,1,1);
		if(owner->frontdir.dot(lastColWarning->midPos+lastColWarning->speed*20 - owner->midPos - owner->speed*20)<0)
			geometricObjects->SetColor(g,1,0.2,0.2,0.6);
		else
			geometricObjects->SetColor(g,1,1,0.2,0.6);
	}
#endif
}
예제 #13
0
void CAirMoveType::UpdateAirPhysics(float rudder, float aileron, float elevator,float engine,const float3& engineVector)
{
	float3 &pos = owner->pos;
	float3 &rightdir = owner->rightdir;
	float3 &frontdir = owner->frontdir;
	float3 &updir = owner->updir;
	float3 &speed = owner->speed;

	lastRudderPos=rudder;
	lastAileronPos=aileron;
	lastElevatorPos=elevator;

	float speedf=speed.Length();
	float3 speeddir=frontdir;
	if(speedf!=0)
		speeddir=speed/speedf;

	float gHeight=ground->GetHeight(pos.x,pos.z);

#ifdef DIRECT_CONTROL_ALLOWED
	if(owner->directControl)
		if((pos.y-gHeight)>wantedHeight*1.2)
			engine=max(0.,min((double)engine,1-(pos.y-gHeight-wantedHeight*1.2)/wantedHeight));
#endif

	speed+=engineVector*maxAcc*engine;

	speed.y+=gs->gravity*myGravity;
	speed*=invDrag;

	float3 wingDir=updir*(1-wingAngle)-frontdir*wingAngle;
	float wingForce=wingDir.dot(speed)*wingDrag;
	speed-=wingDir*wingForce;

	frontdir+=rightdir*rudder*maxRudder*speedf;
	updir+=rightdir*aileron*maxAileron*speedf;
	frontdir+=updir*elevator*maxElevator*speedf;
	frontdir+=(speeddir-frontdir)*frontToSpeed;
	speed+=(frontdir*speedf-speed)*speedToFront;
	pos+=speed;

	if(gHeight>owner->pos.y-owner->model->radius*0.2 && !owner->crashing){
		float3 gNormal=ground->GetNormal(pos.x,pos.z);
		float impactSpeed=-speed.dot(gNormal);

		if(impactSpeed>0){
			if(owner->stunned){
				float damage=0;
				if(impactSpeed>0.5)
					damage+=impactSpeed*impactSpeed*1000;
				if(updir.dot(gNormal)<0.95)
					damage+=(1-(updir.dot(gNormal)))*1000;
				if(damage>0)
					owner->DoDamage(DamageArray()*(damage*0.4),0,ZeroVector);	//only do damage while stunned for now
			}
			pos.y=gHeight+owner->model->radius*0.2+0.01;
			speed+=gNormal*(impactSpeed*1.5);
			speed*=0.95;
			updir=gNormal-frontdir*0.1;
			frontdir=updir.cross(frontdir.cross(updir));
		}
	}

	frontdir.Normalize();
	rightdir=frontdir.cross(updir);
	rightdir.Normalize();
	updir=rightdir.cross(frontdir);

	owner->midPos=pos+frontdir*owner->relMidPos.z + updir*owner->relMidPos.y + rightdir*owner->relMidPos.x;
#ifdef DEBUG_AIRCRAFT
	if(selectedUnits.selectedUnits.find(this)!=selectedUnits.selectedUnits.end()){
		info->AddLine("UpdataAP %.1f %.1f %.1f %.1f",speedf,pos.x,pos.y,pos.z);
//		info->AddLine("Rudders %.1f %.1f %.1f %.1f",rudder,aileron,elevator,engine);
	}
#endif
}
예제 #14
0
void CAirMoveType::UpdateAirPhysics(float rudder, float aileron, float elevator, float engine, const float3& engineVector)
{
	float3& pos = owner->pos;
	SyncedFloat3& rightdir = owner->rightdir;
	SyncedFloat3& frontdir = owner->frontdir;
	SyncedFloat3& updir = owner->updir;
	float3& speed = owner->speed;
	bool nextPosInBounds = true;

	lastRudderPos = rudder;
	lastAileronPos = aileron;
	lastElevatorPos = elevator;

	float speedf = speed.Length();
	float3 speeddir = frontdir;
	if (speedf != 0.0f)
		speeddir = speed / speedf;

	float gHeight = ground->GetHeight(pos.x, pos.z);

	if (owner->directControl) {
		if ((pos.y - gHeight) > wantedHeight * 1.2f) {
			engine = std::max(0.0f, std::min(engine, 1 - (pos.y - gHeight - wantedHeight * 1.2f) / wantedHeight));
		}
		// check next position given current (unadjusted) pos and speed
		nextPosInBounds = (pos + speed).CheckInBounds();
	}


	speed += engineVector * maxAcc * engine;
	speed.y += mapInfo->map.gravity * myGravity;

	if (aircraftState == AIRCRAFT_CRASHING) {
		speed *= crashDrag;
	} else {
		speed *= invDrag;
	}

	float3 wingDir = updir * (1 - wingAngle) - frontdir * wingAngle;
	float wingForce = wingDir.dot(speed) * wingDrag;
	speed -= wingDir * wingForce;

	frontdir += rightdir * rudder * maxRudder * speedf;
	updir += rightdir * aileron * maxAileron * speedf;
	frontdir += updir * elevator * maxElevator * speedf;
	frontdir += (speeddir - frontdir) * frontToSpeed;
	speed += (frontdir * speedf - speed) * speedToFront;

	if (nextPosInBounds) {
		pos += speed;
	}

	// ground collision
	if (gHeight > owner->pos.y - owner->model->radius * 0.2f && !owner->crashing) {
		float3 gNormal = ground->GetNormal(pos.x, pos.z);
		float impactSpeed = -speed.dot(gNormal);

		if (impactSpeed > 0) {
			if (owner->stunned) {
				float damage = 0;
				if (impactSpeed > 0.5f)
					damage += impactSpeed * impactSpeed * 1000;
				if (updir.dot(gNormal) < 0.95f)
					damage += (1 - (updir.dot(gNormal))) * 1000;
				if (damage > 0) {
					// only do damage while stunned for now
					owner->DoDamage(DamageArray() * (damage * 0.4f), 0, ZeroVector);
				}
			}
			pos.y = gHeight + owner->model->radius * 0.2f + 0.01f;
			speed += gNormal * (impactSpeed * 1.5f);
			// fix for mantis #1355
			// aircraft could get stuck in the ground and never recover;
			// this only happened when the speed wasn't high enough.
			// do not reduce speed if it's too low, add a vertical component
			// to help get off the ground instead.
			if (speed.SqLength() > 0.3f*0.3f * owner->unitDef->speed*owner->unitDef->speed)
				speed *= 0.95f;
			else
				speed.y += impactSpeed;
			updir = gNormal - frontdir * 0.1f;
			frontdir = updir.cross(frontdir.cross(updir));
		}
	}

	frontdir.Normalize();
	rightdir = frontdir.cross(updir);
	rightdir.Normalize();
	updir = rightdir.cross(frontdir);

	owner->UpdateMidPos();

#ifdef DEBUG_AIRCRAFT
	GML_RECMUTEX_LOCK(sel); // UpdateAirPhysics

	if (selectedUnits.selectedUnits.find(this) != selectedUnits.selectedUnits.end()) {
		logOutput.Print("UpdataAP %.1f %.1f %.1f %.1f", speedf, pos.x, pos.y, pos.z);
		// logOutput.Print("Rudders %.1f %.1f %.1f %.1f", rudder, aileron, elevator, engine);
	}
#endif
}
예제 #15
0
void CAirMoveType::Update(void)
{
	float3& pos = owner->pos;

	// note: this is only set to false after
	// the plane has finished constructing
	if (useHeading) {
		useHeading = false;
		SetState(AIRCRAFT_TAKEOFF);
	}

	if (owner->stunned) {
		UpdateAirPhysics(0, lastAileronPos, lastElevatorPos, 0, ZeroVector);
		goto EndNormalControl;
	}


	if (owner->directControl && !(aircraftState == AIRCRAFT_CRASHING)) {
		SetState(AIRCRAFT_FLYING);
		DirectControlStruct* dc = owner->directControl;
		inefficientAttackTime = 0;

		if (dc->forward || dc->back || dc->left || dc->right) {
			float aileron = 0;
			float elevator = 0;
			if (dc->forward)
				elevator -= 1;
			if (dc->back)
				elevator += 1;
			if (dc->right)
				aileron += 1;
			if (dc->left)
				aileron -= 1;

			UpdateAirPhysics(0, aileron, elevator, 1, owner->frontdir);
			maneuver = 0;

			goto EndNormalControl; // bad
		}
	}


	if (reservedPad) {
		CUnit* unit = reservedPad->GetUnit();
		float3 relPos = unit->script->GetPiecePos(reservedPad->GetPiece());
		float3 pos = unit->pos + (unit->frontdir * relPos.z) + (unit->updir * relPos.y) + (unit->rightdir * relPos.x);

		if (padStatus == 0) {
			if (aircraftState != AIRCRAFT_FLYING && aircraftState != AIRCRAFT_TAKEOFF) {
				SetState(AIRCRAFT_FLYING);
			}

			goalPos = pos;

			if (pos.SqDistance2D(owner->pos) < (400*400)) {
				padStatus = 1;
			}
		} else if (padStatus == 1) {
			if (aircraftState != AIRCRAFT_LANDING) {
				SetState(AIRCRAFT_LANDING);
			}

			goalPos = pos;
			reservedLandingPos = pos;

			if (owner->pos.SqDistance(pos) < 9 || aircraftState == AIRCRAFT_LANDED) {
				padStatus = 2;
			}
		} else {
			if (aircraftState != AIRCRAFT_LANDED) {
				SetState(AIRCRAFT_LANDED);
			}

			owner->pos = pos;
			owner->AddBuildPower(unit->unitDef->buildSpeed / GAME_SPEED, unit);
			owner->currentFuel = std::min(owner->unitDef->maxFuel, owner->currentFuel + (owner->unitDef->maxFuel / (GAME_SPEED * owner->unitDef->refuelTime)));

			if (owner->health >= owner->maxHealth - 1 && owner->currentFuel >= owner->unitDef->maxFuel) {
				// repaired and filled up, leave the pad
				airBaseHandler->LeaveLandingPad(reservedPad);
				reservedPad = 0;
				padStatus = 0;
				goalPos = oldGoalPos;
				SetState(AIRCRAFT_TAKEOFF);
			}
		}
	} else if ((owner->unitDef->maxFuel > 0.0f && owner->currentFuel <= 0.0f) &&
				padStatus == 0 && maxWantedSpeed > 0.0f) {
		// keep us in the air to reach our landing goalPos
		// (which is hopefully in the vicinity of a pad)
		SetState(AIRCRAFT_FLYING);
	}



	switch (aircraftState) {
		case AIRCRAFT_FLYING: {
	#ifdef DEBUG_AIRCRAFT
			GML_RECMUTEX_LOCK(sel); // Update

			if (selectedUnits.selectedUnits.find(this) != selectedUnits.selectedUnits.end()) {
				logOutput.Print("Flying %i %i %.1f %i", moveState, fireState, inefficientAttackTime, (int) isFighter);
			}
	#endif

			owner->restTime = 0;

			// somewhat hackish, but planes that have attack orders
			// while no pad is available would otherwise continue
			// attacking even if out of fuel
			bool continueAttack =
				(!reservedPad && ((owner->currentFuel > 0.0f) || owner->unitDef->maxFuel <= 0.0f));

			if (continueAttack && ((owner->userTarget && !owner->userTarget->isDead) || owner->userAttackGround)) {
				inefficientAttackTime = std::min(inefficientAttackTime, float(gs->frameNum) - owner->lastFireWeapon);

				if (owner->userTarget) {
					goalPos = owner->userTarget->pos;
				} else {
					goalPos = owner->userAttackPos;
				}
				if (maneuver) {
					UpdateManeuver();
					inefficientAttackTime = 0;
				} else if (isFighter && goalPos.SqDistance(pos) < Square(owner->maxRange * 4)) {
					inefficientAttackTime++;
					UpdateFighterAttack();
				} else {
					inefficientAttackTime = 0;
					UpdateAttack();
				}
			} else {
				inefficientAttackTime = 0;
				UpdateFlying(wantedHeight, 1);
			}
		} break;
		case AIRCRAFT_LANDED:
			inefficientAttackTime = 0;
			UpdateLanded();
			break;
		case AIRCRAFT_LANDING:
			inefficientAttackTime = 0;
			UpdateLanding();
			break;
		case AIRCRAFT_CRASHING:
			owner->crashing = true;
			UpdateAirPhysics(crashRudder, crashAileron, crashElevator, 0, owner->frontdir);
			new CSmokeProjectile(owner->midPos, gs->randVector() * 0.08f, 100 + gs->randFloat() * 50, 5, 0.2f, owner, 0.4f);
			if (!(gs->frameNum & 3) && std::max(0.f, ground->GetApproximateHeight(pos.x, pos.z)) + 5 + owner->radius > pos.y)
				owner->KillUnit(true, false, 0);
			break;
		case AIRCRAFT_TAKEOFF:
			UpdateTakeOff(wantedHeight);
			break;
		default:
			break;
	}

EndNormalControl:



	// handle collisions
	if (pos != oldpos) {
		oldpos = pos;
		bool hitBuilding = false;

		if (collide && (aircraftState == AIRCRAFT_FLYING || aircraftState == AIRCRAFT_CRASHING)) {
			vector<CUnit*> nearUnits = qf->GetUnitsExact(pos, owner->radius + 6);
			vector<CUnit*>::iterator ui;

			for (ui = nearUnits.begin(); ui != nearUnits.end(); ++ui) {
				float sqDist = (pos - (*ui)->pos).SqLength();
				float totRad = owner->radius + (*ui)->radius;

				if (sqDist < totRad * totRad && sqDist != 0) {
					float dist = sqrt(sqDist);
					float3 dif = pos - (*ui)->pos;

					if (dist > 0.0f) {
						dif /= dist;
					}

					if ((*ui)->immobile) {
						pos -= dif * (dist - totRad);
						owner->UpdateMidPos();
						owner->speed *= 0.99f;
						float damage = (((*ui)->speed - owner->speed) * 0.1f).SqLength();
						owner->DoDamage(DamageArray() * damage, 0, ZeroVector);
						(*ui)->DoDamage(DamageArray() * damage, 0, ZeroVector);
						hitBuilding = true;
					} else {
						float part = owner->mass / (owner->mass + (*ui)->mass);
						pos -= dif * (dist - totRad) * (1 - part);
						owner->UpdateMidPos();
						CUnit* u = (CUnit*)(*ui);
						u->pos += dif * (dist - totRad) * (part);
						u->UpdateMidPos();
						float damage = (((*ui)->speed - owner->speed) * 0.1f).SqLength();
						owner->DoDamage(DamageArray() * damage, 0, ZeroVector);
						(*ui)->DoDamage(DamageArray() * damage, 0, ZeroVector);
						owner->speed *= 0.99f;
					}
				}
			}
			if (hitBuilding && owner->crashing) {
				// if our collision sphere overlapped with that
				// of a building and we're crashing, die right
				// now rather than waiting until we're close
				// enough to the ground (which may never happen
				// if eg. we're going down over a crowded field
				// of windmills due to col-det)
				owner->KillUnit(true, false, 0);
				return;
			}
		}

		if (pos.x < 0) {
			pos.x += 1.5f;
			owner->midPos.x += 1.5f;
		} else if (pos.x > float3::maxxpos) {
			pos.x -= 1.5f;
			owner->midPos.x -= 1.5f;
		}

		if (pos.z < 0) {
			pos.z += 1.5f;
			owner->midPos.z += 1.5f;
		} else if (pos.z > float3::maxzpos) {
			pos.z -= 1.5f;
			owner->midPos.z -= 1.5f;
		}
	}



#ifdef DEBUG_AIRCRAFT
	if (lastColWarningType == 1) {
		int g = geometricObjects->AddLine(owner->pos, lastColWarning->pos, 10, 1, 1);
		geometricObjects->SetColor(g, 0.2f, 1, 0.2f, 0.6f);
	} else if (lastColWarningType == 2) {
		int g = geometricObjects->AddLine(owner->pos, lastColWarning->pos, 10, 1, 1);
		if (owner->frontdir.dot(lastColWarning->midPos + lastColWarning->speed * 20 - owner->midPos - owner->speed * 20) < 0)
			geometricObjects->SetColor(g, 1, 0.2f, 0.2f, 0.6f);
		else
			geometricObjects->SetColor(g, 1, 1, 0.2f, 0.6f);
	}
#endif
}
void CTransportUnit::KillUnit(bool selfDestruct, bool reclaimed, CUnit* attacker, bool)
{
	std::list<TransportedUnit>::iterator ti;
	for (ti = transported.begin(); ti != transported.end(); ++ti) {
		CUnit* u = ti->unit;
		const float gh = ground->GetHeight2(u->pos.x, u->pos.z);

		u->transporter = 0;
		u->DeleteDeathDependence(this);

		// prevent a position teleport on the next movetype update if
		// the transport died in a place that the unit being carried
		// could not get to on its own
		if (!u->pos.IsInBounds()) {
			u->KillUnit(false, false, NULL, false);
			continue;
		} else {
			// immobile units can still be transported
			// via script trickery, guard against this
			if (u->unitDef->movedata != NULL && gh < -u->unitDef->movedata->depth) {
				// always treat depth as maxWaterDepth (fails if
				// the transportee is a ship, but so does using
				// UnitDef::{min, max}WaterDepth)
				u->KillUnit(false, false, NULL, false);
				continue;
			}
		}


		if (!unitDef->releaseHeld) {
			if (!selfDestruct) {
				// we don't want it to leave a corpse
				u->DoDamage(DamageArray() * 1000000, 0, ZeroVector);
			}
			u->KillUnit(selfDestruct, reclaimed, attacker);
		} else {
			// place unit near the place of death of the transport
			// if it's a ground transport and uses a piece-in-ground method
			// to hide units
			if (u->pos.y < gh) {
				const float k = (u->radius + radius)*std::max(unitDef->unloadSpread, 1.f);
				// try to unload in a presently unoccupied spot
				// unload on a wreck if suitable position not found
				for (int i = 0; i<10; ++i) {
					float3 pos = u->pos;
					pos.x += gs->randFloat()*2*k - k;
					pos.z += gs->randFloat()*2*k - k;
					pos.y = ground->GetHeight2(u->pos.x, u->pos.z);
					if (qf->GetUnitsExact(pos, u->radius + 2).empty()) {
						u->pos = pos;
						break;
					}
				}
				u->UpdateMidPos();
			} else if (CGroundMoveType* mt = dynamic_cast<CGroundMoveType*>(u->moveType)) {
				mt->StartFlying();
			}

			u->stunned = (u->paralyzeDamage > u->health);
			u->moveType->LeaveTransport();
			u->speed = speed*(0.5f + 0.5f*gs->randFloat());

			eventHandler.UnitUnloaded(u, this);
		}
	}

	CUnit::KillUnit(selfDestruct, reclaimed, attacker);
}
예제 #17
0
void CUnit::SlowUpdate()
{
    --nextPosErrorUpdate;
    if(nextPosErrorUpdate==0) {
        float3 newPosError(gs->randVector());
        newPosError.y*=0.2;
        if(posErrorVector.dot(newPosError)<0)
            newPosError=-newPosError;
        posErrorDelta=(newPosError-posErrorVector)*(1.0/256);
        nextPosErrorUpdate=16;
    }

    for(int a=0; a<gs->activeAllyTeams; ++a) {
        if(losStatus[a] & LOS_INTEAM) {
        } else if(loshandler->InLos(this,a)) {
            if(!(losStatus[a]&LOS_INLOS)) {
                int prevLosStatus = losStatus[a];

                if(mobility || beingBuilt) {
                    losStatus[a]|=(LOS_INLOS | LOS_INRADAR);
                } else {
                    losStatus[a]|=(LOS_INLOS | LOS_INRADAR | LOS_PREVLOS | LOS_CONTRADAR);
                }

                if(!(prevLosStatus&LOS_INRADAR)) {
                    globalAI->UnitEnteredRadar(this,a);
                }
                globalAI->UnitEnteredLos(this,a);
            }
        } else if(radarhandler->InRadar(this,a)) {
            if((losStatus[a] & LOS_INLOS)) {
                globalAI->UnitLeftLos(this,a);
                losStatus[a]&= ~LOS_INLOS;
            } else if(!(losStatus[a] & LOS_INRADAR)) {
                losStatus[a]|= LOS_INRADAR;
                globalAI->UnitEnteredRadar(this,a);
            }
        } else {
            if((losStatus[a]&LOS_INRADAR)) {
                if((losStatus[a]&LOS_INLOS)) {
                    globalAI->UnitLeftLos(this,a);
                    globalAI->UnitLeftRadar(this,a);
                } else {
                    globalAI->UnitLeftRadar(this,a);
                }
                losStatus[a]&= ~(LOS_INLOS | LOS_INRADAR | LOS_CONTRADAR);
            }
        }
    }
    if(paralyzeDamage>0) {
        paralyzeDamage-=maxHealth*(16.f/30.f/40.f);
        if(paralyzeDamage<0)
            paralyzeDamage=0;
        if(paralyzeDamage<health)
            stunned=false;
    }
    if(stunned) {
        isCloaked=false;
        return;
    }

    if(selfDCountdown && !stunned) {
        selfDCountdown--;
        if(selfDCountdown<=1) {
            if(!beingBuilt)
                KillUnit(true,false,0);
            else
                KillUnit(false,true,0);	//avoid unfinished buildings making an explosion
            selfDCountdown=0;
            return;
        }
        ENTER_MIXED;
        if(selfDCountdown&1 && team==gu->myTeam)
            info->AddLine("%s: Self destruct in %i s",unitDef->humanName.c_str(),selfDCountdown/2);
        ENTER_SYNCED;
    }

    if(beingBuilt) {
        if(lastNanoAdd<gs->frameNum-200) {
            health-=maxHealth/(buildTime*0.03);
            buildProgress-=1/(buildTime*0.03);
            AddMetal(metalCost/(buildTime*0.03));
            if(health<0)
                KillUnit(false,true,0);
        }
        return;
    }
    //below is stuff that shouldnt be run while being built

    lastSlowUpdate=gs->frameNum;

    commandAI->SlowUpdate();
    moveType->SlowUpdate();

    metalMake = metalMakeI + metalMakeold;
    metalUse = metalUseI+ metalUseold;
    energyMake = energyMakeI + energyMakeold;
    energyUse = energyUseI + energyUseold;
    metalMakeold = metalMakeI;
    metalUseold = metalUseI;
    energyMakeold = energyMakeI;
    energyUseold = energyUseI;

    metalMakeI=metalUseI=energyMakeI=energyUseI=0;

    AddMetal(unitDef->metalMake*0.5f);
    if(activated)
    {
        if(UseEnergy(unitDef->energyUpkeep*0.5f))
        {
            if(unitDef->isMetalMaker) {
                AddMetal(unitDef->makesMetal*0.5f*uh->metalMakerEfficiency);
                uh->metalMakerIncome+=unitDef->makesMetal;
            } else {
                AddMetal(unitDef->makesMetal*0.5f);
            }
            if(unitDef->extractsMetal>0)
                AddMetal(metalExtract * 0.5f);
        }
        UseMetal(unitDef->metalUpkeep*0.5f);

        if(unitDef->windGenerator>0)
        {
            if(wind.curStrength > unitDef->windGenerator)
            {
                AddEnergy(unitDef->windGenerator*0.5f);
            }
            else
            {
                AddEnergy(wind.curStrength*0.5f);
            }
        }
    }
    AddEnergy(energyTickMake*0.5f);

    if(health<maxHealth)
    {
        health += unitDef->autoHeal;

        if(restTime > unitDef->idleTime)
        {
            health += unitDef->idleAutoHeal;
        }
        if(health>maxHealth)
            health=maxHealth;
    }

    bonusShieldSaved+=0.05f;
    residualImpulse*=0.6;

    if(wantCloak) {
        if(helper->GetClosestEnemyUnitNoLosTest(pos,unitDef->decloakDistance,allyteam)) {
            curCloakTimeout=gs->frameNum+cloakTimeout;
            isCloaked=false;
        }
        if(isCloaked || gs->frameNum>=curCloakTimeout) {
            float cloakCost=unitDef->cloakCost;
            if(speed.SqLength()>0.2)
                cloakCost=unitDef->cloakCostMoving;
            if(UseEnergy(cloakCost * 0.5f)) {
                isCloaked=true;
            } else {
                isCloaked=false;
            }
        } else {
            isCloaked=false;
        }
    } else {
        isCloaked=false;
    }


    if(uh->waterDamage && (physicalState==CSolidObject::Floating || (physicalState==CSolidObject::OnGround && pos.y<=-3 && readmap->mipHeightmap[1][int((pos.z/(SQUARE_SIZE*2))*gs->hmapx+(pos.x/(SQUARE_SIZE*2)))]<-1))) {
        DoDamage(DamageArray()*uh->waterDamage,0,ZeroVector);
    }

    if(unitDef->canKamikaze) {
        if(fireState==2) {
            CUnit* u=helper->GetClosestEnemyUnitNoLosTest(pos,unitDef->kamikazeDist,allyteam);
            if(u && u->physicalState!=CSolidObject::Flying && u->speed.dot(pos - u->pos)<=0)		//self destruct when unit start moving away from mine, should maximize damage
                KillUnit(true,false,0);
        }
        if(userTarget && userTarget->pos.distance(pos)<unitDef->kamikazeDist)
            KillUnit(true,false,0);
        if(userAttackGround && userAttackPos.distance(pos)<unitDef->kamikazeDist)
            KillUnit(true,false,0);
    }

    if(!weapons.empty()) {
        haveTarget=false;
        haveUserTarget=false;

        //aircraft does not want this
        if (moveType->useHeading) {
            frontdir=GetVectorFromHeading(heading);
            if(upright || !unitDef->canmove) {
                updir=UpVector;
                rightdir=frontdir.cross(updir);
            } else {
                updir=ground->GetNormal(pos.x,pos.z);
                rightdir=frontdir.cross(updir);
                rightdir.Normalize();
                frontdir=updir.cross(rightdir);
            }
        }

        if(!dontFire) {
            for(vector<CWeapon*>::iterator wi=weapons.begin(); wi!=weapons.end(); ++wi) {
                CWeapon* w=*wi;
                if(userTarget && !w->haveUserTarget && (haveDGunRequest || !unitDef->canDGun || !w->weaponDef->manualfire))
                    w->AttackUnit(userTarget,true);
                else if(userAttackGround && !w->haveUserTarget && (haveDGunRequest || !unitDef->canDGun || !w->weaponDef->manualfire))
                    w->AttackGround(userAttackPos,true);

                w->SlowUpdate();

                if(w->targetType==Target_None && fireState>0 && lastAttacker && lastAttack+200>gs->frameNum)
                    w->AttackUnit(lastAttacker,false);
            }
        }
    }

    if(moveType->progressState == CMoveType::Active)
    {
        if(/*physicalState == OnGround*/seismicSignature && !(losStatus[gu->myAllyTeam] & LOS_INLOS) &&  radarhandler->InSeismicDistance(this, gu->myAllyTeam))
            new CSimpleGroundFlash(pos + float3(radarhandler->radarErrorSize[gu->myAllyTeam]*(0.5f-gu->usRandFloat()),0,radarhandler->radarErrorSize[gu->myAllyTeam]*(0.5f-gu->usRandFloat())), ph->seismictex, 30, 15, 0, seismicSignature, 1, float3(0.8,0.0,0.0));
    }

    CalculateTerrainType();
    UpdateTerrainType();
}
예제 #18
0
void CTransportUnit::KillUnit(bool selfDestruct, bool reclaimed, CUnit* attacker, bool)
{
	if (!isDead) {
		// guard against recursive invocation via
		//     transportee->KillUnit
		//     helper->Explosion
		//     helper->DoExplosionDamage
		//     unit->DoDamage
		//     unit->KillUnit
		// in the case that unit == this
		isDead = true;

		// ::KillUnit might be called multiple times while !deathScriptFinished,
		// but it makes no sense to kill/detach our transportees more than once
		std::list<TransportedUnit>::iterator ti;

		for (ti = transportedUnits.begin(); ti != transportedUnits.end(); ++ti) {
			CUnit* transportee = ti->unit;
			assert(transportee != this);

			if (transportee->isDead)
				continue;

			const float gh = ground->GetHeightReal(transportee->pos.x, transportee->pos.z);

			transportee->transporter = NULL;
			transportee->DeleteDeathDependence(this, DEPENDENCE_TRANSPORTER);

			// prevent a position teleport on the next movetype update if
			// the transport died in a place that the unit being carried
			// could not get to on its own
			if (!transportee->pos.IsInBounds()) {
				transportee->KillUnit(false, false, NULL, false);
				continue;
			} else {
				// immobile units can still be transported
				// via script trickery, guard against this
				if (!transportee->unitDef->IsAllowedTerrainHeight(gh)) {
					transportee->KillUnit(false, false, NULL, false);
					continue;
				}
			}

			if (!unitDef->releaseHeld) {
				if (!selfDestruct) {
					// we don't want it to leave a corpse
					transportee->DoDamage(DamageArray(1e6f), ZeroVector, NULL, -DAMAGE_EXTSOURCE_KILLED);
				}

				transportee->KillUnit(selfDestruct, reclaimed, attacker);
			} else {
				// place unit near the place of death of the transport
				// if it's a ground transport and uses a piece-in-ground method
				// to hide units
				if (transportee->pos.y < gh) {
					const float k = (transportee->radius + radius) * std::max(unitDef->unloadSpread, 1.0f);

					// try to unload in a presently unoccupied spot
					// unload on a wreck if suitable position not found
					for (int i = 0; i < 10; ++i) {
						float3 pos = transportee->pos;
						pos.x += (gs->randFloat() * 2 * k - k);
						pos.z += (gs->randFloat() * 2 * k - k);
						pos.y = ground->GetHeightReal(transportee->pos.x, transportee->pos.z);

						if (qf->GetUnitsExact(pos, transportee->radius + 2).empty()) {
							transportee->Move3D(pos, false);
							break;
						}
					}
				} else if (CGroundMoveType* mt = dynamic_cast<CGroundMoveType*>(transportee->moveType)) {
					mt->StartFlying();
				}

				transportee->moveType->SlowUpdate();
				transportee->moveType->LeaveTransport();

				// issue a move order so that unit won't try to return to pick-up pos in IdleCheck()
				if (unitDef->canfly && transportee->unitDef->canmove) {
					Command c(CMD_MOVE);
					c.params.push_back(transportee->pos.x);
					c.params.push_back(ground->GetHeightAboveWater(transportee->pos.x, transportee->pos.z));
					c.params.push_back(transportee->pos.z);
					transportee->commandAI->GiveCommand(c);
				}

				transportee->stunned = (transportee->paralyzeDamage > (modInfo.paralyzeOnMaxHealth? transportee->maxHealth: transportee->health));
				transportee->speed = speed * (0.5f + 0.5f * gs->randFloat());

				if (CBuilding* building = dynamic_cast<CBuilding*>(transportee)) {
					// this building may end up in a strange position, so kill it
					building->KillUnit(selfDestruct, reclaimed, attacker);
				}

				eventHandler.UnitUnloaded(transportee, this);
			}
		}

		transportedUnits.clear();

		// make sure CUnit::KillUnit does not return early
		isDead = false;
	}

	CUnit::KillUnit(selfDestruct, reclaimed, attacker);
}