Aircraft: steering code reliability - Page 2

Aircraft: steering code reliability

Discuss the source code and development of Spring Engine in general from a technical point of view. Patches go here too.

Moderator: Moderators

User avatar
MadRat
Posts: 532
Joined: 24 Oct 2006, 13:45

Post by MadRat »

So maxDrift doesn't really set the spread of a group of air units told to land at some specific point?
Archangel of Death
Posts: 854
Joined: 28 Jan 2005, 18:15

Post by Archangel of Death »

maxdrift is for aircraft that do not autoland (I forget the tag), to control how far from their initial position they are allowed to wander in their random float about wanderings.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Ok, I think I know what's wrong with the aircraft-steering-outside-the-world thing... I jumped into First Person View, and have discovered that aircraft that have drifted far, far away do no allow proper access to that POV.

My guess is that these two things might be related. The bigger issue is that aircraft need to be called back onto the map automatically if they leave it by more than a thousand units...
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Hrmm. Aircraft attempt to stop when they hit a waypoint or attack! This means that they're using ILMTitan's code for steering through turns.

It's very clear at this point that aircraft are getting bad data sent to them while steering. Something is passing a bad value to them, causing some (but not all) aircraft to steer wildly off-course. Once they get far enough off-course, they seem to be handed bizarre values for their desired position, which causes them to leave the map entirely. Not good...
10053r
Posts: 297
Joined: 28 Feb 2005, 19:19

Post by 10053r »

Could this be why aircraft seem to consume around 160%* of game resources of ground vehicles?

* In a test I just conducted, the game began to slow after I produced 333 aircraft, while it took 498 vehicles to affect the game speed on a 1.8 GHz Opteron 3000+ with 2 GB of RAM running Edgy Eft Ubuntu Linux.
User avatar
LordMatt
Posts: 3393
Joined: 15 May 2005, 04:26

Post by LordMatt »

10053r wrote:Could this be why aircraft seem to consume around 160%* of game resources of ground vehicles?

* In a test I just conducted, the game began to slow after I produced 333 aircraft, while it took 498 vehicles to affect the game speed on a 1.8 GHz Opteron 3000+ with 2 GB of RAM running Edgy Eft Ubuntu Linux.
uh it might be because aircrafts have another degree of freedom in which to move around in.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Matt, they don't use the full pathfinding code, so in theory they should use a lot fewer resources. And ground units that aren't ships use all three dimensions when steering and moving, I promise- that's how they do stuff like twist and turn and slow down while climbing hills...
User avatar
LordMatt
Posts: 3393
Joined: 15 May 2005, 04:26

Post by LordMatt »

Well, regardless I think argh should write more patches and then be given commit access. :-)
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

I only wish I could figure out the massive spaghetti mess that is the aircraft steering code, frankly, because then I *would* re-write it. Most of it's not terribly complicated- the actual calls to "how do I get from Point A to Point B" are pretty straightforward... it's the loops that are rather disconcerting, plus the exceptions for special behaviors, like the "I'm crashing" code :P

If anybody is willing to help me figure it out, I could put the source up here and gradually work through it. I just lack enough C++ experience to take on something like this without some assistance for certain issues, and I don't want to make things worse...
User avatar
Dragon45
Posts: 2883
Joined: 16 Aug 2004, 04:36

Post by Dragon45 »

Just post the source, im sure some competent coder will be bored enough at some point to look at it and offer fixes here and there :P
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

Code: Select all

#include "StdAfx.h"
#include "AirMoveType.h"
#include "Sim/Misc/QuadField.h"
#include "Map/Ground.h"
#include "Sound.h"
#include "Sim/Misc/LosHandler.h"
#include "Sim/Projectiles/SmokeProjectile.h"
#include "Game/GameHelper.h"
#include "Sim/Weapons/Weapon.h"
#include "Rendering/UnitModels/3DOParser.h"
#include "Sim/Misc/RadarHandler.h"
#include "Sim/Units/COB/CobFile.h"
#include "Sim/Units/COB/CobInstance.h"
#include "LogOutput.h"
#include "Sim/Units/UnitDef.h"
#include "Game/Player.h"
#include "Sim/Misc/GeometricObjects.h"
#include "Mobility.h"
#include "myMath.h"

CR_BIND_DERIVED(CAirMoveType, CMoveType, (NULL));
CR_BIND(CAirMoveType::DrawLine, );
CR_BIND(CAirMoveType::RudderInfo, );

