void CTransportUnit::Update() { CUnit::Update(); if (isDead) { return; } for (std::list<TransportedUnit>::iterator ti = transportedUnits.begin(); ti != transportedUnits.end(); ++ti) { CUnit* transportee = ti->unit; float3 relPiecePos; float3 absPiecePos; if (ti->piece >= 0) { relPiecePos = script->GetPiecePos(ti->piece); } else { relPiecePos = float3(0.0f, -1000.0f, 0.0f); } absPiecePos = pos + (frontdir * relPiecePos.z) + (updir * relPiecePos.y) + (rightdir * relPiecePos.x); transportee->mapSquare = mapSquare; if (unitDef->holdSteady) { // slave transportee orientation to piece if (ti->piece >= 0) { const CMatrix44f& transMat = GetTransformMatrix(true); const CMatrix44f& pieceMat = script->GetPieceMatrix(ti->piece); const CMatrix44f slaveMat = pieceMat * transMat; transportee->SetDirVectors(slaveMat); } } else { // slave transportee orientation to body transportee->heading = heading; transportee->updir = updir; transportee->frontdir = frontdir; transportee->rightdir = rightdir; } transportee->Move3D(absPiecePos, false); transportee->UpdateMidAndAimPos(); transportee->SetHeadingFromDirection(); // see ::AttachUnit if (transportee->stunned) { qf->MovedUnit(transportee); } } }
bool CHoverAirMoveType::HandleCollisions() { const float3& pos = owner->pos; if (pos != oldPos) { oldPos = pos; // check for collisions if not on a pad, not being built, or not taking off // includes an extra condition for transports, which are exempt while loading const bool checkCollisions = collide && !owner->beingBuilt && (padStatus == PAD_STATUS_FLYING) && (aircraftState != AIRCRAFT_TAKEOFF); bool hitBuilding = false; if (!loadingUnits && checkCollisions) { const vector<CUnit*>& nearUnits = quadField->GetUnitsExact(pos, owner->radius + 6); for (vector<CUnit*>::const_iterator ui = nearUnits.begin(); ui != nearUnits.end(); ++ui) { CUnit* unit = *ui; if (unit->transporter != NULL) continue; const float sqDist = (pos - unit->pos).SqLength(); const float totRad = owner->radius + unit->radius; if (sqDist <= 0.1f || sqDist >= (totRad * totRad)) continue; const float dist = math::sqrt(sqDist); const float3 dif = (pos - unit->pos).Normalize(); if (unit->mass >= CSolidObject::DEFAULT_MASS || unit->immobile) { owner->Move3D(-dif * (dist - totRad), true); owner->speed *= 0.99f; hitBuilding = true; } else { const float part = owner->mass / (owner->mass + unit->mass); owner->Move3D(-dif * (dist - totRad) * (1.0f - part), true); unit->Move3D(dif * (dist - totRad) * (part), true); const float colSpeed = -owner->speed.dot(dif) + unit->speed.dot(dif); owner->speed += (dif * colSpeed * (1.0f - part)); unit->speed -= (dif * colSpeed * (part)); } } } if (hitBuilding && owner->IsCrashing()) { owner->KillUnit(NULL, true, false); return true; } if (pos.x < 0.0f) { owner->Move1D(0.6f, 0, true); } else if (pos.x > float3::maxxpos) { owner->Move1D(-0.6f, 0, true); } if (pos.z < 0.0f) { owner->Move1D(0.6f, 2, true); } else if (pos.z > float3::maxzpos) { owner->Move1D(-0.6f, 2, true); } return true; } return false; }
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); }