diff --git a/docs/rh-log.txt b/docs/rh-log.txt index c819c38973..db03561a94 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,4 +1,5 @@ October 9, 2009 (Changes by Graf Zahl) +- Added Gez's thing activation submission. - added a NULL pointer check to fog spawning in unmorphing code. - fixed: frozen corpses need to be treated as solid by z-movement code. - fixed: AAmbientSound::Serialize was adjusting its timer value for savegames diff --git a/src/actor.h b/src/actor.h index 8260b8fbf9..f595e5206a 100644 --- a/src/actor.h +++ b/src/actor.h @@ -431,19 +431,23 @@ enum EBounceFlags }; -// Used to affect the logic for MF5_USESPECIAL and MF6_BUMPSPECIAL +// Used to affect the logic for thing activation through death, USESPECIAL and BUMPSPECIAL // "thing" refers to what has the flag and the special, "trigger" refers to what used or bumped it enum EThingSpecialActivationType { - THINGSPEC_Default = 0, // Normal behavior: a player must be the trigger, and is the activator - THINGSPEC_ThingActs = 1, // The thing itself is the activator of the special - THINGSPEC_ThingTargets = 2, // The thing changes its target to the trigger - THINGSPEC_TriggerTargets = 4, // The trigger changes its target to the thing - THINGSPEC_MonsterTrigger = 8, // The thing can be triggered by a monster - THINGSPEC_MissileTrigger = 16, // The thing can be triggered by a projectile - THINGSPEC_ClearSpecial = 32, // Clears special after successful activation - THINGSPEC_NoDeathSpecial = 64, // Don't activate special on death - THINGSPEC_TriggerActs = 128, // The trigger is the activator of the special (overrides LEVEL_ACTOWNSPECIAL Hexen hack) + THINGSPEC_Default = 0, // Normal behavior: a player must be the trigger, and is the activator + THINGSPEC_ThingActs = 1, // The thing itself is the activator of the special + THINGSPEC_ThingTargets = 1<<1, // The thing changes its target to the trigger + THINGSPEC_TriggerTargets = 1<<2, // The trigger changes its target to the thing + THINGSPEC_MonsterTrigger = 1<<3, // The thing can be triggered by a monster + THINGSPEC_MissileTrigger = 1<<4, // The thing can be triggered by a projectile + THINGSPEC_ClearSpecial = 1<<5, // Clears special after successful activation + THINGSPEC_NoDeathSpecial = 1<<6, // Don't activate special on death + THINGSPEC_TriggerActs = 1<<7, // The trigger is the activator of the special + // (overrides LEVEL_ACTOWNSPECIAL Hexen hack) + THINGSPEC_Activate = 1<<8, // The thing is activated when triggered + THINGSPEC_Deactivate = 1<<9, // The thing is deactivated when triggered + THINGSPEC_Switch = 1<<10, // The thing is alternatively activated and deactivated when triggered }; // [RH] Like msecnode_t, but for the blockmap @@ -807,6 +811,7 @@ public: fixed_t pushfactor; int lastpush; int activationtype; // How the thing behaves when activated with USESPECIAL or BUMPSPECIAL + int lastbump; // Last time the actor was bumped, used to control BUMPSPECIAL int Score; // manipulated by score items, ACS or DECORATE. The engine doesn't use this itself for anything. FNameNoInit Tag; // Strife's tag name. FIXME: should be case sensitive! diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 8c02465556..0602226f83 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -420,17 +420,11 @@ void AActor::Die (AActor *source, AActor *inflictor) // the activator of the script. // New: In Hexen, the thing that died is the activator, // so now a level flag selects who the activator gets to be. - if (special && (!(flags & MF_SPECIAL) || (flags3 & MF3_ISMONSTER)) && !(activationtype & THINGSPEC_NoDeathSpecial)) + // Everything is now moved to P_ActivateThingSpecial(). + if (special && (!(flags & MF_SPECIAL) || (flags3 & MF3_ISMONSTER)) + && !(activationtype & THINGSPEC_NoDeathSpecial)) { - // Activation flags override LEVEL_ACTOWNSPECIAL if set. - AActor *activator = (activationtype & THINGSPEC_TriggerActs ? source : - (activationtype & THINGSPEC_ThingActs ? this : (level.flags & LEVEL_ACTOWNSPECIAL ? this : source))); - if (activationtype & THINGSPEC_ThingTargets) - this->target = source; - if (activationtype & THINGSPEC_TriggerTargets) - source->target = this; - LineSpecials[special] (NULL, activator, false, args[0], args[1], args[2], args[3], args[4]); - special = 0; + P_ActivateThingSpecial(this, source, true); } if (CountsAsKill()) diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 17333f7d7b..f0f2faf1dd 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -1061,6 +1061,30 @@ FUNC(LS_HealThing) return it ? true : false; } +// So that things activated/deactivated by ACS or DECORATE *and* by +// the BUMPSPECIAL or USESPECIAL flags work correctly both ways. +void DoActivateThing(AActor * thing, AActor * activator) +{ + if (thing->activationtype & THINGSPEC_Activate) + { + thing->activationtype &= ~THINGSPEC_Activate; // Clear flag + if (thing->activationtype & THINGSPEC_Switch) // Set other flag if switching + thing->activationtype |= THINGSPEC_Deactivate; + } + thing->Activate (activator); +} + +void DoDeactivateThing(AActor * thing, AActor * activator) +{ + if (thing->activationtype & THINGSPEC_Deactivate) + { + thing->activationtype &= ~THINGSPEC_Deactivate; // Clear flag + if (thing->activationtype & THINGSPEC_Switch) // Set other flag if switching + thing->activationtype |= THINGSPEC_Activate; + } + thing->Deactivate (activator); +} + FUNC(LS_Thing_Activate) // Thing_Activate (tid) { @@ -1076,7 +1100,7 @@ FUNC(LS_Thing_Activate) // Actor might remove itself as part of activation, so get next // one before activating it. AActor *temp = iterator.Next (); - actor->Activate (it); + DoActivateThing(actor, it); actor = temp; count++; } @@ -1085,7 +1109,7 @@ FUNC(LS_Thing_Activate) } else if (it != NULL) { - it->Activate(it); + DoActivateThing(it, it); return true; } return false; @@ -1106,7 +1130,7 @@ FUNC(LS_Thing_Deactivate) // Actor might removes itself as part of deactivation, so get next // one before we activate it. AActor *temp = iterator.Next (); - actor->Deactivate (it); + DoDeactivateThing(actor, it); actor = temp; count++; } @@ -1115,7 +1139,7 @@ FUNC(LS_Thing_Deactivate) } else if (it != NULL) { - it->Deactivate(it); + DoDeactivateThing(it, it); return true; } return false; diff --git a/src/p_lnspec.h b/src/p_lnspec.h index 495947243d..2f5a67810c 100644 --- a/src/p_lnspec.h +++ b/src/p_lnspec.h @@ -196,5 +196,6 @@ typedef int (*lnSpecFunc)(struct line_t *line, extern lnSpecFunc LineSpecials[256]; int P_FindLineSpecial (const char *string, int *min_args=NULL, int *max_args=NULL); +bool P_ActivateThingSpecial(AActor * thing, AActor * trigger, bool death=false); #endif //__P_LNSPEC_H__ diff --git a/src/p_map.cpp b/src/p_map.cpp index fe7f5f790c..3f04ce0d13 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -837,24 +837,13 @@ bool PIT_CheckThing (AActor *thing, FCheckPosition &tm) // Check for MF6_BUMPSPECIAL // By default, only players can activate things by bumping into them - if ((thing->flags6 & MF6_BUMPSPECIAL)) + if ((thing->flags6 & MF6_BUMPSPECIAL) && ((tm.thing->player != NULL) + || ((thing->activationtype & THINGSPEC_MonsterTrigger) && (tm.thing->flags3 & MF3_ISMONSTER)) + || ((thing->activationtype & THINGSPEC_MissileTrigger) && (tm.thing->flags & MF_MISSILE)) + ) && (level.maptime > thing->lastbump)) // Leave the bumper enough time to go away { - if (((tm.thing->player != NULL) - || ((thing->activationtype & THINGSPEC_MonsterTrigger) && (thing->flags3 & MF3_ISMONSTER)) - || ((thing->activationtype & THINGSPEC_MissileTrigger) && (thing->flags & MF_MISSILE)) - )) - { // Target switching mechanism - if (thing->activationtype & THINGSPEC_ThingTargets) thing->target = tm.thing; - if (thing->activationtype & THINGSPEC_TriggerTargets) tm.thing->target = thing; - // Run the special - - int res = LineSpecials[thing->special] (NULL, - ((thing->activationtype & THINGSPEC_ThingActs) ? thing : tm.thing), // Who triggers? - false, thing->args[0], thing->args[1], thing->args[2], thing->args[3], thing->args[4]); - - if (thing->activationtype & THINGSPEC_ClearSpecial && res) thing->special = 0; - - } + if (P_ActivateThingSpecial(thing, tm.thing)) + thing->lastbump = level.maptime + TICRATE; } // Check for skulls slamming into things @@ -3775,18 +3764,9 @@ bool P_UseTraverse(AActor *usething, fixed_t endx, fixed_t endy, bool &foundline // Check for puzzle item use or USESPECIAL flag // Extended to use the same activationtype mechanism as BUMPSPECIAL does if (in->d.thing->flags5 & MF5_USESPECIAL || in->d.thing->special == UsePuzzleItem) - { // Target switching mechanism - if (in->d.thing->activationtype & THINGSPEC_ThingTargets) in->d.thing->target = usething; - if (in->d.thing->activationtype & THINGSPEC_TriggerTargets) usething->target = in->d.thing; - // Run the special - if (LineSpecials[in->d.thing->special] (NULL, // Who triggers? - ((in->d.thing->activationtype & THINGSPEC_ThingActs) ? in->d.thing : usething), false, - in->d.thing->args[0], in->d.thing->args[1], in->d.thing->args[2], - in->d.thing->args[3], in->d.thing->args[4])) - { - if (in->d.thing->activationtype & THINGSPEC_ClearSpecial) in->d.thing->special = 0; + { + if (P_ActivateThingSpecial(in->d.thing, usething)) return true; - } } // Dead things can't talk. if (in->d.thing->health <= 0) @@ -5180,3 +5160,67 @@ static void SpawnDeepSplash (AActor *t1, const FTraceResults &trace, AActor *puf } } } + +//============================================================================= +// +// P_ActivateThingSpecial +// +// Handles the code for things activated by death, USESPECIAL or BUMPSPECIAL +// +//============================================================================= + +bool P_ActivateThingSpecial(AActor * thing, AActor * trigger, bool death) +{ + bool res = false; + + // Target switching mechanism + if (thing->activationtype & THINGSPEC_ThingTargets) thing->target = trigger; + if (thing->activationtype & THINGSPEC_TriggerTargets) trigger->target = thing; + + // State change mechanism. The thing needs to be not dead and to have at least one of the relevant flags + if (!death && (thing->activationtype & (THINGSPEC_Activate|THINGSPEC_Deactivate|THINGSPEC_Switch))) + { + // If a switchable thing does not know whether it should be activated + // or deactivated, the default is to activate it. + if ((thing->activationtype & THINGSPEC_Switch) + && !(thing->activationtype & (THINGSPEC_Activate|THINGSPEC_Deactivate))) + { + thing->activationtype |= THINGSPEC_Activate; + } + // Can it be activated? + if (thing->activationtype & THINGSPEC_Activate) + { + thing->activationtype &= ~THINGSPEC_Activate; // Clear flag + if (thing->activationtype & THINGSPEC_Switch) // Set other flag if switching + thing->activationtype |= THINGSPEC_Deactivate; + thing->Activate(trigger); + res = true; + } + // If not, can it be deactivated? + else if (thing->activationtype & THINGSPEC_Deactivate) + { + thing->activationtype &= ~THINGSPEC_Deactivate; // Clear flag + if (thing->activationtype & THINGSPEC_Switch) // Set other flag if switching + thing->activationtype |= THINGSPEC_Activate; + thing->Deactivate(trigger); + res = true; + } + } + + // Run the special, if any + if (thing->special) + { + res = !! LineSpecials[thing->special] (NULL, + // TriggerActs overrides the level flag, which only concerns thing activated by death + (((death && level.flags & LEVEL_ACTOWNSPECIAL && !(thing->activationtype & THINGSPEC_TriggerActs)) + || (thing->activationtype & THINGSPEC_ThingActs)) // Who triggers? + ? thing : trigger), + false, thing->args[0], thing->args[1], thing->args[2], thing->args[3], thing->args[4]); + + // Clears the special if it was run on thing's death or if flag is set. + if (death || (thing->activationtype & THINGSPEC_ClearSpecial && res)) thing->special = 0; + } + + // Returns the result + return res; +} diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index a8975a5cd7..31972de470 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -300,6 +300,10 @@ void AActor::Serialize (FArchive &arc) << Species << Score << Tag; + if (SaveVersion >= 1904) + { + arc << lastpush << lastbump; + } if (SaveVersion >= 1900) { @@ -3670,7 +3674,7 @@ void AActor::Activate (AActor *activator) if (flags2 & MF2_DORMANT) { flags2 &= ~MF2_DORMANT; - FState *state = FindState("Active"); + FState *state = FindState(NAME_Active); if (state != NULL) { SetState(state); @@ -3690,7 +3694,7 @@ void AActor::Deactivate (AActor *activator) if (!(flags2 & MF2_DORMANT)) { flags2 |= MF2_DORMANT; - FState *state = FindState("Inactive"); + FState *state = FindState(NAME_Inactive); if (state != NULL) { SetState(state); diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index 6d83627aa4..7a84a3ed84 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -465,6 +465,9 @@ static int ParseThingActivation (FScanner &sc) { "THINGSPEC_ClearSpecial", THINGSPEC_ClearSpecial}, { "THINGSPEC_NoDeathSpecial", THINGSPEC_NoDeathSpecial}, { "THINGSPEC_TriggerActs", THINGSPEC_TriggerActs}, + { "THINGSPEC_Activate", THINGSPEC_Activate}, + { "THINGSPEC_Deactivate", THINGSPEC_Deactivate}, + { "THINGSPEC_Switch", THINGSPEC_Switch}, { NULL, 0 } }; diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index 66e7cdc6fa..faa9fba460 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -1072,7 +1072,7 @@ DEFINE_PROPERTY(projectile, 0, Actor) //========================================================================== DEFINE_PROPERTY(activation, N, Actor) { - // How the thing behaves when activated with MF5_USESPECIAL or MF6_BUMPSPECIAL + // How the thing behaves when activated by death, USESPECIAL or BUMPSPECIAL PROP_INT_PARM(val, 0); defaults->activationtype = val; }