taairmovetype.cpp

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

CPP
1,048
字号
		wantedHeight = gah;
	} else {
		h = pos.y - ground->GetHeight(pos.x, pos.z);
		wantedHeight = -2;
	}

	UpdateAirPhysics();

	if (h <= 0) {
		SetState(AIRCRAFT_LANDED);
		pos.y = gah;
	}
}



void CTAAirMoveType::UpdateHeading()
{
	if (aircraftState == AIRCRAFT_TAKEOFF && !owner->unitDef->factoryHeadingTakeoff) {
		return;
	}

	short& heading = owner->heading;
	short deltaHeading = wantedHeading - heading;

	if (forceHeading) {
		deltaHeading = forceHeadingTo - heading;
	}

	if (deltaHeading > 0) {
		heading += deltaHeading > (short int) turnRate ? (short int) turnRate : deltaHeading;
	} else {
		heading += deltaHeading > (short int) (-turnRate) ? deltaHeading : (short int) (-turnRate);
	}
}

void CTAAirMoveType::UpdateBanking(bool noBanking)
{
	SyncedFloat3 &frontdir = owner->frontdir;
	SyncedFloat3 &updir = owner->updir;
	float wantedPitch = 0;

	if (aircraftState == AIRCRAFT_FLYING && flyState == FLY_ATTACKING && circlingPos.y < owner->pos.y) {
		wantedPitch = (circlingPos.y - owner->pos.y) / circlingPos.distance(owner->pos);
	}

	currentPitch = currentPitch * 0.95f + wantedPitch * 0.05f;

	frontdir = GetVectorFromHeading(owner->heading);
	frontdir.y = currentPitch;
	frontdir.Normalize();

	float3 rightdir(frontdir.cross(UpVector));		//temp rightdir
	rightdir.Normalize();

	float wantedBank = 0.0f;
	if (!noBanking) wantedBank = rightdir.dot(deltaSpeed)/accRate*0.5f;

	float limit=min(1.0f,goalPos.distance2D(owner->pos)*0.15f);
	if(wantedBank>limit)
		wantedBank=limit;
	else if(wantedBank<-limit)
		wantedBank=-limit;

	//Adjust our banking to the desired value
	if (currentBank > wantedBank)
		currentBank -= min(0.03f, currentBank - wantedBank);
	else
		currentBank += min(0.03f, wantedBank - currentBank);

	// Calculate a suitable upvector
	updir = rightdir.cross(frontdir);
	updir = updir * cos(currentBank) + rightdir * sin(currentBank);
	owner->rightdir = frontdir.cross(updir);
}


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

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

	float yspeed = speed.y;
	speed.y = 0;

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

	if (delta.dot(speed) > 0) {
		// accelerate
		if (dl < accRate) {
			speed = wantedSpeed;
		} else {
			speed += delta / dl * accRate;
		}
	} else {
		// break
		if (dl < decRate) {
			speed = wantedSpeed;
		} else {
			speed += delta / dl * decRate;
		}
	}

	speed.y = yspeed;
	float h = pos.y - max(ground->GetHeight(pos.x, pos.z),
						  ground->GetHeight(pos.x + speed.x * 40, pos.z + speed.z * 40));

	if (h < 4) {
		speed.x *= 0.95f;
		speed.z *= 0.95f;
	}

	float wh = wantedHeight;

	if (lastColWarningType == 2) {
		const float3 dir = lastColWarning->midPos - owner->midPos;
		const float3 sdir = lastColWarning->speed - speed;

		if (speed.dot(dir + sdir * 20.0f) < 0.0f) {
			if (lastColWarning->midPos.y > owner->pos.y) {
				wh -= 30;
			} else {
				wh += 50;
			}
		}
	}

	float ws;
	if (h < wh) {
		ws = altitudeRate;
		if (speed.y > 0 && (wh - h) / speed.y * accRate * 1.5f < speed.y)
			ws = 0;
	} else {
		ws = -altitudeRate;
		if (speed.y < 0 && (wh - h) / speed.y * accRate * 0.7f < -speed.y)
			ws = 0;
	}

	if (speed.y > ws) {
		speed.y = max(ws, speed.y - accRate * 1.5f);
	} else {
		// let them accelerate upward faster if close to ground
		speed.y = min(ws, speed.y + accRate * (h < 20? 2.0f: 0.7f));
	}

	pos += speed;
}


