taairmovetype.cpp

来自「这是整套横扫千军3D版游戏的源码」· C++ 代码 · 共 1,048 行 · 第 1/2 页

CPP
1,048
字号
#include "StdAfx.h"
#include "TAAirMoveType.h"
#include "Sim/Misc/QuadField.h"
#include "Map/Ground.h"
#include "Sim/Misc/LosHandler.h"
#include "Sim/Misc/RadarHandler.h"
#include "Sim/Units/COB/CobFile.h"
#include "Sim/Units/COB/CobInstance.h"
#include "LogOutput.h"
#include "myMath.h"
#include "Matrix44f.h"
#include "Rendering/UnitModels/3DOParser.h"
#include "Game/Player.h"
#include "Sim/Units/UnitDef.h"
#include "Sim/Misc/GeometricObjects.h"
#include "Mobility.h"
#include "Sim/Units/UnitTypes/TransportUnit.h"
#include "Sim/Units/CommandAI/CommandAI.h"

CR_BIND_DERIVED(CTAAirMoveType, CMoveType, (NULL));

CR_REG_METADATA(CTAAirMoveType, (
	CR_MEMBER(dontCheckCol),

	CR_MEMBER(goalPos),
	CR_MEMBER(oldpos),
	CR_MEMBER(wantedHeight),
	CR_MEMBER(orgWantedHeight),

	CR_MEMBER(reservedLandingPos),
	CR_MEMBER(circlingPos),
	CR_MEMBER(goalDistance),
	CR_MEMBER(waitCounter),
	CR_MEMBER(wantToStop),

	CR_MEMBER(wantedHeading),

	CR_MEMBER(wantedSpeed),
	CR_MEMBER(deltaSpeed),

	CR_MEMBER(currentBank),
	CR_MEMBER(currentPitch),

	CR_MEMBER(turnRate),
	CR_MEMBER(accRate),
	CR_MEMBER(decRate),
	CR_MEMBER(altitudeRate),

	CR_MEMBER(breakDistance),
	CR_MEMBER(dontLand),
	CR_MEMBER(lastMoveRate),

	CR_MEMBER(forceHeading),
	CR_MEMBER(forceHeadingTo),

	CR_MEMBER(maxDrift),

	CR_MEMBER(lastColWarning),
	CR_MEMBER(lastColWarningType),
	CR_MEMBER(collide),

	CR_MEMBER(repairBelowHealth),
	CR_MEMBER(reservedPad),
	CR_MEMBER(padStatus),
	CR_MEMBER(oldGoalPos),

	CR_MEMBER(autoLand),
	CR_RESERVED(63)
	));


CTAAirMoveType::CTAAirMoveType(CUnit* owner) :
	CMoveType(owner),
	aircraftState(AIRCRAFT_LANDED),
	wantedHeight(80),
	altitudeRate(3.0f),
	currentBank(0),
	// we want to take off in direction of factory facing
	wantedHeading(owner? GetHeadingFromFacing(owner->buildFacing): 0),
	wantToStop(false),
	forceHeading(false),
	dontCheckCol(false),
	dontLand(false),
	collide(true),
	lastMoveRate(0),
	waitCounter(0),
	deltaSpeed(ZeroVector),
	accRate(1),
	breakDistance(1),
	circlingPos(ZeroVector),
	decRate(1),
	flyState(FLY_CRUISING),
	forceHeadingTo(wantedHeading),
	goalDistance(1),
	goalPos(owner? owner->pos:float3(0, 0, 0)),
	oldGoalPos(owner? owner->pos:float3(0, 0, 0)),
	turnRate(1),
	wantedSpeed(ZeroVector),
	lastColWarning(0),
	lastColWarningType(0),
	reservedLandingPos(-1, -1, -1),
	maxDrift(1),
	repairBelowHealth(0.30f),
	padStatus(0),
	reservedPad(0),
	currentPitch(0),
	autoLand(true)
{
	if (owner) {
		owner->dontUseWeapons = true;
	}
}

CTAAirMoveType::~CTAAirMoveType(void)
{
	if (reservedPad) {
		airBaseHandler->LeaveLandingPad(reservedPad);
		reservedPad = 0;
	}
}

