From 646681c78b2a4be0236f1132f19a1a02278c576a Mon Sep 17 00:00:00 2001 From: Major Cooke Date: Tue, 13 Oct 2020 14:49:37 -0500 Subject: [PATCH] Merged https://github.com/coelckers/gzdoom/pull/1217 (ThruActorLevel) (cherry picked from commit 6f2c4e09441fa7e3a701db95157dfb64e87c3477) --- src/playsim/actor.h | 4 + src/playsim/p_map.cpp | 128 +++++++++++++++++++++++--- src/playsim/p_mobj.cpp | 3 + src/scripting/thingdef_data.cpp | 1 + src/scripting/vmthunks_actors.cpp | 3 + wadsrc/static/zscript/actors/actor.zs | 10 ++ 6 files changed, 134 insertions(+), 15 deletions(-) diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 2e6ef49ae..8aae71b23 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -416,6 +416,7 @@ enum ActorFlag8 MF8_FALLDAMAGE = 0x00000800, // Monster will take fall damage regardless of map settings. MF8_VIEWPOSNOANGLES = 0x00001000, // [MC] View position takes no angles into account MF8_ABSVIEWPOS = 0x00002000, // [MC] Absolute position, so the actor must update it manually. + MF8_THRUACTORLEVEL = 0x00004000, // Actors with the same ThruActorLevel can pass through each other. }; // --- mobj.renderflags --- @@ -1176,6 +1177,9 @@ public: int RipperLevel; int RipLevelMin; int RipLevelMax; + int16_t ThruActorLevel; + int16_t ThruActorLevelMin; + int16_t ThruActorLevelMax; int ConversationRoot; // THe root of the current dialogue FStrifeDialogueNode* Conversation; // [RH] The dialogue to show when this actor is "used." diff --git a/src/playsim/p_map.cpp b/src/playsim/p_map.cpp index 89ce3f286..8ad024a7d 100644 --- a/src/playsim/p_map.cpp +++ b/src/playsim/p_map.cpp @@ -428,6 +428,8 @@ CCMD(ffcf) // //========================================================================== +static bool CheckThruActorLevel(AActor *first, AActor *second); + bool P_TeleportMove(AActor* thing, const DVector3 &pos, bool telefrag, bool modifyactor) { FCheckPosition tmf; @@ -479,11 +481,11 @@ bool P_TeleportMove(AActor* thing, const DVector3 &pos, bool telefrag, bool modi if (th == thing) continue; - double blockdist = th->radius + tmf.thing->radius; - if (fabs(th->X() - cres2.Position.X) >= blockdist || fabs(th->Y() - cres2.Position.Y) >= blockdist) + if (CheckThruActorLevel(th, tmf.thing)) continue; - if ((th->flags2 | tmf.thing->flags2) & MF2_THRUACTORS) + double blockdist = th->radius + tmf.thing->radius; + if (fabs(th->X() - cres2.Position.X) >= blockdist || fabs(th->Y() - cres2.Position.Y) >= blockdist) continue; if (tmf.thing->flags6 & MF6_THRUSPECIES && tmf.thing->GetSpecies() == th->GetSpecies()) @@ -1195,6 +1197,66 @@ static bool CheckRipLevel(AActor *victim, AActor *projectile) return true; } +//========================================================================== +// +// ThruActorLevel +// +// [MC] Actors with a TA level inside the min/max range of another actor, or +// have +THRUACTORLEVEL flag and have the same TA level as another can pass +// through each other. +// +// Based similarly off of ripper levels. +//========================================================================== + +struct ThruActLev +{ + bool TAFlag; // +THRUACTORLEVEL flag + int16_t TALevel, TAMin, TAMax; + + ThruActLev(AActor *mo) : + TAFlag(false), TALevel(0), TAMin(1), TAMax(32767) + { + if (mo) + { + TAFlag = !!(mo->flags8 & MF8_THRUACTORLEVEL); + TALevel = mo->ThruActorLevel; + TAMin = mo->ThruActorLevelMin; + TAMax = mo->ThruActorLevelMax; + } + } + + ThruActLev () = default; +}; + +// This version is for those that don't have any actors or just one actor to check. +// Needed for information collecting in LineAttack and RailAttack. +static bool CheckThruActorLev(ThruActLev *l1, ThruActLev *l2) +{ + if (!l1 || !l2) + return false; + + // Actor has the +THRUACTORLEVEL flag, can pass through the other if they share the same level. + if (l1->TAFlag || l2->TAFlag) + { + if (l1->TALevel == l2->TALevel) + return true; + } + + // If one actor meets the min/max of the other, they can pass through. + if (l1->TALevel >= l2->TAMin && l1->TALevel <= l2->TAMax) return true; + if (l2->TALevel >= l1->TAMin && l2->TALevel <= l1->TAMax) return true; + return false; +} + +static bool CheckThruActorLevel(AActor *first, AActor *second) +{ + if ((first->flags2 | second->flags2) & MF2_THRUACTORS) + return true; + + ThruActLev l1 = {first}, l2 = {second}; + return CheckThruActorLev(&l1, &l2); +} + //========================================================================== // @@ -1337,7 +1399,7 @@ bool PIT_CheckThing(FMultiBlockThingsIterator &it, FMultiBlockThingsIterator::Ch if (thing == tm.thing) return true; - if ((thing->flags2 | tm.thing->flags2) & MF2_THRUACTORS) + if (CheckThruActorLevel(thing, tm.thing)) return true; if (!((thing->flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE)) || thing->flags6 & MF6_TOUCHY)) @@ -2024,21 +2086,21 @@ int P_TestMobjZ(AActor *actor, bool quick, AActor **pOnmobj) { AActor *thing = cres.thing; - double blockdist = thing->radius + actor->radius; - if (fabs(thing->X() - cres.Position.X) >= blockdist || fabs(thing->Y() - cres.Position.Y) >= blockdist) + if (CheckThruActorLevel(actor, thing)) { continue; } - if ((actor->flags2 | thing->flags2) & MF2_THRUACTORS) - { + if (!(thing->flags & MF_SOLID)) + { // Can't hit thing continue; } if ((actor->flags6 & MF6_THRUSPECIES) && (thing->GetSpecies() == actor->GetSpecies())) { continue; } - if (!(thing->flags & MF_SOLID)) - { // Can't hit thing + double blockdist = thing->radius + actor->radius; + if (fabs(thing->X() - cres.Position.X) >= blockdist || fabs(thing->Y() - cres.Position.Y) >= blockdist) + { continue; } if (thing->flags & (MF_SPECIAL | MF_NOCLIP)) @@ -4421,6 +4483,8 @@ struct Origin bool MThruSpecies; bool ThruSpecies; bool ThruActors; + bool ThruActorLevelCheck; + ThruActLev ThruActorInfo; }; static ETraceStatus CheckForActor(FTraceResults &res, void *userdata) @@ -4446,6 +4510,13 @@ static ETraceStatus CheckForActor(FTraceResults &res, void *userdata) return TRACE_Skip; } + if (data->ThruActorLevelCheck) + { + ThruActLev l2 = {res.Actor}; + + if (CheckThruActorLev(&data->ThruActorInfo, &l2)) + return TRACE_Skip; + } return TRACE_Stop; } @@ -4516,7 +4587,7 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, // [MC] To prevent possible mod breakage, this flag is pretty much necessary. // Somewhere, someone is relying on these to spawn on actors and move through them. - + TData.ThruActorLevelCheck = false; if ((puffDefaults->flags7 & MF7_ALLOWTHRUFLAGS)) { TData.ThruSpecies = (puffDefaults && (puffDefaults->flags6 & MF6_THRUSPECIES)); @@ -4532,6 +4603,12 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, if (tempuff != NULL) { TData.PuffSpecies = tempuff->GetSpecies(); + + if (!TData.ThruActors) + { + TData.ThruActorInfo = { tempuff }; + TData.ThruActorLevelCheck = true; + } tempuff->Destroy(); } } @@ -5134,7 +5211,9 @@ struct RailData AActor *Caller; TArray RailHits; TArray PortalHits; + ThruActLev ThruActorInfo; FName PuffSpecies; + bool ThruActorLevelCheck; bool StopAtOne; bool StopAtInvul; bool ThruGhosts; @@ -5177,6 +5256,13 @@ static ETraceStatus ProcessRailHit(FTraceResults &res, void *userdata) return TRACE_Skip; } + if (data->ThruActorLevelCheck) + { + ThruActLev l2 = {res.Actor}; + + if (CheckThruActorLev(&data->ThruActorInfo, &l2)) + return TRACE_Skip; + } // Invulnerable things completely block the shot if (data->StopAtInvul && res.Actor->flags2 & MF2_INVULNERABLE) { @@ -5267,18 +5353,28 @@ void P_RailAttack(FRailParams *p) rail_data.ThruGhosts = !!(puffDefaults->flags2 & MF2_THRUGHOST); rail_data.ThruSpecies = !!(puffDefaults->flags6 & MF6_THRUSPECIES); rail_data.ThruActors = !!(puffDefaults->flags2 & MF2_THRUACTORS); + rail_data.ThruActorLevelCheck = true; } else { rail_data.ThruGhosts = false; rail_data.MThruSpecies = false; rail_data.ThruActors = false; + rail_data.ThruActorLevelCheck = false; } // used as damage inflictor - AActor *thepuff = NULL; - - if (puffclass != NULL) thepuff = Spawn(source->Level, puffclass, source->Pos(), ALLOW_REPLACE); - rail_data.PuffSpecies = (thepuff != NULL) ? thepuff->GetSpecies() : NAME_None; + AActor *thepuff = nullptr; + rail_data.PuffSpecies = NAME_None; + if (puffclass != nullptr) + { + thepuff = Spawn(source->Level, puffclass, source->Pos(), ALLOW_REPLACE); + if (thepuff) + { + rail_data.PuffSpecies = thepuff->GetSpecies(); + if (!rail_data.ThruActors && rail_data.ThruActorLevelCheck) + rail_data.ThruActorInfo = { thepuff }; + } + } Trace(start, source->Sector, vec, p->distance, MF_SHOOTABLE, ML_BLOCKEVERYTHING, source, trace, flags, ProcessRailHit, &rail_data); @@ -6373,6 +6469,8 @@ int P_PushUp(AActor *thing, FChangePosition *cpos) // Should there be MF2_THRUGHOST / MF3_GHOST checks there too for consistency? // Or would that risk breaking established behavior? THRUGHOST, like MTHRUSPECIES, // is normally for projectiles which would have exploded by now anyway... + if (CheckThruActorLevel(thing, intersect)) + continue; if (thing->flags6 & MF6_THRUSPECIES && thing->GetSpecies() == intersect->GetSpecies()) continue; if ((thing->flags & MF_MISSILE) && (intersect->flags2 & MF2_REFLECTIVE) && (intersect->flags7 & MF7_THRUREFLECT)) diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index dbf06b957..0339b86de 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -345,6 +345,9 @@ void AActor::Serialize(FSerializer &arc) A("ripperlevel", RipperLevel) A("riplevelmin", RipLevelMin) A("riplevelmax", RipLevelMax) + A("thruactorlevel", ThruActorLevel) + A("thruactorlevelmin", ThruActorLevelMin) + A("thruactorlevelmax", ThruActorLevelMax) A("devthreshold", DefThreshold) A("spriteangle", SpriteAngle) A("spriterotation", SpriteRotation) diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 26294f49e..3cee777a0 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -329,6 +329,7 @@ static FFlagDef ActorFlagDefs[]= DEFINE_FLAG(MF8, ABSVIEWANGLES, AActor, flags8), DEFINE_FLAG(MF8, VIEWPOSNOANGLES, AActor, flags8), DEFINE_FLAG(MF8, ABSVIEWPOS, AActor, flags8), + DEFINE_FLAG(MF8, THRUACTORLEVEL, AActor, flags8), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), diff --git a/src/scripting/vmthunks_actors.cpp b/src/scripting/vmthunks_actors.cpp index 110442335..e9e3ba713 100644 --- a/src/scripting/vmthunks_actors.cpp +++ b/src/scripting/vmthunks_actors.cpp @@ -1869,6 +1869,9 @@ DEFINE_FIELD(AActor, FloatBobStrength) DEFINE_FIELD(AActor, RipperLevel) DEFINE_FIELD(AActor, RipLevelMin) DEFINE_FIELD(AActor, RipLevelMax) +DEFINE_FIELD(AActor, ThruActorLevel) +DEFINE_FIELD(AActor, ThruActorLevelMin) +DEFINE_FIELD(AActor, ThruActorLevelMax) DEFINE_FIELD(AActor, Species) DEFINE_FIELD(AActor, alternative) DEFINE_FIELD(AActor, goal) diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index acc821a76..a248465f9 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -171,6 +171,7 @@ class Actor : Thinker native native int RipperLevel; native int RipLevelMin; native int RipLevelMax; + native int16 ThruActorLevel, ThruActorLevelMin, ThruActorLevelMax; native name Species; native Actor Alternative; native Actor goal; @@ -335,6 +336,9 @@ class Actor : Thinker native property Ripperlevel: RipperLevel; property RipLevelMin: RipLevelMin; property RipLevelMax: RipLevelMax; + property ThruActorLevel: ThruActorLevel; + property ThruActorLevelMin: ThruActorLevelMin; + property ThruActorLevelMax: ThruActorLevelMax; property RenderHidden: RenderHidden; property RenderRequired: RenderRequired; property FriendlySeeBlocks: FriendlySeeBlocks; @@ -397,6 +401,9 @@ class Actor : Thinker native RipperLevel 0; RipLevelMin 0; RipLevelMax 0; + ThruActorLevel 0; + ThruActorLevelMin 1; // int16, so [-32767, 32767] is the range. + ThruActorLevelMax 32767; // int16 too DefThreshold 100; BloodType "Blood", "BloodSplatter", "AxeBlood"; ExplosionDamage 128; @@ -863,6 +870,9 @@ class Actor : Thinker native void A_SetRipperLevel(int level) { RipperLevel = level; } void A_SetRipMin(int minimum) { RipLevelMin = minimum; } void A_SetRipMax(int maximum) { RipLevelMax = maximum; } + void A_SetThruActorLevel(int level) { ThruActorLevel = level; } + void A_SetThruActorLevelMin(int minimum) { ThruActorLevelMin = minimum; } + void A_SetThruActorLevelMax(int maximum) { ThruActorLevelMax = maximum; } void A_ScreamAndUnblock() { A_Scream(); A_NoBlocking(); } void A_ActiveAndUnblock() { A_ActiveSound(); A_NoBlocking(); }