Beispiel #1
0
void CTeam::SelfDestruct()
{
	for (CUnitSet::iterator ui = units.begin(); ui != units.end(); ++ui) {
		CUnit* unit = (*ui);
		if (unit != NULL && unit->unitDef->canSelfD) {
			if (unit->beingBuilt) {
				unit->KillUnit(false, true, NULL); // kill units under construction without explosion
			} else {
				unit->KillUnit(true, false, NULL);
			}
		}
	}
	Died();
}
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);
}
Beispiel #3
0
int CUnitScript::GetUnitVal(int val, int p1, int p2, int p3, int p4)
{
	// may happen in case one uses Spring.GetUnitCOBValue (Lua) on a unit with CNullUnitScript
	if (!unit) {
		ShowScriptError("Error: no unit (in GetUnitVal)");
		return 0;
	}

#ifndef _CONSOLE
	switch (val)
	{
	case ACTIVATION:
		if (unit->activated)
			return 1;
		else
			return 0;
		break;
	case STANDINGMOVEORDERS:
		return unit->moveState;
		break;
	case STANDINGFIREORDERS:
		return unit->fireState;
		break;
	case HEALTH: {
		if (p1 <= 0)
			return int((unit->health / unit->maxHealth) * 100.0f);

		const CUnit* u = uh->GetUnit(p1);

		if (u == NULL)
			return 0;
		else
			return int((u->health / u->maxHealth) * 100.0f);
	}
	case INBUILDSTANCE:
		if (unit->inBuildStance)
			return 1;
		else
			return 0;
	case BUSY:
		if (busy)
			return 1;
		else
			return 0;
		break;
	case PIECE_XZ: {
		if (!PieceExists(p1)) {
			ShowScriptError("Invalid piecenumber for get piece_xz");
			break;
		}
		const float3 relPos = GetPiecePos(p1);
		const float3 absPos = unit->pos + unit->frontdir * relPos.z + unit->updir * relPos.y + unit->rightdir * relPos.x;
		return PACKXZ(absPos.x, absPos.z);
	}
	case PIECE_Y: {
		if (!PieceExists(p1)) {
			ShowScriptError("Invalid piecenumber for get piece_y");
			break;
		}
		const float3 relPos = GetPiecePos(p1);
		const float3 absPos = unit->pos + unit->frontdir * relPos.z + unit->updir * relPos.y + unit->rightdir * relPos.x;
		return int(absPos.y * COBSCALE);
	}
	case UNIT_XZ: {
		if (p1 <= 0)
			return PACKXZ(unit->pos.x, unit->pos.z);

		const CUnit* u = uh->GetUnit(p1);

		if (u == NULL)
			return PACKXZ(0, 0);
		else
			return PACKXZ(u->pos.x, u->pos.z);
	}
	case UNIT_Y: {
		if (p1 <= 0)
			return int(unit->pos.y * COBSCALE);

		const CUnit* u = uh->GetUnit(p1);

		if (u == NULL)
			return 0;
		else
			return int(u->pos.y * COBSCALE);
	}
	case UNIT_HEIGHT: {
		if (p1 <= 0)
			return int(unit->radius * COBSCALE);

		const CUnit* u = uh->GetUnit(p1);

		if (u == NULL)
			return 0;
		else
			return int(u->radius * COBSCALE);
	}
	case XZ_ATAN:
		return int(RAD2TAANG*math::atan2((float)UNPACKX(p1), (float)UNPACKZ(p1)) + 32768 - unit->heading);
	case XZ_HYPOT:
		return int(math::hypot((float)UNPACKX(p1), (float)UNPACKZ(p1)) * COBSCALE);
	case ATAN:
		return int(RAD2TAANG*math::atan2((float)p1, (float)p2));
	case HYPOT:
		return int(math::hypot((float)p1, (float)p2));
	case GROUND_HEIGHT:
		return int(ground->GetHeightAboveWater(UNPACKX(p1), UNPACKZ(p1)) * COBSCALE);
	case GROUND_WATER_HEIGHT:
		return int(ground->GetHeightReal(UNPACKX(p1), UNPACKZ(p1)) * COBSCALE);
	case BUILD_PERCENT_LEFT:
		return int((1.0f - unit->buildProgress) * 100);

	case YARD_OPEN:
		if (yardOpen)
			return 1;
		else
			return 0;
	case BUGGER_OFF:
		break;
	case ARMORED:
		if (unit->armoredState)
			return 1;
		else
			return 0;
	case VETERAN_LEVEL:
		return int(100 * unit->experience);
	case CURRENT_SPEED:
		return int(unit->speed.Length() * COBSCALE);
	case ON_ROAD:
		return 0;
	case IN_WATER:
		return (unit->pos.y < 0.0f) ? 1 : 0;
	case MAX_ID:
		return uh->MaxUnits()-1;
	case MY_ID:
		return unit->id;

	case UNIT_TEAM: {
		const CUnit* u = uh->GetUnit(p1);
		return (u != NULL)? unit->team : 0;
	}
	case UNIT_ALLIED: {
		const CUnit* u = uh->GetUnit(p1);

		if (u != NULL) {
			return teamHandler->Ally(unit->allyteam, u->allyteam) ? 1 : 0;
		}

		return 0;
	}
	case UNIT_BUILD_PERCENT_LEFT: {
		const CUnit* u = uh->GetUnit(p1);

		if (u != NULL) {
			return int((1.0f - u->buildProgress) * 100);
		}

		return 0;
	}
	case MAX_SPEED: {
		return int(unit->moveType->GetMaxSpeed() * COBSCALE);
	} break;
	case REVERSING: {
		CGroundMoveType* gmt = dynamic_cast<CGroundMoveType*>(unit->moveType);
		return ((gmt != NULL)? int(gmt->IsReversing()): 0);
	} break;
	case CLOAKED:
		return !!unit->isCloaked;
	case WANT_CLOAK:
		return !!unit->wantCloak;
	case UPRIGHT:
		return !!unit->upright;
	case POW:
		return int(math::pow(((float)p1)/COBSCALE,((float)p2)/COBSCALE)*COBSCALE);
	case PRINT:
		LOG("Value 1: %d, 2: %d, 3: %d, 4: %d", p1, p2, p3, p4);
		break;
	case HEADING: {
		if (p1 <= 0) {
			return unit->heading;
		}

		const CUnit* u = uh->GetUnit(p1);

		if (u != NULL) {
			return u->heading;
		}

		return -1;
	}
	case TARGET_ID: {
		if (unit->weapons[p1 - 1]) {
			const CWeapon* weapon = unit->weapons[p1 - 1];
			const TargetType tType = weapon->targetType;

			if (tType == Target_Unit)
				return unit->weapons[p1 - 1]->targetUnit->id;
			else if (tType == Target_None)
				return -1;
			else if (tType == Target_Pos)
				return -2;
			else // Target_Intercept
				return -3;
		}
		return -4; // weapon does not exist
	}

	case LAST_ATTACKER_ID:
		return unit->lastAttacker? unit->lastAttacker->id: -1;
	case LOS_RADIUS:
		return unit->realLosRadius;
	case AIR_LOS_RADIUS:
		return unit->realAirLosRadius;
	case RADAR_RADIUS:
		return unit->radarRadius;
	case JAMMER_RADIUS:
		return unit->jammerRadius;
	case SONAR_RADIUS:
		return unit->sonarRadius;
	case SONAR_JAM_RADIUS:
		return unit->sonarJamRadius;
	case SEISMIC_RADIUS:
		return unit->seismicRadius;

	case DO_SEISMIC_PING:
		float pingSize;
		if (p1 == 0) {
			pingSize = unit->seismicSignature;
		} else {
			pingSize = p1;
		}
		unit->DoSeismicPing(pingSize);
		break;

	case CURRENT_FUEL:
		return int(unit->currentFuel * float(COBSCALE));
	case TRANSPORT_ID:
		return unit->transporter?unit->transporter->id:-1;

	case SHIELD_POWER: {
		if (unit->shieldWeapon == NULL) {
			return -1;
		}
		const CPlasmaRepulser* shield = (CPlasmaRepulser*) unit->shieldWeapon;
		return int(shield->curPower * float(COBSCALE));
	}

	case STEALTH: {
		return unit->stealth ? 1 : 0;
	}
	case SONAR_STEALTH: {
		return unit->sonarStealth ? 1 : 0;
	}
	case CRASHING:
		return !!unit->crashing;
	case ALPHA_THRESHOLD: {
		return int(unit->alphaThreshold * 255);
	}

	case COB_ID: {
		if (p1 <= 0) {
			return unit->unitDef->cobID;
		} else {
			const CUnit* u = uh->GetUnit(p1);
			return ((u == NULL)? -1 : u->unitDef->cobID);
		}
	}

 	case PLAY_SOUND: {
 		// FIXME: this can currently only work for CCobInstance, because Lua can not get sound IDs
 		// (however, for Lua scripts there is already LuaUnsyncedCtrl::PlaySoundFile)
 		CCobInstance* cob = dynamic_cast<CCobInstance*>(this);
 		if (cob == NULL) {
 			return 1;
 		}
 		const CCobFile* script = cob->GetScriptAddr();
 		if (script == NULL) {
 			return 1;
 		}
		if ((p1 < 0) || (static_cast<size_t>(p1) >= script->sounds.size())) {
			return 1;
		}
		switch (p3) {	//who hears the sound
			case 0:		//ALOS
				if (!loshandler->InAirLos(unit->pos,gu->myAllyTeam)) { return 0; }
				break;
			case 1:		//LOS
				if (!(unit->losStatus[gu->myAllyTeam] & LOS_INLOS)) { return 0; }
				break;
			case 2:		//ALOS or radar
				if (!(loshandler->InAirLos(unit->pos,gu->myAllyTeam) || unit->losStatus[gu->myAllyTeam] & (LOS_INRADAR))) { return 0; }
				break;
			case 3:		//LOS or radar
				if (!(unit->losStatus[gu->myAllyTeam] & (LOS_INLOS | LOS_INRADAR))) { return 0; }
				break;
			case 4:		//everyone
				break;
			case 5:		//allies
				if (unit->allyteam != gu->myAllyTeam) { return 0; }
				break;
			case 6:		//team
				if (unit->team != gu->myTeam) { return 0; }
				break;
			case 7:		//enemies
				if (unit->allyteam == gu->myAllyTeam) { return 0; }
				break;
		}
		if (p4 == 0) {
			Channels::General.PlaySample(script->sounds[p1], unit->pos, unit->speed, float(p2) / COBSCALE);
		} else {
			Channels::General.PlaySample(script->sounds[p1], float(p2) / COBSCALE);
		}
		return 0;
	}
	case SET_WEAPON_UNIT_TARGET: {
		const unsigned int weaponID = p1 - 1;
		const unsigned int targetID = p2;
		const bool userTarget = !!p3;

		if (weaponID >= unit->weapons.size()) {
			return 0;
		}

		CWeapon* weapon = unit->weapons[weaponID];

		if (weapon == NULL) {
			return 0;
		}

		//! if targetID is 0, just sets weapon->haveUserTarget
		//! to false (and targetType to None) without attacking
		CUnit* target = (targetID > 0)? uh->GetUnit(targetID): NULL;
		return (weapon->AttackUnit(target, userTarget) ? 1 : 0);
	}
	case SET_WEAPON_GROUND_TARGET: {
		const int weaponID = p1 - 1;
		const float3 pos = float3(float(UNPACKX(p2)),
		                          float(p3) / float(COBSCALE),
		                          float(UNPACKZ(p2)));
		const bool userTarget = !!p4;
		if ((weaponID < 0) || (static_cast<size_t>(weaponID) >= unit->weapons.size())) {
			return 0;
		}
		CWeapon* weapon = unit->weapons[weaponID];
		if (weapon == NULL) { return 0; }

		return weapon->AttackGround(pos, userTarget) ? 1 : 0;
	}
	case MIN:
		return std::min(p1, p2);
	case MAX:
		return std::max(p1, p2);
	case ABS:
		return abs(p1);
	case KSIN:
		return int(1024*math::sinf(TAANG2RAD*(float)p1));
	case KCOS:
		return int(1024*math::cosf(TAANG2RAD*(float)p1));
	case KTAN:
		return int(1024*math::tanf(TAANG2RAD*(float)p1));
	case SQRT:
		return int(math::sqrt((float)p1));
	case FLANK_B_MODE:
		return unit->flankingBonusMode;
	case FLANK_B_DIR:
		switch (p1) {
			case 1: return int(unit->flankingBonusDir.x * COBSCALE);
			case 2: return int(unit->flankingBonusDir.y * COBSCALE);
			case 3: return int(unit->flankingBonusDir.z * COBSCALE);
			case 4: unit->flankingBonusDir.x = (p2/(float)COBSCALE); return 0;
			case 5: unit->flankingBonusDir.y = (p2/(float)COBSCALE); return 0;
			case 6: unit->flankingBonusDir.z = (p2/(float)COBSCALE); return 0;
			case 7: unit->flankingBonusDir = float3(p2/(float)COBSCALE, p3/(float)COBSCALE, p4/(float)COBSCALE).Normalize(); return 0;
			default: return(-1);
		}
	case FLANK_B_MOBILITY_ADD:
		return int(unit->flankingBonusMobilityAdd * COBSCALE);
	case FLANK_B_MAX_DAMAGE:
		return int((unit->flankingBonusAvgDamage + unit->flankingBonusDifDamage) * COBSCALE);
	case FLANK_B_MIN_DAMAGE:
		return int((unit->flankingBonusAvgDamage - unit->flankingBonusDifDamage) * COBSCALE);
	case KILL_UNIT: {
		//! ID 0 is reserved for the script's owner
		CUnit* u = (p1 > 0)? uh->GetUnit(p1): this->unit;

		if (u == NULL) {
			return 0;
		}

		if (u->beingBuilt) {
			// no explosions and no corpse for units under construction
			u->KillUnit(false, true, NULL);
		} else {
			u->KillUnit(p2 != 0, p3 != 0, NULL);
		}

		return 1;
	}
	case WEAPON_RELOADSTATE: {
		const int np1 = -p1;
		if (p1 > 0 && static_cast<size_t>(p1) <= unit->weapons.size()) {
			return unit->weapons[p1-1]->reloadStatus;
		}
		else if (np1 > 0 && static_cast<size_t>(np1) <= unit->weapons.size()) {
			const int old = unit->weapons[np1 - 1]->reloadStatus;
			unit->weapons[np1 - 1]->reloadStatus = p2;
			return old;
		}
		else {
			return -1;
		}
	}
	case WEAPON_RELOADTIME: {
		const int np1 = -p1;
		if (p1 > 0 && static_cast<size_t>(p1) <= unit->weapons.size()) {
			return unit->weapons[p1-1]->reloadTime;
		}
		else if (np1 > 0 && static_cast<size_t>(np1) <= unit->weapons.size()) {
			const int old = unit->weapons[np1 - 1]->reloadTime;
			unit->weapons[np1 - 1]->reloadTime = p2;
			return old;
		}
		else {
			return -1;
		}
	}
	case WEAPON_ACCURACY: {
		const int np1 = -p1;
		if (p1 > 0 && static_cast<size_t>(p1) <= unit->weapons.size()) {
			return int(unit->weapons[p1-1]->accuracy * COBSCALE);
		}
		else if (np1 > 0 && static_cast<size_t>(np1) <= unit->weapons.size()) {
			const int old = unit->weapons[np1 - 1]->accuracy * COBSCALE;
			unit->weapons[np1 - 1]->accuracy = float(p2) / COBSCALE;
			return old;
		}
		else {
			return -1;
		}
	}
	case WEAPON_SPRAY: {
		const int np1 = -p1;
		if (p1 > 0 && static_cast<size_t>(p1) <= unit->weapons.size()) {
			return int(unit->weapons[p1-1]->sprayAngle * COBSCALE);
		}
		else if (np1 > 0 && static_cast<size_t>(np1) <= unit->weapons.size()) {
			const int old = unit->weapons[np1 - 1]->sprayAngle * COBSCALE;
			unit->weapons[np1 - 1]->sprayAngle = float(p2) / COBSCALE;
			return old;
		}
		else {
			return -1;
		}
	}
	case WEAPON_RANGE: {
		const int np1 = -p1;
		if (p1 > 0 && static_cast<size_t>(p1) <= unit->weapons.size()) {
			return int(unit->weapons[p1 - 1]->range * COBSCALE);
		}
		else if (np1 > 0 && static_cast<size_t>(np1) <= unit->weapons.size()) {
			const int old = unit->weapons[np1 - 1]->range * COBSCALE;
			unit->weapons[np1 - 1]->range = float(p2) / COBSCALE;
			return old;
		}
		else {
			return -1;
		}
	}
	case WEAPON_PROJECTILE_SPEED: {
		const int np1 = -p1;
		if (p1 > 0 && static_cast<size_t>(p1) <= unit->weapons.size()) {
			return int(unit->weapons[p1-1]->projectileSpeed * COBSCALE);
		}
		else if (np1 > 0 && static_cast<size_t>(np1) <= unit->weapons.size()) {
			const int old = unit->weapons[np1 - 1]->projectileSpeed * COBSCALE;
			unit->weapons[np1 - 1]->projectileSpeed = float(p2) / COBSCALE;
			return old;
		}
		else {
			return -1;
		}
	}
	case GAME_FRAME: {
		return gs->frameNum;
	}
	default:
		if ((val >= GLOBAL_VAR_START) && (val <= GLOBAL_VAR_END)) {
			return globalVars[val - GLOBAL_VAR_START];
		}
		else if ((val >= TEAM_VAR_START) && (val <= TEAM_VAR_END)) {
			return teamVars[unit->team][val - TEAM_VAR_START];
		}
		else if ((val >= ALLY_VAR_START) && (val <= ALLY_VAR_END)) {
			return allyVars[unit->allyteam][val - ALLY_VAR_START];
		}
		else if ((val >= UNIT_VAR_START) && (val <= UNIT_VAR_END)) {
			const int varID = val - UNIT_VAR_START;

			if (p1 == 0) {
				return unitVars[varID];
			}
			else if (p1 > 0) {
				// get the unit var for another unit
				const CUnit* u = uh->GetUnit(p1);

				if (u != NULL && u->script != NULL) {
					return u->script->unitVars[varID];
				}
			}
			else {
				// set the unit var for another unit
				p1 = -p1;

				CUnit* u = uh->GetUnit(p1);

				if (u != NULL && u->script != NULL) {
					u->script->unitVars[varID] = p2;
					return 1;
				}
			}
			return 0;
		}
		else {
			LOG_L(L_ERROR,
					"CobError: Unknown get constant %d (params = %d %d %d %d)",
					val, p1, p2, p3, p4);
		}
	}
#endif

	return 0;
}
void CUnitHandler::Update()
{
	auto UNIT_SANITY_CHECK = [](const CUnit* unit) {
		unit->pos.AssertNaNs();
		unit->midPos.AssertNaNs();
		unit->relMidPos.AssertNaNs();
		unit->speed.AssertNaNs();
		unit->deathSpeed.AssertNaNs();
		unit->rightdir.AssertNaNs();
		unit->updir.AssertNaNs();
		unit->frontdir.AssertNaNs();
		if (unit->unitDef->IsGroundUnit()) {
			assert(unit->pos.x >= -(float3::maxxpos * 16.0f));
			assert(unit->pos.x <=  (float3::maxxpos * 16.0f));
			assert(unit->pos.z >= -(float3::maxzpos * 16.0f));
			assert(unit->pos.z <=  (float3::maxzpos * 16.0f));
		}
	};

	DeleteUnitsNow();

	{
		SCOPED_TIMER("Unit::MoveType::Update");

		for (activeUpdateUnit = 0; activeUpdateUnit < activeUnits.size();++activeUpdateUnit) {
			CUnit* unit = activeUnits[activeUpdateUnit];
			AMoveType* moveType = unit->moveType;

			UNIT_SANITY_CHECK(unit);

			if (moveType->Update()) {
				eventHandler.UnitMoved(unit);
			}
			if (!unit->pos.IsInBounds() && (unit->speed.w > MAX_UNIT_SPEED)) {
				// this unit is not coming back, kill it now without any death
				// sequence (so deathScriptFinished becomes true immediately)
				unit->KillUnit(nullptr, false, true, false);
			}

			UNIT_SANITY_CHECK(unit);
			assert(activeUnits[activeUpdateUnit] == unit);
		}
	}

	{
		// Delete dead units
		for (activeUpdateUnit = 0; activeUpdateUnit < activeUnits.size();++activeUpdateUnit) {
			CUnit* unit = activeUnits[activeUpdateUnit];

			if (!unit->deathScriptFinished)
				continue;

			// there are many ways to fiddle with "deathScriptFinished", so a unit
			// may arrive here not having been properly killed (with isDead still
			// false)
			// make sure we always call Killed; no-op if isDead is already true
			unit->KillUnit(nullptr, false, true, true);
			DeleteUnit(unit);

			assert(activeUnits[activeUpdateUnit] == unit);
		}
	}

	{
		SCOPED_TIMER("Unit::UpdateLocalModel");
		for (CUnit* unit: activeUnits) {
			// UnitScript only applies piece-space transforms so
			// we apply the forward kinematics update separately
			// (only if we have any dirty pieces)
			// add ID as offset so the bounding-box update does
			// not run at the same time for every model
			unit->localModel.Update(gs->frameNum + unit->id);
		}
	}

	{
		SCOPED_TIMER("Unit::SlowUpdate");
		assert(activeSlowUpdateUnit >= 0);
		// reset the iterator every <UNIT_SLOWUPDATE_RATE> frames
		if ((gs->frameNum % UNIT_SLOWUPDATE_RATE) == 0) {
			activeSlowUpdateUnit = 0;
		}

		// stagger the SlowUpdate's
		unsigned int n = (activeUnits.size() / UNIT_SLOWUPDATE_RATE) + 1;

		for (; activeSlowUpdateUnit < activeUnits.size() && n != 0; ++activeSlowUpdateUnit) {
			CUnit* unit = activeUnits[activeSlowUpdateUnit];

			UNIT_SANITY_CHECK(unit);
			unit->SlowUpdate();
			unit->SlowUpdateWeapons();
			UNIT_SANITY_CHECK(unit);

			n--;
		}
	}

	{
		SCOPED_TIMER("Unit::Update");

		for (activeUpdateUnit = 0; activeUpdateUnit < activeUnits.size();++activeUpdateUnit) {
			CUnit* unit = activeUnits[activeUpdateUnit];
			UNIT_SANITY_CHECK(unit);
			unit->Update();
			UNIT_SANITY_CHECK(unit);
			assert(activeUnits[activeUpdateUnit] == unit);
		}
	}

	{
		SCOPED_TIMER("Unit::Weapon::Update");

		for (activeUpdateUnit = 0; activeUpdateUnit < activeUnits.size();++activeUpdateUnit) {
			CUnit* unit = activeUnits[activeUpdateUnit];
			if (unit->CanUpdateWeapons()) {
				for (CWeapon* w: unit->weapons) {
					w->Update();
				}
			}
			assert(activeUnits[activeUpdateUnit] == unit);
		}
	}
}
Beispiel #5
0
void CUnitHandler::Update()
{
	{
		GML_STDMUTEX_LOCK(runit); // Update

		if (!unitsToBeRemoved.empty()) {
			GML_RECMUTEX_LOCK(obj); // Update

			while (!unitsToBeRemoved.empty()) {
				eventHandler.DeleteSyncedObjects(); // the unit destructor may invoke eventHandler, so we need to call these for every unit to clear invaild references from the batching systems

				GML_RECMUTEX_LOCK(unit); // Update

				eventHandler.DeleteSyncedUnits();

				GML_RECMUTEX_LOCK(proj); // Update - projectile drawing may access owner() and lead to crash
				GML_RECMUTEX_LOCK(sel);  // Update - unit is removed from selectedUnits in ~CObject, which is too late.
				GML_RECMUTEX_LOCK(quad); // Update - make sure unit does not get partially deleted before before being removed from the quadfield

				CUnit* delUnit = unitsToBeRemoved.back();
				unitsToBeRemoved.pop_back();

				DeleteUnitNow(delUnit);
			}
		}

		eventHandler.UpdateUnits();
	}

	GML::UpdateTicks();

	#define VECTOR_SANITY_CHECK(v)                              \
		assert(!math::isnan(v.x) && !math::isinf(v.x)); \
		assert(!math::isnan(v.y) && !math::isinf(v.y)); \
		assert(!math::isnan(v.z) && !math::isinf(v.z));
	#define MAPPOS_SANITY_CHECK(unit)                          \
		if (unit->unitDef->IsGroundUnit()) {                   \
			assert(unit->pos.x >= -(float3::maxxpos * 16.0f)); \
			assert(unit->pos.x <=  (float3::maxxpos * 16.0f)); \
			assert(unit->pos.z >= -(float3::maxzpos * 16.0f)); \
			assert(unit->pos.z <=  (float3::maxzpos * 16.0f)); \
		}
	#define UNIT_SANITY_CHECK(unit)                 \
		VECTOR_SANITY_CHECK(unit->pos);             \
		VECTOR_SANITY_CHECK(unit->midPos);          \
		VECTOR_SANITY_CHECK(unit->relMidPos);       \
		VECTOR_SANITY_CHECK(unit->speed);           \
		VECTOR_SANITY_CHECK(unit->deathSpeed);      \
		VECTOR_SANITY_CHECK(unit->residualImpulse); \
		VECTOR_SANITY_CHECK(unit->rightdir);        \
		VECTOR_SANITY_CHECK(unit->updir);           \
		VECTOR_SANITY_CHECK(unit->frontdir);        \
		MAPPOS_SANITY_CHECK(unit);

	{
		SCOPED_TIMER("Unit::MoveType::Update");
		std::list<CUnit*>::iterator usi;
		for (usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) {
			CUnit* unit = *usi;
			AMoveType* moveType = unit->moveType;

			UNIT_SANITY_CHECK(unit);

			if (moveType->Update()) {
				eventHandler.UnitMoved(unit);
			}
			if (!unit->pos.IsInBounds() && (unit->speed.SqLength() > (MAX_UNIT_SPEED * MAX_UNIT_SPEED))) {
				// this unit is not coming back, kill it now without any death
				// sequence (so deathScriptFinished becomes true immediately)
				unit->KillUnit(NULL, false, true, false);
			}

			UNIT_SANITY_CHECK(unit);
			GML::GetTicks(unit->lastUnitUpdate);
		}
	}

	{
		SCOPED_TIMER("Unit::Update");
		std::list<CUnit*>::iterator usi;
		for (usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) {
			CUnit* unit = *usi;

			UNIT_SANITY_CHECK(unit);

			if (unit->deathScriptFinished) {
				// there are many ways to fiddle with "deathScriptFinished", so a unit may
				// arrive here without having been properly killed (and isDead still false),
				// which can result in MT deadlocking -- FIXME verify this
				// (KU returns early if isDead)
				unit->KillUnit(NULL, false, true);

				DeleteUnit(unit);
			} else {
				unit->Update();
			}

			UNIT_SANITY_CHECK(unit);
		}
	}

	{
		SCOPED_TIMER("Unit::SlowUpdate");

		// reset the iterator every <UNIT_SLOWUPDATE_RATE> frames
		if ((gs->frameNum & (UNIT_SLOWUPDATE_RATE - 1)) == 0) {
			activeSlowUpdateUnit = activeUnits.begin();
		}

		// stagger the SlowUpdate's
		int n = (activeUnits.size() / UNIT_SLOWUPDATE_RATE) + 1;

		for (; activeSlowUpdateUnit != activeUnits.end() && n != 0; ++activeSlowUpdateUnit) {
			CUnit* unit = *activeSlowUpdateUnit;

			UNIT_SANITY_CHECK(unit);
			unit->SlowUpdate();
			UNIT_SANITY_CHECK(unit);

			n--;
		}
	}
}
Beispiel #6
0
void CUnitHandler::Update()
{
	{
		if (!unitsToBeRemoved.empty()) {
			while (!unitsToBeRemoved.empty()) {
				eventHandler.DeleteSyncedObjects(); // the unit destructor may invoke eventHandler, so we need to call these for every unit to clear invaild references from the batching systems

				eventHandler.DeleteSyncedUnits();

				CUnit* delUnit = unitsToBeRemoved.back();
				unitsToBeRemoved.pop_back();

				DeleteUnitNow(delUnit);
			}
		}

		eventHandler.UpdateUnits();
	}

	#define MAPPOS_SANITY_CHECK(unit)                          \
		if (unit->unitDef->IsGroundUnit()) {                   \
			assert(unit->pos.x >= -(float3::maxxpos * 16.0f)); \
			assert(unit->pos.x <=  (float3::maxxpos * 16.0f)); \
			assert(unit->pos.z >= -(float3::maxzpos * 16.0f)); \
			assert(unit->pos.z <=  (float3::maxzpos * 16.0f)); \
		}
	#define UNIT_SANITY_CHECK(unit)         \
		unit->pos.AssertNaNs();             \
		unit->midPos.AssertNaNs();          \
		unit->relMidPos.AssertNaNs();       \
		unit->speed.AssertNaNs();           \
		unit->deathSpeed.AssertNaNs();      \
		unit->rightdir.AssertNaNs();        \
		unit->updir.AssertNaNs();           \
		unit->frontdir.AssertNaNs();        \
		MAPPOS_SANITY_CHECK(unit);

	{
		SCOPED_TIMER("Unit::MoveType::Update");

		for (auto usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) {
			CUnit* unit = *usi;
			AMoveType* moveType = unit->moveType;

			UNIT_SANITY_CHECK(unit);

			if (moveType->Update()) {
				eventHandler.UnitMoved(unit);
			}
			if (!unit->pos.IsInBounds() && (Square(unit->speed.w) > (MAX_UNIT_SPEED * MAX_UNIT_SPEED))) {
				// this unit is not coming back, kill it now without any death
				// sequence (so deathScriptFinished becomes true immediately)
				unit->KillUnit(NULL, false, true, false);
			}

			UNIT_SANITY_CHECK(unit);
		}
	}

	{
		// Delete dead units
		for (auto usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) {
			CUnit* unit = *usi;

			if (unit->deathScriptFinished) {
				// there are many ways to fiddle with "deathScriptFinished", so a unit may
				// arrive here without having been properly killed (and isDead still false),
				// which can result in MT deadlocking -- FIXME verify this
				// (KU returns early if isDead)
				unit->KillUnit(NULL, false, true);
				DeleteUnit(unit);
			}
		}
	}

	{
		SCOPED_TIMER("Unit::UpdatePieceMatrices");

		for (auto usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) {
			// UnitScript only applies piece-space transforms so
			// we apply the forward kinematics update separately
			// (only if we have any dirty pieces)
			CUnit* unit = *usi;
			unit->localModel->UpdatePieceMatrices();
		}
	}

	{
		SCOPED_TIMER("Unit::Update");

		for (auto usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) {
			CUnit* unit = *usi;
			UNIT_SANITY_CHECK(unit);
			unit->Update();
			UNIT_SANITY_CHECK(unit);
		}
	}

	{
		SCOPED_TIMER("Unit::SlowUpdate");

		// reset the iterator every <UNIT_SLOWUPDATE_RATE> frames
		if ((gs->frameNum & (UNIT_SLOWUPDATE_RATE - 1)) == 0) {
			activeSlowUpdateUnit = activeUnits.begin();
		}

		// stagger the SlowUpdate's
		unsigned int n = (activeUnits.size() / UNIT_SLOWUPDATE_RATE) + 1;

		for (; activeSlowUpdateUnit != activeUnits.end() && n != 0; ++activeSlowUpdateUnit) {
			CUnit* unit = *activeSlowUpdateUnit;

			UNIT_SANITY_CHECK(unit);
			unit->SlowUpdate();
			UNIT_SANITY_CHECK(unit);

			n--;
		}
	}
}
Beispiel #7
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);
}
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);
}
Beispiel #9
0
void CUnitHandler::Update()
{
	{
		GML_STDMUTEX_LOCK(runit); // Update

		if (!toBeRemoved.empty()) {
			GML_RECMUTEX_LOCK(unit); // Update - for anti-deadlock purposes.
			GML_RECMUTEX_LOCK(sel);  // Update - unit is removed from selectedUnits in ~CObject, which is too late.
			GML_RECMUTEX_LOCK(quad); // Update - make sure unit does not get partially deleted before before being removed from the quadfield
			GML_STDMUTEX_LOCK(proj); // Update - projectile drawing may access owner() and lead to crash

			eventHandler.DeleteSyncedUnits();

			while (!toBeRemoved.empty()) {
				CUnit* delUnit = toBeRemoved.back();
				toBeRemoved.pop_back();

				DeleteUnitNow(delUnit);
			}
		}

		eventHandler.UpdateUnits();
	}

	GML_UPDATE_TICKS();

	{
		SCOPED_TIMER("Unit Movetype update");
		std::list<CUnit*>::iterator usi;
		for (usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) {
			CUnit* unit = *usi;
			AMoveType* moveType = unit->moveType;

			if (moveType->Update()) {
				eventHandler.UnitMoved(unit);
			}

			GML_GET_TICKS(unit->lastUnitUpdate);
		}
	}

	{
		SCOPED_TIMER("Unit update");
		std::list<CUnit*>::iterator usi;
		for (usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) {
			CUnit* unit = *usi;

			if (unit->deathScriptFinished) {
				// there are many ways to fiddle with "deathScriptFinished", so a unit may
				// arrive here without having been properly killed (and isDead still false),
				// which can result in MT deadlocking -- FIXME verify this
				// (KU returns early if isDead)
				unit->KillUnit(false, true, NULL);

				DeleteUnit(unit);
			} else {
				unit->Update();
			}
		}
	}

	{
		SCOPED_TIMER("Unit slow update");
		if (!(gs->frameNum & (UNIT_SLOWUPDATE_RATE - 1))) {
			slowUpdateIterator = activeUnits.begin();
		}

		int n = (activeUnits.size() / UNIT_SLOWUPDATE_RATE) + 1;
		for (; slowUpdateIterator != activeUnits.end() && n != 0; ++ slowUpdateIterator) {
			(*slowUpdateIterator)->SlowUpdate(); n--;
		}
	} // for timer destruction
}