diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 9f50c9ab5..261cfb8f3 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -948,7 +948,8 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, bool forcedPain = false; int fakeDamage = 0; int holdDamage = 0; - const int rawdamage = damage; + const int rawdamage = damage; + const bool telefragDamage = (rawdamage >= TELEFRAG_DAMAGE); if (damage < 0) damage = 0; @@ -962,7 +963,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, forcedPain = (MustForcePain(target, inflictor)); // Spectral targets only take damage from spectral projectiles. - if (target->flags4 & MF4_SPECTRAL && damage < TELEFRAG_DAMAGE) + if (target->flags4 & MF4_SPECTRAL && !telefragDamage) { if (inflictor == NULL || !(inflictor->flags4 & MF4_SPECTRAL)) { @@ -987,7 +988,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // 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))) + if ((target->flags2 & MF2_INVULNERABLE) && !telefragDamage && (!(flags & DMG_FORCED))) { // actor is invulnerable if (target->player == NULL) { @@ -1046,7 +1047,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, return -1; } - if ((rawdamage < TELEFRAG_DAMAGE) || (target->flags7 & MF7_LAXTELEFRAGDMG)) // TELEFRAG_DAMAGE may only be reduced with LAXTELEFRAGDMG or it may not guarantee its effect. + if (!telefragDamage || (target->flags7 & MF7_LAXTELEFRAGDMG)) // TELEFRAG_DAMAGE may only be reduced with LAXTELEFRAGDMG or it may not guarantee its effect. { if (player && damage > 1) { @@ -1214,19 +1215,19 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, target->IsTeammate (source)) { //Use the original damage to check for telefrag amount. Don't let the now-amplified damagetypes do it. - if (rawdamage < TELEFRAG_DAMAGE || (target->flags7 & MF7_LAXTELEFRAGDMG)) + if (!telefragDamage || (target->flags7 & MF7_LAXTELEFRAGDMG)) { // Still allow telefragging :-( damage = (int)(damage * level.teamdamage); if (damage < 0) { return damage; - } + } else if (damage == 0) { if (forcedPain) { goto dopain; - } + } else if (fakedPain) { goto fakepain; @@ -1256,7 +1257,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (!(flags & DMG_FORCED)) { // check the real player, not a voodoo doll here for invulnerability effects - if ((rawdamage < TELEFRAG_DAMAGE && ((player->mo->flags2 & MF2_INVULNERABLE) || + if ((!telefragDamage && ((player->mo->flags2 & MF2_INVULNERABLE) || (player->cheats & CF_GODMODE))) || (player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NODAMAGE)) //Absolutely no hurting if NODAMAGE is involved. Same for GODMODE2. @@ -1281,7 +1282,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, { player->mo->Inventory->AbsorbDamage(damage, mod, newdam); } - if ((rawdamage < TELEFRAG_DAMAGE) || (player->mo->flags7 & MF7_LAXTELEFRAGDMG)) //rawdamage is never modified. + if (!telefragDamage || (player->mo->flags7 & MF7_LAXTELEFRAGDMG)) //rawdamage is never modified. { // if we are telefragging don't let the damage value go below that magic value. Some further checks would fail otherwise. damage = newdam; @@ -1306,7 +1307,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } } - if (damage >= player->health && rawdamage < TELEFRAG_DAMAGE + if (damage >= player->health && !telefragDamage && (G_SkillProperty(SKILLP_AutoUseHealth) || deathmatch) && !player->morphTics) { // Try to use some inventory health @@ -1330,7 +1331,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // but telefragging should still do enough damage to kill the player) // Ignore players that are already dead. // [MC]Buddha2 absorbs telefrag damage, and anything else thrown their way. - if (!(flags & DMG_FORCED) && (((player->cheats & CF_BUDDHA2) || (((player->cheats & CF_BUDDHA) || (player->mo->flags7 & MF7_BUDDHA)) && (rawdamage < TELEFRAG_DAMAGE))) && (player->playerstate != PST_DEAD))) + if (!(flags & DMG_FORCED) && (((player->cheats & CF_BUDDHA2) || (((player->cheats & CF_BUDDHA) || (player->mo->flags7 & MF7_BUDDHA)) && !telefragDamage)) && (player->playerstate != PST_DEAD))) { // If this is a voodoo doll we need to handle the real player as well. player->mo->health = target->health = player->health = 1; @@ -1395,7 +1396,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (target->health <= 0) { //[MC]Buddha flag for monsters. - if (!(flags & DMG_FORCED) && ((target->flags7 & MF7_BUDDHA) && (rawdamage < TELEFRAG_DAMAGE) && ((inflictor == NULL || !(inflictor->flags7 & MF7_FOILBUDDHA)) && !(flags & DMG_FOILBUDDHA)))) + if (!(flags & DMG_FORCED) && ((target->flags7 & MF7_BUDDHA) && !telefragDamage && ((inflictor == NULL || !(inflictor->flags7 & MF7_FOILBUDDHA)) && !(flags & DMG_FOILBUDDHA)))) { //FOILBUDDHA or Telefrag damage must kill it. target->health = 1; } diff --git a/src/p_local.h b/src/p_local.h index ebbaeace5..d759828dc 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -392,7 +392,7 @@ enum RADF_NODAMAGE = 8, RADF_THRUSTZ = 16, }; -void P_RadiusAttack (AActor *spot, AActor *source, int damage, int distance, +int P_RadiusAttack (AActor *spot, AActor *source, int damage, int distance, FName damageType, int flags, int fulldamagedistance=0); void P_DelSector_List(); diff --git a/src/p_map.cpp b/src/p_map.cpp index 278249a64..7316e1c96 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -5245,11 +5245,11 @@ CUSTOM_CVAR(Float, splashfactor, 1.f, CVAR_SERVERINFO) // //========================================================================== -void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bombdistance, FName bombmod, +int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bombdistance, FName bombmod, int flags, int fulldamagedistance) { if (bombdistance <= 0) - return; + return 0; fulldamagedistance = clamp(fulldamagedistance, 0, bombdistance - 1); double bombdistancefloat = 1. / (double)(bombdistance - fulldamagedistance); @@ -5264,6 +5264,7 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo bombsource = bombspot; } + int count = 0; while ((it.Next(&cres))) { AActor *thing = cres.thing; @@ -5359,7 +5360,12 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo int newdam = damage; if (!(flags & RADF_NODAMAGE)) + { + //[MC] Don't count actors saved by buddha if already at 1 health. + int prehealth = thing->health; newdam = P_DamageMobj(thing, bombspot, bombsource, damage, bombmod); + if (thing->health < prehealth) count++; + } else if (thing->player == NULL && (!(flags & RADF_NOIMPACTDAMAGE) && !(thing->flags7 & MF7_DONTTHRUST))) thing->flags2 |= MF2_BLASTED; @@ -5425,12 +5431,16 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo damage = int(damage * factor); if (damage > 0) { + //[MC] Don't count actors saved by buddha if already at 1 health. + int prehealth = thing->health; int newdam = P_DamageMobj(thing, bombspot, bombsource, damage, bombmod); P_TraceBleed(newdam > 0 ? newdam : damage, thing, bombspot); + if (thing->health < prehealth) count++; } } } } + return count; } //========================================================================== diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 6904a05d4..b0cfbeba4 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1414,13 +1414,13 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Explode) } } - P_RadiusAttack (self, self->target, damage, distance, self->DamageType, flags, fulldmgdistance); + int count = P_RadiusAttack (self, self->target, damage, distance, self->DamageType, flags, fulldmgdistance); P_CheckSplash(self, distance); if (alert && self->target != NULL && self->target->player != NULL) { P_NoiseAlert(self->target, self); } - return 0; + ACTION_RETURN_INT(count); } //========================================================================== @@ -5888,24 +5888,63 @@ enum RadiusGiveFlags RGF_OBJECTS | RGF_VOODOO | RGF_CORPSES | + RGF_KILLED | RGF_MISSILES | RGF_ITEMS, }; static bool DoRadiusGive(AActor *self, AActor *thing, PClassActor *item, int amount, double distance, int flags, PClassActor *filter, FName species, double mindist) { - // [MC] We only want to make an exception for missiles here. Nothing else. - bool missilePass = !!((flags & RGF_MISSILES) && thing->flags & MF_MISSILE); + + bool doPass = false; + // Always allow self to give, no matter what other flags are specified. Otherwise, not at all. if (thing == self) { if (!(flags & RGF_GIVESELF)) return false; + doPass = true; } else if (thing->flags & MF_MISSILE) { - if (!missilePass) + if (!(flags & RGF_MISSILES)) return false; + doPass = true; } + else if (((flags & RGF_ITEMS) && thing->IsKindOf(RUNTIME_CLASS(AInventory))) || + ((flags & RGF_CORPSES) && thing->flags & MF_CORPSE) || + ((flags & RGF_KILLED) && thing->flags6 & MF6_KILLED)) + { + doPass = true; + } + else if ((flags & (RGF_MONSTERS | RGF_OBJECTS | RGF_PLAYERS | RGF_VOODOO))) + { + // Make sure it's alive as we're not looking for corpses or killed here. + if (!doPass && thing->health > 0) + { + if (thing->player != nullptr) + { + if (((flags & RGF_PLAYERS) && (thing->player->mo == thing)) || + ((flags & RGF_VOODOO) && (thing->player->mo != thing))) + { + doPass = true; + } + } + else + { + if (((flags & RGF_MONSTERS) && (thing->flags3 & MF3_ISMONSTER)) || + ((flags & RGF_OBJECTS) && (!(thing->flags3 & MF3_ISMONSTER)) && + (thing->flags & MF_SHOOTABLE || thing->flags6 & MF6_VULNERABLE))) + { + doPass = true; + } + } + } + } + + // Nothing matched up so don't bother with the rest. + if (!doPass) + return false; + //[MC] Check for a filter, species, and the related exfilter/expecies/either flag(s). bool filterpass = DoCheckClass(thing, filter, !!(flags & RGF_EXFILTER)), speciespass = DoCheckSpecies(thing, species, !!(flags & RGF_EXSPECIES)); @@ -5916,13 +5955,14 @@ static bool DoRadiusGive(AActor *self, AActor *thing, PClassActor *item, int amo return false; } - //Check for target, master, and tracer flagging. - bool targetPass = true; - bool masterPass = true; - bool tracerPass = true; - bool ptrPass = false; if ((thing != self) && (flags & (RGF_NOTARGET | RGF_NOMASTER | RGF_NOTRACER))) { + //Check for target, master, and tracer flagging. + bool targetPass = true; + bool masterPass = true; + bool tracerPass = true; + bool ptrPass = false; + if ((thing == self->target) && (flags & RGF_NOTARGET)) targetPass = false; if ((thing == self->master) && (flags & RGF_NOMASTER)) @@ -5937,35 +5977,8 @@ static bool DoRadiusGive(AActor *self, AActor *thing, PClassActor *item, int amo return false; } - //Next, actor flag checking. - bool selfPass = !!((flags & RGF_GIVESELF) && thing == self); - bool corpsePass = !!((flags & RGF_CORPSES) && thing->flags & MF_CORPSE); - bool killedPass = !!((flags & RGF_KILLED) && thing->flags6 & MF6_KILLED); - bool monsterPass = !!((flags & RGF_MONSTERS) && thing->flags3 & MF3_ISMONSTER); - bool objectPass = !!((flags & RGF_OBJECTS) && (thing->player == NULL) && (!(thing->flags3 & MF3_ISMONSTER)) - && ((thing->flags & MF_SHOOTABLE) || (thing->flags6 & MF6_VULNERABLE))); - bool playerPass = !!((flags & RGF_PLAYERS) && (thing->player != NULL) && (thing->player->mo == thing)); - bool voodooPass = !!((flags & RGF_VOODOO) && (thing->player != NULL) && (thing->player->mo != thing)); - //Self calls priority over the rest of this. - if (!selfPass) + if (doPass) { - //If it's specifically a monster/object/player/voodoo... Can be either or... - if (monsterPass || objectPass || playerPass || voodooPass) - { - //...and is dead, without desire to give to the dead... - if (((thing->health <= 0) && !(corpsePass || killedPass))) - { - //Skip! - return false; - } - } - } - - bool itemPass = !!((flags & RGF_ITEMS) && thing->IsKindOf(RUNTIME_CLASS(AInventory))); - - if (selfPass || monsterPass || corpsePass || killedPass || itemPass || objectPass || missilePass || playerPass || voodooPass) - { - DVector3 diff = self->Vec3To(thing); diff.Z += thing->Height *0.5; if (flags & RGF_CUBE) diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 4af9e33c9..00c4b1e75 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -3548,6 +3548,16 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx) delete this; return NULL; } + if (index->isConstant()) + { + unsigned indexval = static_cast(index)->GetValue().GetInt(); + if (indexval >= arraytype->ElementCount) + { + ScriptPosition.Message(MSG_ERROR, "Array index out of bounds"); + delete this; + return NULL; + } + } ValueType = arraytype->ElementType; if (ValueType->GetRegType() != REGT_INT && ValueType->GetRegType() != REGT_FLOAT) @@ -3583,10 +3593,7 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) if (index->isConstant()) { unsigned indexval = static_cast(index)->GetValue().GetInt(); - if (indexval >= arraytype->ElementCount) - { - I_Error("Array index out of bounds"); - } + assert(indexval < arraytype->ElementCount && "Array index out of bounds"); indexval *= arraytype->ElementSize; if (AddressRequested) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index fa8932050..c80834fdc 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -253,7 +253,7 @@ ACTOR Actor native //: Thinker action native A_Blast(int flags = 0, float strength = 255, float radius = 255, float speed = 20, class blasteffect = "BlastEffect", sound blastsound = "BlastRadius"); action native A_RadiusThrust(int force = 128, int distance = -1, int flags = RTF_AFFECTSOURCE, int fullthrustdistance = 0); action native A_RadiusDamageSelf(int damage = 128, float distance = 128, int flags = 0, class flashtype = "None"); - action native A_Explode(int damage = -1, int distance = -1, int flags = XF_HURTSOURCE, bool alert = false, int fulldamagedistance = 0, int nails = 0, int naildamage = 10, class pufftype = "BulletPuff"); + action native int A_Explode(int damage = -1, int distance = -1, int flags = XF_HURTSOURCE, bool alert = false, int fulldamagedistance = 0, int nails = 0, int naildamage = 10, class pufftype = "BulletPuff"); action native A_Stop(); action native A_Respawn(int flags = 1); action native A_BarrelDestroy();