void CTAAirMoveType::SetGoal(float3 newPos, float distance)
{
	// aircraft need some marginals to avoid uber stacking
	// when lots of them are ordered to one place
	maxDrift = max(16.0f, distance);
	goalPos = newPos;
	oldGoalPos = newPos;
	forceHeading = false;
	wantedHeight = orgWantedHeight;
}

void CTAAirMoveType::SetState(AircraftState newState)
{
	if (newState == aircraftState)
		return;

	// Perform cob animation
	if (aircraftState == AIRCRAFT_LANDED)
		owner->cob->Call(COBFN_StartMoving);
	if (newState == AIRCRAFT_LANDED)
		owner->cob->Call(COBFN_StopMoving);

	if (newState == AIRCRAFT_LANDED) {
		owner->dontUseWeapons = true;
	} else {
		owner->dontUseWeapons = false;
	}

	aircraftState = newState;

	// Do animations
	switch (aircraftState) {
		case AIRCRAFT_LANDED:
			if (padStatus == 0) {
				// don't set us as on ground if we are on pad
				owner->physicalState = CSolidObject::OnGround;
				owner->Block();
			}
		case AIRCRAFT_LANDING:
			owner->Deactivate();
			break;
		case AIRCRAFT_HOVERING:
			wantedHeight = orgWantedHeight;
			wantedSpeed = ZeroVector;
			// fall through...
		default:
			owner->physicalState = CSolidObject::Flying;
			owner->UnBlock();
			owner->Activate();
			reservedLandingPos.x = -1;
			break;
	}

	// Cruise as default
	if (aircraftState == AIRCRAFT_FLYING || aircraftState == AIRCRAFT_HOVERING)
		flyState = FLY_CRUISING;

	owner->isMoving = (aircraftState != AIRCRAFT_LANDED);
	waitCounter = 0;
}

void CTAAirMoveType::StartMoving(float3 pos, float goalRadius)
{
	wantToStop = false;
	owner->isMoving = true;
	waitCounter = 0;
	forceHeading = false;

	switch (aircraftState) {
		case AIRCRAFT_LANDED:
			SetState(AIRCRAFT_TAKEOFF);
			break;
		case AIRCRAFT_TAKEOFF:
			SetState(AIRCRAFT_TAKEOFF);
			break;
		case AIRCRAFT_FLYING:
			if (flyState != FLY_CRUISING)
				flyState = FLY_CRUISING;
			break;
		case AIRCRAFT_LANDING:
			SetState(AIRCRAFT_TAKEOFF);
			break;
		case AIRCRAFT_HOVERING:
			SetState(AIRCRAFT_FLYING);
			break;
		case AIRCRAFT_CRASHING:
			break;
	}

	SetGoal(pos, goalRadius);
	breakDistance = ((maxSpeed * maxSpeed) / decRate);
}

void CTAAirMoveType::StartMoving(float3 pos, float goalRadius, float speed)
{
	StartMoving(pos, goalRadius);
}

void CTAAirMoveType::KeepPointingTo(float3 pos, float distance, bool aggressive)
{
	wantToStop = false;
	forceHeading = false;
	wantedHeight = orgWantedHeight;
	// close in a little to avoid the command AI to override the pos constantly
	distance -= 15;

	// Ignore the exact same order
	if ((aircraftState == AIRCRAFT_FLYING) && (flyState == FLY_CIRCLING || flyState == FLY_ATTACKING) && ((circlingPos - pos).SqLength2D() < 64) && (goalDistance == distance))
		return;

	circlingPos = pos;
	goalDistance = distance;
	goalPos = owner->pos;

	SetState(AIRCRAFT_FLYING);

	if (aggressive) {
		flyState = FLY_ATTACKING;
	} else {
		flyState = FLY_CIRCLING;
	}
}