CR_REG_METADATA(CAirMoveType, (
		CR_MEMBER(subState),

		CR_MEMBER(maneuver),
		CR_MEMBER(maneuverSubState),

		CR_MEMBER(loopbackAttack),
		CR_MEMBER(isFighter),

		CR_MEMBER(wingDrag),
		CR_MEMBER(wingAngle),
		CR_MEMBER(invDrag),
		CR_MEMBER(frontToSpeed),
		CR_MEMBER(speedToFront),
		CR_MEMBER(myGravity),

		CR_MEMBER(maxBank),
		CR_MEMBER(maxPitch),
		CR_MEMBER(maxSpeed),
		CR_MEMBER(turnRadius),
		CR_MEMBER(wantedHeight),

		CR_MEMBER(maxAcc),
		CR_MEMBER(maxAileron),
		CR_MEMBER(maxElevator),
		CR_MEMBER(maxRudder),

		CR_MEMBER(goalPos),

		CR_MEMBER(inSupply),

		CR_MEMBER(reservedLandingPos),

		CR_MEMBER(mySide),
		CR_MEMBER(crashAileron),
		CR_MEMBER(crashElevator),
		CR_MEMBER(crashRudder),

		CR_MEMBER(oldpos),
		CR_MEMBER(oldGoalPos),
		CR_MEMBER(oldSlowUpdatePos),

		CR_MEMBER(lines),

		CR_MEMBER(rudder),
		CR_MEMBER(elevator),
		CR_MEMBER(aileronRight),
		CR_MEMBER(aileronLeft),
		CR_MEMBER(rudders),

		CR_MEMBER(lastRudderUpdate),
		CR_MEMBER(lastRudderPos),
		CR_MEMBER(lastElevatorPos),
		CR_MEMBER(lastAileronPos),
	
		CR_MEMBER(inefficientAttackTime),
		CR_MEMBER(exitVector),

		CR_MEMBER(lastColWarning),
		CR_MEMBER(lastColWarningType),

		CR_MEMBER(repairBelowHealth),
		CR_MEMBER(reservedPad),
		CR_MEMBER(padStatus)));

CR_REG_METADATA_SUB(CAirMoveType, DrawLine, (
		CR_MEMBER(pos1), CR_MEMBER(pos2),
		CR_MEMBER(color)));

CR_REG_METADATA_SUB(CAirMoveType, RudderInfo, (CR_MEMBER(rotation)));


CAirMoveType::CAirMoveType(CUnit* owner):
	CMoveType(owner),
	wingDrag(0.07f),
	wingAngle(0.1f),
	frontToSpeed(0.04f),
	speedToFront(0.01f),
	maxBank(0.55f),
	maxPitch(0.35f),
	maxAcc(0.006f),
	maxAileron(0.04f),
	maxElevator(0.02f),
	maxRudder(0.01f),
	goalPos(1000,0,1000),
	wantedHeight(80),
	invDrag(0.995f),
	aircraftState(AIRCRAFT_LANDED),
	inSupply(0),
	subState(0),
	myGravity(0.8f),
	mySide(1),
	isFighter(false),
	oldpos(0,0,0),
	oldGoalPos(0,1,0),
	maneuver(0),
	maneuverSubState(0),
	inefficientAttackTime(0),
	reservedLandingPos(-1,-1,-1),
	oldSlowUpdatePos(-1,-1,-1),
	lastColWarning(0),
	lastColWarningType(0),
	repairBelowHealth(0.30f),
	padStatus(0),
	reservedPad(0),
	loopbackAttack(false)
{
	turnRadius=150;
	owner->mapSquare+=1;						//to force los recalculation

	//From Aircraft::Init
	maxRudder*=0.99f+gs->randFloat()*0.02f;
	maxElevator*=0.99f+gs->randFloat()*0.02f;
	maxAileron*=0.99f+gs->randFloat()*0.02f;
	maxAcc*=0.99f+gs->randFloat()*0.02f;

	crashAileron=1-gs->randFloat()*gs->randFloat();
	if(gs->randInt()&1)
		crashAileron=-crashAileron;
	crashElevator=gs->randFloat();
	crashRudder=gs->randFloat()-0.5f;

	lastRudderUpdate=gs->frameNum;
	lastElevatorPos=0;
	lastRudderPos=0;
	lastAileronPos=0;

	exitVector=gs->randVector();
	if(exitVector.y<0)
		exitVector.y=-exitVector.y;
	exitVector.y+=1;
}

