AInventory *AAmmo::CreateCopy (AActor *other) { AInventory *copy; int amount = Amount; // extra ammo in baby mode and nightmare mode if (!(ItemFlags&IF_IGNORESKILL)) { amount = FixedMul(amount, G_SkillProperty(SKILLP_AmmoFactor)); } if (GetClass()->ParentClass != RUNTIME_CLASS(AAmmo) && GetClass() != RUNTIME_CLASS(AAmmo)) { const PClass *type = GetParentAmmo(); assert (type->ActorInfo != NULL); if (!GoAway ()) { Destroy (); } copy = static_cast<AInventory *>(Spawn (type, 0, 0, 0, NO_REPLACE)); copy->Amount = amount; copy->BecomeItem (); } else { copy = Super::CreateCopy (other); copy->Amount = amount; } if (copy->Amount > copy->MaxAmount) { // Don't pick up more ammo than you're supposed to be able to carry. copy->Amount = copy->MaxAmount; } return copy; }
bool ABackpackItem::HandlePickup (AInventory *item) { // Since you already have a backpack, that means you already have every // kind of ammo in your inventory, so we don't need to look at the // entire PClass list to discover what kinds of ammo exist, and we don't // have to alter the MaxAmount either. if (item->IsKindOf (RUNTIME_CLASS(ABackpackItem))) { for (AInventory *probe = Owner->Inventory; probe != NULL; probe = probe->Inventory) { if (probe->GetClass()->ParentClass == RUNTIME_CLASS(AAmmo)) { if (probe->Amount < probe->MaxAmount || sv_unlimited_pickup) { int amount = static_cast<AAmmo*>(probe->GetDefault())->BackpackAmount; // extra ammo in baby mode and nightmare mode if (!(item->ItemFlags&IF_IGNORESKILL)) { amount = int(amount * G_SkillProperty(SKILLP_AmmoFactor)); } probe->Amount += amount; if (probe->Amount > probe->MaxAmount && !sv_unlimited_pickup) { probe->Amount = probe->MaxAmount; } } } } // The pickup always succeeds, even if you didn't get anything item->ItemFlags |= IF_PICKUPGOOD; return true; } return false; }
bool CheckCheatmode (bool printmsg) { if ((G_SkillProperty(SKILLP_DisableCheats) || netgame || deathmatch) && (!sv_cheats)) { if (printmsg) Printf ("sv_cheats must be true to enable this command.\n"); return true; } else { return false; } }
AInventory *ABackpackItem::CreateCopy (AActor *other) { // Find every unique type of ammo. Give it to the player if // he doesn't have it already, and double its maximum capacity. for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) { PClass *type = PClassActor::AllActorClasses[i]; if (type->ParentClass == RUNTIME_CLASS(AAmmo)) { PClassActor *atype = static_cast<PClassActor *>(type); AAmmo *ammo = static_cast<AAmmo *>(other->FindInventory(atype)); int amount = static_cast<AAmmo *>(GetDefaultByType(type))->BackpackAmount; // extra ammo in baby mode and nightmare mode if (!(ItemFlags&IF_IGNORESKILL)) { amount = int(amount * G_SkillProperty(SKILLP_AmmoFactor)); } if (amount < 0) amount = 0; if (ammo == NULL) { // The player did not have the ammo. Add it. ammo = static_cast<AAmmo *>(Spawn(atype)); ammo->Amount = bDepleted ? 0 : amount; if (ammo->BackpackMaxAmount > ammo->MaxAmount) { ammo->MaxAmount = ammo->BackpackMaxAmount; } if (ammo->Amount > ammo->MaxAmount) { ammo->Amount = ammo->MaxAmount; } ammo->AttachToOwner (other); } else { // The player had the ammo. Give some more. if (ammo->MaxAmount < ammo->BackpackMaxAmount) { ammo->MaxAmount = ammo->BackpackMaxAmount; } if (!bDepleted && ammo->Amount < ammo->MaxAmount) { ammo->Amount += amount; if (ammo->Amount > ammo->MaxAmount) { ammo->Amount = ammo->MaxAmount; } } } } } return Super::CreateCopy (other); }
bool AAmmo::HandlePickup (AInventory *item) { if (GetClass() == item->GetClass() || (item->IsKindOf (RUNTIME_CLASS(AAmmo)) && static_cast<AAmmo*>(item)->GetParentAmmo() == GetClass())) { if (Amount < MaxAmount || sv_unlimited_pickup) { int receiving = item->Amount; if (!(item->ItemFlags & IF_IGNORESKILL)) { // extra ammo in baby mode and nightmare mode receiving = FixedMul(receiving, G_SkillProperty(SKILLP_AmmoFactor)); } int oldamount = Amount; if (Amount > 0 && Amount + receiving < 0) { Amount = 0x7fffffff; } else { Amount += receiving; } if (Amount > MaxAmount && !sv_unlimited_pickup) { Amount = MaxAmount; } item->ItemFlags |= IF_PICKUPGOOD; // If the player previously had this ammo but ran out, possibly switch // to a weapon that uses it, but only if the player doesn't already // have a weapon pending. assert (Owner != NULL); if (oldamount == 0 && Owner != NULL && Owner->player != NULL) { barrier_cast<APlayerPawn *>(Owner)->CheckWeaponSwitch(GetClass()); } } return true; } if (Inventory != NULL) { return Inventory->HandlePickup (item); } return false; }
bool AWeapon::AddExistingAmmo (AAmmo *ammo, int amount) { if (ammo != NULL && (ammo->Amount < ammo->MaxAmount || sv_unlimited_pickup)) { // extra ammo in baby mode and nightmare mode if (!(ItemFlags&IF_IGNORESKILL)) { amount = int(amount * G_SkillProperty(SKILLP_AmmoFactor)); } ammo->Amount += amount; if (ammo->Amount > ammo->MaxAmount && !sv_unlimited_pickup) { ammo->Amount = ammo->MaxAmount; } return true; } return false; }
// ==================================================================== // // Adds a statistics entry // // ==================================================================== static FSessionStatistics *StatisticsEntry(FStatistics *stats, const char *text, int playtime) { FSessionStatistics s; time_t clock; struct tm *lt; time (&clock); lt = localtime (&clock); if (lt != NULL) mysnprintf(s.name, countof(s.name), "%02d.%02d.%04d",lt->tm_mday, lt->tm_mon+1, lt->tm_year+1900); else strcpy(s.name,"00.00.0000"); s.skill=G_SkillProperty(SKILLP_ACSReturn); strcpy(s.info, text); s.timeneeded=playtime; stats->stats.Push(s); return &stats->stats[stats->stats.Size()-1]; }
void P_AutoUseHealth(player_t *player, int saveHealth) { TArray<AInventory *> NormalHealthItems; TArray<AInventory *> LargeHealthItems; for(AInventory *inv = player->mo->Inventory; inv != NULL; inv = inv->Inventory) { if (inv->Amount > 0 && inv->IsKindOf(RUNTIME_CLASS(AHealthPickup))) { int mode = static_cast<AHealthPickup*>(inv)->autousemode; if (mode == 1) NormalHealthItems.Push(inv); else if (mode == 2) LargeHealthItems.Push(inv); } } int normalhealth = CountHealth(NormalHealthItems); int largehealth = CountHealth(LargeHealthItems); bool skilluse = !!G_SkillProperty(SKILLP_AutoUseHealth); if (skilluse && normalhealth >= saveHealth) { // Use quartz flasks player->health += UseHealthItems(NormalHealthItems, saveHealth); } else if (largehealth >= saveHealth) { // Use mystic urns player->health += UseHealthItems(LargeHealthItems, saveHealth); } else if (skilluse && normalhealth + largehealth >= saveHealth) { // Use mystic urns and quartz flasks player->health += UseHealthItems(NormalHealthItems, saveHealth); if (saveHealth > 0) player->health += UseHealthItems(LargeHealthItems, saveHealth); } player->mo->health = player->health; }
AAmmo *AWeapon::AddAmmo (AActor *other, PClassActor *ammotype, int amount) { AAmmo *ammo; if (ammotype == NULL) { return NULL; } // [BC] This behavior is from the original Doom. Give 5/2 times as much ammo when // we pick up a weapon in deathmatch. if (( deathmatch ) && ( gameinfo.gametype & GAME_DoomChex )) amount = amount * 5 / 2; // extra ammo in baby mode and nightmare mode if (!(this->ItemFlags&IF_IGNORESKILL)) { amount = int(amount * G_SkillProperty(SKILLP_AmmoFactor)); } ammo = static_cast<AAmmo *>(other->FindInventory (ammotype)); if (ammo == NULL) { ammo = static_cast<AAmmo *>(Spawn (ammotype)); ammo->Amount = MIN (amount, ammo->MaxAmount); ammo->AttachToOwner (other); } else if (ammo->Amount < ammo->MaxAmount) { ammo->Amount += amount; if (ammo->Amount > ammo->MaxAmount) { ammo->Amount = ammo->MaxAmount; } } return ammo; }
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BrainSpit) { DSpotState *state = DSpotState::GetSpotState(); AActor *targ; AActor *spit; bool isdefault = false; ACTION_PARAM_START(1); ACTION_PARAM_CLASS(spawntype, 0); // shoot a cube at current target targ = state->GetNextInList(PClass::FindClass("BossTarget"), G_SkillProperty(SKILLP_EasyBossBrain)); if (targ != NULL) { if (spawntype == NULL) { spawntype = PClass::FindClass("SpawnShot"); isdefault = true; } // spawn brain missile spit = P_SpawnMissile (self, targ, spawntype); if (spit != NULL) { // Boss cubes should move freely to their destination so it's // probably best to disable all collision detection for them. if (spit->flags & MF_NOCLIP) spit->flags5 |= MF5_NOINTERACTION; spit->target = targ; spit->master = self; // [RH] Do this correctly for any trajectory. Doom would divide by 0 // if the target had the same y coordinate as the spitter. if ((spit->velx | spit->vely) == 0) { spit->special2 = 0; } else if (abs(spit->vely) > abs(spit->velx)) { spit->special2 = (targ->y - self->y) / spit->vely; } else { spit->special2 = (targ->x - self->x) / spit->velx; } // [GZ] Calculates when the projectile will have reached destination spit->special2 += level.maptime; spit->flags6 |= MF6_BOSSCUBE; } if (!isdefault) { S_Sound(self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NONE); } else { // compatibility fallback S_Sound (self, CHAN_WEAPON, "brain/spit", 1, ATTN_NONE); } } }
bool P_GiveBody (AActor *actor, int num, int max) { if (actor->health <= 0 || (actor->player != NULL && actor->player->playerstate == PST_DEAD)) { // Do not heal dead things. return false; } player_t *player = actor->player; num = clamp(num, -65536, 65536); // prevent overflows for bad values if (player != NULL) { // Max is 0 by default, preserving default behavior for P_GiveBody() // calls while supporting AHealth. if (max <= 0) { max = static_cast<APlayerPawn*>(actor)->GetMaxHealth() + player->mo->stamina; // [MH] First step in predictable generic morph effects if (player->morphTics) { if (player->MorphStyle & MORPH_FULLHEALTH) { if (!(player->MorphStyle & MORPH_ADDSTAMINA)) { max -= player->mo->stamina; } } else // old health behaviour { max = MAXMORPHHEALTH; if (player->MorphStyle & MORPH_ADDSTAMINA) { max += player->mo->stamina; } } } } // [RH] For Strife: A negative body sets you up with a percentage // of your full health. if (num < 0) { num = max * -num / 100; if (player->health < num) { player->health = num; actor->health = num; return true; } } else if (num > 0) { if (player->health < max) { num = int(num * G_SkillProperty(SKILLP_HealthFactor)); if (num < 1) num = 1; player->health += num; if (player->health > max) { player->health = max; } actor->health = player->health; return true; } } } else { // Parameter value for max is ignored on monsters, preserving original // behaviour on AHealth as well as on existing calls to P_GiveBody(). max = actor->SpawnHealth(); if (num < 0) { num = max * -num / 100; if (actor->health < num) { actor->health = num; return true; } } else if (actor->health < max) { actor->health += num; if (actor->health > max) { actor->health = max; } return true; } } return false; }
// Returns the amount of damage actually inflicted upon the target, or -1 if // the damage was cancelled. int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, FName mod, int flags) { unsigned ang; player_t *player = NULL; fixed_t thrust; int temp; int painchance = 0; FState * woundstate = NULL; PainChanceList * pc = NULL; bool justhit = false; bool plrDontThrust = false; bool invulpain = false; bool fakedPain = false; bool forcedPain = false; int fakeDamage = 0; int holdDamage = 0; int rawdamage = damage; if (damage < 0) damage = 0; if (target == NULL || !((target->flags & MF_SHOOTABLE) || (target->flags6 & MF6_VULNERABLE))) { // Shouldn't happen return -1; } //Rather than unnecessarily call the function over and over again, let's be a little more efficient. fakedPain = (isFakePain(target, inflictor, damage)); forcedPain = (MustForcePain(target, inflictor)); // Spectral targets only take damage from spectral projectiles. if (target->flags4 & MF4_SPECTRAL && damage < TELEFRAG_DAMAGE) { if (inflictor == NULL || !(inflictor->flags4 & MF4_SPECTRAL)) { return -1; } } if (target->health <= 0) { if (inflictor && mod == NAME_Ice) { return -1; } else if (target->flags & MF_ICECORPSE) // frozen { target->tics = 1; target->flags6 |= MF6_SHATTERING; target->velx = target->vely = target->velz = 0; } return -1; } // [MC] Changed it to check rawdamage here for consistency, even though that doesn't actually do anything // different here. At any rate, invulnerable is being checked before type factoring, which is then being // checked by player cheats/invul/buddha followed by monster buddha. This is inconsistent. Don't let the // original telefrag damage CHECK (rawdamage) be influenced by outside factors when looking at cheats/invul. if ((target->flags2 & MF2_INVULNERABLE) && (rawdamage < TELEFRAG_DAMAGE) && (!(flags & DMG_FORCED))) { // actor is invulnerable if (target->player == NULL) { if (inflictor == NULL || (!(inflictor->flags3 & MF3_FOILINVUL) && !(flags & DMG_FOILINVUL))) { if (fakedPain) { // big mess here: What do we use for the pain threshold? // We cannot run the various damage filters below so for consistency it needs to be 0. damage = 0; invulpain = true; goto fakepain; } else return -1; } } else { // Players are optionally excluded from getting thrust by damage. if (static_cast<APlayerPawn *>(target)->PlayerFlags & PPF_NOTHRUSTWHENINVUL) { if (fakedPain) plrDontThrust = 1; else return -1; } } } if (inflictor != NULL) { if (inflictor->flags5 & MF5_PIERCEARMOR) flags |= DMG_NO_ARMOR; } MeansOfDeath = mod; FriendlyFire = false; // [RH] Andy Baker's Stealth monsters if (target->flags & MF_STEALTH) { target->alpha = OPAQUE; target->visdir = -1; } if (target->flags & MF_SKULLFLY) { target->velx = target->vely = target->velz = 0; } player = target->player; if (!(flags & DMG_FORCED)) // DMG_FORCED skips all special damage checks, TELEFRAG_DAMAGE may not be reduced at all { if (target->flags2 & MF2_DORMANT) { // Invulnerable, and won't wake up return -1; } if ((rawdamage < TELEFRAG_DAMAGE) || (target->flags7 & MF7_LAXTELEFRAGDMG)) // TELEFRAG_DAMAGE may only be reduced with NOTELEFRAGPIERCE or it may not guarantee its effect. { if (player && damage > 1) { // Take half damage in trainer mode damage = FixedMul(damage, G_SkillProperty(SKILLP_DamageFactor)); } // Special damage types if (inflictor) { if (inflictor->flags4 & MF4_SPECTRAL) { if (player != NULL) { if (!deathmatch && inflictor->FriendPlayer > 0) return -1; } else if (target->flags4 & MF4_SPECTRAL) { if (inflictor->FriendPlayer == 0 && !target->IsHostile(inflictor)) return -1; } } damage = inflictor->DoSpecialDamage(target, damage, mod); if (damage < 0) { return -1; } } int olddam = damage; if (damage > 0 && source != NULL) { damage = FixedMul(damage, source->DamageMultiply); // Handle active damage modifiers (e.g. PowerDamage) if (damage > 0 && source->Inventory != NULL) { source->Inventory->ModifyDamage(damage, mod, damage, false); } } // Handle passive damage modifiers (e.g. PowerProtection), provided they are not afflicted with protection penetrating powers. if (damage > 0 && (target->Inventory != NULL) && !(flags & DMG_NO_PROTECT)) { target->Inventory->ModifyDamage(damage, mod, damage, true); } if (damage > 0 && !(flags & DMG_NO_FACTOR)) { damage = FixedMul(damage, target->DamageFactor); if (damage > 0) { damage = DamageTypeDefinition::ApplyMobjDamageFactor(damage, mod, target->GetClass()->ActorInfo->DamageFactors); } } if (damage >= 0) { damage = target->TakeSpecialDamage(inflictor, source, damage, mod); } // '<0' is handled below. This only handles the case where damage gets reduced to 0. if (damage == 0 && olddam > 0) { { // Still allow FORCEPAIN if (forcedPain) { goto dopain; } else if (fakedPain) { goto fakepain; } return -1; } } } if (target->flags5 & MF5_NODAMAGE) { damage = 0; } } if (damage < 0) { // any negative value means that something in the above chain has cancelled out all damage and all damage effects, including pain. return -1; } // Push the target unless the source's weapon's kickback is 0. // (i.e. Gauntlets/Chainsaw) if (!plrDontThrust && inflictor && inflictor != target // [RH] Not if hurting own self && !(target->flags & MF_NOCLIP) && !(inflictor->flags2 & MF2_NODMGTHRUST) && !(flags & DMG_THRUSTLESS) && !(target->flags7 & MF7_DONTTHRUST) && (source == NULL || source->player == NULL || !(source->flags2 & MF2_NODMGTHRUST))) { int kickback; if (inflictor && inflictor->projectileKickback) kickback = inflictor->projectileKickback; else if (!source || !source->player || !source->player->ReadyWeapon) kickback = gameinfo.defKickback; else kickback = source->player->ReadyWeapon->Kickback; if (kickback) { AActor *origin = (source && (flags & DMG_INFLICTOR_IS_PUFF))? source : inflictor; // If the origin and target are in exactly the same spot, choose a random direction. // (Most likely cause is from telefragging somebody during spawning because they // haven't moved from their spawn spot at all.) if (origin->x == target->x && origin->y == target->y) { ang = pr_kickbackdir.GenRand32(); } else { ang = R_PointToAngle2 (origin->x, origin->y, target->x, target->y); } // Calculate this as float to avoid overflows so that the // clamping that had to be done here can be removed. double fltthrust; fltthrust = mod == NAME_MDK ? 10 : 32; if (target->Mass > 0) { fltthrust = clamp((damage * 0.125 * kickback) / target->Mass, 0., fltthrust); } thrust = FLOAT2FIXED(fltthrust); // Don't apply ultra-small damage thrust if (thrust < FRACUNIT/100) thrust = 0; // make fall forwards sometimes if ((damage < 40) && (damage > target->health) && (target->z - origin->z > 64*FRACUNIT) && (pr_damagemobj()&1) // [RH] But only if not too fast and not flying && thrust < 10*FRACUNIT && !(target->flags & MF_NOGRAVITY) && (inflictor == NULL || !(inflictor->flags5 & MF5_NOFORWARDFALL)) ) { ang += ANG180; thrust *= 4; } ang >>= ANGLETOFINESHIFT; if (source && source->player && (flags & DMG_INFLICTOR_IS_PUFF) && source->player->ReadyWeapon != NULL && (source->player->ReadyWeapon->WeaponFlags & WIF_STAFF2_KICKBACK)) { // Staff power level 2 target->velx += FixedMul (10*FRACUNIT, finecosine[ang]); target->vely += FixedMul (10*FRACUNIT, finesine[ang]); if (!(target->flags & MF_NOGRAVITY)) { target->velz += 5*FRACUNIT; } } else { target->velx += FixedMul (thrust, finecosine[ang]); target->vely += FixedMul (thrust, finesine[ang]); } } }
void G_InitNew (const char *mapname, bool bTitleLevel) { EGameSpeed oldSpeed; bool wantFast; int i; G_ClearHubInfo(); if (!savegamerestore) { G_ClearSnapshots (); P_RemoveDefereds (); // [RH] Mark all levels as not visited for (unsigned int i = 0; i < wadlevelinfos.Size(); i++) wadlevelinfos[i].flags = wadlevelinfos[i].flags & ~LEVEL_VISITED; } UnlatchCVars (); G_VerifySkill(); UnlatchCVars (); if (paused) { paused = 0; S_ResumeSound (false); } if (StatusBar != NULL) { StatusBar->Destroy(); StatusBar = NULL; } if (bTitleLevel) { StatusBar = new DBaseStatusBar (0); } else if (SBarInfoScript[SCRIPT_CUSTOM] != NULL) { int cstype = SBarInfoScript[SCRIPT_CUSTOM]->GetGameType(); //Did the user specify a "base" if(cstype == GAME_Strife) { StatusBar = CreateStrifeStatusBar(); } else if(cstype == GAME_Any) //Use the default, empty or custom. { StatusBar = CreateCustomStatusBar(SCRIPT_CUSTOM); } else { StatusBar = CreateCustomStatusBar(SCRIPT_DEFAULT); } } if (StatusBar == NULL) { if (gameinfo.gametype & (GAME_DoomChex|GAME_Heretic|GAME_Hexen)) { StatusBar = CreateCustomStatusBar (SCRIPT_DEFAULT); } else if (gameinfo.gametype == GAME_Strife) { StatusBar = CreateStrifeStatusBar (); } else { StatusBar = new DBaseStatusBar (0); } } GC::WriteBarrier(StatusBar); StatusBar->AttachToPlayer (&players[consoleplayer]); StatusBar->NewGame (); setsizeneeded = true; if (gameinfo.gametype == GAME_Strife || (SBarInfoScript[SCRIPT_CUSTOM] != NULL && SBarInfoScript[SCRIPT_CUSTOM]->GetGameType() == GAME_Strife)) { // Set the initial quest log text for Strife. for (i = 0; i < MAXPLAYERS; ++i) { players[i].SetLogText ("Find help"); } } // [RH] If this map doesn't exist, bomb out if (!P_CheckMapData(mapname)) { I_Error ("Could not find map %s\n", mapname); } oldSpeed = GameSpeed; wantFast = !!G_SkillProperty(SKILLP_FastMonsters); GameSpeed = wantFast ? SPEED_Fast : SPEED_Normal; if (!savegamerestore) { if (!netgame && !demorecording && !demoplayback) { // [RH] Change the random seed for each new single player game // [ED850] The demo already sets the RNG. rngseed = use_staticrng ? staticrngseed : (rngseed + 1); } FRandom::StaticClearRandom (); P_ClearACSVars(true); level.time = 0; level.maptime = 0; level.totaltime = 0; if (!multiplayer || !deathmatch) { InitPlayerClasses (); } // force players to be initialized upon first level load for (i = 0; i < MAXPLAYERS; i++) players[i].playerstate = PST_ENTER; // [BC] STAT_StartNewGame(mapname); } usergame = !bTitleLevel; // will be set false if a demo paused = 0; demoplayback = false; automapactive = false; viewactive = true; V_SetBorderNeedRefresh(); //Added by MC: Initialize bots. if (!deathmatch) { bglobal.Init (); } level.MapName = mapname; if (bTitleLevel) { gamestate = GS_TITLELEVEL; } else if (gamestate != GS_STARTUP) { gamestate = GS_LEVEL; } G_DoLoadLevel (0, false); }
// Returns the amount of damage actually inflicted upon the target, or -1 if // the damage was cancelled. int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, FName mod, int flags) { unsigned ang; player_t *player = NULL; fixed_t thrust; int temp; int painchance = 0; FState * woundstate = NULL; PainChanceList * pc = NULL; bool justhit = false; if (target == NULL || !((target->flags & MF_SHOOTABLE) || (target->flags6 & MF6_VULNERABLE))) { // Shouldn't happen return -1; } // Spectral targets only take damage from spectral projectiles. if (target->flags4 & MF4_SPECTRAL && damage < TELEFRAG_DAMAGE) { if (inflictor == NULL || !(inflictor->flags4 & MF4_SPECTRAL)) { return -1; } } if (target->health <= 0) { if (inflictor && mod == NAME_Ice) { return -1; } else if (target->flags & MF_ICECORPSE) // frozen { target->tics = 1; target->flags6 |= MF6_SHATTERING; target->velx = target->vely = target->velz = 0; } return -1; } if ((target->flags2 & MF2_INVULNERABLE) && damage < TELEFRAG_DAMAGE && !(flags & DMG_FORCED)) { // actor is invulnerable if (target->player == NULL) { if (inflictor == NULL || (!(inflictor->flags3 & MF3_FOILINVUL) && !(flags & DMG_FOILINVUL))) { return -1; } } else { // Players are optionally excluded from getting thrust by damage. if (static_cast<APlayerPawn *>(target)->PlayerFlags & PPF_NOTHRUSTWHENINVUL) { return -1; } } } if (inflictor != NULL) { if (inflictor->flags5 & MF5_PIERCEARMOR) flags |= DMG_NO_ARMOR; } MeansOfDeath = mod; FriendlyFire = false; // [RH] Andy Baker's Stealth monsters if (target->flags & MF_STEALTH) { target->alpha = OPAQUE; target->visdir = -1; } if (target->flags & MF_SKULLFLY) { target->velx = target->vely = target->velz = 0; } if (!(flags & DMG_FORCED)) // DMG_FORCED skips all special damage checks { if (target->flags2 & MF2_DORMANT) { // Invulnerable, and won't wake up return -1; } player = target->player; if (player && damage > 1 && damage < TELEFRAG_DAMAGE) { // Take half damage in trainer mode damage = FixedMul(damage, G_SkillProperty(SKILLP_DamageFactor)); } // Special damage types if (inflictor) { if (inflictor->flags4 & MF4_SPECTRAL) { if (player != NULL) { if (!deathmatch && inflictor->FriendPlayer > 0) return -1; } else if (target->flags4 & MF4_SPECTRAL) { if (inflictor->FriendPlayer == 0 && !target->IsHostile(inflictor)) return -1; } } damage = inflictor->DoSpecialDamage (target, damage, mod); if (damage == -1) { return -1; } } // Handle active damage modifiers (e.g. PowerDamage) if (source != NULL && source->Inventory != NULL) { int olddam = damage; source->Inventory->ModifyDamage(olddam, mod, damage, false); if (olddam != damage && damage <= 0) { // Still allow FORCEPAIN if (MustForcePain(target, inflictor)) { goto dopain; } return -1; } } // Handle passive damage modifiers (e.g. PowerProtection) if (target->Inventory != NULL) { int olddam = damage; target->Inventory->ModifyDamage(olddam, mod, damage, true); if (olddam != damage && damage <= 0) { // Still allow FORCEPAIN if (MustForcePain(target, inflictor)) { goto dopain; } return -1; } } if (!(flags & DMG_NO_FACTOR)) { damage = FixedMul(damage, target->DamageFactor); if (damage >= 0) { damage = DamageTypeDefinition::ApplyMobjDamageFactor(damage, mod, target->GetClass()->ActorInfo->DamageFactors); } if (damage <= 0) { // Still allow FORCEPAIN if (MustForcePain(target, inflictor)) { goto dopain; } return -1; } } damage = target->TakeSpecialDamage (inflictor, source, damage, mod); } if (damage == -1) { return -1; } // Push the target unless the source's weapon's kickback is 0. // (i.e. Gauntlets/Chainsaw) if (inflictor && inflictor != target // [RH] Not if hurting own self && !(target->flags & MF_NOCLIP) && !(inflictor->flags2 & MF2_NODMGTHRUST) && !(flags & DMG_THRUSTLESS) && (source == NULL || source->player == NULL || !(source->flags2 & MF2_NODMGTHRUST))) { int kickback; if (inflictor && inflictor->projectileKickback) kickback = inflictor->projectileKickback; else if (!source || !source->player || !source->player->ReadyWeapon) kickback = gameinfo.defKickback; else kickback = source->player->ReadyWeapon->Kickback; if (kickback) { AActor *origin = (source && (flags & DMG_INFLICTOR_IS_PUFF))? source : inflictor; // If the origin and target are in exactly the same spot, choose a random direction. // (Most likely cause is from telefragging somebody during spawning because they // haven't moved from their spawn spot at all.) if (origin->x == target->x && origin->y == target->y) { ang = pr_kickbackdir.GenRand32(); } else { ang = R_PointToAngle2 (origin->x, origin->y, target->x, target->y); } // Calculate this as float to avoid overflows so that the // clamping that had to be done here can be removed. double fltthrust; fltthrust = mod == NAME_MDK ? 10 : 32; if (target->Mass > 0) { fltthrust = clamp((damage * 0.125 * kickback) / target->Mass, 0., fltthrust); } thrust = FLOAT2FIXED(fltthrust); // Don't apply ultra-small damage thrust if (thrust < FRACUNIT/100) thrust = 0; // make fall forwards sometimes if ((damage < 40) && (damage > target->health) && (target->z - origin->z > 64*FRACUNIT) && (pr_damagemobj()&1) // [RH] But only if not too fast and not flying && thrust < 10*FRACUNIT && !(target->flags & MF_NOGRAVITY) && (inflictor == NULL || !(inflictor->flags5 & MF5_NOFORWARDFALL)) ) { ang += ANG180; thrust *= 4; } ang >>= ANGLETOFINESHIFT; if (source && source->player && (flags & DMG_INFLICTOR_IS_PUFF) && source->player->ReadyWeapon != NULL && (source->player->ReadyWeapon->WeaponFlags & WIF_STAFF2_KICKBACK)) { // Staff power level 2 target->velx += FixedMul (10*FRACUNIT, finecosine[ang]); target->vely += FixedMul (10*FRACUNIT, finesine[ang]); if (!(target->flags & MF_NOGRAVITY)) { target->velz += 5*FRACUNIT; } } else { target->velx += FixedMul (thrust, finecosine[ang]); target->vely += FixedMul (thrust, finesine[ang]); } } }