void CTAAirMoveType::ExecuteStop()
{
	wantToStop = false;

	switch (aircraftState) {
		case AIRCRAFT_TAKEOFF:
			SetState(AIRCRAFT_LANDING);
			// trick to land directly
			waitCounter = 30;
			break;
		case AIRCRAFT_FLYING:
			if (owner->unitDef->DontLand()) {
				goalPos = owner->pos;
				SetState(AIRCRAFT_HOVERING);
			} else if (dontLand || !autoLand) {
				goalPos = owner->pos;
				wantedSpeed = ZeroVector;
			} else {
				SetState(AIRCRAFT_LANDING);
			}
			break;
		case AIRCRAFT_LANDING:
			break;
		case AIRCRAFT_LANDED:
			break;
		case AIRCRAFT_CRASHING:
			break;
		case AIRCRAFT_HOVERING:
			break;
	}
}

void CTAAirMoveType::StopMoving()
{
	wantToStop = true;
	forceHeading = false;
	owner->isMoving = false;
	wantedHeight = orgWantedHeight;
}

void CTAAirMoveType::Idle()
{
	StopMoving();
}



void CTAAirMoveType::UpdateLanded()
{
	float3& pos = owner->pos;

	// dont place on ground if we are on a repair pad
	if (padStatus == 0) {
		if (owner->unitDef->canSubmerge)
			pos.y = ground->GetApproximateHeight(pos.x, pos.z);
		else
			pos.y = ground->GetHeight(pos.x, pos.z);
	}

	owner->speed = ZeroVector;
}

void CTAAirMoveType::UpdateTakeoff()
{
	float3 &pos = owner->pos;
	wantedSpeed = ZeroVector;
	wantedHeight = orgWantedHeight;

	UpdateAirPhysics();

	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 > orgWantedHeight * 0.8f) {
		SetState(AIRCRAFT_FLYING);
	}
}



// Move the unit around a bit.. and when it gets too far away from goal position it switches to normal flying instead
void CTAAirMoveType::UpdateHovering()
{
	float driftSpeed = owner->unitDef->dlHoverFactor;	
	float3 dir = goalPos - owner->pos;

	// move towards goal position if it's not immediately behind us when we have more waypoints to get to
	if (aircraftState != AIRCRAFT_LANDING && (owner->commandAI->HasMoreMoveCommands() &&
		dir.Length2D() < 120) && (goalPos - owner->pos).Normalize().distance(dir) > 1) {
		dir = owner->frontdir;
	}

	wantedSpeed += float3(dir.x, 0.0f, dir.z) * driftSpeed * 0.03f;
	// damping
	wantedSpeed *= 0.97f;
	// random movement (a sort of fake wind effect)
	wantedSpeed += float3(gs->randFloat() - 0.5f, 0.0f, gs->randFloat() - 0.5f) * driftSpeed * 0.5f;

	UpdateAirPhysics();
}