CAirMoveType::~CAirMoveType(void)
{
	if(reservedPad){
		airBaseHandler->LeaveLandingPad(reservedPad);
		reservedPad=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.distance2D(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(unit->unitDef->buildSpeed/30,unit);
			owner->currentFuel = 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){
				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()) {
		logOutput.Print("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);
		SAFE_NEW CSmokeProjectile(owner->midPos,gs->randVector()*0.08f,100+gs->randFloat()*50,5,0.2f,owner,0.4f);
		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.1f).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.1f).SqLength();
						owner->DoDamage(DamageArray()*damage,0,ZeroVector);
						(*ui)->DoDamage(DamageArray()*damage,0,ZeroVector);
						owner->speed*=0.99f;
					}
				}
			}
		}
		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 CAirMoveType::SlowUpdate(void)
{
	if(aircraftState!=AIRCRAFT_LANDED && owner->unitDef->maxFuel>0)
		owner->currentFuel = max (0.f, owner->currentFuel - (16.f/GAME_SPEED));

	if(!reservedPad && aircraftState==AIRCRAFT_FLYING && owner->health<owner->maxHealth*repairBelowHealth){
		CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,owner->unitDef->minAirBasePower);
		if(lp){
			AddDeathDependence(lp);
			reservedPad=lp;
			padStatus=0;
			oldGoalPos=goalPos;
		}
	}
	if(owner->pos!=oldSlowUpdatePos){
		oldSlowUpdatePos=owner->pos;
		if(owner->pos.y - ground->GetApproximateHeight(owner->pos.x, owner->pos.z) > wantedHeight * 5 + 100)	//try to handle aircraft getting unlimited height
			owner->pos.y = ground->GetApproximateHeight(owner->pos.x, owner->pos.z) + wantedHeight * 5  + 100;

		int newmapSquare=ground->GetSquare(owner->pos);
		if(newmapSquare!=owner->mapSquare){
			owner->mapSquare=newmapSquare;
			float oldlh=owner->losHeight;
			float h=owner->pos.y-ground->GetApproximateHeight(owner->pos.x,owner->pos.z);
			owner->losHeight=h+5;
			loshandler->MoveUnit(owner,false);
			if(owner->hasRadarCapacity)
				radarhandler->MoveUnit(owner);

			owner->losHeight=oldlh;
		}
		qf->MovedUnit(owner);
		owner->isUnderWater=owner->pos.y+owner->model->height<0;
	}
}

void CAirMoveType::UpdateManeuver(void)
{
#ifdef DEBUG_AIRCRAFT
	if(selectedUnits.selectedUnits.find(this)!=selectedUnits.selectedUnits.end()){
		logOutput.Print("UpdataMan %i %i",maneuver,maneuverSubState);
	}
#endif
	float speedf=owner->speed.Length();
	switch(maneuver){
	case 1:{	//immelman 
		int aileron=0,elevator=0;
		if(owner->updir.y>0){
			if(owner->rightdir.y>maxAileron*speedf){
				aileron=1;
			}else if(owner->rightdir.y<-maxAileron*speedf){
				aileron=-1;
			}
		}
		if(fabs(owner->rightdir.y)<maxAileron*3*speedf || owner->updir.y<0)
			elevator=1;
		UpdateAirPhysics(0,aileron,elevator,1,owner->frontdir);
		if((owner->updir.y<0 && owner->frontdir.y<0) || speedf<0.8f)
			maneuver=0;
		if(owner->pos.y - ground->GetApproximateHeight(owner->pos.x, owner->pos.z) > wantedHeight * 4)	//some seem to report that the "unlimited altitude" thing is because of these maneuvers
			maneuver = 0;
		break;}
	case 2:{	//inverted immelman
		int aileron=0,elevator=0;
		if(maneuverSubState==0){
			if(owner->rightdir.y>=0){
				aileron=-1;
			}else{
				aileron=1;
			}
		}
		if(owner->frontdir.y<-0.7f)
			maneuverSubState=1;
		if(maneuverSubState==1 || owner->updir.y<0)
			elevator=1;
		UpdateAirPhysics(0,aileron,elevator,1,owner->frontdir);

		if((owner->updir.y>0 && owner->frontdir.y>0 && maneuverSubState==1) || speedf<0.2f)
			maneuver=0;
		break;}
	default:
		UpdateAirPhysics(0,0,0,1,owner->frontdir);
		maneuver=0;
		break;
	}
}


