From afea6ff51fc57c2fecdd041196e11300d35abd7f Mon Sep 17 00:00:00 2001 From: Major Cooke Date: Sat, 21 Jan 2023 11:25:42 -0600 Subject: [PATCH] Merged https://github.com/ZDoom/gzdoom/pull/1949 (Shadow Aiming) --- src/playsim/actor.h | 23 ++- src/playsim/p_actionfunctions.cpp | 14 +- src/playsim/p_enemy.cpp | 18 +- src/playsim/p_map.cpp | 6 + src/playsim/p_mobj.cpp | 33 ++-- src/playsim/shadowinlines.h | 233 ++++++++++++++++++++++++++ src/scripting/thingdef_data.cpp | 8 +- src/scripting/thingdef_properties.cpp | 1 + src/scripting/vmthunks_actors.cpp | 2 + wadsrc/static/zscript/actors/actor.zs | 5 + 10 files changed, 295 insertions(+), 48 deletions(-) create mode 100644 src/playsim/shadowinlines.h diff --git a/src/playsim/actor.h b/src/playsim/actor.h index b1c848e40..d95dd0dcf 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -206,8 +206,7 @@ enum ActorFlag2 // but still considered solid MF2_INVULNERABLE = 0x08000000, // mobj is invulnerable MF2_DORMANT = 0x10000000, // thing is dormant - MF2_ARGSDEFINED = 0x20000000, // Internal flag used by DECORATE to signal that the - // args should not be taken from the mapthing definition + MF2_ARGSDEFINED = 0x20000000, // Internal flag used by DECORATE to signal that the args should not be taken from the mapthing definition MF2_SEEKERMISSILE = 0x40000000, // is a seeker (for reflection) MF2_REFLECTIVE = 0x80000000, // reflects missiles }; @@ -394,11 +393,13 @@ enum ActorFlag7 MF7_SPRITEANGLE = 0x02000000, // [MC] Utilize the SpriteAngle property and lock the rotation to the degrees specified. MF7_SMASHABLE = 0x04000000, // dies if hitting the floor. MF7_NOSHIELDREFLECT = 0x08000000, // will not be reflected by shields. - MF7_FORCEZERORADIUSDMG = 0x10000000, // passes zero radius damage on to P_DamageMobj, this is necessary in some cases where DoSpecialDamage gets overrideen. + MF7_FORCEZERORADIUSDMG = 0x10000000,// passes zero radius damage on to P_DamageMobj, this is necessary in some cases where DoSpecialDamage gets overrideen. MF7_NOINFIGHTSPECIES = 0x20000000, // don't start infights with one's own species. MF7_FORCEINFIGHTING = 0x40000000, // overrides a map setting of 'no infighting'. MF7_INCHASE = 0x80000000, // [RH] used by A_Chase and A_Look to avoid recursion }; + +// --- mobj.flags8 --- enum ActorFlag8 { MF8_FRIGHTENING = 0x00000001, // for those moments when halloween just won't do @@ -426,12 +427,21 @@ enum ActorFlag8 MF8_STAYONLIFT = 0x02000000, // MBF AI enhancement. MF8_DONTFOLLOWPLAYERS = 0x04000000, // [inkoalawetrust] Friendly monster will not follow players. MF8_SEEFRIENDLYMONSTERS = 0X08000000, // [inkoalawetrust] Hostile monster can see friendly monsters. - MF8_CROSSLINECHECK = 0x10000000, // [MC]Enables CanCrossLine virtual + MF8_CROSSLINECHECK = 0x10000000, // [MC] Enables CanCrossLine virtual MF8_MASTERNOSEE = 0x20000000, // Don't show object in first person if their master is the current camera. MF8_ADDLIGHTLEVEL = 0x40000000, // [MC] Actor light level is additive with sector. MF8_ONLYSLAMSOLID = 0x80000000, // [B] Things with skullfly will ignore non-solid Actors. }; +// --- mobj.flags9 --- +enum ActorFlag9 +{ + MF9_SHADOWAIM = 0x00000001, // [inkoalawetrust] Monster still gets aim penalty from aiming at shadow actors even with MF6_SEEINVISIBLE on. + MF9_DOSHADOWBLOCK = 0x00000002, // [inkoalawetrust] Should the monster look for SHADOWBLOCK actors ? + MF9_SHADOWBLOCK = 0x00000004, // [inkoalawetrust] Actors in the line of fire with this flag trigger the MF_SHADOW aiming penalty. + MF9_SHADOWAIMVERT = 0x00000008, // [inkoalawetrust] Monster aim is also offset vertically when aiming at shadow actors. +}; + // --- mobj.renderflags --- enum ActorRenderFlag { @@ -586,6 +596,7 @@ typedef TFlags ActorFlags5; typedef TFlags ActorFlags6; typedef TFlags ActorFlags7; typedef TFlags ActorFlags8; +typedef TFlags ActorFlags9; typedef TFlags ActorRenderFlags; typedef TFlags ActorRenderFlags2; typedef TFlags ActorBounceFlags; @@ -598,6 +609,7 @@ DEFINE_TFLAGS_OPERATORS (ActorFlags5) DEFINE_TFLAGS_OPERATORS (ActorFlags6) DEFINE_TFLAGS_OPERATORS (ActorFlags7) DEFINE_TFLAGS_OPERATORS (ActorFlags8) +DEFINE_TFLAGS_OPERATORS (ActorFlags9) DEFINE_TFLAGS_OPERATORS (ActorRenderFlags) DEFINE_TFLAGS_OPERATORS (ActorRenderFlags2) DEFINE_TFLAGS_OPERATORS (ActorBounceFlags) @@ -1072,6 +1084,7 @@ public: ActorFlags6 flags6; // Shit! Where did all the flags go? ActorFlags7 flags7; // WHO WANTS TO BET ON 8!? ActorFlags8 flags8; // I see your 8, and raise you a bet for 9. + ActorFlags9 flags9; // Happy ninth actor flag field GZDoom ! double Floorclip; // value to use for floor clipping double radius, Height; // for movement checking @@ -1188,6 +1201,8 @@ public: double Gravity; // [GRB] Gravity factor double Friction; double pushfactor; + double ShadowAimFactor; // [inkoalawetrust] How much the actors' aim is affected when attacking shadow actors. + double ShadowPenaltyFactor;// [inkoalawetrust] How much the shadow actor affects its' shooters' aim. int bouncecount; // Strife's grenades only bounce twice before exploding int FastChaseStrafeCount; int lastpush; diff --git a/src/playsim/p_actionfunctions.cpp b/src/playsim/p_actionfunctions.cpp index 157ef9336..690cbfcc1 100644 --- a/src/playsim/p_actionfunctions.cpp +++ b/src/playsim/p_actionfunctions.cpp @@ -70,20 +70,21 @@ #include "actorinlines.h" #include "types.h" #include "model.h" +#include "shadowinlines.h" static FRandom pr_camissile ("CustomActorfire"); static FRandom pr_cabullet ("CustomBullet"); static FRandom pr_cwjump ("CustomWpJump"); static FRandom pr_cwpunch ("CustomWpPunch"); static FRandom pr_grenade ("ThrowGrenade"); -static FRandom pr_crailgun ("CustomRailgun"); + FRandom pr_crailgun ("CustomRailgun"); static FRandom pr_spawndebris ("SpawnDebris"); static FRandom pr_spawnitemex ("SpawnItemEx"); static FRandom pr_burst ("Burst"); static FRandom pr_monsterrefire ("MonsterRefire"); static FRandom pr_teleport("A_Teleport"); static FRandom pr_bfgselfdamage("BFGSelfDamage"); -FRandom pr_cajump("CustomJump"); + FRandom pr_cajump("CustomJump"); //========================================================================== // @@ -1226,11 +1227,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CustomRailgun) self->Angles.Yaw = self->AngleTo(self->target,- self->target->Vel.X * veleffect, -self->target->Vel.Y * veleffect); } - if (self->target->flags & MF_SHADOW) - { - DAngle rnd = DAngle::fromDeg(pr_crailgun.Random2() * (45. / 256.)); - self->Angles.Yaw += rnd; - } + A_CustomRailgun_ShadowHandling(self, spawnofs_xy, spawnofs_z, spread_xy, flags); } if (!(flags & CRF_EXPLICITANGLE)) @@ -2092,6 +2089,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Respawn) self->flags6 = defs->flags6; self->flags7 = defs->flags7; self->flags8 = defs->flags8; + self->flags9 = defs->flags9; self->SetState (self->SpawnState); self->renderflags &= ~RF_INVISIBLE; @@ -3526,7 +3524,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_WolfAttack) hitchance -= idist * (dodge ? 16 : 8); // While we're here, we may as well do something for this: - if (self->target->flags & MF_SHADOW) + if (A_WolfAttack_ShadowHandling(self)) { hitchance >>= 2; } diff --git a/src/playsim/p_enemy.cpp b/src/playsim/p_enemy.cpp index 7eea4cefb..22a86ee22 100644 --- a/src/playsim/p_enemy.cpp +++ b/src/playsim/p_enemy.cpp @@ -50,6 +50,7 @@ #include "vm.h" #include "actorinlines.h" #include "a_ceiling.h" +#include "shadowinlines.h" #include "gi.h" @@ -61,8 +62,8 @@ static FRandom pr_lookformonsters ("LookForMonsters"); static FRandom pr_lookforplayers ("LookForPlayers"); static FRandom pr_scaredycat ("Anubis"); FRandom pr_chase ("Chase"); -static FRandom pr_facetarget ("FaceTarget"); -static FRandom pr_railface ("RailFace"); + FRandom pr_facetarget ("FaceTarget"); + FRandom pr_railface ("RailFace"); static FRandom pr_look2 ("LookyLooky"); static FRandom pr_look3 ("IGotHooky"); static FRandom pr_slook ("SlooK"); @@ -3018,15 +3019,11 @@ void A_Face(AActor *self, AActor *other, DAngle max_turn, DAngle max_pitch, DAng self->Angles.Pitch = other_pitch; } self->Angles.Pitch += pitch_offset; + A_Face_ShadowHandling(self, other, max_pitch, other_pitch, true); } - - // This will never work well if the turn angle is limited. - if (max_turn == nullAngle && (self->Angles.Yaw == other_angle) && other->flags & MF_SHADOW && !(self->flags6 & MF6_SEEINVISIBLE) ) - { - self->Angles.Yaw += DAngle::fromDeg(pr_facetarget.Random2() * (45 / 256.)); - } + A_Face_ShadowHandling(self,other,max_turn,other_angle,false); } void A_FaceTarget(AActor *self) @@ -3073,10 +3070,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_MonsterRail) // Let the aim trail behind the player self->Angles.Yaw = self->AngleTo(self->target, -self->target->Vel.X * 3, -self->target->Vel.Y * 3); - if (self->target->flags & MF_SHADOW && !(self->flags6 & MF6_SEEINVISIBLE)) - { - self->Angles.Yaw += DAngle::fromDeg(pr_railface.Random2() * 45./256); - } + A_MonsterRail_ShadowHandling(self); FRailParams p; diff --git a/src/playsim/p_map.cpp b/src/playsim/p_map.cpp index cf709d5e2..4f2d8f406 100644 --- a/src/playsim/p_map.cpp +++ b/src/playsim/p_map.cpp @@ -92,6 +92,7 @@ #include "r_sky.h" #include "g_levellocals.h" #include "actorinlines.h" +#include CVAR(Bool, cl_bloodsplats, true, CVAR_ARCHIVE) CVAR(Int, sv_smartaim, 0, CVAR_ARCHIVE | CVAR_SERVERINFO) @@ -4452,6 +4453,11 @@ DAngle P_AimLineAttack(AActor *t1, DAngle angle, double distance, FTranslatedLin { *pLineTarget = *result; } + + DAngle newPitch = P_AimLineAttack_ShadowHandling(t1,target,result->linetarget,shootz); + if (newPitch != nullAngle) + result->pitch = newPitch; + return result->linetarget ? result->pitch : t1->Angles.Pitch; } diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index 1c0052abe..e5955d116 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -99,6 +99,7 @@ #include "actorinlines.h" #include "a_dynlight.h" #include "fragglescript/t_fs.h" +#include "shadowinlines.h" // MACROS ------------------------------------------------------------------ @@ -120,7 +121,6 @@ EXTERN_CVAR (Int, cl_rockettrails) // PRIVATE DATA DEFINITIONS ------------------------------------------------ static FRandom pr_explodemissile ("ExplodeMissile"); -FRandom pr_bounce ("Bounce"); static FRandom pr_reflect ("Reflect"); static FRandom pr_nightmarerespawn ("NightmareRespawn"); static FRandom pr_botspawnmobj ("BotSpawnActor"); @@ -133,7 +133,6 @@ static FRandom pr_splat ("FAxeSplatter"); static FRandom pr_ripperblood ("RipperBlood"); static FRandom pr_chunk ("Chunk"); static FRandom pr_checkmissilespawn ("CheckMissileSpawn"); -static FRandom pr_spawnmissile ("SpawnMissile"); static FRandom pr_missiledamage ("MissileDamage"); static FRandom pr_multiclasschoice ("MultiClassChoice"); static FRandom pr_rockettrail("RocketTrail"); @@ -142,6 +141,8 @@ static FRandom pr_uniquetid("UniqueTID"); // PUBLIC DATA DEFINITIONS ------------------------------------------------- FRandom pr_spawnmobj ("SpawnActor"); +FRandom pr_bounce("Bounce"); +FRandom pr_spawnmissile("SpawnMissile"); CUSTOM_CVAR (Float, sv_gravity, 800.f, CVAR_SERVERINFO|CVAR_NOSAVE|CVAR_NOINITCALL) { @@ -233,6 +234,7 @@ void AActor::Serialize(FSerializer &arc) A("flags6", flags6) A("flags7", flags7) A("flags8", flags8) + A("flags9", flags9) A("weaponspecial", weaponspecial) A("special1", special1) A("special2", special2) @@ -6646,7 +6648,7 @@ AActor *P_SpawnMissileXYZ (DVector3 pos, AActor *source, AActor *dest, PClassAct if (dest == NULL) { - Printf ("P_SpawnMissilyXYZ: Tried to shoot %s from %s with no dest\n", + Printf ("P_SpawnMissileXYZ: Tried to shoot %s from %s with no destination\n", type->TypeName.GetChars(), source->GetClass()->TypeName.GetChars()); return NULL; } @@ -6685,21 +6687,8 @@ AActor *P_SpawnMissileXYZ (DVector3 pos, AActor *source, AActor *dest, PClassAct } th->Vel = velocity.Resized(speed); - // invisible target: rotate velocity vector in 2D - // [RC] Now monsters can aim at invisible player as if they were fully visible. - if (dest->flags & MF_SHADOW && !(source->flags6 & MF6_SEEINVISIBLE)) - { - DAngle an = DAngle::fromDeg(pr_spawnmissile.Random2() * (22.5 / 256)); - double c = an.Cos(); - double s = an.Sin(); - - double newx = th->Vel.X * c - th->Vel.Y * s; - double newy = th->Vel.X * s + th->Vel.Y * c; - - th->Vel.X = newx; - th->Vel.Y = newy; - } - + P_SpawnMissileXYZ_ShadowHandling(source,dest,th,pos); + th->AngleFromVel(); if (th->flags4 & MF4_SPECTRAL) @@ -6820,14 +6809,11 @@ AActor *P_SpawnMissileZAimed (AActor *source, double z, AActor *dest, PClassActo an = source->Angles.Yaw; - if (dest->flags & MF_SHADOW) - { - an += DAngle::fromDeg(pr_spawnmissile.Random2() * (16. / 360.)); - } dist = source->Distance2D (dest); speed = GetDefaultSpeed (type); dist /= speed; vz = dist != 0 ? (dest->Z() - source->Z())/dist : speed; + an += P_SpawnMissileZAimed_ShadowHandling(source, dest, vz, speed, source->PosAtZ(z)); return P_SpawnMissileAngleZSpeed (source, z, type, an, vz, speed); } @@ -7692,6 +7678,9 @@ void PrintMiscActorInfo(AActor *query) Printf("\n flags8: %x", query->flags8.GetValue()); for (flagi = 0; flagi <= 31; flagi++) if (query->flags8 & ActorFlags8::FromInt(1<flags9.GetValue()); + for (flagi = 0; flagi <= 31; flagi++) + if (query->flags9 & ActorFlags9::FromInt(1 << flagi)) Printf(" %s", FLAG_NAME(1 << flagi, flags9)); Printf("\nBounce flags: %x\nBounce factors: f:%f, w:%f", query->BounceFlags.GetValue(), query->bouncefactor, query->wallbouncefactor); diff --git a/src/playsim/shadowinlines.h b/src/playsim/shadowinlines.h new file mode 100644 index 000000000..a0a0083b0 --- /dev/null +++ b/src/playsim/shadowinlines.h @@ -0,0 +1,233 @@ +#pragma once + +#include "actor.h" +#include "r_defs.h" +#include "m_random.h" + +//----------------------------------------------------------------------------- +// +// DESCRIPTION: +// Handling of MF_SHADOW related code for attack and aiming functions. +// +//----------------------------------------------------------------------------- + + +// RNG VARIABLES ------------------------------------------------ +extern FRandom pr_spawnmissile; +extern FRandom pr_facetarget; +extern FRandom pr_railface; +extern FRandom pr_crailgun; +static FRandom pr_shadowaimz("VerticalShadowAim"); + +//========================================================================== +// +// Generic checks +// +//========================================================================== + +struct ShadowCheckData +{ + AActor* HitShadow; +}; + +static ETraceStatus CheckForShadowBlockers(FTraceResults& res, void* userdata) +{ + ShadowCheckData* output = (ShadowCheckData*)userdata; + if (res.HitType == TRACE_HitActor && res.Actor && (res.Actor->flags9 & MF9_SHADOWBLOCK)) + { + output->HitShadow = res.Actor; + return TRACE_Stop; + } + + if (res.HitType != TRACE_HitActor) + { + return TRACE_Stop; + } + + return TRACE_Continue; +} + +// [inkoalawetrust] Check if an MF9_SHADOWBLOCK actor is standing between t1 and t2. +inline bool P_CheckForShadowBlock(AActor* t1, AActor* t2, DVector3 pos, double& penaltyFactor) +{ + FTraceResults result; + ShadowCheckData ShadowCheck; + ShadowCheck.HitShadow = nullptr; + DVector3 dir; + double dist; + if (t2) + { + dir = t1->Vec3To(t2); + dist = dir.Length(); + } + //No second actor, fall back to shooting at facing direction. + else + { + dir = DRotator(-(t1->Angles.Pitch), t1->Angles.Yaw, t1->Angles.Yaw); + dist = 65536.0; //Arbitrary large value. + } + + Trace(pos, t1->Sector, dir, dist, ActorFlags::FromInt(0xFFFFFFFF), ML_BLOCKEVERYTHING, t1, result, 0, CheckForShadowBlockers, &ShadowCheck); + + //Use the penalty factor of the shadowblocker that was hit. Otherwise, use the factor passed by PerformShadowChecks(). + if (ShadowCheck.HitShadow) + { + penaltyFactor = ShadowCheck.HitShadow->ShadowPenaltyFactor; + } + + return ShadowCheck.HitShadow; +} + +inline bool AffectedByShadows(AActor* self, AActor* other) +{ + return (!(self->flags6 & MF6_SEEINVISIBLE) || self->flags9 & MF9_SHADOWAIM); +} + +inline bool CheckForShadows(AActor* self, AActor* other, DVector3 pos, double& penaltyFactor) +{ + return ((other && other->flags & MF_SHADOW) || self->flags9 & MF9_DOSHADOWBLOCK && P_CheckForShadowBlock(self, other, pos, penaltyFactor)); +} + +inline bool PerformShadowChecks(AActor* self, AActor* other, DVector3 pos, double& penaltyFactor) +{ + if (other != nullptr) penaltyFactor = other->ShadowPenaltyFactor; //Use target penalty factor by default. + else penaltyFactor = 1.0; + return (AffectedByShadows(self, other) && CheckForShadows(self, other, pos, penaltyFactor)); +} + +//========================================================================== +// +// Function-specific inlines. +// +//========================================================================== + +inline void P_SpawnMissileXYZ_ShadowHandling(AActor* source, AActor* target, AActor* missile, DVector3 pos) +{ + double penaltyFactor; + // invisible target: rotate velocity vector in 2D + // [RC] Now monsters can aim at invisible player as if they were fully visible. + if (PerformShadowChecks(source, target, pos, penaltyFactor)) + { + DAngle an = DAngle::fromDeg(pr_spawnmissile.Random2() * (22.5 / 256)) * source->ShadowAimFactor * penaltyFactor; + double c = an.Cos(); + double s = an.Sin(); + + double newx = missile->Vel.X * c - missile->Vel.Y * s; + double newy = missile->Vel.X * s + missile->Vel.Y * c; + + missile->Vel.X = newx; + missile->Vel.Y = newy; + + if (source->flags9 & MF9_SHADOWAIMVERT) + { + DAngle pitch = DAngle::fromDeg(pr_spawnmissile.Random2() * (22.5 / 256)) * source->ShadowAimFactor * penaltyFactor; + double newz = -pitch.Sin() * missile->Speed; + missile->Vel.Z = newz; + } + } + return; +} + +//P_SpawnMissileZAimed uses a local variable for the angle it passes on. +inline DAngle P_SpawnMissileZAimed_ShadowHandling(AActor* source, AActor* target, double& vz, double speed, DVector3 pos) +{ + double penaltyFactor; + if (PerformShadowChecks(source, target, pos, penaltyFactor)) + { + if (source->flags9 & MF9_SHADOWAIMVERT) + { + DAngle pitch = DAngle::fromDeg(pr_spawnmissile.Random2() * (16. / 360.)) * source->ShadowAimFactor * penaltyFactor; + vz += -pitch.Sin() * speed; //Modify the Z velocity pointer that is then passed to P_SpawnMissileAngleZSpeed. + } + return DAngle::fromDeg(pr_spawnmissile.Random2() * (16. / 360.)) * source->ShadowAimFactor * penaltyFactor; + } + return nullAngle; +} + +inline void A_Face_ShadowHandling(AActor* self, AActor* other, DAngle max_turn, DAngle other_angle, bool vertical) +{ + double penaltyFactor; + if (!vertical) + { + // This will never work well if the turn angle is limited. + if (max_turn == nullAngle && (self->Angles.Yaw == other_angle) && PerformShadowChecks(self, other, self->PosAtZ(self->Center()), penaltyFactor)) + { + self->Angles.Yaw += DAngle::fromDeg(pr_facetarget.Random2() * (45 / 256.)) * self->ShadowAimFactor * penaltyFactor; + } + } + else + { + //Randomly offset the pitch when looking at shadows. + if (self->flags9 & MF9_SHADOWAIMVERT && max_turn == nullAngle && (self->Angles.Pitch == other_angle) && PerformShadowChecks(self, other, self->PosAtZ(self->Center()), penaltyFactor)) + { + self->Angles.Pitch += DAngle::fromDeg(pr_facetarget.Random2() * (45 / 256.)) * self->ShadowAimFactor * penaltyFactor; + } + } + return; +} + +inline void A_MonsterRail_ShadowHandling(AActor* self) +{ + double penaltyFactor; + double shootZ = self->Center() - self->FloatSpeed - self->Floorclip; // The formula P_RailAttack uses, minus offset_z since A_MonsterRail doesn't use it. + + if (PerformShadowChecks(self, self->target, self->PosAtZ(shootZ), penaltyFactor)) + { + self->Angles.Yaw += DAngle::fromDeg(pr_railface.Random2() * 45. / 256) * self->ShadowAimFactor * penaltyFactor; + if (self->flags9 & MF9_SHADOWAIMVERT) + self->Angles.Pitch += DAngle::fromDeg(pr_railface.Random2() * 45. / 256) * self->ShadowAimFactor * penaltyFactor; + } + return; +} + +//Also passes parameters to determine a firing position for the SHADOWBLOCK check. +inline void A_CustomRailgun_ShadowHandling(AActor* self, double spawnofs_xy, double spawnofs_z, DAngle spread_xy, int flags) +{ + double penaltyFactor; + // [inkoalawetrust] The exact formula P_RailAttack uses to determine where the railgun trace should spawn from. + DVector2 shootXY = (self->Vec2Angle(spawnofs_xy, (self->Angles.Yaw + spread_xy) - DAngle::fromDeg(90.))); + double shootZ = self->Center() - self->FloatSpeed + spawnofs_z - self->Floorclip; + if (flags & 16) shootZ += self->AttackOffset(); //16 is RGF_CENTERZ + DVector3 checkPos; + checkPos.X = shootXY.X; + checkPos.Y = shootXY.Y; + checkPos.Z = shootZ; + + if (PerformShadowChecks(self, self->target, checkPos, penaltyFactor)) + { + self->Angles.Yaw += DAngle::fromDeg(pr_crailgun.Random2() * (45. / 256.)) * self->ShadowAimFactor * penaltyFactor; + if (self->flags9 & MF9_SHADOWAIMVERT) + { + self->Angles.Pitch += DAngle::fromDeg(pr_crailgun.Random2() * (45. / 256.)) * self->ShadowAimFactor * penaltyFactor; + } + } + return; +} + +//If anything is returned, then AimLineAttacks' result pitch is changed to that value. +inline DAngle P_AimLineAttack_ShadowHandling(AActor*source, AActor* target, AActor* linetarget, double shootZ) +{ + double penaltyFactor; + AActor* mo; + if (target) + mo = target; + else + mo = linetarget; + + // [inkoalawetrust] Randomly offset the vertical aim of monsters. Roughly uses the SSG vertical spread. + if (source->player == NULL && source->flags9 & MF9_SHADOWAIMVERT && PerformShadowChecks (source, mo, source->PosAtZ (shootZ), penaltyFactor)) + { + if (linetarget) + return DAngle::fromDeg(pr_shadowaimz.Random2() * (28.388 / 256.)) * source->ShadowAimFactor * penaltyFactor; //Change the autoaims' pitch to this. + else + source->Angles.Pitch = DAngle::fromDeg(pr_shadowaimz.Random2() * (28.388 / 256.)) * source->ShadowAimFactor * penaltyFactor; + } + return nullAngle; +} + +//A_WolfAttack directly harms the target instead of firing a hitscan or projectile. So it handles shadows by lowering the chance of harming the target. +inline bool A_WolfAttack_ShadowHandling(AActor* self) +{ + double p; //Does nothing. + return (PerformShadowChecks(self, self->target, self->PosAtZ(self->Center()), p)); +} \ No newline at end of file diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 2861456d8..e50f95149 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -345,6 +345,11 @@ static FFlagDef ActorFlagDefs[]= DEFINE_FLAG(MF8, ADDLIGHTLEVEL, AActor, flags8), DEFINE_FLAG(MF8, ONLYSLAMSOLID, AActor, flags8), + DEFINE_FLAG(MF9, SHADOWAIM, AActor, flags9), + DEFINE_FLAG(MF9, DOSHADOWBLOCK, AActor, flags9), + DEFINE_FLAG(MF9, SHADOWBLOCK, AActor, flags9), + DEFINE_FLAG(MF9, SHADOWAIMVERT, AActor, flags9), + // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), DEFINE_FLAG2(FX_ROCKET, ROCKETTRAIL, AActor, effects), @@ -353,8 +358,7 @@ static FFlagDef ActorFlagDefs[]= DEFINE_FLAG(RF, FORCEYBILLBOARD, AActor, renderflags), DEFINE_FLAG(RF, FORCEXYBILLBOARD, AActor, renderflags), DEFINE_FLAG(RF, ROLLSPRITE, AActor, renderflags), // [marrub] roll the sprite billboard - // [fgsfds] Flat sprites - DEFINE_FLAG(RF, FLATSPRITE, AActor, renderflags), + DEFINE_FLAG(RF, FLATSPRITE, AActor, renderflags), // [fgsfds] Flat sprites DEFINE_FLAG(RF, WALLSPRITE, AActor, renderflags), DEFINE_FLAG(RF, DONTFLIP, AActor, renderflags), DEFINE_FLAG(RF, ROLLCENTER, AActor, renderflags), diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp index 1202d2489..675fef34a 100644 --- a/src/scripting/thingdef_properties.cpp +++ b/src/scripting/thingdef_properties.cpp @@ -988,6 +988,7 @@ DEFINE_PROPERTY(clearflags, 0, Actor) defaults->flags6 = 0; defaults->flags7 = 0; defaults->flags8 = 0; + defaults->flags9 = 0; } //========================================================================== diff --git a/src/scripting/vmthunks_actors.cpp b/src/scripting/vmthunks_actors.cpp index c9c459557..20a168bda 100644 --- a/src/scripting/vmthunks_actors.cpp +++ b/src/scripting/vmthunks_actors.cpp @@ -2109,6 +2109,8 @@ DEFINE_FIELD_NAMED(AActor, ViewAngles.Yaw, viewangle) DEFINE_FIELD_NAMED(AActor, ViewAngles.Pitch, viewpitch) DEFINE_FIELD_NAMED(AActor, ViewAngles.Roll, viewroll) DEFINE_FIELD(AActor, LightLevel) +DEFINE_FIELD(AActor, ShadowAimFactor) +DEFINE_FIELD(AActor, ShadowPenaltyFactor) DEFINE_FIELD_X(FCheckPosition, FCheckPosition, thing); DEFINE_FIELD_X(FCheckPosition, FCheckPosition, pos); diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index 30c763463..eef45e845 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -248,6 +248,7 @@ class Actor : Thinker native native double ViewAngle, ViewPitch, ViewRoll; native double RadiusDamageFactor; // Radius damage factor native double SelfDamageFactor; + native double ShadowAimFactor, ShadowPenaltyFactor; native double StealthAlpha; native int WoundHealth; // Health needed to enter wound state native readonly color BloodColor; @@ -361,6 +362,8 @@ class Actor : Thinker native property FriendlySeeBlocks: FriendlySeeBlocks; property ThruBits: ThruBits; property LightLevel: LightLevel; + property ShadowAimFactor: ShadowAimFactor; + property ShadowPenaltyFactor: ShadowPenaltyFactor; // need some definition work first //FRenderStyle RenderStyle; @@ -438,6 +441,8 @@ class Actor : Thinker native FastSpeed -1; RadiusDamageFactor 1; SelfDamageFactor 1; + ShadowAimFactor 1; + ShadowPenaltyFactor 1; StealthAlpha 0; WoundHealth 6; GibHealth int.min;