sources » TechnoClass » ReceiveDamage
/*
* eDamageState values:
* 0 dsUnaffected - not affected (100% health)
* 1 dsUnchanged - didn't change state
* 2 dsNowYellow - changed to yellow (only if was in green, otherwise remains unchanged)
* 3 dsNowRed - changed to red
* 4 dsDeath - changed to dead, Health = 0
* 5 dsPostMortem - dead, just cleaning up
*/
eDamageState TechnoClass::ReceiveDamage(int &Damage, int unknown, WarheadTypeClass *WH, TechnoClass *attacker, bool ignoreDefenses, int arg10, HouseClass *attackerHouse) {
bool negativeDamage = Damage < 0;
if( !ignoreDefenses && !negativeDamage ) {
float typeArmorMult = this->OwningPlayer->GetTypeArmorMultiplier(this->GetType()); // [Country]VehicleArmorMult or such
float selfArmorMult = this->ArmorMultiplier; // armor crate or such
Damage /= (typeArmorMult * selfArmorMult);
if( this->IsVeteran() || this->IsElite() ) {
TechnoTypeClass *Type = this->GetType();
if (
( this->IsVeteran() && ( Type->VeteranAbilities & ABILITY_STRONGER ) ) ||
( this->IsElite() &&
( ( Type->VeteranAbilities & ABILITY_STRONGER )
|| ( Type->EliteAbilities & ABILITY_STRONGER ) ) )
) {
Damage /= Rules->VeteranArmor;
}
}
Damage = max( Damage, 1 ); // if damage < 1 then damage = 1
}
if( attacker ) {
if ( this->GetType()->TypeImmune ) {
if( attacker->GetType() == this->GetType() ) {
if( attacker->OwningPlayer == this->OwningPlayer ) {
return dsUnaffected;
}
}
}
}
if( this->IsIronCurtained() ) {
if( !ignoreDefenses && !negativeDamage ) {
int CLDisableFlags = DISABLE_NONE;
if( this->IronCurtained ) {
CLDisableFlags = DISABLE_GREEN | DISABLE_BLUE;
}
FlashbangWarheadAt(
Damage * 2,
WH,
this->pos.X,
this->pos.Y,
this->pos.Z,
1, // override warhead's Bright= setting and flash anyway
CLDisableFlags);
return dsUnaffected;
}
}
if( this->GetType()->DamageReducesReadiness ) {
float ratio = Damage / this->GetType()->Strength;
ratio *= this->GetType()->ReadinessReductionMultiplier;
curAmmo = this->currentAmmo;
this->currentAmmo = int (curAmmo - this->GetType()->Ammo * ratio);
this->Update_Reloading();
}
if( this->Bunker ) {
if( !ignoreDefenses ) {
if( this->What_Am_I() == IS_BUILDING ) {
if( WH && WH->PenetratesBunker ) {
Damage = 0;
return dsUnaffected;
}
} else {
if( WH && !WH->PenetratesBunker ) {
if( this->GetCell()->GetBuilding() == this->Bunker ) {
Damage = 0;
return dsUnaffected;
}
}
}
}
}
if( WH ) {
if( WH->Radiation && this->ImmuneToRadiation ) {
Damage = 0;
return dsUnaffected;
}
if( WH->PsychicDamage && this->ImmuneToPsionicWeapons ) {
Damage = 0;
return dsUnaffected;
}
if( WH->Poison && this->ImmuneToPoison ) {
Damage = 0;
return dsUnaffected;
}
if( !WH->AffectsAllies && attacker && this->OwningPlayer->IsAlly(attacker->OwningPlayer) ) {
Damage = 0;
return dsUnaffected;
}
if( WH->Psychedelic ) {
if( this->OwningPlayer->IsAlly(attackerHouse) ) {
Damage = 0;
return dsUnaffected;
}
if( this->GetType()->ImmuneToPsionics ) {
Damage = 0;
return dsUnaffected;
}
if( this->What_Am_I() == IS_BUILDING ) {
Damage = 0;
return dsUnaffected;
}
Damage = GetVersedDamage(Damage, this->GetType()->Armor, WH);
this->BerzerkDurationLeft = Damage;
if( !this->Berzerk ) {
this->Berzerk = 1;
if( this ) {
AITeamClass *Team = this->BelongsToTeam;
if( Team ) {
Team->LiberateMember(this, -1, 0);
}
}
this->SetTarget(NULL);
this->QueueMission(MISSION_HUNT);
return dsUnaffected;
}
}
}
eDamageState state = ObjectClass *::ReceiveDamage(damage, attackerType, WH, attacker, ignoreDefenses, a10, attackerHouse);
if( target ) {
TechnoTypeClass *Type = this->GetType();
float damagedForCredits = Type->GetCost(attacker->OwningPlayer) * (IntToFloat(Damage) / Strength);
this->OwningPlayer->DamagedForCredits(FloatToInt(damageInCredits));
}
if( state == dsDeath ) {
if( this->What_Am_I() == IS_BUILDING && WH && WH->CausesDelayKill ) {
BuildingClass *Building;
if( this ) {
Building = (BuildingClass *)this;
} else {
Building = NULL;
}
if( Building->BuildingType->EligibleForDelayKill ) {
float DKFrames = WH->DelayKillFrames;
float DKMax = WH->DelayKillAtMax;
float x1 = (DKMax * DKFrames) - DKFrames;
DKSpread = FloatToInt(WH->CellSpread) * 256;
x1 /= DKSpread;
x1 = x1 * int(arg9) + x1;
int xx1 = FloatToInt(x1);
if( Building->C4Planted ) {
if( Building->C4Timer.StartingFrame != -1 ) {
if( CurrentFrame - Building->C4Timer.StartingFrame < Building->C4Timer.Duration ) {
Building->C4Timer.Duration -= (CurrentFrame - Building->C4Timer.StartingFrame);
} else {
Building->C4Timer.Duration = 0;
}
if( xx1 < Building->C4Timer.Duration ) {
Building->C4Timer.StartFrame = CurrentFrame;
Building->C4Timer.Duration = xx1;
}
Building->C4Planted = 1;
this->Alive = 1;
this->Health = 1;
return dsPostMortem;
}
} else {
Building->C4Planted = 1;
Building->C4Timer.StartFrame = CurrentFrame;
Building->C4Timer.Duration = xx1;
this->Alive = 1;
this->Health = 1;
return dsPostMortem;
}
}
}
}
if( state == dsPostMortem ) {
return state;
}
if( state != dsUnaffected ) {
this->RadarFlashTimer.StartFrame = CurrentFrame;
this->RadarFlashTimer.Duration = Rules->RadarCombatFlashTime;
if( state != dsDeath ) {
if( this->GetType()->CanDisguise && !this->GetType()->PermaDisguise ) {
if( this->IsDisguised() ) {
this->ClearDisguise();
}
this->DisguiseBlinkTimer.StartFrame = CurrentFrame;
this->DisguiseBlinkTimer.Duration = Damage * 2;
}
}
}
if( !this->Health ) {
state = dsDeath;
}
bool doUnchangedAnyway = 1;
switch( state ) {
case dsNowYellow: {
doUnchangedAnyway = 0;
if( this->GetType()->VoiceFeedback.Length ) {
if( Random_Ranged(0, 100) <= 30 ) {
if( this->OwningPlayer->IsPlayer() ) {
VocClass::PlayIndex(this->GetType()->VoiceFeedback[Random() % this->GetType()->VoiceFeedback.Length], 0, this->pos);
}
}
}
} // no break - falls through to the next case
case dsUnchanged:
case dsPostMortem: {
if( doUnchangedAnyway ) {
if( this->GetType()->DamageSound ) {
VocClass::PlayIndex(this->GetType()->DamageSound, 0, this->pos);
}
if( this->GetType()->DamageSound ) { // oh, it's you again
VocClass::PlayIndex(this->GetType()->DamageSound, 0, this->pos);
}
if( this->GetType()->ToProtect || this->neverUsedAlwaysFalse ) {
if( !this->OwningPlayer->IsHumanControlled() ) {
if( target ) {
this->ProtectMeFrom(target);
}
}
}
if( state == dsDeath ) {
return state;
}
}
} // no break - falls through to the next case
case dsUnaffected:
case dsNowRed: {
if( target ) {
if( !this->OwningPlayer->IsAlliedObject(target) ) {
this->HasBeenAttacked = 1;
}
}
this->Uncloak();
if( this->GetHealthPercentage() > Rules->ConditionYellow ) {
if( this->DamagedParticleSystem ) {
this->DamagedParticleSystem->UnInit();
}
} else if ( state == dsNowYellow || state == dsNowRed ) {
vector<ParticleSystemTypeClass *> Parts;
TechnoTypeClass *Type = this->GetType();
for( int i = 0; i < Type->DamageParticleSystems.Length; ++i) {
ParticleSystemTypeClass *part = Type->DamageParticleSystems[i];
if( part->BehavesLike == BEHAVES_SMOKE ) {
Parts.Append(part);
}
}
if( !this->DamagedParticleSystem && Parts.Length ) {
if( this->GetHeight() > -10 ) {
dwXYZ *coords = this->GetType()->GetDamageParticlePosition();
ParticleSystemClass *PartSys = new ParticleSystemClass(
Parts[Random_Ranged(0, Parts.Length - 1)], // ParticleSystemTypeClass *psType
this->Pos, // dwXYZ *Position
NULL, // ObjectClass *Target
this, // TechnoClass *Owner,
this->Pos, // dwXYZ *particleSpawnPosition,
0 // int unknown
);
this->DamagedParticleSystem = PartSys;
}
}
}
if( !damageNegative ) {
if( this->ShouldRetaliate(target) ) {
if( target ) {
int idxWeapon = this->SelectWeapon(target);
if( !this->IsCloseEnough(target, idxWeapon) && this->OwningPlayer->IsHumanControlled() ) {
dwXYZ *xyzTarget = target->GetCoords();
dwXYZ *xyzMyself = this->GetCoords();
int Distance = FloatToInt(float_sqrt(XYZ_Distance(xyzTarget, xyzMyself)));
float SightRange = (this->GetType()->Sight + 0.5) * 256.0;
if( Distance < SightRange ) {
this->SetNewTarget(MISSION_MOVE, target, 0);
}
} else {
this->SetNewTarget(MISSION_MOVE, target, 0);
}
}
if( this ) {
if( this->derivationFlags & dfFoot ) { // Infantry/Vehicle/Aircraft, no Building
if( !this->Target && !this->Destination ) {
if( Rules->PlayerScatter ||
( this->IsVeteran() && ( Type->VeteranAbilities & ABILITY_SCATTER ) ) ||
( this->IsElite() &&
( ( Type->VeteranAbilities & ABILITY_SCATTER )
|| ( Type->EliteAbilities & ABILITY_SCATTER ) ) ) {
this->Scatter(this->Pos, 1, 0);
return state;
}
}
}
}
}
if( this->derivationFlags & dfFoot ) {
if( this->GetMissionData()->Scatter ) {
if( !this->IsBusy ) {
if( !this->Locomotor->IsMoving() ) {
if( !this->Target && !this->Destination ) {
if( this->What_Am_I() != IS_AIRCRAFT ) {
if( this->OwningPlayer->IsHumanControlled() ) {
if( Rules->PlayerScatter ||
( this->IsVeteran() && ( Type->VeteranAbilities & ABILITY_SCATTER ) ) ||
( this->IsElite() &&
( ( Type->VeteranAbilities & ABILITY_SCATTER )
|| ( Type->EliteAbilities & ABILITY_SCATTER ) ) ) {
this->Scatter(this->Pos, 1, 0);
return state;
}
}
}
}
}
}
}
}
}
return state;
}
break;
case dsDead: {
if( this->SlaveManager ) {
this->SlaveManager->ReleaseSlaves();
}
if( this->DrainTarget ) {
if( this->DrainAnimation ) {
this->DrainAnimation->UnInit();
this->DrainAnimation = NULL;
}
if( this->DrainTarget ) {
this->DrainTarget->DrainingMe = NULL;
if( this->DrainTarget->OwningPlayer ) {
this->DrainTarget->OwningPlayer->RecheckTechLevels = 1;
}
this->DrainTarget = NULL;
}
}
if( this->DrainingMe ) {
if( this->DrainingMe->DrainAnimation ) {
this->DrainingMe->DrainAnimation->UnInit();
this->DrainingMe->DrainAnimation = NULL;
}
if( this->DrainingMe->DrainTarget ) {
this->DrainingMe->DrainTarget->DrainingMe = NULL;
this->DrainingMe->DrainTarget->OwningPlayer->RecheckTechLevels = 1;
this->DrainingMe->DrainTarget = NULL;
}
}
if( this->CaptureManager ) {
this->CaptureManager->ReleaseAll();
}
if( this->GetType()->VoiceDie.Length && this->OwningPlayer->IsPlayer() ) {
VocClass::PlayIndex(this->GetType()->VoiceDie[Random() % this->GetType()->VoiceDie.Length], 0, this->pos);
}
if( this->GetType()->DieSound.Length && this->OwningPlayer->IsPlayer() ) {
VocClass::PlayIndex(this->GetType()->DieSound[Random() % this->GetType()->DieSound.Length], 0, this->pos);
}
if ( this->SpawnManager ) {
this->SpawnManager->KamikazeNodes();
}
this->Deselect();
if( this->FireParticleSystem ) {
this->FireParticleSystem->UnInit();
this->FireParticleSystem = NULL;
}
if( this->GetHeight > 10 || Map->GetCellAt(this->pos)->LandType != IS_WATER ) {
int totalDebris;
if( this->GetType->MaxDebris > 0 ) {
totalDebris = Random_Ranged(this->GetType()->MinDebris, this->GetType()->MaxDebris - 1);
if( this->GetType()->DebrisTypes.Length && totalDebris) {
for( int i = 0; i < this->GetType()->DebrisMaximums.Length; ++i ) {
int amount = Random() % this->GetType()->DebrisMaximums[i];
amount = max(amount, totalDebris);
for( int j = 0; j < amount; ++j) {
new VoxelAnimationClass(this->GetType()->DebrisTypes[i], this->GetCoords());
--totalDebris;
}
}
}
}
if( this->GetType()->DebrisAnims.Length ) {
while( totalDebris ) {
new AnimClass(
// AnimType
this->GetType()->DebrisAnims[Random_Ranged(0, this->GetType()->DebrisAnims.Length - 1)],
this->GetCoords(), // Coords
0,
1, // RepeatTimes
sizeof(AnimClass),
0, // ForceZAdjust
0);
--totalDebris;
}
} else if( !this->GetType()->DebrisAnims.Length ) { // department of redundancy department
while( totalDebris ) {
new AnimClass(
// AnimType
Rules->MetallicDebris[Random_Ranged(0, Rules->MetallicDebris.Length - 1)],
this->GetCoords(), // Coords
0,
1, // RepeatTimes
sizeof(AnimClass),
0, // ForceZAdjust
0);
--totalDebris;
}
}
WeaponTypeClass *Weapon = this->GetWeapon(this->CurrentTurretNumber);
if( this->GetType()->Explodes ||
( this->IsVeteran() && ( Type->VeteranAbilities & ABILITY_EXPLODES ) ) ||
( this->IsElite() &&
( ( Type->VeteranAbilities & ABILITY_EXPLODES ) || ( Type->EliteAbilities & ABILITY_EXPLODES ) ) ) ) {
if( Weapon && Weapon->Suicide ) {
while( this->Passengers.First ) {
FootClass *Hiker = this->GetFirstPassenger;
if( Hiker->BelongsToTeam ) {
Hiker->BelongsToTeam->LiberateMember(Hiker, -1, 0);
}
Hiker = this->RemoveFirstPassenger();
if( Hiker ) {
Hiker->AnnounceDestruction();
Hiker->UnInit();
}
}
this->FireDeathWeapon(0);
}
}
}
if( this->IvanBomb ) {
this->IvanBomb->Detonate();
return state;
}
return state;
}
break;
}
}