void CAirMoveType::UpdateFighterAttack(void)
{
	float3 &pos = owner->pos;
	SyncedFloat3 &rightdir = owner->rightdir;
	SyncedFloat3 &frontdir = owner->frontdir;
	SyncedFloat3 &updir = owner->updir;
	float3 &speed = owner->speed;

	float speedf=owner->speed.Length();
	if(speedf<0.01f){
		UpdateAirPhysics(0,0,0,1,owner->frontdir);
		return;
	}

	if(!((gs->frameNum+owner->id)&3))
		CheckForCollision();

	bool groundTarget=!owner->userTarget || !owner->userTarget->unitDef->canfly;
	bool airTarget=owner->userTarget && owner->userTarget->unitDef->canfly && !owner->userTarget->unitDef->hoverAttack;	//only "real" aircrafts (non gunship)
	if(groundTarget){
		if(frontdir.dot(goalPos-pos)<0 && (pos-goalPos).SqLength()<turnRadius*turnRadius*(loopbackAttack?4:1)){
			float3 dif=pos-goalPos;
			dif.y=0;
			dif.Normalize();
			goalPos=goalPos+dif*turnRadius*4;
		} else if (loopbackAttack && !airTarget){
			bool hasFired=false;
			if(!owner->weapons.empty() && owner->weapons[0]->reloadStatus > gs->frameNum && owner->weapons[0]->salvoLeft==0)
				hasFired=true;
			if( frontdir.dot(goalPos-pos)<owner->maxRange*(hasFired?1.0f:0.7f)){
				maneuver=1;
			}
		} else if(frontdir.dot(goalPos-pos)<owner->maxRange*0.7f){
			goalPos+=exitVector*(owner->userTarget?owner->userTarget->radius+owner->radius+10:owner->radius+40);
		}
	}
	float3 tgp=goalPos+(goalPos-oldGoalPos)*8;
	oldGoalPos=goalPos;
	goalPos=tgp;

	float goalLength=(goalPos-pos).Length();
	float3 goalDir=(goalPos-pos)/goalLength;
	
	float aileron=0;
	float rudder=0;
	float elevator=0;
	float engine=0;

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

	float goalDot=rightdir.dot(goalDir);
	goalDot/=goalDir.dot(frontdir)*0.5f+0.501f;

	if(goalDir.dot(frontdir)<-0.2f+inefficientAttackTime*0.002f && frontdir.y>-0.2f && speedf>2.0f && gs->randFloat()>0.996f)
		maneuver=1;

	if(goalDir.dot(frontdir)<-0.2f+inefficientAttackTime*0.002f && fabs(frontdir.y)<0.2f && gs->randFloat()>0.996f && gHeight+400<pos.y){
		maneuver=2;
		maneuverSubState=0;
	}

	//roll
	if(speedf>0.45f && pos.y+owner->speed.y*60*fabs(frontdir.y)+min(0.f,updir.y)*150>gHeight+60+fabs(rightdir.y)*150){
		float goalBankDif=goalDot+rightdir.y*0.2f;
		if(goalBankDif>maxAileron*speedf*4){
			aileron=1;
		} else if(goalBankDif<-maxAileron*speedf*4){
			aileron=-1;
		} else {
			aileron=goalBankDif/(maxAileron*speedf*4);
		}
	} else {
		if(rightdir.y>0){
			if(rightdir.y>maxAileron*speedf || frontdir.y<-0.7f)
				aileron=1;
			else
				aileron=rightdir.y/(maxAileron*speedf);
		} else {
			if(rightdir.y<-maxAileron*speedf || frontdir.y<-0.7f)
				aileron=-1;
			else 
				aileron=rightdir.y/(maxAileron*speedf);
		}
	}

	//yaw
	if(pos.y>gHeight+30){
		if(goalDot<-maxRudder*speedf){
			rudder=-1;
		} else if(goalDot>maxRudder*speedf){
			rudder=1;
		} else {
			rudder=goalDot/(maxRudder*speedf);
		}
	}

	float upside=1;
	if(updir.y<-0.3f)
		upside=-1;

	//pitch
	if(speedf<1.5f){
		if(frontdir.y<0.0f){
			elevator=upside;
		} else if(frontdir.y>0.0f){
			elevator=-upside;
		}
	} else {
		float gHeight2=ground->GetHeight(pos.x+speed.x*40,pos.z+speed.z*40);
		float hdif=max(gHeight,gHeight2)+60-pos.y-frontdir.y*speedf*20;
		float minPitch;//=min(1.0f,hdif/(maxElevator*speedf*speedf*20));

		if(hdif<-(maxElevator*speedf*speedf*20)){
			minPitch=-1;
		} else if(hdif>(maxElevator*speedf*speedf*20)){
			minPitch=1;
		} else {
			minPitch=hdif/(maxElevator*speedf*speedf*20);
		}

/*		if(pos.y+min(0,owner->speed.y)*70*fabs(frontdir.y)+min(0,updir.y)*50<gHeight+50){
		if(frontdir.y<0.5f){
			elevator=upside;
		} else if(frontdir.y>0.55f){
			elevator=-upside;
		}*/
//	} else {
		if(lastColWarning && lastColWarningType==2 && frontdir.dot(lastColWarning->pos+lastColWarning->speed*20-pos-owner->speed*20)<0){
/*			float pitchMod=updir.y>0?1:-1;
			if(lastColWarning->pos.y>pos.y)
				elevator=-pitchMod;
			else
				elevator=pitchMod;
/*/			elevator=updir.dot(lastColWarning->midPos-owner->midPos)>0?-1:1;/**/
		} else {
			float hdif=goalDir.dot(updir);
			if(hdif<-maxElevator*speedf){
				elevator=-1;
			} else if(hdif>maxElevator*speedf){
				elevator=1;
			} else {
				elevator=hdif/(maxElevator*speedf);
			}
		}
		if(elevator*upside<minPitch)
			elevator=minPitch*upside;
	}
#ifdef DEBUG_AIRCRAFT
	if(selectedUnits.selectedUnits.find(this)!=selectedUnits.selectedUnits.end()){
		logOutput.Print("FAttack %.1f %.1f %.2f",pos.y-gHeight,goalLength,goalDir.dot(frontdir));
	}
#endif

	if(groundTarget)
		engine=1;
	else
		engine=min(1.f,(float)(goalLength/owner->maxRange+1-goalDir.dot(frontdir)*0.7f));

	UpdateAirPhysics(rudder,aileron,elevator,engine,owner->frontdir);
/*
	std::vector<CWeapon*>::iterator wi;
	for(wi=owner->weapons.begin();wi!=owner->weapons.end();++wi){
		(*wi)->targetPos=goalPos;
		if(owner->userTarget){
			(*wi)->AttackUnit(owner->userTarget,true);
		}
	}*/
/*	DrawLine dl;
	dl.color=UpVector;
	dl.pos1=pos;
	dl.pos2=goalPos;
	lines.push_back(dl);
	dl.color=float3(1,0,0);
	dl.pos1=pos;
	dl.pos2=pos+frontdir*maxRange;
	lines.push_back(dl);/**/
}