void CTAAirMoveType::UpdateMoveRate()
{
	int curRate;

	// currentspeed is not used correctly for vertical movement, so compensate with this hax
	if ((aircraftState == AIRCRAFT_LANDING) || (aircraftState == AIRCRAFT_TAKEOFF)) {
		curRate = 1;
	}
	else {
		float curSpeed = owner->speed.Length();
		curRate = (int) ((curSpeed / maxSpeed) * 3);

		if (curRate < 0)
			curRate = 0;
		if (curRate > 2)
			curRate = 2;
	}

	if (curRate != lastMoveRate) {
		owner->cob->Call(COBFN_MoveRate0 + curRate);
		lastMoveRate = curRate;
	}
}


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

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

	// Allow us to stop if wanted
	if (wantToStop)
		ExecuteStop();

	float3 lastSpeed = speed;

	if (owner->stunned) {
		wantedSpeed = ZeroVector;
		UpdateAirPhysics();
	} else {
#ifdef DIRECT_CONTROL_ALLOWED
		if (owner->directControl) {
			DirectControlStruct* dc = owner->directControl;
			SetState(AIRCRAFT_FLYING);

			float3 forward = dc->viewDir;
			float3 flatForward = forward;
			flatForward.y = 0;
			flatForward.Normalize();
			float3 right = forward.cross(UpVector);
			float3 nextPos = pos + speed;
			wantedSpeed = ZeroVector;

			if (dc->forward)
				wantedSpeed += flatForward;
			if (dc->back)
				wantedSpeed -= flatForward;
			if (dc->right)
				wantedSpeed += right;
			if (dc->left)
				wantedSpeed -= right;
			wantedSpeed.Normalize();
			wantedSpeed *= maxSpeed;

			if (!nextPos.CheckInBounds()) {
				speed = ZeroVector;
			}

			UpdateAirPhysics();
			wantedHeading = GetHeadingFromVector(flatForward.x, flatForward.z);
		} else
#endif
		{

			if (reservedPad) {
				CUnit* unit = reservedPad->GetUnit();
				float3 relPos = unit->localmodel->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.distance2D(owner->pos) < 400) {
						padStatus = 1;
					}
				} else if (padStatus == 1) {
					if (aircraftState != AIRCRAFT_FLYING)
						SetState(AIRCRAFT_FLYING);
					flyState = FLY_LANDING;

					goalPos = pos;
					reservedLandingPos = pos;
					wantedHeight = pos.y - ground->GetHeight(pos.x, pos.z);

					if (owner->pos.distance(pos) < 3 || aircraftState == AIRCRAFT_LANDED) {
						padStatus = 2;
					}
				} 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);
					}
				}
			}

			// Main state handling
			switch (aircraftState) {
				case AIRCRAFT_LANDED:
					UpdateLanded();
					break;
				case AIRCRAFT_TAKEOFF:
					UpdateTakeoff();
					break;
				case AIRCRAFT_FLYING:
					UpdateFlying();
					break;
				case AIRCRAFT_LANDING:
					UpdateLanding();
					break;
				case AIRCRAFT_HOVERING:
					UpdateHovering();
					break;
				case AIRCRAFT_CRASHING:
					break;
			}
		}
	}

	// Banking requires deltaSpeed.y = 0
	deltaSpeed = speed - lastSpeed;
	deltaSpeed.y = 0;

	// Turn and bank and move
	UpdateHeading();
	UpdateBanking(aircraftState == AIRCRAFT_HOVERING);			// updates dirs
	owner->midPos = pos + frontdir * owner->relMidPos.z + updir * owner->relMidPos.y + rightdir * owner->relMidPos.x;

	// Push other units out of the way
	if (pos != oldpos && aircraftState != AIRCRAFT_TAKEOFF && padStatus == 0) {
		oldpos = pos;

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

			for (ui = nearUnits.begin(); ui != nearUnits.end(); ++ui) {
				if ((*ui)->transporter)
					continue;

				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)->mass >= 100000 || (*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;
					} 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 colSpeed = -owner->speed.dot(dif) + u->speed.dot(dif);
						owner->speed += dif * colSpeed * (1 - part);
						u->speed -= dif * colSpeed * (part);
					}
				}
			}
		}
		if (pos.x < 0) {
			pos.x += 0.6f;
			owner->midPos.x += 0.6f;
		} else if (pos.x > float3::maxxpos) {
			pos.x -= 0.6f;
			owner->midPos.x -= 0.6f;
		}

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

void CTAAirMoveType::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;
		}
	}

	UpdateMoveRate();

	// Update LOS stuff
	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);
}

//Returns true if indicated position is a suitable landing spot
bool CTAAirMoveType::CanLandAt(float3 pos)
{
	if (dontLand)
		return false;

	if (!autoLand)
		return false;

	if (pos.x < 0 || pos.z < 0 || pos.x > float3::maxxpos || pos.z > float3::maxzpos)
		return false;

	float h = ground->GetApproximateHeight(pos.x, pos.z);
	if ((h < 0) && !(owner -> unitDef -> floater || owner -> unitDef -> canSubmerge))
		return false;

	float3 tpos = owner->pos;
	owner->pos = pos;
	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++) {
			CObject* o = readmap->groundBlockingObjectMap[z * gs->mapx + x];
			if (o && o != owner) {
				return false;
			}
		}
	}
	return true;
}

void CTAAirMoveType::ForceHeading(short h)
{
	forceHeading = true;
	forceHeadingTo = h;
}

void CTAAirMoveType::SetWantedAltitude(float altitude)
{
	if (altitude == 0) {
		wantedHeight = orgWantedHeight;
	} else {
		wantedHeight = altitude;
	}
}

void CTAAirMoveType::CheckForCollision(void)
{
	if (!collide) return;

	SyncedFloat3& pos = owner->midPos;
	SyncedFloat3 forward = owner->speed;
	forward.Normalize();
	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;
			// note: the radius is multiplied by two since we rely on aircraft having small spheres (see unitloader)
			float minOrtoDif = ((*ui)->radius + owner->radius) * 2 + frontLength * 0.05f + 5;

			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 CTAAirMoveType::DependentDied(CObject* o)
{
	if (o == reservedPad) {
		reservedPad = 0;
		SetState(AIRCRAFT_FLYING);
		goalPos = oldGoalPos;
	}
	if (o == lastColWarning) {
		lastColWarning = 0;
		lastColWarningType = 0;
	}

	CMoveType::DependentDied(o);
}

⌨️ 快捷键说明

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