void CTAAirMoveType::UpdateFlying()
{
	float3 &pos = owner->pos;
	float3 &speed = owner->speed;

	// Direction to where we would like to be
	float3 dir = goalPos - pos;
	owner->restTime = 0;

	// don't change direction for waypoints we just flew over and missed slightly
	if (flyState != FLY_LANDING && (owner->commandAI->HasMoreMoveCommands() &&
		dir.Length2D() < 100) && (goalPos - pos).Normalize().distance(dir) < 1) {
		dir = owner->frontdir;
	}

	// are we there yet?
	bool closeToGoal = (dir.SqLength2D() < maxDrift * maxDrift) && (fabs(ground->GetHeight(pos.x, pos.z) - pos.y + wantedHeight) < maxDrift);

	if (flyState == FLY_ATTACKING)
		closeToGoal = (dir.SqLength2D() < 400);

	if (closeToGoal) {
		// pretty close
		switch (flyState) {
			case FLY_CRUISING:
				if (dontLand || (++waitCounter < 55 && dynamic_cast<CTransportUnit*>(owner)) || !autoLand) {
					// transport aircraft need some time to detect that they can pickup
					if (dynamic_cast<CTransportUnit*>(owner)) {
						wantedSpeed = ZeroVector;
						if (waitCounter > 60) {
							wantedHeight = orgWantedHeight;
						}
					} else {
						wantedSpeed = ZeroVector;
						// SetState(AIRCRAFT_HOVERING);
					}
				} else {
					wantedHeight = orgWantedHeight;
					SetState(AIRCRAFT_LANDING);
				}
				return;
			case FLY_CIRCLING:
				// break;
				waitCounter++;
				if (waitCounter > 100) {
					if (owner->unitDef->airStrafe) {
						float3 relPos = pos - circlingPos;
						if (relPos.x < 0.0001f && relPos.x > -0.0001f)
							relPos.x = 0.0001f;
						relPos.y = 0;
						relPos.Normalize();
						CMatrix44f rot;
						rot.RotateY(1.0f);
						float3 newPos = rot.Mul(relPos);

						// Make sure the point is on the circle
						newPos = newPos.Normalize() * goalDistance;

						//Go there in a straight line
						goalPos = circlingPos + newPos;
					}
					waitCounter = 0;
				}
				break;
			case FLY_ATTACKING:{
				if (owner->unitDef->airStrafe) {
					float3 relPos = pos - circlingPos;
					if (relPos.x < 0.0001f && relPos.x > -0.0001f)
						relPos.x = 0.0001f;
					relPos.y = 0;
					relPos.Normalize();
					CMatrix44f rot;
					if (gs->randFloat() > 0.5f)
						rot.RotateY(0.6f + gs->randFloat() * 0.6f);
					else
						rot.RotateY(-(0.6f + gs->randFloat() * 0.6f));
					float3 newPos = rot.Mul(relPos);
					newPos = newPos.Normalize() * goalDistance;

					// Go there in a straight line
					goalPos = circlingPos + newPos;
				}
				break;
			}
			case FLY_LANDING:{
				break;
			}
		}
	}

	// not there yet, so keep going
	dir.y = 0;
	float realMax = maxSpeed;
	float dist = dir.Length2D();

	// If we are close to our goal, we should go slow enough to be able to break in time
	// new additional rule: if in attack mode or if we have more move orders then this is
	// an intermediate waypoint, don't slow down (FIXME)

	/// if (flyState != FLY_ATTACKING && dist < breakDistance && !owner->commandAI->HasMoreMoveCommands()) {
	if (flyState != FLY_ATTACKING && dist < breakDistance) {
		realMax = dist / (speed.Length2D() + 0.01f) * decRate;
	}

	wantedSpeed = dir.Normalize() * realMax;
	UpdateAirPhysics();

	// Point toward goal or forward - unless we just passed it to get to another goal
	if ((flyState == FLY_ATTACKING) || (flyState == FLY_CIRCLING)) {
		dir = circlingPos - pos;
	} else if (flyState != FLY_LANDING && (owner->commandAI->HasMoreMoveCommands() &&
			   dist < 120) && (goalPos - pos).Normalize().distance(dir) > 1) {
		dir = owner->frontdir;
	} else {
		dir = goalPos - pos;
	}

	if (dir.SqLength2D() > 1) {
		int h = GetHeadingFromVector(dir.x, dir.z);
		wantedHeading = (h == 0)? wantedHeading: h;
	}
}



void CTAAirMoveType::UpdateLanding()
{
	float3& pos = owner->pos;
	float3& speed = owner->speed;

	// We want to land, and therefore cancel our speed first
	wantedSpeed = ZeroVector;

	waitCounter++;

	// Hang around for a while so queued commands have a chance to take effect
	if (waitCounter < 30) {
		UpdateAirPhysics();
		return;
	}

	if (reservedLandingPos.x < 0) {
		if (CanLandAt(pos)) {
			// found a landing spot
			reservedLandingPos = pos;
			goalPos = pos;
			owner->physicalState = CSolidObject::OnGround;
			owner->Block();
			owner->physicalState = CSolidObject::Flying;
			owner->Deactivate();
			owner->cob->Call(COBFN_StopMoving);
		} else {
			if (goalPos.distance2D(pos) < 30) {
				goalPos = goalPos + gs->randVector() * 300;
				goalPos.CheckInBounds();
			}
			flyState = FLY_LANDING;
			UpdateFlying();
			return;
		}
	}
	// We should wait until we actually have stopped smoothly
	if (speed.SqLength2D() > 1) {
		UpdateFlying();
		return;
	}

	// We have stopped, time to land
	float gah = ground->GetApproximateHeight(pos.x, pos.z);
	float h = 0.0f;

	// if aircraft submergible and above water we want height of ocean floor
	if ((owner->unitDef->canSubmerge) && (gah < 0)) {
		h = pos.y - gah;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?