void CAirMoveType::UpdateAttack(void)
{
/*	std::vector<CWeapon*>::iterator wi;
	for(wi=owner->weapons.begin();wi!=owner->weapons.end();++wi){
		(*wi)->targetPos=goalPos;
		if(owner->userTarget){
			(*wi)->AttackUnit(owner->userTarget,true);
		}
	}
*/
	UpdateFlying(wantedHeight,1);
}


void CAirMoveType::UpdateFlying(float wantedHeight,float engine)
{
	float3 &pos = owner->pos;
	SyncedFloat3 &rightdir = owner->rightdir;
	SyncedFloat3 &frontdir = owner->frontdir;
	SyncedFloat3 &updir = owner->updir;
	float3 &speed = owner->speed;

	float speedf=speed.Length();
	float goalLength=(goalPos-pos).Length();
	float3 goalDir=(goalPos-pos)/goalLength;
	goalDir.Normalize();

	float aileron=0;
	float rudder=0;
	float elevator=0;

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

	if(!((gs->frameNum+owner->id)&3))
		CheckForCollision();

	float otherThreat=0;
	float3 otherDir;
	if(lastColWarning){
		float3 otherDif=lastColWarning->pos-pos;
		float otherLength=otherDif.Length();
		otherDir=otherDif/otherLength;
		otherThreat=max(1200.f,goalLength)/otherLength*0.036f;
	}

	float goalDot=rightdir.dot(goalDir);
	goalDot/=goalDir.dot(frontdir)*0.5f+0.501f;
	if(goalDir.dot(frontdir)<-0.1f && goalLength<turnRadius
#ifdef DIRECT_CONTROL_ALLOWED
		&& (!owner->directControl || owner->directControl->mouse2)
#endif
		)
		goalDot=-goalDot;
	if(lastColWarning){
		goalDot-=otherDir.dot(rightdir)*otherThreat;
	}
	//roll
	if(speedf>1.5f && pos.y+speed.y*10>gHeight+wantedHeight*0.6f){
		float goalBankDif=goalDot+rightdir.y*0.5f;
		if(goalBankDif>maxAileron*speedf*4 && rightdir.y>-maxBank){
			aileron=1;
		} else if(goalBankDif<-maxAileron*speedf*4 && rightdir.y<maxBank){
			aileron=-1;
		} else {
			if(fabs(rightdir.y)<maxBank)
				aileron=goalBankDif/(maxAileron*speedf*4);
			else {
				if(rightdir.y<0 && goalBankDif<0)
					aileron=-1;
				else if(rightdir.y>0 && goalBankDif>0)
					aileron=1;
			}
		}
	} else {
		if(rightdir.y>0.01f){
			aileron=1;
		} else if(rightdir.y<-0.01f){
			aileron=-1;
		}
	}

	//yaw
	if(pos.y>gHeight+15){
		if(goalDot<-maxRudder*speedf*2){
			rudder=-1;;
		} else if(goalDot>maxRudder*speedf*2){
			rudder=1;
		} else {
			rudder=goalDot/(maxRudder*speedf*2);
		}
	}

	//pitch
	if(speedf>0.8f){
		if(lastColWarningType==2 && frontdir.dot(lastColWarning->midPos+lastColWarning->speed*20 - owner->midPos - owner->speed*20)<0){
/*			float pitchMod=updir.y>0?1:-1;
			if(lastColWarning->pos.y>pos.y)
				elevator=-pitchMod;
			else
				elevator=pitchMod;
/*/			elevator=updir.dot(lastColWarning->midPos-owner->midPos)>0?-1:1;/**/
		} else {
			float gHeight2=ground->GetHeight(pos.x+speed.x*40,pos.z+speed.z*40);
			float hdif=max(gHeight,gHeight2)+wantedHeight-pos.y-frontdir.y*speedf*20;
			if(hdif<-(maxElevator*speedf*speedf*20) && frontdir.y>-maxPitch){
				elevator=-1;
			} else if(hdif>(maxElevator*speedf*speedf*20) && frontdir.y<maxPitch){
				elevator=1;
			} else {
				if(fabs(frontdir.y)<maxPitch)
					elevator=hdif/(maxElevator*speedf*speedf*20);
			}
		}
	} else {
		if (frontdir.y < -0.1f) {
			elevator = 1;
		} else if (frontdir.y > 0.15f) {
			elevator = -1;
		}
	}

	UpdateAirPhysics(rudder, aileron, elevator, engine, owner->frontdir);
}



