diff --git a/src/actor.h b/src/actor.h index 6e0c625f1..9ae11696b 100644 --- a/src/actor.h +++ b/src/actor.h @@ -320,6 +320,8 @@ enum MF6_SHATTERING = 0x00020000, // marks an ice corpse for forced shattering MF6_KILLED = 0x00040000, // Something that was killed (but not necessarily a corpse) MF6_BLOCKEDBYSOLIDACTORS = 0x00080000, // Blocked by solid actors, even if not solid itself + MF6_ADDITIVEPOISONDAMAGE = 0x00100000, + MF6_ADDITIVEPOISONDURATION = 0x00200000, // --- mobj.renderflags --- @@ -505,7 +507,6 @@ enum AMETA_BloodColor, // colorized blood AMETA_GibHealth, // negative health below which this monster dies an extreme death AMETA_WoundHealth, // health needed to enter wound state - AMETA_PoisonDamage, // Amount of poison damage AMETA_FastSpeed, // Speed in fast mode AMETA_RDFactor, // Radius damage factor AMETA_CameraHeight, // Height of camera when used as such @@ -843,6 +844,15 @@ public: AActor *BlockingMobj; // Actor that blocked the last move line_t *BlockingLine; // Line that blocked the last move + int PoisonDamage; // Damage received per tic from poison. + int PoisonDuration; // Duration left for receiving poison damage. + int PoisonPeriod; // How often poison damage is applied. (Every X tics.) + + int PoisonDamageReceived; // Damage received per tic from poison. + int PoisonDurationReceived; // Duration left for receiving poison damage. + int PoisonPeriodReceived; // How often poison damage is applied. (Every X tics.) + TObjPtr Poisoner; // Last source of received poison damage. + // a linked list of sectors where this object appears struct msecnode_t *touching_sectorlist; // phares 3/14/98 diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 7dfd7398f..d4db86436 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1369,6 +1369,34 @@ dopain: target->flags |= MF_JUSTHIT; // fight back! } +void P_PoisonMobj (AActor *target, AActor *inflictor, AActor *source, int damage, int duration, int period) +{ + int olddamage = target->PoisonDamageReceived; + int oldduration = target->PoisonDurationReceived; + + target->Poisoner = source; + + if (inflictor->flags6 & MF6_ADDITIVEPOISONDAMAGE) + { + target->PoisonDamageReceived += damage; + } + else + { + target->PoisonDamageReceived = damage; + } + + if (inflictor->flags6 & MF6_ADDITIVEPOISONDURATION) + { + target->PoisonDurationReceived += duration; + } + else + { + target->PoisonDurationReceived = duration; + } + + target->PoisonPeriodReceived = period; +} + bool AActor::OkayToSwitchTarget (AActor *other) { if (other == this) diff --git a/src/p_local.h b/src/p_local.h index 387089f68..a69f2549d 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -480,6 +480,7 @@ extern FBlockNode** blocklinks; // for thing chains // void P_TouchSpecialThing (AActor *special, AActor *toucher); void P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, FName mod, int flags=0); +void P_PoisonMobj (AActor *target, AActor *inflictor, AActor *source, int damage, int duration, int period); bool P_GiveBody (AActor *actor, int num); bool P_PoisonPlayer (player_t *player, AActor *poisoner, AActor *source, int poison); void P_PoisonDamage (player_t *player, AActor *source, int damage, bool playPainSound); diff --git a/src/p_map.cpp b/src/p_map.cpp index 34f396317..3ee9b15f2 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -1088,6 +1088,13 @@ bool PIT_CheckThing (AActor *thing, FCheckPosition &tm) P_RipperBlood (tm.thing, thing); } S_Sound (tm.thing, CHAN_BODY, "misc/ripslop", 1, ATTN_IDLE); + + // Do poisoning (if using new style poison) + if (tm.thing->PoisonDuration != INT_MIN) + { + P_PoisonMobj(thing, tm.thing, tm.thing->target, tm.thing->PoisonDamage, tm.thing->PoisonDuration, tm.thing->PoisonPeriod); + } + damage = tm.thing->GetMissileDamage (3, 2); P_DamageMobj (thing, tm.thing, tm.thing->target, damage, tm.thing->DamageType); if (!(tm.thing->flags3 & MF3_BLOODLESSIMPACT)) @@ -1109,6 +1116,13 @@ bool PIT_CheckThing (AActor *thing, FCheckPosition &tm) return true; } } + + // Do poisoning (if using new style poison) + if (tm.thing->PoisonDuration != INT_MIN) + { + P_PoisonMobj(thing, tm.thing, tm.thing->target, tm.thing->PoisonDamage, tm.thing->PoisonDuration, tm.thing->PoisonPeriod); + } + // Do damage damage = tm.thing->GetMissileDamage ((tm.thing->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1); if ((damage > 0) || (tm.thing->flags6 & MF6_FORCEPAIN)) @@ -3471,6 +3485,13 @@ AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, trace.Actor, srcangle, srcpitch); } } + + // Allow puffs to inflict poison damage, so that hitscans can poison, too. + if (puffDefaults->PoisonDuration != INT_MIN) + { + P_PoisonMobj(trace.Actor, puff ? puff : t1, t1, puffDefaults->PoisonDamage, puffDefaults->PoisonDuration, puffDefaults->PoisonPeriod); + } + // [GZ] If MF6_FORCEPAIN is set, we need to call P_DamageMobj even if damage is 0! // Note: The puff may not yet be spawned here so we must check the class defaults, not the actor. if (damage || (puffDefaults->flags6 & MF6_FORCEPAIN)) @@ -3840,6 +3861,10 @@ void P_RailAttack (AActor *source, int damage, int offset, int color1, int color P_TraceBleed (damage, x, y, z, RailHits[i].HitActor, source->angle, pitch); } if (spawnpuff) P_SpawnPuff (source, puffclass, x, y, z, source->angle - ANG90, 1, PF_HITTHING); + + if (puffDefaults && puffDefaults->PoisonDuration != INT_MIN) + P_PoisonMobj(RailHits[i].HitActor, thepuff ? thepuff : source, source, puffDefaults->PoisonDamage, puffDefaults->PoisonDuration, puffDefaults->PoisonPeriod); + P_DamageMobj (RailHits[i].HitActor, thepuff? thepuff:source, source, damage, damagetype, DMG_INFLICTOR_IS_PUFF); } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 6fd05c040..9c1e195ee 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -346,6 +346,11 @@ void AActor::Serialize (FArchive &arc) WeaveIndexZ = 0; } } + if (SaveVersion >= 2450) + { + arc << PoisonDamageReceived << PoisonDurationReceived << PoisonPeriodReceived << Poisoner; + arc << PoisonDamage << PoisonDuration << PoisonPeriod; + } // Skip past uservar array in old savegames if (SaveVersion < 1933) @@ -3314,6 +3319,18 @@ void AActor::Tick () { return; } + + // Check for poison damage, but only once per PoisonPeriod tics (or once per second if none). + if (PoisonDurationReceived && (level.time % (PoisonPeriodReceived ? PoisonPeriodReceived : TICRATE) == 0)) + { + P_DamageMobj(this, NULL, Poisoner, PoisonDamageReceived, NAME_Poison, 0); + + --PoisonDurationReceived; + + // Must clear damage when duration is done, otherwise it + // could be added to with ADDITIVEPOISONDAMAGE. + if (!PoisonDurationReceived) PoisonDamageReceived = 0; + } } // cycle through states, calling action functions at transitions @@ -5444,10 +5461,10 @@ int AActor::DoSpecialDamage (AActor *target, int damage) { if (target->player) { - int poisondamage = GetClass()->Meta.GetMetaInt(AMETA_PoisonDamage); - if (poisondamage > 0) + // Only do this for old style poison damage. + if (PoisonDamage > 0 && PoisonDuration == INT_MIN) { - P_PoisonPlayer (target->player, this, this->target, poisondamage); + P_PoisonPlayer (target->player, this, this->target, PoisonDamage); damage >>= 1; } } diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index 7ebb680d8..b08e48719 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -224,6 +224,8 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF6, JUMPDOWN, AActor, flags6), DEFINE_FLAG(MF6, VULNERABLE, AActor, flags6), DEFINE_FLAG(MF6, NOTRIGGER, AActor, flags6), + DEFINE_FLAG(MF6, ADDITIVEPOISONDAMAGE, AActor, flags6), + DEFINE_FLAG(MF6, ADDITIVEPOISONDURATION, AActor, flags6), DEFINE_FLAG(MF6, BLOCKEDBYSOLIDACTORS, AActor, flags6), // Effect flags diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index 0452cb951..ffe287035 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -999,10 +999,26 @@ DEFINE_PROPERTY(maxdropoffheight, F, Actor) //========================================================================== // //========================================================================== -DEFINE_PROPERTY(poisondamage, I, Actor) +DEFINE_PROPERTY(poisondamage, Iii, Actor) { - PROP_INT_PARM(i, 0); - info->Class->Meta.SetMetaInt (AMETA_PoisonDamage, i); + PROP_INT_PARM(poisondamage, 0); + PROP_INT_PARM(poisonduration, 1); + PROP_INT_PARM(poisonperiod, 2); + + defaults->PoisonDamage = poisondamage; + if (PROP_PARM_COUNT == 1) + { + defaults->PoisonDuration = INT_MIN; + } + else + { + defaults->PoisonDuration = poisonduration; + + if (PROP_PARM_COUNT > 2) + defaults->PoisonPeriod = poisonperiod; + else + defaults->PoisonPeriod = 0; + } } //==========================================================================