void CAirMoveType::UpdateLanded(void)
{
	float3 &pos = owner->pos;
	SyncedFloat3 &rightdir = owner->rightdir;
	SyncedFloat3 &frontdir = owner->frontdir;
	SyncedFloat3 &updir = owner->updir;
	float3 &speed = owner -> speed;

	speed = ZeroVector;

	updir = ground->GetNormal(pos.x, pos.z);
	frontdir.Normalize();
	rightdir = frontdir.cross(updir);
	rightdir.Normalize();
	updir = rightdir.cross(frontdir);
}

void CAirMoveType::UpdateTakeOff(float wantedHeight)
{
	float3& pos=owner->pos;
	float3& speed=owner->speed;

	float h = 0.0f;
	if (owner->unitDef->canSubmerge)
		h = pos.y - ground->GetApproximateHeight(pos.x, pos.z);
	else
		h = pos.y - ground->GetHeight(pos.x,pos.z);
	
	if (h > wantedHeight)
		SetState(AIRCRAFT_FLYING);

	if ((h + speed.y * 20) < (wantedHeight * 1.02f))
		speed.y += maxAcc;
	else
		speed.y -= maxAcc;

	if (h > wantedHeight * 0.4f)
		speed += owner->frontdir * maxAcc;

	speed *= invDrag;

	owner->frontdir.Normalize();
	owner->rightdir = owner->frontdir.cross(owner->updir);
	owner->rightdir.Normalize();
	owner->updir = owner->rightdir.cross(owner->frontdir);

	pos += speed;
	owner->midPos = pos + (owner->frontdir * owner->relMidPos.z) + (owner->updir * owner->relMidPos.y) + (owner->rightdir * owner->relMidPos.x);
}

void CAirMoveType::UpdateLanding(void)
{
	float3 &pos = owner->pos;
	SyncedFloat3 &rightdir = owner->rightdir;
	SyncedFloat3 &frontdir = owner->frontdir;
	SyncedFloat3 &updir = owner->updir;
	float3 &speed = owner->speed;
	float speedf = speed.Length();

	// find a landing spot if we dont have one
	if (reservedLandingPos.x < 0) {
		reservedLandingPos = FindLandingPos();
		if (reservedLandingPos.x > 0) {
			reservedLandingPos.y += wantedHeight;
			float3 tp = pos;
			pos = reservedLandingPos;
			owner->physicalState = CSolidObject::OnGround;
			owner->Block();
			owner->physicalState = CSolidObject::Flying;
			pos = tp;
			owner->Deactivate();
			owner->cob->Call(COBFN_StopMoving);
		} else {
			goalPos.CheckInBounds();
			UpdateFlying(wantedHeight, 1);
			return;
		}
	} else {
		// see if landing spot is still empty
		/*
		float3 tpos = owner->pos;
		owner->pos = reservedLandingPos;
		int2 mp = owner->GetMapPos();
		owner->pos = tpos;

		for (int z = mp.y; z < mp.y + owner -> ysize; z++) {
			for (int x = mp.x; x < mp.x + owner -> xsize; x++) {
				if (readmap -> groundBlockingObjectMap[z * gs -> mapx + x] != owner){
					owner -> UnBlock();
					reservedLandingPos.x = -1;
					UpdateFlying(wantedHeight, 1);
					return;
				}
			}
		}*/
	}

	// update our speed
	float3 dif = reservedLandingPos - pos;
	float dist = dif.Length();
	dif /= dist;
	
	float wsf = min(owner->unitDef->speed, dist / speedf * 1.8f * maxAcc);
	float3 wantedSpeed = dif * wsf;

	float3 delta = wantedSpeed - speed;
	float dl = delta.Length();

	if (dl < maxAcc * 3)
		speed = wantedSpeed;
	else
		speed += delta / dl * maxAcc * 3;

	pos += speed;

	// make the aircraft right itself up and turn toward goal
	if (rightdir.y < -0.01f)
		updir -= rightdir * 0.02f;
	else if (rightdir.y > 0.01f)
		updir += rightdir * 0.02f;

	if (frontdir.y < -0.01f)
		frontdir += updir * 0.02f;
	else if (frontdir.y > 0.01f)
		frontdir -= updir * 0.02f;

	if (rightdir.dot(dif) > 0.01f)
		frontdir += rightdir * 0.02f;
	else if (rightdir.dot(dif) < -0.01f)
		frontdir -= rightdir * 0.02f;

	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);

	// see if we are at the reserved (not user-clicked) landing spot
	if (dist < 1) {
		float gh = ground -> GetHeight(pos.x, pos.z);
		float gah = ground -> GetApproximateHeight(pos.x, pos.z);
		float alt = 0.0f;

		// can we submerge and are we still above water?
		if ((owner -> unitDef -> canSubmerge) && (gah < 0)) {
			alt = pos.y - gah;
			reservedLandingPos.y = gah;
		}
		else {
			alt = pos.y - gh;
			reservedLandingPos.y = gh;
		}

		if (alt <= 1)
			SetState(AIRCRAFT_LANDED);
	}
}



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;

	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.2f)
			engine=max(0.0f,min(engine,1-(pos.y-gHeight-wantedHeight*1.2f)/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.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)
					owner->DoDamage(DamageArray()*(damage*0.4f),0,ZeroVector);	//only do damage while stunned for now
			}
			pos.y=gHeight+owner->model->radius*0.2f+0.01f;
			speed+=gNormal*(impactSpeed*1.5f);
			speed*=0.95f;
			updir=gNormal-frontdir*0.1f;
			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()){
		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
}

void CAirMoveType::SetState(CAirMoveType::AircraftState state)
{
	if(aircraftState==AIRCRAFT_CRASHING || state==aircraftState)
		return;

/*	if (state == AIRCRAFT_LANDING)
		owner->cob->Call(COBFN_Deactivate);
		else if (state == aircraft_flying)
		//cob->Call(COBFN_Activate); */
	
	if (state == AIRCRAFT_FLYING) {
		owner->Activate();
		owner->cob->Call(COBFN_StartMoving);
	}

	if(state==AIRCRAFT_LANDED){
		owner->heading=GetHeadingFromVector(owner->frontdir.x, owner->frontdir.z);
		owner->physicalState = CSolidObject::OnGround;
		owner->useAirLos=false;
	}else{
		owner->physicalState = CSolidObject::Flying;
		owner->useAirLos=true;
		if(state!=AIRCRAFT_LANDING){
			reservedLandingPos.x=-1;
			owner->UnBlock();
		}
	}

	if(aircraftState==AIRCRAFT_LANDED && reservedLandingPos.x>0){
		reservedLandingPos.x=-1;
	}
	subState=0;
	if(state!=AIRCRAFT_TAKEOFF || aircraftState==AIRCRAFT_LANDED)		//make sure we only go into takeoff if actually landed
		aircraftState=state;
	else
		aircraftState=AIRCRAFT_TAKEOFF;
}

void CAirMoveType::ImpulseAdded(void)
{
	if(aircraftState==AIRCRAFT_FLYING){
		owner->speed+=owner->residualImpulse;
		owner->residualImpulse=ZeroVector;
	}
}



float3 CAirMoveType::FindLandingPos(void)
{
	float3 ret(-1, -1, -1);
	float3 tryPos = owner->pos + owner->speed * owner->speed.Length() / (maxAcc * 3);
	tryPos.y = ground -> GetHeight2(tryPos.x, tryPos.z);

	if ((tryPos.y < 0) && !(owner -> unitDef -> floater || owner -> unitDef -> canSubmerge)) {
		return ret;
	}

	tryPos.CheckInBounds();

	float3 tpos = owner->pos;
	owner->pos = tryPos;
	int2 mp = owner->GetMapPos();
	owner->pos = tpos;

	for (int z = mp.y; z < mp.y + owner -> ysize; z++) {
		for (int x = mp.x; x < mp.x + owner -> xsize; x++) {
			if (readmap -> groundBlockingObjectMap[z * gs->mapx + x]) {
				return ret;
			}
		}
	}

	if (ground -> GetSlope(tryPos.x, tryPos.z) > 0.03f)
		return ret;

	return tryPos;
}



void CAirMoveType::CheckForCollision(void)
{
	SyncedFloat3& pos=owner->midPos;
	SyncedFloat3& forward=owner->frontdir;
	float3 midTestPos=pos+forward*121;

	std::vector<CUnit*> others=qf->GetUnitsExact(midTestPos,115);

	float dist=200;
	if(lastColWarning){
		DeleteDeathDependence(lastColWarning);
		lastColWarning=0;
		lastColWarningType=0;
	}

	for(std::vector<CUnit*>::iterator ui=others.begin();ui!=others.end();++ui){
		if(*ui==owner || !(*ui)->unitDef->canfly)
			continue;
		SyncedFloat3& op=(*ui)->midPos;
		float3 dif=op-pos;
		float3 forwardDif=forward*(forward.dot(dif));
		if(forwardDif.SqLength()<dist*dist){
			float frontLength=forwardDif.Length();
			float3 ortoDif=dif-forwardDif;
			float minOrtoDif=((*ui)->radius+owner->radius)*2+frontLength*0.1f+10;		//note that the radiuses is multiplied by two since we rely on the aircrafts having to small radiuses (see unitloader)
			if(ortoDif.SqLength()<minOrtoDif*minOrtoDif){
				dist=frontLength;
				lastColWarning=(*ui);
			}
		}
	}
	if(lastColWarning){
		lastColWarningType=2;
		AddDeathDependence(lastColWarning);
		return;
	}
	for(std::vector<CUnit*>::iterator ui=others.begin();ui!=others.end();++ui){
		if(*ui==owner)
			continue;
		if(((*ui)->midPos-pos).SqLength()<dist*dist){
			lastColWarning=*ui;
		}
	}
	if(lastColWarning){
		lastColWarningType=1;
		AddDeathDependence(lastColWarning);
	}
	return;
}

void CAirMoveType::DependentDied(CObject* o)
{
	if(o==reservedPad){
		reservedPad=0;
		SetState(AIRCRAFT_FLYING);
		goalPos=oldGoalPos;
	}

	if(o==lastColWarning){
		lastColWarning=0;
		lastColWarningType=0;
	}
	CMoveType::DependentDied(o);
}

void CAirMoveType::SetMaxSpeed(float speed)
{
	maxSpeed=speed;
	if(owner->unitDef->maxAcc!=0 && owner->unitDef->speed!=0){
		float drag=1.0f/(maxSpeed/GAME_SPEED*1.1f/maxAcc) - wingAngle*wingAngle*wingDrag;		//meant to set the drag such that the maxspeed becomes what it should be
		invDrag = 1-drag;
	}
}
User avatar
Guessmyname
Posts: 3301
Joined: 28 Apr 2005, 21:07

Post by Guessmyname »

saucy

I can actually understand bits of that...
Tobi
Spring Developer
Posts: 4598
Joined: 01 Jun 2005, 11:36

Post by Tobi »

rather post diff instead of full source, so it's actually possible to see what you modified...
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Post by Argh »

I just posted full source as a reference.
User avatar
MadRat
Posts: 532
Joined: 24 Oct 2006, 13:45

Post by MadRat »

Funny, we've got the ability to set turnRadius, yet one of the first groups of functions defined it as 150. So much for the unit's fbi.

Is this comment in the //pitch section a typo?

/*/ elevator=updir.dot(lastColWarning->midPos-owner->midPos)>0?-1:1;/**/

Shouldn't a comment be within a /* ... */ format?
Post Reply

Return to “Engine”