From 44683657f22e29330f302cd80f82e71e7012e617 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 20 Sep 2014 17:34:57 -0500 Subject: [PATCH 01/47] - Added jpalomo's A_SetSpeed patch. --- src/thingdef/thingdef_codeptr.cpp | 14 ++++++++++++++ src/thingdef/thingdef_expression.cpp | 1 + wadsrc/static/actors/actor.txt | 2 ++ 3 files changed, 17 insertions(+) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index f25a0818eb..f80e89aad2 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4980,3 +4980,17 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropItem) P_DropItem(self, spawntype, amount, chance); } + +//========================================================================== +// +// A_SetSpeed +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_FIXED(speed, 0); + + self->Speed = speed; +} diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index c578ddb2bf..7dbf5ec36d 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -88,6 +88,7 @@ DEFINE_MEMBER_VARIABLE(height, AActor) DEFINE_MEMBER_VARIABLE(radius, AActor) DEFINE_MEMBER_VARIABLE(reactiontime, AActor) DEFINE_MEMBER_VARIABLE(meleerange, AActor) +DEFINE_MEMBER_VARIABLE(Speed, AActor) //========================================================================== diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 501933f04a..e66226c1ed 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -62,6 +62,7 @@ ACTOR Actor native //: Thinker native fixed_t radius; native int reactiontime; native fixed_t meleerange; + native fixed_t speed; // Meh, MBF redundant functions. Only for DeHackEd support. action native A_Turn(float angle = 0); @@ -302,6 +303,7 @@ ACTOR Actor native //: Thinker action native A_SetTics(int tics); action native A_SetDamageType(name damagetype); action native A_DropItem(class item, int dropamount = -1, int chance = 256); + action native A_SetSpeed(float speed); action native A_CheckSightOrRange(float distance, state label); action native A_CheckRange(float distance, state label); From cbf72fe9922e221ab441f7579c229382af72ab27 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 21 Sep 2014 08:32:39 +0200 Subject: [PATCH 02/47] - fixed: Since the serializing code cannot distinguish between user variables of the same name coming from different actors in an inheritance chain, this kind of name duplication must be prohibited. --- src/thingdef/thingdef_parse.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index 498003b4ee..88e6084b64 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -374,6 +374,8 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClass *cls) FScriptPosition::ErrorCounter++; } + + FName symname = sc.String; if (sc.CheckToken('[')) { @@ -391,6 +393,15 @@ static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClass *cls) } sc.MustGetToken(';'); + // We must ensure that we do not define duplicates, even when they come from a parent table. + if (symt->FindSymbol(symname, true) != NULL) + { + sc.ScriptMessage ("'%s' is already defined in '%s' or one of its ancestors.", + symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); + FScriptPosition::ErrorCounter++; + return; + } + PSymbolVariable *sym = new PSymbolVariable(symname); sym->offset = cls->Extend(sizeof(int) * (valuetype.Type == VAL_Array ? valuetype.size : 1)); sym->ValueType = valuetype; From f92cdb0ce799901e54795dbc70fe383f62b2ad27 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 21 Sep 2014 09:11:33 +0200 Subject: [PATCH 03/47] - fixed: When the SectorAction's TriggerAction method was renamed, the inherited one in the MusicChanger was overlooked. (Bit thanks to the C++ specs for allowing to override a non-virtual method with a virtual one without even showing a warning...) --- src/s_advsound.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index 5b2e4e7c18..f7a40da9b5 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -2347,14 +2347,14 @@ class AMusicChanger : public ASectorAction { DECLARE_CLASS (AMusicChanger, ASectorAction) public: - virtual bool TriggerAction (AActor *triggerer, int activationType); + virtual bool DoTriggerAction (AActor *triggerer, int activationType); virtual void Tick(); virtual void PostBeginPlay(); }; IMPLEMENT_CLASS(AMusicChanger) -bool AMusicChanger::TriggerAction (AActor *triggerer, int activationType) +bool AMusicChanger::DoTriggerAction (AActor *triggerer, int activationType) { if (activationType & SECSPAC_Enter) { @@ -2364,7 +2364,7 @@ bool AMusicChanger::TriggerAction (AActor *triggerer, int activationType) reactiontime = 30; } } - return Super::TriggerAction (triggerer, activationType); + return Super::DoTriggerAction (triggerer, activationType); } void AMusicChanger::Tick() From 313b2293193a7ba1d55cd85ffe52797f0e07d093 Mon Sep 17 00:00:00 2001 From: GitExl Date: Sun, 21 Sep 2014 16:43:17 +0200 Subject: [PATCH 04/47] Add PickActor ACS function. --- src/p_acs.cpp | 33 +++++++++++++++++++++++++++++++++ src/p_local.h | 1 + src/p_map.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index c5ea7031d2..fd41d6e51d 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4368,6 +4368,7 @@ enum EACSFunctions ACSF_ChangeActorPitch, // 80 ACSF_GetArmorInfo, ACSF_DropInventory, + ACSF_PickActor, /* Zandronum's - these must be skipped when we reach 99! -100:ResetMap(0), @@ -5593,6 +5594,38 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } break; + case ACSF_PickActor: + if (argCount >= 5) + { + actor = SingleActorFromTID(args[0], activator); + if (actor == NULL) + { + return 0; + } + + DWORD actorMask = MF_SHOOTABLE; + if (argCount >= 6) { + actorMask = args[5]; + } + + DWORD wallMask = ML_BLOCKEVERYTHING | ML_BLOCKHITSCAN; + if (argCount >= 7) { + wallMask = args[6]; + } + + AActor* pickedActor = P_LinePickActor(actor, args[1] << 16, args[3], args[2] << 16, actorMask, wallMask); + if (pickedActor == NULL) { + return 0; + } + + pickedActor->RemoveFromHash(); + pickedActor->tid = args[4]; + pickedActor->AddToHash(); + + return 1; + } + break; + default: break; } diff --git a/src/p_local.h b/src/p_local.h index 58d9d02e50..939018734a 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -462,6 +462,7 @@ enum // P_LineAttack flags AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, int pitch, int damage, FName damageType, const PClass *pufftype, int flags = 0, AActor **victim = NULL, int *actualdamage = NULL); AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, int pitch, int damage, FName damageType, FName pufftype, int flags = 0, AActor **victim = NULL, int *actualdamage = NULL); +AActor *P_LinePickActor (AActor *t1, angle_t angle, fixed_t distance, int pitch, DWORD actorMask, DWORD wallMask); void P_TraceBleed (int damage, fixed_t x, fixed_t y, fixed_t z, AActor *target, angle_t angle, int pitch); void P_TraceBleed (int damage, AActor *target, angle_t angle, int pitch); void P_TraceBleed (int damage, AActor *target, AActor *missile); // missile version diff --git a/src/p_map.cpp b/src/p_map.cpp index 4ea8762ec8..2274e75719 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -3783,6 +3783,52 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, return NULL; } +//========================================================================== +// +// P_LinePickActor +// +//========================================================================== + +AActor *P_LinePickActor(AActor *t1, angle_t angle, fixed_t distance, int pitch, + DWORD actorMask, DWORD wallMask) +{ + fixed_t vx, vy, vz, shootz; + + angle >>= ANGLETOFINESHIFT; + pitch = (angle_t)(pitch) >> ANGLETOFINESHIFT; + + vx = FixedMul(finecosine[pitch], finecosine[angle]); + vy = FixedMul(finecosine[pitch], finesine[angle]); + vz = -finesine[pitch]; + + shootz = t1->z - t1->floorclip + (t1->height >> 1); + if (t1->player != NULL) + { + shootz += FixedMul(t1->player->mo->AttackZOffset, t1->player->crouchfactor); + } + else + { + shootz += 8 * FRACUNIT; + } + + FTraceResults trace; + Origin TData; + + TData.Caller = t1; + TData.hitGhosts = true; + + if (Trace(t1->x, t1->y, shootz, t1->Sector, vx, vy, vz, distance, + actorMask, wallMask, t1, trace, TRACE_NoSky, CheckForActor, &TData)) + { + if (trace.HitType == TRACE_HitActor) + { + return trace.Actor; + } + } + + return NULL; +} + //========================================================================== // // From bb3d2fa5350cbb3b37147be73ac45a5d9c6fd3d5 Mon Sep 17 00:00:00 2001 From: GitExl Date: Tue, 23 Sep 2014 12:10:39 +0200 Subject: [PATCH 05/47] Add CanRaiseActor ACS function. --- src/p_acs.cpp | 21 +++++++++++++++++++++ src/p_local.h | 1 + src/p_things.cpp | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index fd41d6e51d..86ab02272b 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4369,6 +4369,7 @@ enum EACSFunctions ACSF_GetArmorInfo, ACSF_DropInventory, ACSF_PickActor, + ACSF_CanRaiseActor, /* Zandronum's - these must be skipped when we reach 99! -100:ResetMap(0), @@ -5626,6 +5627,26 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } break; + case ACSF_CanRaiseActor: + if (argCount >= 1) { + if (args[0] == 0) { + actor = SingleActorFromTID(args[0], activator); + if (actor != NULL) { + return P_Thing_CanRaise(actor); + } + } + + FActorIterator iterator(args[0]); + bool canraiseall = false; + while ((actor = iterator.Next())) + { + canraiseall = !P_Thing_CanRaise(actor) | canraiseall; + } + + return !canraiseall; + } + break; + default: break; } diff --git a/src/p_local.h b/src/p_local.h index 939018734a..8ba1dd40bb 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -171,6 +171,7 @@ int P_Thing_Damage (int tid, AActor *whofor0, int amount, FName type); void P_Thing_SetVelocity(AActor *actor, fixed_t vx, fixed_t vy, fixed_t vz, bool add, bool setbob); void P_RemoveThing(AActor * actor); bool P_Thing_Raise(AActor *thing); +bool P_Thing_CanRaise(AActor *thing); const PClass *P_GetSpawnableType(int spawnnum); // diff --git a/src/p_things.cpp b/src/p_things.cpp index e62c65ae78..2a93ee31e0 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -445,6 +445,40 @@ bool P_Thing_Raise(AActor *thing) return true; } +bool P_Thing_CanRaise(AActor *thing) +{ + FState * RaiseState = thing->GetRaiseState(); + if (RaiseState == NULL) + { + return false; + } + + AActor *info = thing->GetDefault(); + + // Check against real height and radius + int oldflags = thing->flags; + fixed_t oldheight = thing->height; + fixed_t oldradius = thing->radius; + + thing->flags |= MF_SOLID; + thing->height = info->height; + thing->radius = info->radius; + + bool check = P_CheckPosition (thing, thing->x, thing->y); + + // Restore checked properties + thing->flags = oldflags; + thing->radius = oldradius; + thing->height = oldheight; + + if (!check) + { + return false; + } + + return true; +} + void P_Thing_SetVelocity(AActor *actor, fixed_t vx, fixed_t vy, fixed_t vz, bool add, bool setbob) { if (actor != NULL) From 79d9a573bb2520355d387f4b02fc6fc455b1c146 Mon Sep 17 00:00:00 2001 From: John Palomo Jr Date: Wed, 24 Sep 2014 03:56:20 -0400 Subject: [PATCH 06/47] Added AProp_MeleeRange to SetActorProperty. --- src/p_acs.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index fd41d6e51d..983728c8dc 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -3843,6 +3843,10 @@ void DLevelScript::DoSetActorProperty (AActor *actor, int property, int value) actor->reactiontime = value; break; + case APROP_MeleeRange: + actor->meleerange = value; + break; + case APROP_ViewHeight: if (actor->IsKindOf (RUNTIME_CLASS (APlayerPawn))) static_cast(actor)->ViewHeight = value; From b1f87295b86487686b789f9cd730be525bc6fbe6 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 25 Sep 2014 16:47:41 -0500 Subject: [PATCH 07/47] - Added A_DamageSelf. - A_DamageSelf(int amount, name damagetype) --- src/thingdef/thingdef_codeptr.cpp | 31 +++++++++++++++++++++++++++---- wadsrc/static/actors/actor.txt | 1 + 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index f80e89aad2..aff2629e43 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -3532,7 +3532,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS) //=========================================================================== // -// A_DamageMaster (int amount) +// A_DamageMaster (int amount, str damagetype) // Damages the master of this child by the specified amount. Negative values heal. // //=========================================================================== @@ -3558,7 +3558,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) //=========================================================================== // -// A_DamageChildren (amount) +// A_DamageChildren (amount, str damagetype) // Damages the children of this master by the specified amount. Negative values heal. // //=========================================================================== @@ -3592,7 +3592,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) //=========================================================================== // -// A_DamageSiblings (amount) +// A_DamageSiblings (int amount, str damagetype) // Damages the siblings of this master by the specified amount. Negative values heal. // //=========================================================================== @@ -4993,4 +4993,27 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) ACTION_PARAM_FIXED(speed, 0); self->Speed = speed; -} +} + +//=========================================================================== +// +// A_DamageSelf (int amount, str damagetype) +// Damages the calling actor by the specified amount. Negative values heal. +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + + if (amount > 0) + { + P_DamageMobj(self, self, self, amount, DamageType, DMG_NO_ARMOR); + } + else if (amount < 0) + { + amount = -amount; + P_GiveBody(self, amount); + } +} \ No newline at end of file diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index e66226c1ed..95d055afb8 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -304,6 +304,7 @@ ACTOR Actor native //: Thinker action native A_SetDamageType(name damagetype); action native A_DropItem(class item, int dropamount = -1, int chance = 256); action native A_SetSpeed(float speed); + action native A_DamageSelf(int amount, name damagetype = "none"); action native A_CheckSightOrRange(float distance, state label); action native A_CheckRange(float distance, state label); From 542b8a7171a8e5daec4a0ec206c88c9bbde16a9e Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sat, 16 Aug 2014 21:15:39 +1200 Subject: [PATCH 08/47] Added network load balancing - Guests can now attempt to match latency with the arbitrator. (net_loadbalance) - Added althud feature to show arbitrator and local latency. (hud_showlag 1 is on for netgames, 2 is always on) --- src/d_net.cpp | 34 +++++++++++++++++++++++---- src/d_net.h | 1 + src/g_shared/shared_hud.cpp | 47 +++++++++++++++++++++++++++++++++++++ wadsrc/static/menudef.txt | 8 +++++++ 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 9ba6eefa00..8bbe70e434 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -117,6 +117,7 @@ int playerfornode[MAXNETNODES]; int maketic; int skiptics; int ticdup; +int arb_maketic; // Arbitrators maketic difference void D_ProcessEvents (void); void G_BuildTiccmd (ticcmd_t *cmd); @@ -151,6 +152,8 @@ CUSTOM_CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) } } +CVAR(Bool, net_loadbalance, true, CVAR_SERVERINFO) + // [RH] Special "ticcmds" get stored in here static struct TicSpecial { @@ -347,6 +350,9 @@ int NetbufferSize () k += netbuffer[k] + 1; } + if (doomcom.remotenode == nodeforplayer[Net_Arbitrator]) + k++; + if (netbuffer[0] & NCMD_MULTI) { count = netbuffer[k]; @@ -570,6 +576,9 @@ bool HGetPacket (void) if (doomcom.datalength != NetbufferSize ()) { + Printf("Bad packet length %i (calculated %i)\n", + doomcom.datalength, NetbufferSize()); + if (debugfile) fprintf (debugfile,"---bad packet length %i (calculated %i)\n", doomcom.datalength, NetbufferSize()); @@ -782,6 +791,11 @@ void GetPackets (void) } } + if (netconsole == Net_Arbitrator) + { + arb_maketic = netbuffer[k++]; + } + playerbytes[0] = netconsole; if (netbuffer[0] & NCMD_MULTI) { @@ -1190,6 +1204,12 @@ void NetUpdate (void) } } + if (consoleplayer == Net_Arbitrator) + { + // The number of tics we just made should be removed from the count. + netbuffer[k++] = ((maketic - newtics - gametic) / ticdup); + } + if (numtics > 0) { int l; @@ -1298,10 +1318,16 @@ void NetUpdate (void) // very jerky. The way I have it written right now basically means // that it won't adapt. Fortunately, player prediction helps // alleviate the lag somewhat. - - if (NetMode != NET_PacketServer) + int average = 0; + if (net_loadbalance) + average = (((maketic - newtics - gametic) / ticdup) + arb_maketic) / 2; + if (NetMode == NET_PeerToPeer) { - mastertics = nettics[nodeforplayer[Net_Arbitrator]]; + mastertics = nettics[nodeforplayer[Net_Arbitrator]] + average; + } + else if (NetMode == NET_PacketServer) + { + mastertics = mastertics + average; } if (nettics[0] <= mastertics) { @@ -2713,7 +2739,7 @@ void Net_SkipCommand (int type, BYTE **stream) CCMD (pings) { int i; - + Printf("%d (%d ms) arbitrator buffer time\n", arb_maketic * ticdup, (arb_maketic * ticdup) * (1000 / TICRATE)); for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) Printf ("% 4d %s\n", currrecvtime[i] - lastrecvtime[i], diff --git a/src/d_net.h b/src/d_net.h index 4cd3e66f5b..8d352b25da 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -143,6 +143,7 @@ extern struct ticcmd_t localcmds[LOCALCMDTICS]; extern int maketic; extern int nettics[MAXNETNODES]; +extern int arb_maketic; extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; extern int ticdup; diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index 2daeff7a85..67a6bd82c7 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -37,6 +37,7 @@ // copy would be. #include "doomtype.h" +#include "doomdef.h" #include "v_video.h" #include "gi.h" #include "c_cvars.h" @@ -48,6 +49,7 @@ #include "p_local.h" #include "doomstat.h" #include "g_level.h" +#include "d_net.h" #include @@ -73,6 +75,7 @@ CVAR (Bool, hud_showscore, false, CVAR_ARCHIVE); // for user maintained score CVAR (Bool, hud_showweapons, true, CVAR_ARCHIVE); // Show weapons collected CVAR (Int , hud_showtime, 0, CVAR_ARCHIVE); // Show time on HUD CVAR (Int , hud_timecolor, CR_GOLD,CVAR_ARCHIVE); // Color of in-game time on HUD +CVAR (Int , hud_showlag, 0, CVAR_ARCHIVE); // Show input latency (maketic - gametic difference) CVAR (Int, hud_ammo_red, 25, CVAR_ARCHIVE) // ammo percent less than which status is red CVAR (Int, hud_ammo_yellow, 50, CVAR_ARCHIVE) // ammo percent less is yellow more green @@ -917,6 +920,49 @@ static void DrawTime() DrawHudText(SmallFont, hud_timecolor, timeString, hudwidth - width, height, FRACUNIT); } +//--------------------------------------------------------------------------- +// +// Draw in-game latency +// +//--------------------------------------------------------------------------- + +static void DrawLatency() +{ + if (hud_showlag <= 0 || + (hud_showlag == 1 && !netgame) || + hud_showlag > 2) + { + return; + } + + int localdelay = (maketic - gametic) * (1000 / TICRATE); + int arbitratordelay = (arb_maketic * ticdup) * (1000 / TICRATE); + int color = CR_GREEN; + if (MAX(localdelay, arbitratordelay) > 200) + { + color = CR_YELLOW; + } + if (MAX(localdelay, arbitratordelay) > 400) + { + color = CR_ORANGE; + } + if (MAX(localdelay, arbitratordelay) >= ((BACKUPTICS / 2 - 1) * ticdup) * (1000 / TICRATE)) + { + color = CR_RED; + } + + char tempstr[32]; + + const int millis = (level.time % TICRATE) * (1000 / TICRATE); + mysnprintf(tempstr, sizeof(tempstr), "a:%dms - l:%dms", arbitratordelay, localdelay); + + const int characterCount = strlen(tempstr); + const int width = SmallFont->GetCharWidth('0') * characterCount + 2; // small offset from screen's border + const int height = SmallFont->GetHeight() * 2; + + DrawHudText(SmallFont, color, tempstr, hudwidth - width, height, FRACUNIT); +} + //--------------------------------------------------------------------------- // @@ -982,6 +1028,7 @@ void DrawHUD() if (idmypos) DrawCoordinates(CPlayer); DrawTime(); + DrawLatency(); } else { diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 324c94e6b2..66d7537f6d 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -805,6 +805,13 @@ OptionValue "AltHUDTime" 9, "System" } +OptionValue "AltHUDLag" +{ + 0, "Off" + 1, "Netgames only" + 2, "Always" +} + OptionMenu "AltHUDOptions" { Title "Alternative HUD" @@ -819,6 +826,7 @@ OptionMenu "AltHUDOptions" Option "Show weapons", "hud_showweapons", "OnOff" Option "Show time", "hud_showtime", "AltHUDTime" Option "Time color", "hud_timecolor", "TextColors" + Option "Show network latency", "hud_showlag", "AltHUDLag" Slider "Red ammo display below %", "hud_ammo_red", 0, 100, 1, 0 Slider "Yellow ammo display below %", "hud_ammo_yellow", 0, 100, 1, 0 Slider "Red health display below", "hud_health_red", 0, 100, 1, 0 From 97586c317ebaba058d25e981b968b90787516a83 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 18 Aug 2014 21:08:17 +1200 Subject: [PATCH 09/47] Further refinements to network balancing - Added delay times of all players to the scoreboard - Removed balancing from packet-server (tried it, didn't work) - Calculations remove an extra tic to account for possible bias --- src/d_net.cpp | 36 ++++++++++++++++-------------------- src/d_net.h | 4 +++- src/g_shared/shared_hud.cpp | 4 ++-- src/hu_scores.cpp | 26 +++++++++++++++++++------- wadsrc/static/language.enu | 1 + 5 files changed, 41 insertions(+), 30 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 8bbe70e434..ff24744642 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -110,6 +110,7 @@ unsigned int lastrecvtime[MAXPLAYERS]; // [RH] Used for pings unsigned int currrecvtime[MAXPLAYERS]; unsigned int lastglobalrecvtime; // Identify the last time a packet was recieved. bool hadlate; +int netdelay[MAXNETNODES]; // Used for storing network delay times. int nodeforplayer[MAXPLAYERS]; int playerfornode[MAXNETNODES]; @@ -117,7 +118,6 @@ int playerfornode[MAXNETNODES]; int maketic; int skiptics; int ticdup; -int arb_maketic; // Arbitrators maketic difference void D_ProcessEvents (void); void G_BuildTiccmd (ticcmd_t *cmd); @@ -152,7 +152,7 @@ CUSTOM_CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) } } -CVAR(Bool, net_loadbalance, true, CVAR_SERVERINFO) +CVAR(Bool, net_ticbalance, false, CVAR_SERVERINFO) // [RH] Special "ticcmds" get stored in here static struct TicSpecial @@ -350,8 +350,8 @@ int NetbufferSize () k += netbuffer[k] + 1; } - if (doomcom.remotenode == nodeforplayer[Net_Arbitrator]) - k++; + // Network delay byte + k++; if (netbuffer[0] & NCMD_MULTI) { @@ -791,10 +791,8 @@ void GetPackets (void) } } - if (netconsole == Net_Arbitrator) - { - arb_maketic = netbuffer[k++]; - } + // Pull current network delay from node + netdelay[netnode] = netbuffer[k++]; playerbytes[0] = netconsole; if (netbuffer[0] & NCMD_MULTI) @@ -1204,11 +1202,9 @@ void NetUpdate (void) } } - if (consoleplayer == Net_Arbitrator) - { - // The number of tics we just made should be removed from the count. - netbuffer[k++] = ((maketic - newtics - gametic) / ticdup); - } + // Send current network delay + // The number of tics we just made should be removed from the count. + netbuffer[k++] = ((maketic - numtics - gametic) / ticdup); if (numtics > 0) { @@ -1319,16 +1315,16 @@ void NetUpdate (void) // that it won't adapt. Fortunately, player prediction helps // alleviate the lag somewhat. int average = 0; - if (net_loadbalance) - average = (((maketic - newtics - gametic) / ticdup) + arb_maketic) / 2; + if (NetMode == NET_PeerToPeer) { + // Try to guess ahead the time it takes to send responses to the arbitrator + // [ED850] It seems that there is a bias based on network adaption (which the netwrok arbitrator doesn't do), + // so I have set this up to assume one less tic, which appears to balance it out. + if (net_ticbalance) + average = ((netdelay[0] + ARBITRATOR_DELAY) / 2) - 1; mastertics = nettics[nodeforplayer[Net_Arbitrator]] + average; } - else if (NetMode == NET_PacketServer) - { - mastertics = mastertics + average; - } if (nettics[0] <= mastertics) { gametime--; @@ -2739,7 +2735,7 @@ void Net_SkipCommand (int type, BYTE **stream) CCMD (pings) { int i; - Printf("%d (%d ms) arbitrator buffer time\n", arb_maketic * ticdup, (arb_maketic * ticdup) * (1000 / TICRATE)); + Printf("%d (%d ms) arbitrator buffer time\n", ARBITRATOR_DELAY * ticdup, (ARBITRATOR_DELAY * ticdup) * (1000 / TICRATE)); for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) Printf ("% 4d %s\n", currrecvtime[i] - lastrecvtime[i], diff --git a/src/d_net.h b/src/d_net.h index 8d352b25da..1e019031ca 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -143,7 +143,9 @@ extern struct ticcmd_t localcmds[LOCALCMDTICS]; extern int maketic; extern int nettics[MAXNETNODES]; -extern int arb_maketic; +extern int netdelay[MAXNETNODES]; +extern int nodeforplayer[MAXPLAYERS]; +#define ARBITRATOR_DELAY netdelay[nodeforplayer[Net_Arbitrator]] extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; extern int ticdup; diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index 67a6bd82c7..e83e2e9885 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -935,8 +935,8 @@ static void DrawLatency() return; } - int localdelay = (maketic - gametic) * (1000 / TICRATE); - int arbitratordelay = (arb_maketic * ticdup) * (1000 / TICRATE); + int localdelay = (netdelay[0] * ticdup) * (1000 / TICRATE); + int arbitratordelay = (ARBITRATOR_DELAY * ticdup) * (1000 / TICRATE); int color = CR_GREEN; if (MAX(localdelay, arbitratordelay) > 200) { diff --git a/src/hu_scores.cpp b/src/hu_scores.cpp index af5d1bbaa6..7d6c5fb1eb 100644 --- a/src/hu_scores.cpp +++ b/src/hu_scores.cpp @@ -48,6 +48,7 @@ #include "d_player.h" #include "hu_stuff.h" #include "gstrings.h" +#include "d_net.h" // MACROS ------------------------------------------------------------------ @@ -61,7 +62,7 @@ static void HU_DoDrawScores (player_t *, player_t *[MAXPLAYERS]); static void HU_DrawTimeRemaining (int y); -static void HU_DrawPlayer (player_t *, bool, int, int, int, int, int, int, int, int); +static void HU_DrawPlayer(player_t *, bool, int, int, int, int, int, int, int, int, int); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- @@ -228,7 +229,7 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER int maxnamewidth, maxscorewidth, maxiconheight; int numTeams = 0; int x, y, ypadding, bottom; - int col2, col3, col4; + int col2, col3, col4, col5; if (deathmatch) { @@ -309,12 +310,14 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER const char *text_color = GStrings("SCORE_COLOR"), *text_frags = GStrings(deathmatch ? "SCORE_FRAGS" : "SCORE_KILLS"), - *text_name = GStrings("SCORE_NAME"); + *text_name = GStrings("SCORE_NAME"), + *text_delay = GStrings("SCORE_DELAY"); col2 = (SmallFont->StringWidth(text_color) + 8) * CleanXfac; col3 = col2 + (SmallFont->StringWidth(text_frags) + 8) * CleanXfac; col4 = col3 + maxscorewidth * CleanXfac; - x = (SCREENWIDTH >> 1) - ((maxnamewidth * CleanXfac + col4) >> 1); + col5 = col4 + (maxnamewidth + 8) * CleanXfac; + x = (SCREENWIDTH >> 1) - (((SmallFont->StringWidth(text_delay) * CleanXfac) + col5) >> 1); screen->DrawText (SmallFont, color, x, y, text_color, DTA_CleanNoMove, true, TAG_DONE); @@ -325,6 +328,9 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER screen->DrawText (SmallFont, color, x + col4, y, text_name, DTA_CleanNoMove, true, TAG_DONE); + screen->DrawText(SmallFont, color, x + col5, y, text_delay, + DTA_CleanNoMove, true, TAG_DONE); + y += height + 6 * CleanYfac; bottom -= height; @@ -332,7 +338,7 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER { if (playeringame[sortedplayers[i] - players]) { - HU_DrawPlayer (sortedplayers[i], player==sortedplayers[i], x, col2, col3, col4, maxnamewidth, y, ypadding, lineheight); + HU_DrawPlayer(sortedplayers[i], player == sortedplayers[i], x, col2, col3, col4, col5, maxnamewidth, y, ypadding, lineheight); y += lineheight + CleanYfac; } } @@ -377,7 +383,7 @@ static void HU_DrawTimeRemaining (int y) // //========================================================================== -static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, int col3, int col4, int maxnamewidth, int y, int ypadding, int height) +static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, int col3, int col4, int col5, int maxnamewidth, int y, int ypadding, int height) { int color; char str[80]; @@ -387,12 +393,13 @@ static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, // The teamplay mode uses colors to show teams, so we need some // other way to do highlighting. And it may as well be used for // all modes for the sake of consistancy. - screen->Dim(MAKERGB(200,245,255), 0.125f, col1 - 12*CleanXfac, y - 1, col4 + (maxnamewidth + 24)*CleanXfac, height + 2); + screen->Dim(MAKERGB(200,245,255), 0.125f, col1 - 12*CleanXfac, y - 1, col5 + (maxnamewidth + 24)*CleanXfac, height + 2); } col2 += col1; col3 += col1; col4 += col1; + col5 += col1; color = HU_GetRowColor(player, highlight); HU_DrawColorBar(col1, y, height, (int)(player - players)); @@ -412,6 +419,11 @@ static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, screen->DrawText (SmallFont, color, col4, y + ypadding, player->userinfo.GetName(), DTA_CleanNoMove, true, TAG_DONE); + mysnprintf(str, countof(str), "%d", (netdelay[nodeforplayer[(int)(player - players)]] * ticdup) * (1000 / TICRATE)); + + screen->DrawText(SmallFont, color, col5, y + ypadding, str, + DTA_CleanNoMove, true, TAG_DONE); + if (teamplay && Teams[player->userinfo.GetTeam()].GetLogo().IsNotEmpty ()) { FTexture *pic = TexMan[Teams[player->userinfo.GetTeam()].GetLogo().GetChars ()]; diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 4990cb4172..a92e22dfec 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -836,6 +836,7 @@ SCORE_BONUS = "BONUS"; SCORE_COLOR = "COLOR"; SCORE_SECRET = "SECRET"; SCORE_NAME = "NAME"; +SCORE_DELAY = "DELAY(ms)"; SCORE_KILLS = "KILLS"; SCORE_FRAGS = "FRAGS"; SCORE_DEATHS = "DEATHS"; From ded3bc73be14c7606c1970a8cffc0c04bee40b5a Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 18 Aug 2014 21:46:48 +1200 Subject: [PATCH 10/47] New extratic method - Extratic changed to server var, that can be changed at any time (net_extratic) - Added net_extratic 2 which resends all unconfirmed tics - Corrected bad variable typo in delay reporting --- src/d_net.cpp | 42 +++++++++++++++++++++++++++++------------- src/d_net.h | 1 - src/i_net.cpp | 5 ----- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index ff24744642..466a56c325 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -117,7 +117,7 @@ int playerfornode[MAXNETNODES]; int maketic; int skiptics; -int ticdup; +int ticdup; void D_ProcessEvents (void); void G_BuildTiccmd (ticcmd_t *cmd); @@ -153,6 +153,17 @@ CUSTOM_CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) } CVAR(Bool, net_ticbalance, false, CVAR_SERVERINFO) +CUSTOM_CVAR(Int, net_extratic, 0, CVAR_SERVERINFO) +{ + if (self < 0) + { + self = 0; + } + else if (self > 3) + { + I_SetFPSLimit(-1); + } +} // [RH] Special "ticcmds" get stored in here static struct TicSpecial @@ -1166,7 +1177,15 @@ void NetUpdate (void) if (numtics > BACKUPTICS) I_Error ("NetUpdate: Node %d missed too many tics", i); - resendto[i] = MAX (0, lowtic - doomcom.extratics); + switch (net_extratic) + { + case 0: + default: + resendto[i] = MAX(0, lowtic); break; + case 1: resendto[i] = MAX(0, lowtic - 1); break; + case 2: resendto[i] = MAX(0, lowtic - (lowtic - nettics[i])); break; + case 3: resendto[i] = MAX(0, lowtic - (BACKUPTICS / 2 - 1)); break; + } if (numtics == 0 && resendOnly && !remoteresend[i] && nettics[i]) { @@ -1204,7 +1223,7 @@ void NetUpdate (void) // Send current network delay // The number of tics we just made should be removed from the count. - netbuffer[k++] = ((maketic - numtics - gametic) / ticdup); + netbuffer[k++] = ((maketic - newtics - gametic) / ticdup); if (numtics > 0) { @@ -1319,7 +1338,7 @@ void NetUpdate (void) if (NetMode == NET_PeerToPeer) { // Try to guess ahead the time it takes to send responses to the arbitrator - // [ED850] It seems that there is a bias based on network adaption (which the netwrok arbitrator doesn't do), + // [ED850] It seems that there is a bias based on network adaption (which the arbitrator doesn't do), // so I have set this up to assume one less tic, which appears to balance it out. if (net_ticbalance) average = ((netdelay[0] + ARBITRATOR_DELAY) / 2) - 1; @@ -1368,9 +1387,8 @@ void NetUpdate (void) // // 0 One byte set to NCMD_SETUP+2 // 1 One byte for ticdup setting -// 2 One byte for extratics setting -// 3 One byte for NetMode setting -// 4 String with starting map's name +// 2 One byte for NetMode setting +// 3 String with starting map's name // . Four bytes for the RNG seed // . Stream containing remaining game info // @@ -1451,10 +1469,9 @@ bool DoArbitrate (void *userdata) data->gotsetup[0] = 0x80; ticdup = doomcom.ticdup = netbuffer[1]; - doomcom.extratics = netbuffer[2]; - NetMode = netbuffer[3]; + NetMode = netbuffer[2]; - stream = &netbuffer[4]; + stream = &netbuffer[3]; s = ReadString (&stream); startmap = s; delete[] s; @@ -1519,9 +1536,8 @@ bool DoArbitrate (void *userdata) { netbuffer[0] = NCMD_SETUP+2; netbuffer[1] = (BYTE)doomcom.ticdup; - netbuffer[2] = (BYTE)doomcom.extratics; - netbuffer[3] = NetMode; - stream = &netbuffer[4]; + netbuffer[2] = NetMode; + stream = &netbuffer[3]; WriteString (startmap, &stream); WriteLong (rngseed, &stream); C_WriteCVars (&stream, CVAR_SERVERINFO, true); diff --git a/src/d_net.h b/src/d_net.h index 1e019031ca..64c4e0f43f 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -70,7 +70,6 @@ struct doomcom_t // info common to all nodes SWORD numnodes; // console is always node 0. SWORD ticdup; // 1 = no duplication, 2-5 = dup for slow nets - SWORD extratics; // 1 = send a backup tic in every packet #ifdef DJGPP SWORD pad[5]; // keep things aligned for DOS drivers #endif diff --git a/src/i_net.cpp b/src/i_net.cpp index 889688b485..e03c2c0e80 100644 --- a/src/i_net.cpp +++ b/src/i_net.cpp @@ -938,11 +938,6 @@ bool I_InitNetwork (void) doomcom.ticdup = 1; } - if (Args->CheckParm ("-extratic")) - doomcom.extratics = 1; - else - doomcom.extratics = 0; - v = Args->CheckValue ("-port"); if (v) { From 4041f56cc8d9d9105ff86e7d1f7b6d1b0ad00900 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 18 Aug 2014 21:47:01 +1200 Subject: [PATCH 11/47] Bumped net version --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index c8917212d9..92b1816a1a 100644 --- a/src/version.h +++ b/src/version.h @@ -51,7 +51,7 @@ const char *GetVersionString(); // Version identifier for network games. // Bump it every time you do a release unless you're certain you // didn't change anything that will affect sync. -#define NETGAMEVERSION 230 +#define NETGAMEVERSION 231 // Version stored in the ini's [LastRun] section. // Bump it if you made some configuration change that you want to From 78f8bf19d2f60ffef414a30ab004c082599d658a Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 18 Aug 2014 22:45:51 +1200 Subject: [PATCH 12/47] Extratics mode 3 didn't work well - Removed Extratics mode 3 - Fixed a typo --- src/d_net.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 466a56c325..84bfe5856e 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -159,9 +159,9 @@ CUSTOM_CVAR(Int, net_extratic, 0, CVAR_SERVERINFO) { self = 0; } - else if (self > 3) + else if (self > 2) { - I_SetFPSLimit(-1); + self = 2; } } @@ -1183,8 +1183,7 @@ void NetUpdate (void) default: resendto[i] = MAX(0, lowtic); break; case 1: resendto[i] = MAX(0, lowtic - 1); break; - case 2: resendto[i] = MAX(0, lowtic - (lowtic - nettics[i])); break; - case 3: resendto[i] = MAX(0, lowtic - (BACKUPTICS / 2 - 1)); break; + case 2: resendto[i] = MAX(0, nettics[i]); break; } if (numtics == 0 && resendOnly && !remoteresend[i] && nettics[i]) From c585eee82ba971ca92ffe18d53dd164238f38673 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 18 Aug 2014 22:55:13 +1200 Subject: [PATCH 13/47] Don't need to MAX() mode 2 --- src/d_net.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 84bfe5856e..7de8592695 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -1183,7 +1183,7 @@ void NetUpdate (void) default: resendto[i] = MAX(0, lowtic); break; case 1: resendto[i] = MAX(0, lowtic - 1); break; - case 2: resendto[i] = MAX(0, nettics[i]); break; + case 2: resendto[i] = nettics[i]; break; } if (numtics == 0 && resendOnly && !remoteresend[i] && nettics[i]) From 6cd4fd81518f8e608250cf7c5004208164653392 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 18 Aug 2014 23:13:41 +1200 Subject: [PATCH 14/47] Added menu options for network settings --- wadsrc/static/menudef.txt | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 66d7537f6d..496f1e066f 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -341,6 +341,7 @@ OptionMenu "OptionsMenu" Submenu "Automap Options", "AutomapOptions" Submenu "HUD Options", "HUDOptions" Submenu "Miscellaneous Options", "MiscOptions" + Submenu "Network Options", "NetworkOptions" Submenu "Sound Options", "SoundOptions" Submenu "Display Options", "VideoOptions" Submenu "Set video mode", "VideoModeMenu" @@ -1599,3 +1600,27 @@ OptionMenu VideoModeMenu class VideoModeMenu } +/*======================================= + * + * Network options menu + * + *=======================================*/ + +OptionMenu NetworkOptions +{ + Title "NETWORK OPTIONS" + StaticText "Local options", 1 + Option "Movement prediction", "cl_noprediction", "OffOn" + StaticText " " + StaticText "Host options", 1 + Option "Extra Tics", "net_extratic", "ExtraTicMode" + Option "Latency balancing", "net_ticbalance", "OnOff" + +} + +OptionValue ExtraTicMode +{ + 0, "None" + 1, "1" + 2, "All unacknowledged" +} From 4db8b3e421d2018ac9d57abf5cbdf249adca4004 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 19 Aug 2014 22:30:33 +1200 Subject: [PATCH 15/47] Made delay updates less erratic --- src/d_net.cpp | 15 +++++++++++---- src/d_net.h | 3 +-- src/g_shared/shared_hud.cpp | 8 +++++--- src/hu_scores.cpp | 9 ++++++++- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 7de8592695..6e2877495a 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -110,7 +110,7 @@ unsigned int lastrecvtime[MAXPLAYERS]; // [RH] Used for pings unsigned int currrecvtime[MAXPLAYERS]; unsigned int lastglobalrecvtime; // Identify the last time a packet was recieved. bool hadlate; -int netdelay[MAXNETNODES]; // Used for storing network delay times. +int netdelay[MAXNETNODES][BACKUPTICS]; // Used for storing network delay times. int nodeforplayer[MAXPLAYERS]; int playerfornode[MAXNETNODES]; @@ -803,7 +803,7 @@ void GetPackets (void) } // Pull current network delay from node - netdelay[netnode] = netbuffer[k++]; + netdelay[netnode][(nettics[netnode]+1) % BACKUPTICS] = netbuffer[k++]; playerbytes[0] = netconsole; if (netbuffer[0] & NCMD_MULTI) @@ -1340,7 +1340,15 @@ void NetUpdate (void) // [ED850] It seems that there is a bias based on network adaption (which the arbitrator doesn't do), // so I have set this up to assume one less tic, which appears to balance it out. if (net_ticbalance) - average = ((netdelay[0] + ARBITRATOR_DELAY) / 2) - 1; + { + for (i = 0; i < BACKUPTICS; i++) + { + average += netdelay[nodeforplayer[Net_Arbitrator]][i]; + } + average /= BACKUPTICS; + average = ((netdelay[0][nettics[0] % BACKUPTICS] + average) / 2) - 1; + } + mastertics = nettics[nodeforplayer[Net_Arbitrator]] + average; } if (nettics[0] <= mastertics) @@ -2750,7 +2758,6 @@ void Net_SkipCommand (int type, BYTE **stream) CCMD (pings) { int i; - Printf("%d (%d ms) arbitrator buffer time\n", ARBITRATOR_DELAY * ticdup, (ARBITRATOR_DELAY * ticdup) * (1000 / TICRATE)); for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) Printf ("% 4d %s\n", currrecvtime[i] - lastrecvtime[i], diff --git a/src/d_net.h b/src/d_net.h index 64c4e0f43f..07921a3012 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -142,9 +142,8 @@ extern struct ticcmd_t localcmds[LOCALCMDTICS]; extern int maketic; extern int nettics[MAXNETNODES]; -extern int netdelay[MAXNETNODES]; +extern int netdelay[MAXNETNODES][BACKUPTICS]; extern int nodeforplayer[MAXPLAYERS]; -#define ARBITRATOR_DELAY netdelay[nodeforplayer[Net_Arbitrator]] extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; extern int ticdup; diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index e83e2e9885..110675791c 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -934,9 +934,11 @@ static void DrawLatency() { return; } - - int localdelay = (netdelay[0] * ticdup) * (1000 / TICRATE); - int arbitratordelay = (ARBITRATOR_DELAY * ticdup) * (1000 / TICRATE); + int i, localdelay = 0, arbitratordelay = 0; + for (i = 0; i < BACKUPTICS; i++) localdelay += netdelay[0][i]; + for (i = 0; i < BACKUPTICS; i++) arbitratordelay += netdelay[nodeforplayer[Net_Arbitrator]][i]; + localdelay = ((localdelay / BACKUPTICS) * ticdup) * (1000 / TICRATE); + arbitratordelay = ((arbitratordelay / BACKUPTICS) * ticdup) * (1000 / TICRATE); int color = CR_GREEN; if (MAX(localdelay, arbitratordelay) > 200) { diff --git a/src/hu_scores.cpp b/src/hu_scores.cpp index 7d6c5fb1eb..8b9a7d0ad9 100644 --- a/src/hu_scores.cpp +++ b/src/hu_scores.cpp @@ -419,7 +419,14 @@ static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, screen->DrawText (SmallFont, color, col4, y + ypadding, player->userinfo.GetName(), DTA_CleanNoMove, true, TAG_DONE); - mysnprintf(str, countof(str), "%d", (netdelay[nodeforplayer[(int)(player - players)]] * ticdup) * (1000 / TICRATE)); + int avgdelay = 0; + for (int i = 0; i < BACKUPTICS; i++) + { + avgdelay += netdelay[nodeforplayer[(int)(player - players)]][i]; + } + avgdelay /= BACKUPTICS; + + mysnprintf(str, countof(str), "%d", (avgdelay * ticdup) * (1000 / TICRATE)); screen->DrawText(SmallFont, color, col5, y + ypadding, str, DTA_CleanNoMove, true, TAG_DONE); From 42e6737803adfd3984ea3481667c0d668165f92e Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 19 Aug 2014 22:31:45 +1200 Subject: [PATCH 16/47] Player prediction now updates listener --- src/p_user.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_user.cpp b/src/p_user.cpp index fbe24cc832..9c3d7f3217 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -2728,6 +2728,8 @@ void P_PredictPlayer (player_t *player) P_PlayerThink (player); player->mo->Tick (); } + + S_UpdateSounds(players[consoleplayer].camera); // move positional sounds } extern msecnode_t *P_AddSecnode (sector_t *s, AActor *thing, msecnode_t *nextnode); From c661da299592b16981425edbf06914e5f4dcbc2f Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Wed, 20 Aug 2014 19:41:07 +1200 Subject: [PATCH 17/47] Stop negative tic counts from corrupting messages --- src/d_net.cpp | 2 +- src/i_net.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 6e2877495a..dfee7460ec 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -1173,7 +1173,7 @@ void NetUpdate (void) netbuffer[k++] = lowtic; } - numtics = lowtic - realstart; + numtics = MAX(0, lowtic - realstart); if (numtics > BACKUPTICS) I_Error ("NetUpdate: Node %d missed too many tics", i); diff --git a/src/i_net.cpp b/src/i_net.cpp index e03c2c0e80..3ec9d78813 100644 --- a/src/i_net.cpp +++ b/src/i_net.cpp @@ -208,11 +208,11 @@ void PacketSend (void) { I_FatalError("Netbuffer overflow!"); } + assert(!(doomcom.data[0] & NCMD_COMPRESSED)); uLong size = TRANSMIT_SIZE - 1; if (doomcom.datalength >= 10) { - assert(!(doomcom.data[0] & NCMD_COMPRESSED)); TransmitBuffer[0] = doomcom.data[0] | NCMD_COMPRESSED; c = compress2(TransmitBuffer + 1, &size, doomcom.data + 1, doomcom.datalength - 1, 9); size += 1; From 530f4746731ac6d13c7b86646b84f4b996dd7d1a Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Thu, 21 Aug 2014 16:31:29 +1200 Subject: [PATCH 18/47] Shifted netmode reporting for guests - The netmode is now reported after a guest has received it. - Minor code cleanup --- src/d_net.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index dfee7460ec..fb6e9722d6 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -1173,7 +1173,7 @@ void NetUpdate (void) netbuffer[k++] = lowtic; } - numtics = MAX(0, lowtic - realstart); + numtics = lowtic - realstart; if (numtics > BACKUPTICS) I_Error ("NetUpdate: Node %d missed too many tics", i); @@ -1181,12 +1181,12 @@ void NetUpdate (void) { case 0: default: - resendto[i] = MAX(0, lowtic); break; + resendto[i] = lowtic; break; case 1: resendto[i] = MAX(0, lowtic - 1); break; case 2: resendto[i] = nettics[i]; break; } - if (numtics == 0 && resendOnly && !remoteresend[i] && nettics[i]) + if (numtics <= 0 && resendOnly && !remoteresend[i] && nettics[i]) { continue; } @@ -1692,15 +1692,18 @@ void D_CheckNetGame (void) consoleplayer = doomcom.consoleplayer; - v = Args->CheckValue ("-netmode"); - if (v != NULL) + if (consoleplayer == Net_Arbitrator) { - NetMode = atoi (v) != 0 ? NET_PacketServer : NET_PeerToPeer; - } - if (doomcom.numnodes > 1) - { - Printf ("Selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode. (%s)\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server", - v != NULL ? "forced" : "auto"); + v = Args->CheckValue("-netmode"); + if (v != NULL) + { + NetMode = atoi(v) != 0 ? NET_PacketServer : NET_PeerToPeer; + } + if (doomcom.numnodes > 1) + { + Printf("Selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode. (%s)\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server", + v != NULL ? "forced" : "auto"); + } } // [RH] Setup user info @@ -1728,6 +1731,11 @@ void D_CheckNetGame (void) for (i = 0; i < doomcom.numnodes; i++) nodeingame[i] = true; + if (consoleplayer != Net_Arbitrator && doomcom.numnodes > 1) + { + Printf("Arbitrator selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode.\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server"); + } + Printf ("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom.numplayers, doomcom.numnodes); } From ad0a1ad86576a5b5fd0205f9eb6de895dc4feccd Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Thu, 21 Aug 2014 16:41:59 +1200 Subject: [PATCH 19/47] Readded -extratic for compatibility with launchers --- src/d_net.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/d_net.cpp b/src/d_net.cpp index fb6e9722d6..06886e1757 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -1704,6 +1704,11 @@ void D_CheckNetGame (void) Printf("Selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode. (%s)\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server", v != NULL ? "forced" : "auto"); } + + if (Args->CheckParm("-extratic")) + { + net_extratic = 1; + } } // [RH] Setup user info From 53b6e7d4d537c204646012bdb03b8f607402f6ee Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sat, 23 Aug 2014 15:17:11 +1200 Subject: [PATCH 20/47] Added silent line teleport prediction - Allow activation of line teleport specials during prediction - Moved prediction functions to improve uncapped framerates --- src/d_main.cpp | 4 ++-- src/p_map.cpp | 24 +++++++++++++++--------- src/p_spec.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ src/p_spec.h | 1 + src/p_user.cpp | 7 +++++-- src/r_utility.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ src/r_utility.h | 2 ++ 7 files changed, 107 insertions(+), 13 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index ab33bfaec9..8a8751041e 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -756,9 +756,9 @@ void D_Display () } screen->SetBlendingRect(viewwindowx, viewwindowy, viewwindowx + viewwidth, viewwindowy + viewheight); - P_PredictPlayer(&players[consoleplayer]); + //P_PredictPlayer(&players[consoleplayer]); Renderer->RenderView(&players[consoleplayer]); - P_UnPredictPlayer(); + //P_UnPredictPlayer(); if ((hw2d = screen->Begin2D(viewactive))) { // Redraw everything every frame when using 2D accel diff --git a/src/p_map.cpp b/src/p_map.cpp index 2274e75719..f0087e2201 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -380,7 +380,9 @@ bool P_TeleportMove(AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefra // ... and some items can never be telefragged while others will be telefragged by everything that teleports upon them. if ((StompAlwaysFrags && !(th->flags6 & MF6_NOTELEFRAG)) || (th->flags7 & MF7_ALWAYSTELEFRAG)) { - P_DamageMobj(th, thing, thing, TELEFRAG_DAMAGE, NAME_Telefrag, DMG_THRUSTLESS); + // Don't actually damage if predicting a teleport + if (thing->player == NULL || !(thing->player->cheats & CF_PREDICTING)) + P_DamageMobj(th, thing, thing, TELEFRAG_DAMAGE, NAME_Telefrag, DMG_THRUSTLESS); continue; } return false; @@ -1981,13 +1983,6 @@ bool P_TryMove(AActor *thing, fixed_t x, fixed_t y, thing->AdjustFloorClip(); } - // [RH] Don't activate anything if just predicting - if (thing->player && (thing->player->cheats & CF_PREDICTING)) - { - thing->flags6 &= ~MF6_INTRYMOVE; - return true; - } - // if any special lines were hit, do the effect if (!(thing->flags & (MF_TELEPORT | MF_NOCLIP))) { @@ -1998,7 +1993,11 @@ bool P_TryMove(AActor *thing, fixed_t x, fixed_t y, oldside = P_PointOnLineSide(oldx, oldy, ld); if (side != oldside && ld->special && !(thing->flags6 & MF6_NOTRIGGER)) { - if (thing->player) + if (thing->player && (thing->player->cheats & CF_PREDICTING)) + { + P_PredictLine(ld, thing, oldside, SPAC_Cross); + } + else if (thing->player) { P_ActivateLine(ld, thing, oldside, SPAC_Cross); } @@ -2024,6 +2023,13 @@ bool P_TryMove(AActor *thing, fixed_t x, fixed_t y, } } + // [RH] Don't activate anything if just predicting + if (thing->player && (thing->player->cheats & CF_PREDICTING)) + { + thing->flags6 &= ~MF6_INTRYMOVE; + return true; + } + // [RH] Check for crossing fake floor/ceiling newsec = thing->Sector; if (newsec->heightsec && oldsec->heightsec && newsec->SecActTarget) diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 00ed322c0c..4ac1c59d95 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -73,6 +73,7 @@ static FRandom pr_playerinspecialsector ("PlayerInSpecialSector"); void P_SetupPortals(); +EXTERN_CVAR(Bool, cl_predict_specials) IMPLEMENT_POINTY_CLASS (DScroller) DECLARE_POINTER (m_Interpolations[0]) @@ -408,6 +409,47 @@ bool P_TestActivateLine (line_t *line, AActor *mo, int side, int activationType) return true; } +//============================================================================ +// +// P_PredictLine +// +//============================================================================ + +bool P_PredictLine(line_t *line, AActor *mo, int side, int activationType) +{ + int lineActivation; + INTBOOL buttonSuccess; + BYTE special; + + // Only predict a very specifc section of specials + if (line->special != Teleport_Line) + { + return false; + } + + if (!P_TestActivateLine(line, mo, side, activationType) || !cl_predict_specials) + { + return false; + } + + if (line->locknumber > 0) return false; + lineActivation = line->activation; + buttonSuccess = false; + buttonSuccess = P_ExecuteSpecial(line->special, + line, mo, side == 1, line->args[0], + line->args[1], line->args[2], + line->args[3], line->args[4]); + + special = line->special; + + // end of changed code + if (developer && buttonSuccess) + { + Printf("Line special %d predicted on line %i\n", special, int(line - lines)); + } + return true; +} + // // P_PlayerInSpecialSector // Called every tic frame diff --git a/src/p_spec.h b/src/p_spec.h index c9bb6eded0..906a38a8c2 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -166,6 +166,7 @@ void P_UpdateSpecials (void); // when needed bool P_ActivateLine (line_t *ld, AActor *mo, int side, int activationType); bool P_TestActivateLine (line_t *ld, AActor *mo, int side, int activationType); +bool P_PredictLine (line_t *ld, AActor *mo, int side, int activationType); void P_PlayerInSpecialSector (player_t *player, sector_t * sector=NULL); void P_PlayerOnSpecialFlat (player_t *player, int floorType); diff --git a/src/p_user.cpp b/src/p_user.cpp index 9c3d7f3217..7b049a2df7 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -62,6 +62,7 @@ static FRandom pr_skullpop ("SkullPop"); // Variables for prediction CVAR (Bool, cl_noprediction, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR(Bool, cl_predict_specials, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) static player_t PredictionPlayerBackup; static BYTE PredictionActorBackup[sizeof(AActor)]; static TArray PredictionTouchingSectorsBackup; @@ -2722,14 +2723,16 @@ void P_PredictPlayer (player_t *player) } act->BlockNode = NULL; + bool NoInterpolateOld = R_GetViewInterpolationStatus(); for (int i = gametic; i < maxtic; ++i) { + if (!NoInterpolateOld) + R_RebuildViewInterpolation(player); + player->cmd = localcmds[i % LOCALCMDTICS]; P_PlayerThink (player); player->mo->Tick (); } - - S_UpdateSounds(players[consoleplayer].camera); // move positional sounds } extern msecnode_t *P_AddSecnode (sector_t *s, AActor *thing, msecnode_t *nextnode); diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 5bf38ad26e..19d446b5b5 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -729,6 +729,46 @@ void R_ClearPastViewer (AActor *actor) } } +//========================================================================== +// +// R_RebuildViewInterpolation +// +//========================================================================== + +void R_RebuildViewInterpolation(player_t *player) +{ + InterpolationViewer *iview; + if (NoInterpolateView) + { + if (player != NULL && player->camera != NULL) + { + iview = FindPastViewer(player->camera); + } + + if (iview == NULL) + return; + + NoInterpolateView = false; + iview->oviewx = iview->nviewx; + iview->oviewy = iview->nviewy; + iview->oviewz = iview->nviewz; + iview->oviewpitch = iview->nviewpitch; + iview->oviewangle = iview->nviewangle; + } +} + +//========================================================================== +// +// R_GetViewInterpolationStatus +// +//========================================================================== + +bool R_GetViewInterpolationStatus() +{ + return NoInterpolateView; +} + + //========================================================================== // // R_SetupFrame diff --git a/src/r_utility.h b/src/r_utility.h index 85ca7c410e..2d9aac086e 100644 --- a/src/r_utility.h +++ b/src/r_utility.h @@ -61,6 +61,8 @@ inline angle_t R_PointToAngle (fixed_t x, fixed_t y) { return R_PointToAngle2 (v subsector_t *R_PointInSubsector (fixed_t x, fixed_t y); fixed_t R_PointToDist2 (fixed_t dx, fixed_t dy); void R_ResetViewInterpolation (); +void R_RebuildViewInterpolation(player_t *player); +bool R_GetViewInterpolationStatus(); void R_SetViewSize (int blocks); void R_SetFOV (float fov); float R_GetFOV (); From 9e68983b4469906102b7d810fe02e1756d5a3540 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 26 Aug 2014 22:29:39 +1200 Subject: [PATCH 21/47] Added standard teleports to line prediction - Added standard teleports to line prediction - Menudef for line special prediction --- src/p_spec.cpp | 3 ++- src/p_teleport.cpp | 27 +++++++++++++++++---------- wadsrc/static/menudef.txt | 1 + 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 4ac1c59d95..d58062a7e8 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -422,7 +422,8 @@ bool P_PredictLine(line_t *line, AActor *mo, int side, int activationType) BYTE special; // Only predict a very specifc section of specials - if (line->special != Teleport_Line) + if (line->special != Teleport_Line && + line->special != Teleport) { return false; } diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index 50470d9eb4..b64c04a5ae 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -96,6 +96,8 @@ void P_SpawnTeleportFog(fixed_t x, fixed_t y, fixed_t z, int spawnid) bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, bool useFog, bool sourceFog, bool keepOrientation, bool bHaltVelocity, bool keepHeight) { + bool predicting = (thing->player && (thing->player->cheats & CF_PREDICTING)); + fixed_t oldx; fixed_t oldy; fixed_t oldz; @@ -181,7 +183,7 @@ bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, angle = thing->angle; } // Spawn teleport fog at source and destination - if (sourceFog) + if (sourceFog && !predicting) { fixed_t fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT; AActor *fog = Spawn (oldx, oldy, oldz + fogDelta, ALLOW_REPLACE); @@ -189,11 +191,14 @@ bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, } if (useFog) { - fixed_t fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT; - an = angle >> ANGLETOFINESHIFT; - AActor *fog = Spawn (x + 20*finecosine[an], - y + 20*finesine[an], thing->z + fogDelta, ALLOW_REPLACE); - fog->target = thing; + if (!predicting) + { + fixed_t fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT; + an = angle >> ANGLETOFINESHIFT; + AActor *fog = Spawn(x + 20 * finecosine[an], + y + 20 * finesine[an], thing->z + fogDelta, ALLOW_REPLACE); + fog->target = thing; + } if (thing->player) { // [RH] Zoom player's field of vision @@ -226,7 +231,7 @@ bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, return true; } -static AActor *SelectTeleDest (int tid, int tag) +static AActor *SelectTeleDest (int tid, int tag, bool norandom) { AActor *searcher; @@ -276,7 +281,7 @@ static AActor *SelectTeleDest (int tid, int tag) } else { - if (count != 1) + if (count != 1 && !norandom) { count = 1 + (pr_teleport() % count); } @@ -323,6 +328,8 @@ static AActor *SelectTeleDest (int tid, int tag) bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, bool fog, bool sourceFog, bool keepOrientation, bool haltVelocity, bool keepHeight) { + bool predicting = (thing->player && (thing->player->cheats & CF_PREDICTING)); + AActor *searcher; fixed_t z; angle_t angle = 0; @@ -342,7 +349,7 @@ bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, bool { // Don't teleport if hit back of line, so you can get out of teleporter. return 0; } - searcher = SelectTeleDest (tid, tag); + searcher = SelectTeleDest(tid, tag, predicting); if (searcher == NULL) { return false; @@ -390,7 +397,7 @@ bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, bool thing->velx = FixedMul(velx, c) - FixedMul(vely, s); thing->vely = FixedMul(vely, c) + FixedMul(velx, s); } - if ((velx | vely) == 0 && thing->player != NULL && thing->player->mo == thing) + if ((velx | vely) == 0 && thing->player != NULL && thing->player->mo == thing && !predicting) { thing->player->mo->PlayIdle (); } diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 496f1e066f..6d313faf14 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1611,6 +1611,7 @@ OptionMenu NetworkOptions Title "NETWORK OPTIONS" StaticText "Local options", 1 Option "Movement prediction", "cl_noprediction", "OffOn" + Option "Predict line actions", "cl_predict_specials", "OnOff" StaticText " " StaticText "Host options", 1 Option "Extra Tics", "net_extratic", "ExtraTicMode" From 8f82243f4c5a3ca729aa5e400b3447b64d8965e1 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Tue, 26 Aug 2014 22:31:42 +1200 Subject: [PATCH 22/47] Don't balance if already the slowest node - Network balancing shouldn't be run if already too far behind - Don't save network settings - Added net_fakelatency for debug builds --- src/d_net.cpp | 125 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 112 insertions(+), 13 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 06886e1757..eb8cb4eeb7 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -152,8 +152,8 @@ CUSTOM_CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) } } -CVAR(Bool, net_ticbalance, false, CVAR_SERVERINFO) -CUSTOM_CVAR(Int, net_extratic, 0, CVAR_SERVERINFO) +CVAR(Bool, net_ticbalance, false, CVAR_SERVERINFO | CVAR_NOSAVE) +CUSTOM_CVAR(Int, net_extratic, 0, CVAR_SERVERINFO | CVAR_NOSAVE) { if (self < 0) { @@ -165,6 +165,19 @@ CUSTOM_CVAR(Int, net_extratic, 0, CVAR_SERVERINFO) } } +#ifdef _DEBUG +CVAR(Int, net_fakelatency, 0, 0); + +struct PacketStore +{ + int timer; + doomcom_t message; +}; + +static TArray InBuffer; +static TArray OutBuffer; +#endif + // [RH] Special "ticcmds" get stored in here static struct TicSpecial { @@ -504,7 +517,30 @@ void HSendPacket (int node, int len) doomcom.remotenode = node; doomcom.datalength = len; - I_NetCmd (); +#ifdef _DEBUG + if (net_fakelatency / 2 > 0) + { + PacketStore store; + store.message = doomcom; + store.timer = I_GetTime(false) + ((net_fakelatency / 2) / (1000 / TICRATE)); + OutBuffer.Push(store); + } + else + I_NetCmd(); + + for (unsigned int i = 0; i < OutBuffer.Size(); i++) + { + if (OutBuffer[i].timer <= I_GetTime(false)) + { + doomcom = OutBuffer[i].message; + I_NetCmd(); + OutBuffer.Delete(i); + i = -1; + } + } +#else + I_NetCmd(); +#endif } // @@ -526,12 +562,42 @@ bool HGetPacket (void) if (demoplayback) return false; - + doomcom.command = CMD_GET; I_NetCmd (); + +#ifdef _DEBUG + if (net_fakelatency / 2 > 0 && doomcom.remotenode != -1) + { + PacketStore store; + store.message = doomcom; + store.timer = I_GetTime(false) + ((net_fakelatency / 2) / (1000 / TICRATE)); + InBuffer.Push(store); + doomcom.remotenode = -1; + } if (doomcom.remotenode == -1) + { + bool gotmessage = false; + for (unsigned int i = 0; i < InBuffer.Size(); i++) + { + if (InBuffer[i].timer <= I_GetTime(false)) + { + doomcom = InBuffer[i].message; + InBuffer.Delete(i); + gotmessage = true; + break; + } + } + if (!gotmessage) + return false; + } +#else + if (doomcom.remotenode == -1) + { return false; + } +#endif if (debugfile) { @@ -1173,7 +1239,7 @@ void NetUpdate (void) netbuffer[k++] = lowtic; } - numtics = lowtic - realstart; + numtics = MAX(0, lowtic - realstart); if (numtics > BACKUPTICS) I_Error ("NetUpdate: Node %d missed too many tics", i); @@ -1186,7 +1252,7 @@ void NetUpdate (void) case 2: resendto[i] = nettics[i]; break; } - if (numtics <= 0 && resendOnly && !remoteresend[i] && nettics[i]) + if (numtics == 0 && resendOnly && !remoteresend[i] && nettics[i]) { continue; } @@ -1332,24 +1398,49 @@ void NetUpdate (void) // very jerky. The way I have it written right now basically means // that it won't adapt. Fortunately, player prediction helps // alleviate the lag somewhat. - int average = 0; if (NetMode == NET_PeerToPeer) { - // Try to guess ahead the time it takes to send responses to the arbitrator + // Try to guess ahead the time it takes to send responses to the slowest node // [ED850] It seems that there is a bias based on network adaption (which the arbitrator doesn't do), // so I have set this up to assume one less tic, which appears to balance it out. + int totalavg = 0; if (net_ticbalance) { - for (i = 0; i < BACKUPTICS; i++) + // We shouldn't adapt if we are already the slowest node, otherwise it just adds more latency + bool slow = true; + int nodeavg = 0; + for (i = 1; i < MAXNETNODES; i++) { - average += netdelay[nodeforplayer[Net_Arbitrator]][i]; + if (!nodeingame[i]) + continue; + + if (netdelay[i][0] > netdelay[0][0]) + { + slow = false; + break; + } + } + + if (!slow) + { + int totalnodes = 0; + for (i = 0; i < MAXNETNODES; i++) + { + if (!nodeingame[i]) + continue; + + totalnodes++; + nodeavg = 0; + for (j = 0; j < BACKUPTICS; j++) nodeavg += netdelay[i][j]; + totalavg += (nodeavg / BACKUPTICS); + } + + totalavg = (totalavg / totalnodes) - 1; } - average /= BACKUPTICS; - average = ((netdelay[0][nettics[0] % BACKUPTICS] + average) / 2) - 1; } - mastertics = nettics[nodeforplayer[Net_Arbitrator]] + average; + mastertics = nettics[nodeforplayer[Net_Arbitrator]] + totalavg; } if (nettics[0] <= mastertics) { @@ -1867,6 +1958,9 @@ void TryRunTics (void) { C_Ticker(); M_Ticker(); + // Repredict the player for new buffered movement + P_UnPredictPlayer(); + P_PredictPlayer(&players[consoleplayer]); } return; } @@ -1902,6 +1996,9 @@ void TryRunTics (void) { C_Ticker (); M_Ticker (); + // Repredict the player for new buffered movement + P_UnPredictPlayer(); + P_PredictPlayer(&players[consoleplayer]); return; } } @@ -1915,6 +2012,7 @@ void TryRunTics (void) // run the count tics if (counts > 0) { + P_UnPredictPlayer(); while (counts--) { if (gametic > lowtic) @@ -1934,6 +2032,7 @@ void TryRunTics (void) NetUpdate (); // check for new console commands } + P_PredictPlayer(&players[consoleplayer]); S_UpdateSounds (players[consoleplayer].camera); // move positional sounds } } From 84cf79980397df87594816452fc917fdd2da6899 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Fri, 26 Sep 2014 15:37:14 +1200 Subject: [PATCH 23/47] Final changes to balancing --- src/d_net.cpp | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index eb8cb4eeb7..c0585a3661 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -111,6 +111,7 @@ unsigned int currrecvtime[MAXPLAYERS]; unsigned int lastglobalrecvtime; // Identify the last time a packet was recieved. bool hadlate; int netdelay[MAXNETNODES][BACKUPTICS]; // Used for storing network delay times. +int lastaverage; int nodeforplayer[MAXPLAYERS]; int playerfornode[MAXNETNODES]; @@ -1407,36 +1408,26 @@ void NetUpdate (void) int totalavg = 0; if (net_ticbalance) { - // We shouldn't adapt if we are already the slowest node, otherwise it just adds more latency - bool slow = true; - int nodeavg = 0; - for (i = 1; i < MAXNETNODES; i++) - { - if (!nodeingame[i]) - continue; + // We shouldn't adapt if we are already the slower then the arbitrator, otherwise it just adds more latency + int nodeavg = 0, arbavg = 0; - if (netdelay[i][0] > netdelay[0][0]) - { - slow = false; - break; - } + for (j = 0; j < BACKUPTICS; j++) + { + arbavg += netdelay[nodeforplayer[Net_Arbitrator]][j]; + nodeavg += netdelay[0][j]; } + arbavg /= BACKUPTICS; + nodeavg /= BACKUPTICS; - if (!slow) + if (arbavg > nodeavg) { - int totalnodes = 0; - for (i = 0; i < MAXNETNODES; i++) - { - if (!nodeingame[i]) - continue; - - totalnodes++; - nodeavg = 0; - for (j = 0; j < BACKUPTICS; j++) nodeavg += netdelay[i][j]; - totalavg += (nodeavg / BACKUPTICS); - } - - totalavg = (totalavg / totalnodes) - 1; + lastaverage = totalavg = ((arbavg + nodeavg) / 2); + } + else + { + if (nodeavg > (arbavg + 2) && lastaverage > 0) + lastaverage--; + totalavg = lastaverage; } } From e25b91d5a1abf72f172a311fac0ed19882f4a1a8 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Fri, 26 Sep 2014 16:11:52 +1200 Subject: [PATCH 24/47] Cleanup --- src/d_main.cpp | 4 ++-- src/d_net.cpp | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 8a8751041e..a24586bc4d 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -756,9 +756,9 @@ void D_Display () } screen->SetBlendingRect(viewwindowx, viewwindowy, viewwindowx + viewwidth, viewwindowy + viewheight); - //P_PredictPlayer(&players[consoleplayer]); + Renderer->RenderView(&players[consoleplayer]); - //P_UnPredictPlayer(); + if ((hw2d = screen->Begin2D(viewactive))) { // Redraw everything every frame when using 2D accel diff --git a/src/d_net.cpp b/src/d_net.cpp index c0585a3661..d33f47e38a 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -1402,13 +1402,10 @@ void NetUpdate (void) if (NetMode == NET_PeerToPeer) { - // Try to guess ahead the time it takes to send responses to the slowest node - // [ED850] It seems that there is a bias based on network adaption (which the arbitrator doesn't do), - // so I have set this up to assume one less tic, which appears to balance it out. int totalavg = 0; if (net_ticbalance) { - // We shouldn't adapt if we are already the slower then the arbitrator, otherwise it just adds more latency + // Try to guess ahead the time it takes to send responses to the slowest node int nodeavg = 0, arbavg = 0; for (j = 0; j < BACKUPTICS; j++) @@ -1419,12 +1416,14 @@ void NetUpdate (void) arbavg /= BACKUPTICS; nodeavg /= BACKUPTICS; + // We shouldn't adapt if we are already the arbitrator isn't what we are waiting for, otherwise it just adds more latency if (arbavg > nodeavg) { lastaverage = totalavg = ((arbavg + nodeavg) / 2); } else { + // Allow room to guess two tics ahead if (nodeavg > (arbavg + 2) && lastaverage > 0) lastaverage--; totalavg = lastaverage; From 422e83a1b91dd749288f1314689db08f722ce340 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 25 Sep 2014 23:12:25 -0500 Subject: [PATCH 25/47] - Added more A_Damage functions: - A_DamageTarget - A_DamageTracer -Both take a number for how much damage to deal and the damagetype to inflict. Negative numbers means healing. --- src/thingdef/thingdef_codeptr.cpp | 64 ++++++++++++++++++++++++++++--- wadsrc/static/actors/actor.txt | 2 + 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index aff2629e43..6c44fa79e8 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5007,13 +5007,67 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); - if (amount > 0) + if (amount > 0) + { + P_DamageMobj(self, self, self, amount, DamageType, DMG_NO_ARMOR); + } + else if (amount < 0) + { + amount = -amount; + P_GiveBody(self, amount); + } +} + +//=========================================================================== +// +// A_DamageTarget (int amount, str damagetype) +// Damages the target of the actor by the specified amount. Negative values heal. +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + + if (self->target != NULL) { - P_DamageMobj(self, self, self, amount, DamageType, DMG_NO_ARMOR); + + if (amount > 0) + { + P_DamageMobj(self->target, self, self, amount, DamageType, DMG_NO_ARMOR); + } + else if (amount < 0) + { + amount = -amount; + P_GiveBody(self->target, amount); + } } - else if (amount < 0) +} + +//=========================================================================== +// +// A_DamageTracer (int amount, str damagetype) +// Damages the tracer of the actor by the specified amount. Negative values heal. +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + + if (self->target != NULL) { - amount = -amount; - P_GiveBody(self, amount); + + if (amount > 0) + { + P_DamageMobj(self->tracer, self, self, amount, DamageType, DMG_NO_ARMOR); + } + else if (amount < 0) + { + amount = -amount; + P_GiveBody(self->tracer, amount); + } } } \ No newline at end of file diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 95d055afb8..448d83f097 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -305,6 +305,8 @@ ACTOR Actor native //: Thinker action native A_DropItem(class item, int dropamount = -1, int chance = 256); action native A_SetSpeed(float speed); action native A_DamageSelf(int amount, name damagetype = "none"); + action native A_DamageTarget(int amount, name damagetype = "none"); + action native A_DamageTracer(int amount, name damagetype = "none"); action native A_CheckSightOrRange(float distance, state label); action native A_CheckRange(float distance, state label); From 6586fa29fd67323978a15376fd91c5a3945938ea Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 25 Sep 2014 23:56:10 -0500 Subject: [PATCH 26/47] - Added FDARI's A_JumpIf CheckClass submission. - bool CheckClass(string classname, int ptr_select = aaptr_default, bool match_superclass = false) --- src/namedef.h | 1 + src/thingdef/thingdef_function.cpp | 76 ++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/src/namedef.h b/src/namedef.h index a4c8da6388..1e7fbd0d3a 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -298,6 +298,7 @@ xx(Abs) xx(ACS_NamedExecuteWithResult) xx(CallACS) xx(Sqrt) +xx(CheckClass) // Various actor names which are used internally xx(MapSpot) diff --git a/src/thingdef/thingdef_function.cpp b/src/thingdef/thingdef_function.cpp index 2858e9a86e..9bf80dcaaf 100644 --- a/src/thingdef/thingdef_function.cpp +++ b/src/thingdef/thingdef_function.cpp @@ -43,6 +43,8 @@ #include "tarray.h" #include "thingdef.h" #include "thingdef_exp.h" +#include "actor.h" +#include "actorptrselect.h" static TMap CreatorMap; @@ -185,3 +187,77 @@ public: GLOBALFUNCTION_ADDER(Sqrt); +//========================================================================== +// +// Function: checkclass +// +//========================================================================== + +class FxGlobalFunctionCall_CheckClass : public FxGlobalFunctionCall +{ + public: + GLOBALFUNCTION_DEFINE(CheckClass); + + FxExpression *Resolve(FCompileContext& ctx) + { + CHECKRESOLVED(); + + if (!ResolveArgs(ctx, 1, 3, false)) + return NULL; + + for (int i = ArgList->Size(); i > 1;) + { + if (!(*ArgList)[--i]->ValueType.isNumeric()) + { + ScriptPosition.Message(MSG_ERROR, "numeric value expected for parameter"); + delete this; + return NULL; + } + } + + switch ((*ArgList)[0]->ValueType.Type) + { + case VAL_Class: case VAL_Name:break; + default: + ScriptPosition.Message(MSG_ERROR, "actor class expected for parameter"); + delete this; + return NULL; + } + + ValueType = VAL_Float; + return this; + } + + ExpVal EvalExpression(AActor *self) + { + ExpVal ret; + ret.Type = VAL_Int; + + const PClass * checkclass; + { + ExpVal v = (*ArgList)[0]->EvalExpression(self); + checkclass = v.GetClass(); + if (!checkclass) + { + checkclass = PClass::FindClass(v.GetName()); + if (!checkclass) { ret.Int = 0; return ret; } + } + } + + bool match_superclass = false; + int pick_pointer = AAPTR_DEFAULT; + + switch (ArgList->Size()) + { + case 3: match_superclass = (*ArgList)[2]->EvalExpression(self).GetBool(); + case 2: pick_pointer = (*ArgList)[1]->EvalExpression(self).GetInt(); + } + + self = COPY_AAPTR(self, pick_pointer); + if (!self){ ret.Int = 0; return ret; } + ret.Int = match_superclass ? checkclass->IsAncestorOf(self->GetClass()) : checkclass == self->GetClass(); + return ret; + } + }; + +GLOBALFUNCTION_ADDER(CheckClass); \ No newline at end of file From 54ccf5d44d0facf13f6d1af8c5ef9531daafad1c Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Fri, 26 Sep 2014 10:52:11 +0200 Subject: [PATCH 27/47] - Fixed a possible uninitialized condition. In the function R_RebuildViewInterpolation, the pointer 'iview' was not initialized when the player or its camera were NULL, hence 'iview == NULL' was garbage. Also, the function FindPastViewer does not return NULL, hence the mentioned check is not needed at all. Just return early if the player camera does not exist. --- src/r_utility.cpp | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 19d446b5b5..585e3dcf3f 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -737,24 +737,20 @@ void R_ClearPastViewer (AActor *actor) void R_RebuildViewInterpolation(player_t *player) { - InterpolationViewer *iview; - if (NoInterpolateView) - { - if (player != NULL && player->camera != NULL) - { - iview = FindPastViewer(player->camera); - } + if (player == NULL || player->camera == NULL) + return; - if (iview == NULL) - return; + if (!NoInterpolateView) + return; + NoInterpolateView = false; - NoInterpolateView = false; - iview->oviewx = iview->nviewx; - iview->oviewy = iview->nviewy; - iview->oviewz = iview->nviewz; - iview->oviewpitch = iview->nviewpitch; - iview->oviewangle = iview->nviewangle; - } + InterpolationViewer *iview = FindPastViewer(player->camera); + + iview->oviewx = iview->nviewx; + iview->oviewy = iview->nviewy; + iview->oviewz = iview->nviewz; + iview->oviewpitch = iview->nviewpitch; + iview->oviewangle = iview->nviewangle; } //========================================================================== From 5b71ce6dcb631cbfa8b902e3c3eac658e22b1eb2 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Fri, 26 Sep 2014 11:48:20 -0500 Subject: [PATCH 28/47] - Added FDARI's A_JumpIfHealthLower patch. Now with pointer accessibility. --- src/thingdef/thingdef_codeptr.cpp | 12 ++++++++++-- wadsrc/static/actors/actor.txt | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 6c44fa79e8..608e1c605d 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -601,11 +601,19 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Jump) //========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfHealthLower) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_INT(health, 0); ACTION_PARAM_STATE(jump, 1); + ACTION_PARAM_INT(ptr_selector, 2); - if (self->health < health) ACTION_JUMP(jump); + AActor *measured; + + measured = COPY_AAPTR(self, ptr_selector); + + if (measured && measured->health < health) + { + ACTION_JUMP(jump); + } ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! } diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 448d83f097..d4c79c3de9 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -204,7 +204,7 @@ ACTOR Actor native //: Thinker action native A_CustomMissile(class missiletype, float spawnheight = 32, int spawnofs_xy = 0, float angle = 0, int flags = 0, float pitch = 0); action native A_CustomBulletAttack(float spread_xy, float spread_z, int numbullets, int damageperbullet, class pufftype = "BulletPuff", float range = 0, int flags = 0); action native A_CustomRailgun(int damage, int spawnofs_xy = 0, color color1 = "", color color2 = "", int flags = 0, bool aim = false, float maxdiff = 0, class pufftype = "BulletPuff", float spread_xy = 0, float spread_z = 0, float range = 0, int duration = 0, float sparsity = 1.0, float driftspeed = 1.0, class spawnclass = "none", float spawnofs_z = 0); - action native A_JumpIfHealthLower(int health, state label); + action native A_JumpIfHealthLower(int health, state label, int ptr_selector = AAPTR_DEFAULT); action native A_JumpIfCloser(float distance, state label); action native A_JumpIfTracerCloser(float distance, state label); action native A_JumpIfMasterCloser(float distance, state label); From 0735cb95502ccebc354295e2d38c506ba86afcc0 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 27 Sep 2014 00:10:31 -0500 Subject: [PATCH 29/47] - Updated many functions. - Added A_KillTarget(damagetype, int flags). - Added A_KillTracer(damagetype, int flags). - Added A_RemoveTarget. - Added A_RemoveTracer. A_Kill (Master/Target/Tracer/Children/Siblings): - KILS_FOILINVUL: foils invulnerability. - KILS_KILLMISSILES: destroys projectiles. Does not work on invulnerable projectiles without KILS_FOILINVUL, and doesn't work at all on missiles with NODAMAGE flag. - KILS_NOMONSTERS: actors that are monsters will not be killed. A_Damage (Self/Target/Master/Tracer/Children/Siblings): - DMSS_FOILINVUL: foils invulnerability. - DMSS_AFFECTARMOR: damages the actor's armor instead of bypassing it entirely. - DMSS_KILL: damages the actor by its remaining health (useful for modular DECORATE programming). - Added A_SpawnItemEx flags: - SXF_SETTARGET: sets the calling actor as the target. - SXF_SETTRACER: sets the calling actor as the tracer. Both of these functions take priority similar to SXF_SETMASTER over SXF_TRANSFERPOINTERS. --- src/thingdef/thingdef_codeptr.cpp | 363 +++++++++++++++++++++++++---- wadsrc/static/actors/actor.txt | 22 +- wadsrc/static/actors/constants.txt | 12 + 3 files changed, 342 insertions(+), 55 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 608e1c605d..c257a4f707 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1765,6 +1765,8 @@ enum SIX_Flags SIXF_TRANSFERSTENCILCOL = 1 << 17, SIXF_TRANSFERALPHA = 1 << 18, SIXF_TRANSFERRENDERSTYLE = 1 << 19, + SIXF_SETTARGET = 1 << 20, + SIXF_SETTRACER = 1 << 21, }; static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) @@ -1827,7 +1829,6 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) { // If this is a monster transfer all friendliness information mo->CopyFriendliness(originator, true); - if (flags & SIXF_SETMASTER) mo->master = originator; // don't let it attack you (optional)! } else if (originator->player) { @@ -1857,6 +1858,14 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) { mo->master = originator; } + if (flags & SIXF_SETTARGET) + { + mo->target = originator; + } + if (flags & SIXF_SETTRACER) + { + mo->tracer = originator; + } if (flags & SIXF_TRANSFERSCALE) { mo->scaleX = self->scaleX; @@ -2666,31 +2675,60 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIf) } +enum KILS +{ + KILS_FOILINVUL = 1 << 0, + KILS_KILLMISSILES = 1 << 1, + KILS_NOMONSTERS = 1 << 2, +}; + //=========================================================================== // -// A_KillMaster +// A_KillMaster(damagetype, int flags) // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) { - ACTION_PARAM_START(1); + ACTION_PARAM_START(2); ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); if (self->master != NULL) { - P_DamageMobj(self->master, self, self, self->master->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + if ((self->master->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) + { + //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! + //Check to see if it's invulnerable. Disregarded if foilinvul is on, but never works on a missile with NODAMAGE + //since that's the whole point of it. + if ((!(self->master->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(self->master->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(self->master, NULL, NULL); + } + } + if (!(flags & KILS_NOMONSTERS)) + { + if (flags & KILS_FOILINVUL) + { + P_DamageMobj(self->master, self, self, self->master->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); + } + else + { + P_DamageMobj(self->master, self, self, self->master->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + } + } } } //=========================================================================== // -// A_KillChildren +// A_KillChildren(damagetype, int flags) // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) { - ACTION_PARAM_START(1); + ACTION_PARAM_START(2); ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); TThinkerIterator it; AActor *mo; @@ -2699,20 +2737,38 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) { if (mo->master == self) { - P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + if ((mo->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) + { + if ((!(mo->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(mo->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(mo, NULL, NULL); + } + } + if (!(flags & KILS_NOMONSTERS)) + { + if (flags & KILS_FOILINVUL) + { + P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); + } + else + { + P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + } + } } } } //=========================================================================== // -// A_KillSiblings +// A_KillSiblings(damagetype, int flags) // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) { - ACTION_PARAM_START(1); + ACTION_PARAM_START(2); ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); TThinkerIterator it; AActor *mo; @@ -2723,7 +2779,24 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) { if (mo->master == self->master && mo != self) { - P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + if ((mo->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) + { + if ((!(mo->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(mo->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(mo, NULL, NULL); + } + } + if (!(flags & KILS_NOMONSTERS)) + { + if (flags & KILS_FOILINVUL) + { + P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); + } + else + { + P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + } + } } } } @@ -3537,24 +3610,47 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS) ACTION_JUMP(jump); } +enum DMSS +{ + DMSS_FOILINVUL = 1, + DMSS_AFFECTARMOR = 2, + DMSS_KILL = 4, +}; //=========================================================================== // -// A_DamageMaster (int amount, str damagetype) +// A_DamageMaster (int amount, str damagetype, int flags) // Damages the master of this child by the specified amount. Negative values heal. // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + if (self->master != NULL) { - if (amount > 0) + if ((amount > 0) || (flags & DMSS_KILL)) { - P_DamageMobj(self->master, self, self, amount, DamageType, DMG_NO_ARMOR); + if (!(self->master->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) + { + if (flags & DMSS_KILL) + { + P_DamageMobj(self->master, self, self, self->master->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); + } + if (flags & DMSS_AFFECTARMOR) + { + P_DamageMobj(self->master, self, self, amount, DamageType, DMG_FOILINVUL); + } + else + { + P_DamageMobj(self->master, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } + } } else if (amount < 0) { @@ -3566,7 +3662,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) //=========================================================================== // -// A_DamageChildren (amount, str damagetype) +// A_DamageChildren (amount, str damagetype, int flags) // Damages the children of this master by the specified amount. Negative values heal. // //=========================================================================== @@ -3575,17 +3671,32 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) TThinkerIterator it; AActor * mo; - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); while ( (mo = it.Next()) ) { if (mo->master == self) { - if (amount > 0) + if ((amount > 0) || (flags & DMSS_KILL)) //Bypass if kill flag is present; it won't matter. It intends to kill them. { - P_DamageMobj(mo, self, self, amount, DamageType, DMG_NO_ARMOR); + if (!(mo->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) + { + if (flags & DMSS_KILL) + { + P_DamageMobj(mo, self, self, mo->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); + } + if (flags & DMSS_AFFECTARMOR) + { + P_DamageMobj(mo, self, self, amount, DamageType, DMG_FOILINVUL); + } + else + { + P_DamageMobj(mo, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } + } } else if (amount < 0) { @@ -3600,7 +3711,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) //=========================================================================== // -// A_DamageSiblings (int amount, str damagetype) +// A_DamageSiblings (int amount, str damagetype, int flags) // Damages the siblings of this master by the specified amount. Negative values heal. // //=========================================================================== @@ -3609,24 +3720,39 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) TThinkerIterator it; AActor * mo; - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); if (self->master != NULL) { - while ( (mo = it.Next()) ) + while ((mo = it.Next())) { if (mo->master == self->master && mo != self) { - if (amount > 0) + if ((amount > 0) || (flags & DMSS_KILL)) { - P_DamageMobj(mo, self, self, amount, DamageType, DMG_NO_ARMOR); - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(mo, amount); + if (!(mo->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) + { + if (flags & DMSS_KILL) + { + P_DamageMobj(mo, self, self, mo->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); + } + if (flags & DMSS_AFFECTARMOR) + { + P_DamageMobj(mo, self, self, amount, DamageType, DMG_FOILINVUL); + } + else + { + P_DamageMobj(mo, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } + } + else if (amount < 0) + { + amount = -amount; + P_GiveBody(mo, amount); + } } } } @@ -5005,45 +5131,75 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) //=========================================================================== // -// A_DamageSelf (int amount, str damagetype) +// A_DamageSelf (int amount, str damagetype, int flags) // Damages the calling actor by the specified amount. Negative values heal. // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); - if (amount > 0) + if ((amount > 0) || (flags & DMSS_KILL)) + { + if (!(self->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) { - P_DamageMobj(self, self, self, amount, DamageType, DMG_NO_ARMOR); - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(self, amount); + if (flags & DMSS_KILL) + { + P_DamageMobj(self, self, self, self->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); + } + if (flags & DMSS_AFFECTARMOR) + { + P_DamageMobj(self, self, self, amount, DamageType, DMG_FOILINVUL); + } + else + { + //[MC] DMG_FOILINVUL is needed for making the damage occur on the actor. + P_DamageMobj(self, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } } + } + else if (amount < 0) + { + amount = -amount; + P_GiveBody(self, amount); + } } //=========================================================================== // -// A_DamageTarget (int amount, str damagetype) +// A_DamageTarget (int amount, str damagetype, int flags) // Damages the target of the actor by the specified amount. Negative values heal. // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); if (self->target != NULL) { - - if (amount > 0) + if ((amount > 0) || (flags & DMSS_KILL)) { - P_DamageMobj(self->target, self, self, amount, DamageType, DMG_NO_ARMOR); + if (!(self->target->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) + { + if (flags & DMSS_KILL) + { + P_DamageMobj(self->target, self, self, self->target->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); + } + if (flags & DMSS_AFFECTARMOR) + { + P_DamageMobj(self->target, self, self, amount, DamageType, DMG_FOILINVUL); + } + else + { + P_DamageMobj(self->target, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } + } } else if (amount < 0) { @@ -5055,22 +5211,37 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) //=========================================================================== // -// A_DamageTracer (int amount, str damagetype) +// A_DamageTracer (int amount, str damagetype, int flags) // Damages the tracer of the actor by the specified amount. Negative values heal. // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_INT(amount, 0); ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); if (self->target != NULL) { - if (amount > 0) + if ((amount > 0) || (flags & DMSS_KILL)) { - P_DamageMobj(self->tracer, self, self, amount, DamageType, DMG_NO_ARMOR); + if (!(self->tracer->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) + { + if (flags & DMSS_KILL) + { + P_DamageMobj(self->tracer, self, self, self->tracer->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); + } + if (flags & DMSS_AFFECTARMOR) + { + P_DamageMobj(self->tracer, self, self, amount, DamageType, DMG_FOILINVUL); + } + else + { + P_DamageMobj(self->tracer, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } + } } else if (amount < 0) { @@ -5078,4 +5249,104 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) P_GiveBody(self->tracer, amount); } } +} + +//=========================================================================== +// +// A_KillTarget(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + if (self->target != NULL) + { + if ((self->target->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) + { + //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! + //Check to see if it's invulnerable. Disregarded if foilinvul is on, but never works on a missile with NODAMAGE + //since that's the whole point of it. + if ((!(self->target->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(self->target->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(self->target, NULL, NULL); + } + } + if (!(flags & KILS_NOMONSTERS)) + { + if (flags & KILS_FOILINVUL) + { + P_DamageMobj(self->target, self, self, self->target->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); + } + else + { + P_DamageMobj(self->target, self, self, self->target->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + } + } + } +} + +//=========================================================================== +// +// A_KillTracer(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + if (self->tracer != NULL) + { + if ((self->tracer->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) + { + //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! + //Check to see if it's invulnerable. Disregarded if foilinvul is on, but never works on a missile with NODAMAGE + //since that's the whole point of it. + if ((!(self->tracer->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(self->tracer->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(self->tracer, NULL, NULL); + } + } + if (!(flags & KILS_NOMONSTERS)) + { + if (flags & KILS_FOILINVUL) + { + P_DamageMobj(self->tracer, self, self, self->tracer->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); + } + else + { + P_DamageMobj(self->tracer, self, self, self->tracer->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + } + } + } +} + +//=========================================================================== +// +// A_RemoveTarget +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) +{ + if (self->target != NULL) + { + P_RemoveThing(self->target); + } +} + +//=========================================================================== +// +// A_RemoveTracer +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RemoveTracer) +{ + if (self->tracer != NULL) + { + P_RemoveThing(self->tracer); + } } \ No newline at end of file diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index d4c79c3de9..52a0338fe5 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -237,9 +237,9 @@ ACTOR Actor native //: Thinker action native A_RemoveMaster(); action native A_RemoveChildren(bool removeall = false); action native A_RemoveSiblings(bool removeall = false); - action native A_KillMaster(name damagetype = "none"); - action native A_KillChildren(name damagetype = "none"); - action native A_KillSiblings(name damagetype = "none"); + action native A_KillMaster(name damagetype = "none", int flags = 0); + action native A_KillChildren(name damagetype = "none", int flags = 0); + action native A_KillSiblings(name damagetype = "none", int flags = 0); action native A_RaiseMaster(); action native A_RaiseChildren(); action native A_RaiseSiblings(); @@ -275,9 +275,9 @@ ACTOR Actor native //: Thinker action native A_CheckLOF(state jump, int flags = 0, float range = 0, float minrange = 0, float angle = 0, float pitch = 0, float offsetheight = 0, float offsetwidth = 0, int ptr_target = AAPTR_DEFAULT); action native A_JumpIfTargetInLOS (state label, float fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0); action native A_JumpIfInTargetLOS (state label, float fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0); - action native A_DamageMaster(int amount, name damagetype = "none"); - action native A_DamageChildren(int amount, name damagetype = "none"); - action native A_DamageSiblings(int amount, name damagetype = "none"); + action native A_DamageMaster(int amount, name damagetype = "none", int flags = 0); + action native A_DamageChildren(int amount, name damagetype = "none", int flags = 0); + action native A_DamageSiblings(int amount, name damagetype = "none", int flags = 0); action native A_SelectWeapon(class whichweapon); action native A_Punch(); action native A_Feathers(); @@ -304,9 +304,13 @@ ACTOR Actor native //: Thinker action native A_SetDamageType(name damagetype); action native A_DropItem(class item, int dropamount = -1, int chance = 256); action native A_SetSpeed(float speed); - action native A_DamageSelf(int amount, name damagetype = "none"); - action native A_DamageTarget(int amount, name damagetype = "none"); - action native A_DamageTracer(int amount, name damagetype = "none"); + action native A_DamageSelf(int amount, name damagetype = "none", int flags = 0); + action native A_DamageTarget(int amount, name damagetype = "none", int flags = 0); + action native A_DamageTracer(int amount, name damagetype = "none", int flags = 0); + action native A_KillTarget(name damagetype = "none", int flags = 0); + action native A_KillTracer(name damagetype = "none", int flags = 0); + action native A_RemoveTarget(); + action native A_RemoveTracer(); action native A_CheckSightOrRange(float distance, state label); action native A_CheckRange(float distance, state label); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index bf998e6b07..1f2df20ccd 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -67,6 +67,8 @@ const int SXF_CLEARCALLERSPECIAL = 65536; const int SXF_TRANSFERSTENCILCOL = 131072; const int SXF_TRANSFERALPHA = 262144; const int SXF_TRANSFERRENDERSTYLE = 524288; +const int SXF_SETTARGET = 1048576; +const int SXF_SETTRACER = 2097152; // Flags for A_Chase const int CHF_FASTCHASE = 1; @@ -360,6 +362,16 @@ enum CLOFF_NOAIM = CLOFF_NOAIM_VERT|CLOFF_NOAIM_HORZ }; +// Flags for A_Kill (Master/Target/Tracer/Children/Siblings) series + +const int KILS_FOILINVUL = 1; +const int KILS_KILLMISSILES = 2; +const int KILS_NOMONSTERS = 4; + +// Flags for A_Damage (Master/Target/Tracer/Children/Siblings/Self) series +const int DMSS_FOILINVUL = 1; +const int DMSS_AFFECTARMOR = 2; +const int DMSS_KILL = 4; // Flags for A_AlertMonsters const int AMF_TARGETEMITTER = 1; From afaa88a4604028d968041bcd708e4a42354a7ae7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 27 Sep 2014 08:48:36 +0200 Subject: [PATCH 30/47] - consolidated the common portion of the 6 different A_Damage* functions into a subfunction. --- src/thingdef/thingdef_codeptr.cpp | 335 ++++++++++-------------------- 1 file changed, 106 insertions(+), 229 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index c257a4f707..3893b3a1b8 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -3610,156 +3610,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS) ACTION_JUMP(jump); } -enum DMSS -{ - DMSS_FOILINVUL = 1, - DMSS_AFFECTARMOR = 2, - DMSS_KILL = 4, -}; - -//=========================================================================== -// -// A_DamageMaster (int amount, str damagetype, int flags) -// Damages the master of this child by the specified amount. Negative values heal. -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - - - if (self->master != NULL) - { - if ((amount > 0) || (flags & DMSS_KILL)) - { - if (!(self->master->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) - { - if (flags & DMSS_KILL) - { - P_DamageMobj(self->master, self, self, self->master->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); - } - if (flags & DMSS_AFFECTARMOR) - { - P_DamageMobj(self->master, self, self, amount, DamageType, DMG_FOILINVUL); - } - else - { - P_DamageMobj(self->master, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); - } - } - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(self->master, amount); - } - } -} - -//=========================================================================== -// -// A_DamageChildren (amount, str damagetype, int flags) -// Damages the children of this master by the specified amount. Negative values heal. -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) -{ - TThinkerIterator it; - AActor * mo; - - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - while ( (mo = it.Next()) ) - { - if (mo->master == self) - { - if ((amount > 0) || (flags & DMSS_KILL)) //Bypass if kill flag is present; it won't matter. It intends to kill them. - { - if (!(mo->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) - { - if (flags & DMSS_KILL) - { - P_DamageMobj(mo, self, self, mo->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); - } - if (flags & DMSS_AFFECTARMOR) - { - P_DamageMobj(mo, self, self, amount, DamageType, DMG_FOILINVUL); - } - else - { - P_DamageMobj(mo, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); - } - } - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(mo, amount); - } - } - } -} - -// [KS] *** End of my modifications *** - -//=========================================================================== -// -// A_DamageSiblings (int amount, str damagetype, int flags) -// Damages the siblings of this master by the specified amount. Negative values heal. -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) -{ - TThinkerIterator it; - AActor * mo; - - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - if (self->master != NULL) - { - while ((mo = it.Next())) - { - if (mo->master == self->master && mo != self) - { - if ((amount > 0) || (flags & DMSS_KILL)) - { - if (!(mo->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) - { - if (flags & DMSS_KILL) - { - P_DamageMobj(mo, self, self, mo->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); - } - if (flags & DMSS_AFFECTARMOR) - { - P_DamageMobj(mo, self, self, amount, DamageType, DMG_FOILINVUL); - } - else - { - P_DamageMobj(mo, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); - } - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(mo, amount); - } - } - } - } - } -} - - //=========================================================================== // // Modified code pointer from Skulltag @@ -5131,8 +4981,51 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) //=========================================================================== // -// A_DamageSelf (int amount, str damagetype, int flags) -// Damages the calling actor by the specified amount. Negative values heal. +// Common A_Damage handler +// +// A_Damage* (int amount, str damagetype, int flags) +// Damages the specified actor by the specified amount. Negative values heal. +// +//=========================================================================== + +enum DMSS +{ + DMSS_FOILINVUL = 1, + DMSS_AFFECTARMOR = 2, + DMSS_KILL = 4, +}; + +static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags) +{ + if ((amount > 0) || (flags & DMSS_KILL)) + { + if (!(dmgtarget->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) + { + if (flags & DMSS_KILL) + { + P_DamageMobj(dmgtarget, self, self, dmgtarget->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); + } + if (flags & DMSS_AFFECTARMOR) + { + P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL); + } + else + { + //[MC] DMG_FOILINVUL is needed for making the damage occur on the actor. + P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } + } + } + else if (amount < 0) + { + amount = -amount; + P_GiveBody(dmgtarget, amount); + } +} + +//=========================================================================== +// +// // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) @@ -5142,36 +5035,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); - if ((amount > 0) || (flags & DMSS_KILL)) - { - if (!(self->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) - { - if (flags & DMSS_KILL) - { - P_DamageMobj(self, self, self, self->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); - } - if (flags & DMSS_AFFECTARMOR) - { - P_DamageMobj(self, self, self, amount, DamageType, DMG_FOILINVUL); - } - else - { - //[MC] DMG_FOILINVUL is needed for making the damage occur on the actor. - P_DamageMobj(self, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); - } - } - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(self, amount); - } + DoDamage(self, self, amount, DamageType, flags); } //=========================================================================== // -// A_DamageTarget (int amount, str damagetype, int flags) -// Damages the target of the actor by the specified amount. Negative values heal. +// // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) @@ -5181,38 +5050,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); - if (self->target != NULL) - { - if ((amount > 0) || (flags & DMSS_KILL)) - { - if (!(self->target->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) - { - if (flags & DMSS_KILL) - { - P_DamageMobj(self->target, self, self, self->target->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); - } - if (flags & DMSS_AFFECTARMOR) - { - P_DamageMobj(self->target, self, self, amount, DamageType, DMG_FOILINVUL); - } - else - { - P_DamageMobj(self->target, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); - } - } - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(self->target, amount); - } - } + if (self->target != NULL) DoDamage(self->target, self, amount, DamageType, flags); } //=========================================================================== // -// A_DamageTracer (int amount, str damagetype, int flags) -// Damages the tracer of the actor by the specified amount. Negative values heal. +// // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) @@ -5222,31 +5065,65 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) ACTION_PARAM_NAME(DamageType, 1); ACTION_PARAM_INT(flags, 2); - if (self->target != NULL) - { + if (self->tracer != NULL) DoDamage(self->tracer, self, amount, DamageType, flags); +} - if ((amount > 0) || (flags & DMSS_KILL)) +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + if (self->master != NULL) DoDamage(self->master, self, amount, DamageType, flags); +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + TThinkerIterator it; + AActor * mo; + + while ( (mo = it.Next()) ) + { + if (mo->master == self) DoDamage(mo, self, amount, DamageType, flags); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + TThinkerIterator it; + AActor * mo; + + if (self->master != NULL) + { + while ((mo = it.Next())) { - if (!(self->tracer->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) - { - if (flags & DMSS_KILL) - { - P_DamageMobj(self->tracer, self, self, self->tracer->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); - } - if (flags & DMSS_AFFECTARMOR) - { - P_DamageMobj(self->tracer, self, self, amount, DamageType, DMG_FOILINVUL); - } - else - { - P_DamageMobj(self->tracer, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); - } - } - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(self->tracer, amount); + if (mo->master == self->master && mo != self) DoDamage(mo, self, amount, DamageType, flags); } } } From e025f4090221746fc35db4065a2d1455117d737b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 27 Sep 2014 08:54:18 +0200 Subject: [PATCH 31/47] - more redundancy removal: Consolidated the common part of the A_Kill* functions into a subfunction. --- src/thingdef/thingdef_codeptr.cpp | 267 +++++++++++------------------- 1 file changed, 96 insertions(+), 171 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 3893b3a1b8..7c26535805 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -2675,133 +2675,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIf) } -enum KILS -{ - KILS_FOILINVUL = 1 << 0, - KILS_KILLMISSILES = 1 << 1, - KILS_NOMONSTERS = 1 << 2, -}; - -//=========================================================================== -// -// A_KillMaster(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - - if (self->master != NULL) - { - if ((self->master->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) - { - //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! - //Check to see if it's invulnerable. Disregarded if foilinvul is on, but never works on a missile with NODAMAGE - //since that's the whole point of it. - if ((!(self->master->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(self->master->flags5 & MF5_NODAMAGE)) - { - P_ExplodeMissile(self->master, NULL, NULL); - } - } - if (!(flags & KILS_NOMONSTERS)) - { - if (flags & KILS_FOILINVUL) - { - P_DamageMobj(self->master, self, self, self->master->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); - } - else - { - P_DamageMobj(self->master, self, self, self->master->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); - } - } - } -} - -//=========================================================================== -// -// A_KillChildren(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - - TThinkerIterator it; - AActor *mo; - - while ( (mo = it.Next()) ) - { - if (mo->master == self) - { - if ((mo->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) - { - if ((!(mo->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(mo->flags5 & MF5_NODAMAGE)) - { - P_ExplodeMissile(mo, NULL, NULL); - } - } - if (!(flags & KILS_NOMONSTERS)) - { - if (flags & KILS_FOILINVUL) - { - P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); - } - else - { - P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); - } - } - } - } -} - -//=========================================================================== -// -// A_KillSiblings(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - - TThinkerIterator it; - AActor *mo; - - if (self->master != NULL) - { - while ( (mo = it.Next()) ) - { - if (mo->master == self->master && mo != self) - { - if ((mo->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) - { - if ((!(mo->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(mo->flags5 & MF5_NODAMAGE)) - { - P_ExplodeMissile(mo, NULL, NULL); - } - } - if (!(flags & KILS_NOMONSTERS)) - { - if (flags & KILS_FOILINVUL) - { - P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); - } - else - { - P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); - } - } - } - } - } -} - //=========================================================================== // // A_CountdownArg @@ -5128,6 +5001,46 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) } } + +//=========================================================================== +// +// A_Kill*(damagetype, int flags) +// +//=========================================================================== +enum KILS +{ + KILS_FOILINVUL = 1 << 0, + KILS_KILLMISSILES = 1 << 1, + KILS_NOMONSTERS = 1 << 2, +}; + +static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags) +{ + if ((killtarget->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) + { + //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! + //Check to see if it's invulnerable. Disregarded if foilinvul is on, but never works on a missile with NODAMAGE + //since that's the whole point of it. + if ((!(killtarget->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(killtarget->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(self->target, NULL, NULL); + } + } + if (!(flags & KILS_NOMONSTERS)) + { + if (flags & KILS_FOILINVUL) + { + P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); + } + else + { + P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + } + } +} + + + //=========================================================================== // // A_KillTarget(damagetype, int flags) @@ -5139,30 +5052,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) ACTION_PARAM_NAME(damagetype, 0); ACTION_PARAM_INT(flags, 1); - if (self->target != NULL) - { - if ((self->target->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) - { - //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! - //Check to see if it's invulnerable. Disregarded if foilinvul is on, but never works on a missile with NODAMAGE - //since that's the whole point of it. - if ((!(self->target->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(self->target->flags5 & MF5_NODAMAGE)) - { - P_ExplodeMissile(self->target, NULL, NULL); - } - } - if (!(flags & KILS_NOMONSTERS)) - { - if (flags & KILS_FOILINVUL) - { - P_DamageMobj(self->target, self, self, self->target->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); - } - else - { - P_DamageMobj(self->target, self, self, self->target->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); - } - } - } + if (self->target != NULL) DoKill(self->target, self, damagetype, flags); } //=========================================================================== @@ -5176,32 +5066,67 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) ACTION_PARAM_NAME(damagetype, 0); ACTION_PARAM_INT(flags, 1); - if (self->tracer != NULL) + if (self->tracer != NULL) DoKill(self->tracer, self, damagetype, flags); +} + +//=========================================================================== +// +// A_KillMaster(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + if (self->master != NULL) DoKill(self->master, self, damagetype, flags); +} + +//=========================================================================== +// +// A_KillChildren(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + TThinkerIterator it; + AActor *mo; + + while ( (mo = it.Next()) ) { - if ((self->tracer->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) + if (mo->master == self) DoKill(mo, self, damagetype, flags); + } +} + +//=========================================================================== +// +// A_KillSiblings(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + TThinkerIterator it; + AActor *mo; + + if (self->master != NULL) + { + while ( (mo = it.Next()) ) { - //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! - //Check to see if it's invulnerable. Disregarded if foilinvul is on, but never works on a missile with NODAMAGE - //since that's the whole point of it. - if ((!(self->tracer->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(self->tracer->flags5 & MF5_NODAMAGE)) - { - P_ExplodeMissile(self->tracer, NULL, NULL); - } - } - if (!(flags & KILS_NOMONSTERS)) - { - if (flags & KILS_FOILINVUL) - { - P_DamageMobj(self->tracer, self, self, self->tracer->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); - } - else - { - P_DamageMobj(self->tracer, self, self, self->tracer->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); - } + if (mo->master == self->master && mo != self) DoKill(mo, self, damagetype, flags); } } } + //=========================================================================== // // A_RemoveTarget From 68c481945ad95098390b75153ea14b83e13c2cb7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 27 Sep 2014 09:36:38 +0200 Subject: [PATCH 32/47] - extended parameter list of A_BFGSpray. --- src/g_doom/a_doomweaps.cpp | 29 ++++++++++++++++++++++------- wadsrc/static/actors/actor.txt | 2 +- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index 424e691e72..38c823e181 100644 --- a/src/g_doom/a_doomweaps.cpp +++ b/src/g_doom/a_doomweaps.cpp @@ -546,6 +546,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireBFG) P_SpawnPlayerMissile (self, 0, 0, 0, PClass::FindClass("BFGBall"), self->angle, NULL, NULL, !!(dmflags2 & DF2_NO_FREEAIMBFG)); } + // // A_BFGSpray // Spawn a BFG explosion on every monster in view @@ -559,14 +560,21 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray) AActor *thingToHit; AActor *linetarget; - ACTION_PARAM_START(3); + ACTION_PARAM_START(7); ACTION_PARAM_CLASS(spraytype, 0); ACTION_PARAM_INT(numrays, 1); ACTION_PARAM_INT(damagecnt, 2); + ACTION_PARAM_ANGLE(angle, 3); + ACTION_PARAM_FIXED(distance, 4); + ACTION_PARAM_ANGLE(vrange, 5); + ACTION_PARAM_INT(defdamage, 6); if (spraytype == NULL) spraytype = PClass::FindClass("BFGExtra"); if (numrays <= 0) numrays = 40; if (damagecnt <= 0) damagecnt = 15; + if (angle == 0) angle = ANG90; + if (distance <= 0) distance = 16 * 64 * FRACUNIT; + if (vrange == 0) vrange = ANGLE_1 * 32; // [RH] Don't crash if no target if (!self->target) @@ -575,10 +583,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray) // offset angles from its attack angle for (i = 0; i < numrays; i++) { - an = self->angle - ANG90/2 + ANG90/numrays*i; + an = self->angle - angle/2 + angle/numrays*i; // self->target is the originator (player) of the missile - P_AimLineAttack (self->target, an, 16*64*FRACUNIT, &linetarget, ANGLE_1*32); + P_AimLineAttack (self->target, an, distance, &linetarget, vrange); if (!linetarget) continue; @@ -589,10 +597,17 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray) if (spray && (spray->flags5 & MF5_PUFFGETSOWNER)) spray->target = self->target; - - damage = 0; - for (j = 0; j < damagecnt; ++j) - damage += (pr_bfgspray() & 7) + 1; + if (defdamage == 0) + { + damage = 0; + for (j = 0; j < damagecnt; ++j) + damage += (pr_bfgspray() & 7) + 1; + } + else + { + // if this is used, damagecnt will be ignored + damage = defdamage; + } thingToHit = linetarget; int newdam = P_DamageMobj (thingToHit, self->target, self->target, damage, spray != NULL? FName(spray->DamageType) : FName(NAME_BFGSplash), diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 52a0338fe5..d404f23a2d 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -70,7 +70,7 @@ ACTOR Actor native //: Thinker // End of MBF redundant functions. action native A_MonsterRail(); - action native A_BFGSpray(class spraytype = "BFGExtra", int numrays = 40, int damagecount = 15); + action native A_BFGSpray(class spraytype = "BFGExtra", int numrays = 40, int damagecount = 15, float angle = 90, float distance = 16*64, float vrange = 32, int damage = 0); action native A_Pain(); action native A_NoBlocking(); action native A_XScream(); From 68a5db3c8c429736c001d412c6a90703a2aa2288 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 27 Sep 2014 13:22:14 -0500 Subject: [PATCH 33/47] - Added SXF_NOPOINTERS for A_SpawnItemEx. - Added WARPF_ABSOLUTEPOSITION for A_Warp. --- src/thingdef/thingdef_codeptr.cpp | 112 +++++++++++++++++------------ wadsrc/static/actors/constants.txt | 48 +++++++------ 2 files changed, 92 insertions(+), 68 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 7c26535805..297cb81ec2 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1767,6 +1767,7 @@ enum SIX_Flags SIXF_TRANSFERRENDERSTYLE = 1 << 19, SIXF_SETTARGET = 1 << 20, SIXF_SETTRACER = 1 << 21, + SIXF_NOPOINTERS = 1 << 22, }; static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) @@ -1854,6 +1855,13 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) // If this is a missile or something else set the target to the originator mo->target = originator ? originator : self; } + if (flags & SIXF_NOPOINTERS) + { + //[MC]Intentionally eliminate pointers. Overrides TRANSFERPOINTERS, but is overridden by SETMASTER/TARGET/TRACER. + mo->target = NULL; + mo->master = NULL; + mo->tracer = NULL; + } if (flags & SIXF_SETMASTER) { mo->master = originator; @@ -4378,7 +4386,8 @@ enum WARPF WARPF_STOP = 0x80, WARPF_TOFLOOR = 0x100, - WARPF_TESTONLY = 0x200 + WARPF_TESTONLY = 0x200, + WARPF_ABSOLUTEPOSITION = 0x400, }; DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) @@ -4411,59 +4420,72 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) { angle += (flags & WARPF_USECALLERANGLE) ? self->angle : reference->angle; } - - if (!(flags & WARPF_ABSOLUTEOFFSET)) + if (!(flags & WARPF_ABSOLUTEPOSITION)) { - angle_t fineangle = angle>>ANGLETOFINESHIFT; - oldx = xofs; - - // (borrowed from A_SpawnItemEx, assumed workable) - // in relative mode negative y values mean 'left' and positive ones mean 'right' - // This is the inverse orientation of the absolute mode! - - xofs = FixedMul(oldx, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); - yofs = FixedMul(oldx, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); - } - - oldx = self->x; - oldy = self->y; - oldz = self->z; - - if (flags & WARPF_TOFLOOR) - { - // set correct xy - - self->SetOrigin( - reference->x + xofs, - reference->y + yofs, - reference->z); - - // now the caller's floorz should be appropriate for the assigned xy-position - // assigning position again with - - if (zofs) + if (!(flags & WARPF_ABSOLUTEOFFSET)) { - // extra unlink, link and environment calculation + angle_t fineangle = angle >> ANGLETOFINESHIFT; + oldx = xofs; + + // (borrowed from A_SpawnItemEx, assumed workable) + // in relative mode negative y values mean 'left' and positive ones mean 'right' + // This is the inverse orientation of the absolute mode! + + xofs = FixedMul(oldx, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); + yofs = FixedMul(oldx, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); + } + + oldx = self->x; + oldy = self->y; + oldz = self->z; + + if (flags & WARPF_TOFLOOR) + { + // set correct xy + self->SetOrigin( - self->x, - self->y, - self->floorz + zofs); + reference->x + xofs, + reference->y + yofs, + reference->z); + + // now the caller's floorz should be appropriate for the assigned xy-position + // assigning position again with + + if (zofs) + { + // extra unlink, link and environment calculation + self->SetOrigin( + self->x, + self->y, + self->floorz + zofs); + } + else + { + // if there is no offset, there should be no ill effect from moving down to the + // already identified floor + + // A_Teleport does the same thing anyway + self->z = self->floorz; + } } else { - // if there is no offset, there should be no ill effect from moving down to the - // already identified floor - - // A_Teleport does the same thing anyway - self->z = self->floorz; + self->SetOrigin( + reference->x + xofs, + reference->y + yofs, + reference->z + zofs); } } - else + else //[MC] The idea behind "absolute" is meant to be "absolute". Override everything, just like A_SpawnItemEx's. { - self->SetOrigin( - reference->x + xofs, - reference->y + yofs, - reference->z + zofs); + if (flags & WARPF_TOFLOOR) + { + self->SetOrigin(xofs, yofs, self->floorz + zofs); + } + else + { + self->SetOrigin(xofs, yofs, zofs); + } } if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(self)) diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 1f2df20ccd..3419561f8b 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -46,29 +46,30 @@ const int FBF_NOFLASH = 16; const int FBF_NORANDOMPUFFZ = 32; // Flags for A_SpawnItemEx -const int SXF_TRANSFERTRANSLATION = 1; -const int SXF_ABSOLUTEPOSITION = 2; -const int SXF_ABSOLUTEANGLE = 4; -const int SXF_ABSOLUTEMOMENTUM = 8; -const int SXF_ABSOLUTEVELOCITY = 8; -const int SXF_SETMASTER = 16; -const int SXF_NOCHECKPOSITION = 32; -const int SXF_TELEFRAG = 64; -const int SXF_CLIENTSIDE = 128; // only used by Skulltag -const int SXF_TRANSFERAMBUSHFLAG = 256; -const int SXF_TRANSFERPITCH = 512; -const int SXF_TRANSFERPOINTERS = 1024; -const int SXF_USEBLOODCOLOR = 2048; -const int SXF_CLEARCALLERTID = 4096; -const int SXF_MULTIPLYSPEED = 8192; -const int SXF_TRANSFERSCALE = 16384; -const int SXF_TRANSFERSPECIAL = 32768; -const int SXF_CLEARCALLERSPECIAL = 65536; -const int SXF_TRANSFERSTENCILCOL = 131072; -const int SXF_TRANSFERALPHA = 262144; -const int SXF_TRANSFERRENDERSTYLE = 524288; -const int SXF_SETTARGET = 1048576; -const int SXF_SETTRACER = 2097152; +const int SXF_TRANSFERTRANSLATION = 1 << 0; +const int SXF_ABSOLUTEPOSITION = 1 << 1; +const int SXF_ABSOLUTEANGLE = 1 << 2; +const int SXF_ABSOLUTEMOMENTUM = 1 << 3; //Since "momentum" is declared to be deprecated in the expressions, for compatibility +const int SXF_ABSOLUTEVELOCITY = 1 << 3; //purposes, this was made. It does the same thing though. Do not change the value. +const int SXF_SETMASTER = 1 << 4; +const int SXF_NOCHECKPOSITION = 1 << 5; +const int SXF_TELEFRAG = 1 << 6; +const int SXF_CLIENTSIDE = 1 << 7; // only used by Skulltag +const int SXF_TRANSFERAMBUSHFLAG = 1 << 8; +const int SXF_TRANSFERPITCH = 1 << 9; +const int SXF_TRANSFERPOINTERS = 1 << 10; +const int SXF_USEBLOODCOLOR = 1 << 11; +const int SXF_CLEARCALLERTID = 1 << 12; +const int SXF_MULTIPLYSPEED = 1 << 13; +const int SXF_TRANSFERSCALE = 1 << 14; +const int SXF_TRANSFERSPECIAL = 1 << 15; +const int SXF_CLEARCALLERSPECIAL = 1 << 16; +const int SXF_TRANSFERSTENCILCOL = 1 << 17; +const int SXF_TRANSFERALPHA = 1 << 18; +const int SXF_TRANSFERRENDERSTYLE = 1 << 19; +const int SXF_SETTARGET = 1 << 20; +const int SXF_SETTRACER = 1 << 21; +const int SXF_NOPOINTERS = 1 << 22; // Flags for A_Chase const int CHF_FASTCHASE = 1; @@ -316,6 +317,7 @@ Const Int WARPF_COPYINTERPOLATION = 0x40; Const Int WARPF_STOP = 0x80; Const Int WARPF_TOFLOOR = 0x100; Const Int WARPF_TESTONLY = 0x200; +Const Int WAPRF_ABSOLUTEPOSITION = 0x400; // flags for A_SetPitch/SetAngle const int SPF_FORCECLAMP = 1; From 97d5d614c481150b20d3c874b59024f2af20cca7 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 27 Sep 2014 16:50:24 -0500 Subject: [PATCH 34/47] - Fixed: SXF_NOPOINTERS didn't clear LastHeard, causing monsters to spawn with targets. --- .gitattributes | 63 + src/thingdef/thingdef_codeptr.cpp | 10351 ++++++++++++++-------------- 2 files changed, 5239 insertions(+), 5175 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..1ff0c42304 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 297cb81ec2..4266c01c76 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1,5176 +1,5177 @@ -/* -** thingdef.cpp -** -** Code pointers for Actor definitions -** -**--------------------------------------------------------------------------- -** Copyright 2002-2006 Christoph Oelckers -** Copyright 2004-2006 Randy Heit -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be -** covered by the terms of the GNU General Public License as published by -** the Free Software Foundation; either version 2 of the License, or (at -** your option) any later version. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -#include "gi.h" -#include "g_level.h" -#include "actor.h" -#include "info.h" -#include "sc_man.h" -#include "tarray.h" -#include "w_wad.h" -#include "templates.h" -#include "r_defs.h" -#include "a_pickups.h" -#include "s_sound.h" -#include "cmdlib.h" -#include "p_lnspec.h" -#include "p_enemy.h" -#include "a_action.h" -#include "decallib.h" -#include "m_random.h" -#include "i_system.h" -#include "p_local.h" -#include "c_console.h" -#include "doomerrors.h" -#include "a_sharedglobal.h" -#include "thingdef/thingdef.h" -#include "v_video.h" -#include "v_font.h" -#include "doomstat.h" -#include "v_palette.h" -#include "g_shared/a_specialspot.h" -#include "actorptrselect.h" -#include "m_bbox.h" -#include "r_data/r_translate.h" -#include "p_trace.h" -#include "gstrings.h" - - -static FRandom pr_camissile ("CustomActorfire"); -static FRandom pr_camelee ("CustomMelee"); -static FRandom pr_cabullet ("CustomBullet"); -static FRandom pr_cajump ("CustomJump"); -static FRandom pr_cwbullet ("CustomWpBullet"); -static FRandom pr_cwjump ("CustomWpJump"); -static FRandom pr_cwpunch ("CustomWpPunch"); -static FRandom pr_grenade ("ThrowGrenade"); -static 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"); - -//========================================================================== -// -// ACustomInventory :: CallStateChain -// -// Executes the code pointers in a chain of states -// until there is no next state -// -//========================================================================== - -bool ACustomInventory::CallStateChain (AActor *actor, FState * State) -{ - StateCallData StateCall; - bool result = false; - int counter = 0; - - while (State != NULL) - { - // Assume success. The code pointer will set this to false if necessary - StateCall.State = State; - StateCall.Result = true; - if (State->CallAction(actor, this, &StateCall)) - { - // collect all the results. Even one successful call signifies overall success. - result |= StateCall.Result; - } - - - // Since there are no delays it is a good idea to check for infinite loops here! - counter++; - if (counter >= 10000) break; - - if (StateCall.State == State) - { - // Abort immediately if the state jumps to itself! - if (State == State->GetNextState()) - { - return false; - } - - // If both variables are still the same there was no jump - // so we must advance to the next state. - State = State->GetNextState(); - } - else - { - State = StateCall.State; - } - } - return result; -} - -//========================================================================== -// -// A_RearrangePointers -// -// Allow an actor to change its relationship to other actors by -// copying pointers freely between TARGET MASTER and TRACER. -// Can also assign null value, but does not duplicate A_ClearTarget. -// -//========================================================================== - - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RearrangePointers) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_INT(ptr_target, 0); - ACTION_PARAM_INT(ptr_master, 1); - ACTION_PARAM_INT(ptr_tracer, 2); - ACTION_PARAM_INT(flags, 3); - - // Rearrange pointers internally - - // Fetch all values before modification, so that all fields can get original values - AActor - *gettarget = self->target, - *getmaster = self->master, - *gettracer = self->tracer; - - switch (ptr_target) // pick the new target - { - case AAPTR_MASTER: - self->target = getmaster; - if (!(PTROP_UNSAFETARGET & flags)) VerifyTargetChain(self); - break; - case AAPTR_TRACER: - self->target = gettracer; - if (!(PTROP_UNSAFETARGET & flags)) VerifyTargetChain(self); - break; - case AAPTR_NULL: - self->target = NULL; - // THIS IS NOT "A_ClearTarget", so no other targeting info is removed - break; - } - - // presently permitting non-monsters to set master - switch (ptr_master) // pick the new master - { - case AAPTR_TARGET: - self->master = gettarget; - if (!(PTROP_UNSAFEMASTER & flags)) VerifyMasterChain(self); - break; - case AAPTR_TRACER: - self->master = gettracer; - if (!(PTROP_UNSAFEMASTER & flags)) VerifyMasterChain(self); - break; - case AAPTR_NULL: - self->master = NULL; - break; - } - - switch (ptr_tracer) // pick the new tracer - { - case AAPTR_TARGET: - self->tracer = gettarget; - break; // no verification deemed necessary; the engine never follows a tracer chain(?) - case AAPTR_MASTER: - self->tracer = getmaster; - break; // no verification deemed necessary; the engine never follows a tracer chain(?) - case AAPTR_NULL: - self->tracer = NULL; - break; - } -} - -//========================================================================== -// -// A_TransferPointer -// -// Copy one pointer (MASTER, TARGET or TRACER) from this actor (SELF), -// or from this actor's MASTER, TARGET or TRACER. -// -// You can copy any one of that actor's pointers -// -// Assign the copied pointer to any one pointer in SELF, -// MASTER, TARGET or TRACER. -// -// Any attempt to make an actor point to itself will replace the pointer -// with a null value. -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TransferPointer) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(ptr_source, 0); - ACTION_PARAM_INT(ptr_recepient, 1); - ACTION_PARAM_INT(ptr_sourcefield, 2); - ACTION_PARAM_INT(ptr_recepientfield, 3); - ACTION_PARAM_INT(flags, 4); - - AActor *source, *recepient; - - // Exchange pointers with actors to whom you have pointers (or with yourself, if you must) - - source = COPY_AAPTR(self, ptr_source); - COPY_AAPTR_NOT_NULL(self, recepient, ptr_recepient); // pick an actor to store the provided pointer value - - // convert source from dataprovider to data - - source = COPY_AAPTR(source, ptr_sourcefield); - - if (source == recepient) source = NULL; // The recepient should not acquire a pointer to itself; will write NULL - - if (ptr_recepientfield == AAPTR_DEFAULT) ptr_recepientfield = ptr_sourcefield; // If default: Write to same field as data was read from - - ASSIGN_AAPTR(recepient, ptr_recepientfield, source, flags); -} - -//========================================================================== -// -// A_CopyFriendliness -// -// Join forces with one of the actors you are pointing to (MASTER by default) -// -// Normal CopyFriendliness reassigns health. This function will not. -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CopyFriendliness) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(ptr_source, 0); - - if (self->player) return; - - AActor *source; - COPY_AAPTR_NOT_NULL(self, source, ptr_source); - self->CopyFriendliness(source, false, false); // No change in current target or health -} - -//========================================================================== -// -// Simple flag changers -// -//========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_SetSolid) -{ - self->flags |= MF_SOLID; -} - -DEFINE_ACTION_FUNCTION(AActor, A_UnsetSolid) -{ - self->flags &= ~MF_SOLID; -} - -DEFINE_ACTION_FUNCTION(AActor, A_SetFloat) -{ - self->flags |= MF_FLOAT; -} - -DEFINE_ACTION_FUNCTION(AActor, A_UnsetFloat) -{ - self->flags &= ~(MF_FLOAT|MF_INFLOAT); -} - -//========================================================================== -// -// Customizable attack functions which use actor parameters. -// -//========================================================================== -static void DoAttack (AActor *self, bool domelee, bool domissile, - int MeleeDamage, FSoundID MeleeSound, const PClass *MissileType,fixed_t MissileHeight) -{ - if (self->target == NULL) return; - - A_FaceTarget (self); - if (domelee && MeleeDamage>0 && self->CheckMeleeRange ()) - { - int damage = pr_camelee.HitDice(MeleeDamage); - if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); - int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee); - P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - } - else if (domissile && MissileType != NULL) - { - // This seemingly senseless code is needed for proper aiming. - self->z += MissileHeight + self->GetBobOffset() - 32*FRACUNIT; - AActor *missile = P_SpawnMissileXYZ (self->x, self->y, self->z + 32*FRACUNIT, self, self->target, MissileType, false); - self->z -= MissileHeight + self->GetBobOffset() - 32*FRACUNIT; - - if (missile) - { - // automatic handling of seeker missiles - if (missile->flags2&MF2_SEEKERMISSILE) - { - missile->tracer=self->target; - } - P_CheckMissileSpawn(missile, self->radius); - } - } -} - -DEFINE_ACTION_FUNCTION(AActor, A_MeleeAttack) -{ - int MeleeDamage = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeDamage, 0); - FSoundID MeleeSound = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeSound, 0); - DoAttack(self, true, false, MeleeDamage, MeleeSound, NULL, 0); -} - -DEFINE_ACTION_FUNCTION(AActor, A_MissileAttack) -{ - const PClass *MissileType=PClass::FindClass((ENamedName) self->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None)); - fixed_t MissileHeight= self->GetClass()->Meta.GetMetaFixed (ACMETA_MissileHeight, 32*FRACUNIT); - DoAttack(self, false, true, 0, 0, MissileType, MissileHeight); -} - -DEFINE_ACTION_FUNCTION(AActor, A_ComboAttack) -{ - int MeleeDamage = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeDamage, 0); - FSoundID MeleeSound = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeSound, 0); - const PClass *MissileType=PClass::FindClass((ENamedName) self->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None)); - fixed_t MissileHeight= self->GetClass()->Meta.GetMetaFixed (ACMETA_MissileHeight, 32*FRACUNIT); - DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BasicAttack) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_INT(MeleeDamage, 0); - ACTION_PARAM_SOUND(MeleeSound, 1); - ACTION_PARAM_CLASS(MissileType, 2); - ACTION_PARAM_FIXED(MissileHeight, 3); - - if (MissileType == NULL) return; - DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); -} - -//========================================================================== -// -// Custom sound functions. -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlaySound) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_SOUND(soundid, 0); - ACTION_PARAM_INT(channel, 1); - ACTION_PARAM_FLOAT(volume, 2); - ACTION_PARAM_BOOL(looping, 3); - ACTION_PARAM_FLOAT(attenuation, 4); - - if (!looping) - { - S_Sound (self, channel, soundid, volume, attenuation); - } - else - { - if (!S_IsActorPlayingSomething (self, channel&7, soundid)) - { - S_Sound (self, channel | CHAN_LOOP, soundid, volume, attenuation); - } - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StopSound) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(slot, 0); - - S_StopSound(self, slot); -} - -//========================================================================== -// -// These come from a time when DECORATE constants did not exist yet and -// the sound interface was less flexible. As a result the parameters are -// not optimal and these functions have been deprecated in favor of extending -// A_PlaySound and A_StopSound. -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlayWeaponSound) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_SOUND(soundid, 0); - - S_Sound (self, CHAN_WEAPON, soundid, 1, ATTN_NORM); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlaySoundEx) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_SOUND(soundid, 0); - ACTION_PARAM_NAME(channel, 1); - ACTION_PARAM_BOOL(looping, 2); - ACTION_PARAM_INT(attenuation_raw, 3); - - float attenuation; - switch (attenuation_raw) - { - case -1: attenuation = ATTN_STATIC; break; // drop off rapidly - default: - case 0: attenuation = ATTN_NORM; break; // normal - case 1: - case 2: attenuation = ATTN_NONE; break; // full volume - } - - if (channel < NAME_Auto || channel > NAME_SoundSlot7) - { - channel = NAME_Auto; - } - - if (!looping) - { - S_Sound (self, int(channel) - NAME_Auto, soundid, 1, attenuation); - } - else - { - if (!S_IsActorPlayingSomething (self, int(channel) - NAME_Auto, soundid)) - { - S_Sound (self, (int(channel) - NAME_Auto) | CHAN_LOOP, soundid, 1, attenuation); - } - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StopSoundEx) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_NAME(channel, 0); - - if (channel > NAME_Auto && channel <= NAME_SoundSlot7) - { - S_StopSound (self, int(channel) - NAME_Auto); - } -} - -//========================================================================== -// -// Generic seeker missile function -// -//========================================================================== -static FRandom pr_seekermissile ("SeekerMissile"); -enum -{ - SMF_LOOK = 1, - SMF_PRECISE = 2, - SMF_CURSPEED = 4, -}; -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SeekerMissile) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(ang1, 0); - ACTION_PARAM_INT(ang2, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_INT(chance, 3); - ACTION_PARAM_INT(distance, 4); - - if ((flags & SMF_LOOK) && (self->tracer == 0) && (pr_seekermissile()tracer = P_RoughMonsterSearch (self, distance, true); - } - if (!P_SeekerMissile(self, clamp(ang1, 0, 90) * ANGLE_1, clamp(ang2, 0, 90) * ANGLE_1, !!(flags & SMF_PRECISE), !!(flags & SMF_CURSPEED))) - { - if (flags & SMF_LOOK) - { // This monster is no longer seekable, so let us look for another one next time. - self->tracer = NULL; - } - } -} - -//========================================================================== -// -// Hitscan attack with a customizable amount of bullets (specified in damage) -// -//========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_BulletAttack) -{ - int i; - int bangle; - int slope; - - if (!self->target) return; - - A_FaceTarget (self); - bangle = self->angle; - - slope = P_AimLineAttack (self, bangle, MISSILERANGE); - - S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM); - for (i = self->GetMissileDamage (0, 1); i > 0; --i) - { - int angle = bangle + (pr_cabullet.Random2() << 20); - int damage = ((pr_cabullet()%5)+1)*3; - P_LineAttack(self, angle, MISSILERANGE, slope, damage, - NAME_Hitscan, NAME_BulletPuff); - } -} - - -//========================================================================== -// -// Do the state jump -// -//========================================================================== -static void DoJump(AActor * self, FState * CallingState, FState *jumpto, StateCallData *statecall) -{ - if (jumpto == NULL) return; - - if (statecall != NULL) - { - statecall->State = jumpto; - } - else if (self->player != NULL && CallingState == self->player->psprites[ps_weapon].state) - { - P_SetPsprite(self->player, ps_weapon, jumpto); - } - else if (self->player != NULL && CallingState == self->player->psprites[ps_flash].state) - { - P_SetPsprite(self->player, ps_flash, jumpto); - } - else if (CallingState == self->state) - { - self->SetState (jumpto); - } - else - { - // something went very wrong. This should never happen. - assert(false); - } -} - -// This is just to avoid having to directly reference the internally defined -// CallingState and statecall parameters in the code below. -#define ACTION_JUMP(offset) DoJump(self, CallingState, offset, statecall) - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Jump) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(count, 0); - ACTION_PARAM_INT(maxchance, 1); - - if (count >= 2 && (maxchance >= 256 || pr_cajump() < maxchance)) - { - int jumps = 2 + (count == 2? 0 : (pr_cajump() % (count - 1))); - ACTION_PARAM_STATE(jumpto, jumps); - ACTION_JUMP(jumpto); - } - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfHealthLower) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(health, 0); - ACTION_PARAM_STATE(jump, 1); - ACTION_PARAM_INT(ptr_selector, 2); - - AActor *measured; - - measured = COPY_AAPTR(self, ptr_selector); - - if (measured && measured->health < health) - { - ACTION_JUMP(jump); - } - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetOutsideMeleeRange) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - if (!self->CheckMeleeRange()) - { - ACTION_JUMP(jump); - } - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInsideMeleeRange) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - if (self->CheckMeleeRange()) - { - ACTION_JUMP(jump); - } - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! -} -//========================================================================== -// -// State jump function -// -//========================================================================== -void DoJumpIfCloser(AActor *target, DECLARE_PARAMINFO) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_FIXED(dist, 0); - ACTION_PARAM_STATE(jump, 1); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - // No target - no jump - if (target != NULL && P_AproxDistance(self->x-target->x, self->y-target->y) < dist && - ( (self->z > target->z && self->z - (target->z + target->height) < dist) || - (self->z <=target->z && target->z - (self->z + self->height) < dist) - ) - ) - { - ACTION_JUMP(jump); - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfCloser) -{ - AActor *target; - - if (!self->player) - { - target = self->target; - } - else - { - // Does the player aim at something that can be shot? - P_BulletSlope(self, &target); - } - DoJumpIfCloser(target, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTracerCloser) -{ - DoJumpIfCloser(self->tracer, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfMasterCloser) -{ - DoJumpIfCloser(self->master, PUSH_PARAMINFO); -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -void DoJumpIfInventory(AActor * owner, DECLARE_PARAMINFO) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_CLASS(Type, 0); - ACTION_PARAM_INT(ItemAmount, 1); - ACTION_PARAM_STATE(JumpOffset, 2); - ACTION_PARAM_INT(setowner, 3); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - if (!Type) return; - COPY_AAPTR_NOT_NULL(owner, owner, setowner); // returns if owner ends up being NULL - - AInventory *Item = owner->FindInventory(Type); - - if (Item) - { - if (ItemAmount > 0) - { - if (Item->Amount >= ItemAmount) - ACTION_JUMP(JumpOffset); - } - else if (Item->Amount >= Item->MaxAmount) - { - ACTION_JUMP(JumpOffset); - } - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInventory) -{ - DoJumpIfInventory(self, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetInventory) -{ - DoJumpIfInventory(self->target, PUSH_PARAMINFO); -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfArmorType) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_NAME(Type, 0); - ACTION_PARAM_STATE(JumpOffset, 1); - ACTION_PARAM_INT(amount, 2); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - ABasicArmor * armor = (ABasicArmor *) self->FindInventory(NAME_BasicArmor); - - if (armor && armor->ArmorType == Type && armor->Amount >= amount) - ACTION_JUMP(JumpOffset); -} - -//========================================================================== -// -// Parameterized version of A_Explode -// -//========================================================================== - -enum -{ - XF_HURTSOURCE = 1, - XF_NOTMISSILE = 4, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Explode) -{ - ACTION_PARAM_START(8); - ACTION_PARAM_INT(damage, 0); - ACTION_PARAM_INT(distance, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_BOOL(alert, 3); - ACTION_PARAM_INT(fulldmgdistance, 4); - ACTION_PARAM_INT(nails, 5); - ACTION_PARAM_INT(naildamage, 6); - ACTION_PARAM_CLASS(pufftype, 7); - - if (damage < 0) // get parameters from metadata - { - damage = self->GetClass()->Meta.GetMetaInt (ACMETA_ExplosionDamage, 128); - distance = self->GetClass()->Meta.GetMetaInt (ACMETA_ExplosionRadius, damage); - flags = !self->GetClass()->Meta.GetMetaInt (ACMETA_DontHurtShooter); - alert = false; - } - else - { - if (distance <= 0) distance = damage; - } - // NailBomb effect, from SMMU but not from its source code: instead it was implemented and - // generalized from the documentation at http://www.doomworld.com/eternity/engine/codeptrs.html - - if (nails) - { - angle_t ang; - for (int i = 0; i < nails; i++) - { - ang = i*(ANGLE_MAX/nails); - // Comparing the results of a test wad with Eternity, it seems A_NailBomb does not aim - P_LineAttack (self, ang, MISSILERANGE, 0, - //P_AimLineAttack (self, ang, MISSILERANGE), - naildamage, NAME_Hitscan, pufftype); - } - } - - P_RadiusAttack (self, self->target, damage, distance, self->DamageType, flags, fulldmgdistance); - P_CheckSplash(self, distance<target != NULL && self->target->player != NULL) - { - validcount++; - P_RecursiveSound (self->Sector, self->target, false, 0); - } -} - -//========================================================================== -// -// A_RadiusThrust -// -//========================================================================== - -enum -{ - RTF_AFFECTSOURCE = 1, - RTF_NOIMPACTDAMAGE = 2, - RTF_NOTMISSILE = 4, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusThrust) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(force, 0); - ACTION_PARAM_INT(distance, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_INT(fullthrustdistance, 3); - - bool sourcenothrust = false; - - if (force == 0) force = 128; - if (distance <= 0) distance = abs(force); - - // Temporarily negate MF2_NODMGTHRUST on the shooter, since it renders this function useless. - if (!(flags & RTF_NOTMISSILE) && self->target != NULL && self->target->flags2 & MF2_NODMGTHRUST) - { - sourcenothrust = true; - self->target->flags2 &= ~MF2_NODMGTHRUST; - } - - P_RadiusAttack (self, self->target, force, distance, self->DamageType, flags | RADF_NODAMAGE, fullthrustdistance); - P_CheckSplash(self, distance << FRACBITS); - - if (sourcenothrust) - { - self->target->flags2 |= MF2_NODMGTHRUST; - } -} - -//========================================================================== -// -// Execute a line special / script -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CallSpecial) -{ - ACTION_PARAM_START(6); - ACTION_PARAM_INT(special, 0); - ACTION_PARAM_INT(arg1, 1); - ACTION_PARAM_INT(arg2, 2); - ACTION_PARAM_INT(arg3, 3); - ACTION_PARAM_INT(arg4, 4); - ACTION_PARAM_INT(arg5, 5); - - bool res = !!P_ExecuteSpecial(special, NULL, self, false, arg1, arg2, arg3, arg4, arg5); - - ACTION_SET_RESULT(res); -} - -//========================================================================== -// -// The ultimate code pointer: Fully customizable missiles! -// -//========================================================================== -enum CM_Flags -{ - CMF_AIMMODE = 3, - CMF_TRACKOWNER = 4, - CMF_CHECKTARGETDEAD = 8, - - CMF_ABSOLUTEPITCH = 16, - CMF_OFFSETPITCH = 32, - CMF_SAVEPITCH = 64, - - CMF_ABSOLUTEANGLE = 128 -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMissile) -{ - ACTION_PARAM_START(6); - ACTION_PARAM_CLASS(ti, 0); - ACTION_PARAM_FIXED(SpawnHeight, 1); - ACTION_PARAM_INT(Spawnofs_XY, 2); - ACTION_PARAM_ANGLE(Angle, 3); - ACTION_PARAM_INT(flags, 4); - ACTION_PARAM_ANGLE(pitch, 5); - - int aimmode = flags & CMF_AIMMODE; - - AActor * targ; - AActor * missile; - - if (self->target != NULL || aimmode==2) - { - if (ti) - { - angle_t ang = (self->angle - ANGLE_90) >> ANGLETOFINESHIFT; - fixed_t x = Spawnofs_XY * finecosine[ang]; - fixed_t y = Spawnofs_XY * finesine[ang]; - fixed_t z = SpawnHeight + self->GetBobOffset() - 32*FRACUNIT + (self->player? self->player->crouchoffset : 0); - - switch (aimmode) - { - case 0: - default: - // same adjustment as above (in all 3 directions this time) - for better aiming! - self->x += x; - self->y += y; - self->z += z; - missile = P_SpawnMissileXYZ(self->x, self->y, self->z + 32*FRACUNIT, self, self->target, ti, false); - self->x -= x; - self->y -= y; - self->z -= z; - break; - - case 1: - missile = P_SpawnMissileXYZ(self->x+x, self->y+y, self->z + self->GetBobOffset() + SpawnHeight, self, self->target, ti, false); - break; - - case 2: - self->x += x; - self->y += y; - missile = P_SpawnMissileAngleZSpeed(self, self->z + self->GetBobOffset() + SpawnHeight, ti, self->angle, 0, GetDefaultByType(ti)->Speed, self, false); - self->x -= x; - self->y -= y; - - flags |= CMF_ABSOLUTEPITCH; - - break; - } - - if (missile) - { - // Use the actual velocity instead of the missile's Speed property - // so that this can handle missiles with a high vertical velocity - // component properly. - - fixed_t missilespeed; - - if ( (CMF_ABSOLUTEPITCH|CMF_OFFSETPITCH) & flags) - { - if (CMF_OFFSETPITCH & flags) - { - FVector2 velocity (missile->velx, missile->vely); - pitch += R_PointToAngle2(0,0, (fixed_t)velocity.Length(), missile->velz); - } - ang = pitch >> ANGLETOFINESHIFT; - missilespeed = abs(FixedMul(finecosine[ang], missile->Speed)); - missile->velz = FixedMul(finesine[ang], missile->Speed); - } - else - { - FVector2 velocity (missile->velx, missile->vely); - missilespeed = (fixed_t)velocity.Length(); - } - - if (CMF_SAVEPITCH & flags) - { - missile->pitch = pitch; - // In aimmode 0 and 1 without absolutepitch or offsetpitch, the pitch parameter - // contains the unapplied parameter. In that case, it is set as pitch without - // otherwise affecting the spawned actor. - } - - missile->angle = (CMF_ABSOLUTEANGLE & flags) ? Angle : missile->angle + Angle ; - - ang = missile->angle >> ANGLETOFINESHIFT; - missile->velx = FixedMul (missilespeed, finecosine[ang]); - missile->vely = FixedMul (missilespeed, finesine[ang]); - - // handle projectile shooting projectiles - track the - // links back to a real owner - if (self->isMissile(!!(flags & CMF_TRACKOWNER))) - { - AActor * owner=self ;//->target; - while (owner->isMissile(!!(flags & CMF_TRACKOWNER)) && owner->target) owner=owner->target; - targ=owner; - missile->target=owner; - // automatic handling of seeker missiles - if (self->flags & missile->flags2 & MF2_SEEKERMISSILE) - { - missile->tracer=self->tracer; - } - } - else if (missile->flags2&MF2_SEEKERMISSILE) - { - // automatic handling of seeker missiles - missile->tracer=self->target; - } - // we must redo the spectral check here because the owner is set after spawning so the FriendPlayer value may be wrong - if (missile->flags4 & MF4_SPECTRAL) - { - if (missile->target != NULL) - { - missile->SetFriendPlayer(missile->target->player); - } - else - { - missile->FriendPlayer = 0; - } - } - P_CheckMissileSpawn(missile, self->radius); - } - } - } - else if (flags & CMF_CHECKTARGETDEAD) - { - // Target is dead and the attack shall be aborted. - if (self->SeeState != NULL && (self->health > 0 || !(self->flags3 & MF3_ISMONSTER))) self->SetState(self->SeeState); - } -} - -//========================================================================== -// -// An even more customizable hitscan attack -// -//========================================================================== -enum CBA_Flags -{ - CBAF_AIMFACING = 1, - CBAF_NORANDOM = 2, - CBAF_EXPLICITANGLE = 4, - CBAF_NOPITCH = 8, - CBAF_NORANDOMPUFFZ = 16, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack) -{ - ACTION_PARAM_START(7); - ACTION_PARAM_ANGLE(Spread_XY, 0); - ACTION_PARAM_ANGLE(Spread_Z, 1); - ACTION_PARAM_INT(NumBullets, 2); - ACTION_PARAM_INT(DamagePerBullet, 3); - ACTION_PARAM_CLASS(pufftype, 4); - ACTION_PARAM_FIXED(Range, 5); - ACTION_PARAM_INT(Flags, 6); - - if(Range==0) Range=MISSILERANGE; - - int i; - int bangle; - int bslope = 0; - int laflags = (Flags & CBAF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; - - if (self->target || (Flags & CBAF_AIMFACING)) - { - if (!(Flags & CBAF_AIMFACING)) A_FaceTarget (self); - bangle = self->angle; - - if (!pufftype) pufftype = PClass::FindClass(NAME_BulletPuff); - - if (!(Flags & CBAF_NOPITCH)) bslope = P_AimLineAttack (self, bangle, MISSILERANGE); - - S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM); - for (i=0 ; itarget) - return; - - A_FaceTarget (self); - if (self->CheckMeleeRange ()) - { - if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); - int newdam = P_DamageMobj (self->target, self, self, damage, DamageType); - if (bleed) P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - } - else - { - if (MissSound) S_Sound (self, CHAN_WEAPON, MissSound, 1, ATTN_NORM); - } -} - -//========================================================================== -// -// A fully customizable combo attack -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomComboAttack) -{ - ACTION_PARAM_START(6); - ACTION_PARAM_CLASS(ti, 0); - ACTION_PARAM_FIXED(SpawnHeight, 1); - ACTION_PARAM_INT(damage, 2); - ACTION_PARAM_SOUND(MeleeSound, 3); - ACTION_PARAM_NAME(DamageType, 4); - ACTION_PARAM_BOOL(bleed, 5); - - if (!self->target) - return; - - A_FaceTarget (self); - if (self->CheckMeleeRange ()) - { - if (DamageType==NAME_None) DamageType = NAME_Melee; // Melee is the default type - if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); - int newdam = P_DamageMobj (self->target, self, self, damage, DamageType); - if (bleed) P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); - } - else if (ti) - { - // This seemingly senseless code is needed for proper aiming. - self->z += SpawnHeight + self->GetBobOffset() - 32*FRACUNIT; - AActor *missile = P_SpawnMissileXYZ (self->x, self->y, self->z + 32*FRACUNIT, self, self->target, ti, false); - self->z -= SpawnHeight + self->GetBobOffset() - 32*FRACUNIT; - - if (missile) - { - // automatic handling of seeker missiles - if (missile->flags2&MF2_SEEKERMISSILE) - { - missile->tracer=self->target; - } - P_CheckMissileSpawn(missile, self->radius); - } - } -} - -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfNoAmmo) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - if (!ACTION_CALL_FROM_WEAPON()) return; - - if (!self->player->ReadyWeapon->CheckAmmo(self->player->ReadyWeapon->bAltFire, false, true)) - { - ACTION_JUMP(jump); - } - -} - - -//========================================================================== -// -// An even more customizable hitscan attack -// -//========================================================================== -enum FB_Flags -{ - FBF_USEAMMO = 1, - FBF_NORANDOM = 2, - FBF_EXPLICITANGLE = 4, - FBF_NOPITCH = 8, - FBF_NOFLASH = 16, - FBF_NORANDOMPUFFZ = 32, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets) -{ - ACTION_PARAM_START(7); - ACTION_PARAM_ANGLE(Spread_XY, 0); - ACTION_PARAM_ANGLE(Spread_Z, 1); - ACTION_PARAM_INT(NumberOfBullets, 2); - ACTION_PARAM_INT(DamagePerBullet, 3); - ACTION_PARAM_CLASS(PuffType, 4); - ACTION_PARAM_INT(Flags, 5); - ACTION_PARAM_FIXED(Range, 6); - - if (!self->player) return; - - player_t * player=self->player; - AWeapon * weapon=player->ReadyWeapon; - - int i; - int bangle; - int bslope = 0; - int laflags = (Flags & FBF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; - - if ((Flags & FBF_USEAMMO) && weapon) - { - if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo - } - - if (Range == 0) Range = PLAYERMISSILERANGE; - - if (!(Flags & FBF_NOFLASH)) static_cast(self)->PlayAttacking2 (); - - if (!(Flags & FBF_NOPITCH)) bslope = P_BulletSlope(self); - bangle = self->angle; - - if (!PuffType) PuffType = PClass::FindClass(NAME_BulletPuff); - - if (weapon != NULL) - { - S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); - } - - if ((NumberOfBullets==1 && !player->refire) || NumberOfBullets==0) - { - int damage = DamagePerBullet; - - if (!(Flags & FBF_NORANDOM)) - damage *= ((pr_cwbullet()%3)+1); - - P_LineAttack(self, bangle, Range, bslope, damage, NAME_Hitscan, PuffType, laflags); - } - else - { - if (NumberOfBullets == -1) NumberOfBullets = 1; - for (i=0 ; iplayer) return; - - - player_t *player=self->player; - AWeapon * weapon=player->ReadyWeapon; - AActor *linetarget; - - if (UseAmmo && weapon) - { - if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo - } - - if (ti) - { - angle_t ang = (self->angle - ANGLE_90) >> ANGLETOFINESHIFT; - fixed_t x = SpawnOfs_XY * finecosine[ang]; - fixed_t y = SpawnOfs_XY * finesine[ang]; - fixed_t z = SpawnHeight; - fixed_t shootangle = self->angle; - - if (Flags & FPF_AIMATANGLE) shootangle += Angle; - - // Temporarily adjusts the pitch - fixed_t SavedPlayerPitch = self->pitch; - self->pitch -= pitch; - AActor * misl=P_SpawnPlayerMissile (self, x, y, z, ti, shootangle, &linetarget); - self->pitch = SavedPlayerPitch; - - // automatic handling of seeker missiles - if (misl) - { - if (Flags & FPF_TRANSFERTRANSLATION) misl->Translation = self->Translation; - if (linetarget && misl->flags2&MF2_SEEKERMISSILE) misl->tracer=linetarget; - if (!(Flags & FPF_AIMATANGLE)) - { - // This original implementation is to aim straight ahead and then offset - // the angle from the resulting direction. - FVector3 velocity(misl->velx, misl->vely, 0); - fixed_t missilespeed = (fixed_t)velocity.Length(); - misl->angle += Angle; - angle_t an = misl->angle >> ANGLETOFINESHIFT; - misl->velx = FixedMul (missilespeed, finecosine[an]); - misl->vely = FixedMul (missilespeed, finesine[an]); - } - } - } -} - - -//========================================================================== -// -// A_CustomPunch -// -// Berserk is not handled here. That can be done with A_CheckIfInventory -// -//========================================================================== - -enum -{ - CPF_USEAMMO = 1, - CPF_DAGGER = 2, - CPF_PULLIN = 4, - CPF_NORANDOMPUFFZ = 8, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(Damage, 0); - ACTION_PARAM_BOOL(norandom, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_CLASS(PuffType, 3); - ACTION_PARAM_FIXED(Range, 4); - ACTION_PARAM_FIXED(LifeSteal, 5); - - if (!self->player) return; - - player_t *player=self->player; - AWeapon * weapon=player->ReadyWeapon; - - - angle_t angle; - int pitch; - AActor * linetarget; - int actualdamage; - - if (!norandom) Damage *= (pr_cwpunch()%8+1); - - angle = self->angle + (pr_cwpunch.Random2() << 18); - if (Range == 0) Range = MELEERANGE; - pitch = P_AimLineAttack (self, angle, Range, &linetarget); - - // only use ammo when actually hitting something! - if ((flags & CPF_USEAMMO) && linetarget && weapon) - { - if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo - } - - if (!PuffType) PuffType = PClass::FindClass(NAME_BulletPuff); - int puffFlags = LAF_ISMELEEATTACK | ((flags & CPF_NORANDOMPUFFZ) ? LAF_NORANDOMPUFFZ : 0); - - P_LineAttack (self, angle, Range, pitch, Damage, NAME_Melee, PuffType, puffFlags, &linetarget, &actualdamage); - - // turn to face target - if (linetarget) - { - if (LifeSteal && !(linetarget->flags5 & MF5_DONTDRAIN)) - P_GiveBody (self, (actualdamage * LifeSteal) >> FRACBITS); - - if (weapon != NULL) - { - S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); - } - - self->angle = R_PointToAngle2 (self->x, - self->y, - linetarget->x, - linetarget->y); - - if (flags & CPF_PULLIN) self->flags |= MF_JUSTATTACKED; - if (flags & CPF_DAGGER) P_DaggerAlert (self, linetarget); - } -} - - -//========================================================================== -// -// customizable railgun attack function -// -//========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RailAttack) -{ - ACTION_PARAM_START(16); - ACTION_PARAM_INT(Damage, 0); - ACTION_PARAM_INT(Spawnofs_XY, 1); - ACTION_PARAM_BOOL(UseAmmo, 2); - ACTION_PARAM_COLOR(Color1, 3); - ACTION_PARAM_COLOR(Color2, 4); - ACTION_PARAM_INT(Flags, 5); - ACTION_PARAM_FLOAT(MaxDiff, 6); - ACTION_PARAM_CLASS(PuffType, 7); - ACTION_PARAM_ANGLE(Spread_XY, 8); - ACTION_PARAM_ANGLE(Spread_Z, 9); - ACTION_PARAM_FIXED(Range, 10); - ACTION_PARAM_INT(Duration, 11); - ACTION_PARAM_FLOAT(Sparsity, 12); - ACTION_PARAM_FLOAT(DriftSpeed, 13); - ACTION_PARAM_CLASS(SpawnClass, 14); - ACTION_PARAM_FIXED(Spawnofs_Z, 15); - - if(Range==0) Range=8192*FRACUNIT; - if(Sparsity==0) Sparsity=1.0; - - if (!self->player) return; - - AWeapon * weapon=self->player->ReadyWeapon; - - // only use ammo when actually hitting something! - if (UseAmmo) - { - if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo - } - - angle_t angle; - angle_t slope; - - if (Flags & RAF_EXPLICITANGLE) - { - angle = Spread_XY; - slope = Spread_Z; - } - else - { - angle = pr_crailgun.Random2() * (Spread_XY / 255); - slope = pr_crailgun.Random2() * (Spread_Z / 255); - } - - P_RailAttack (self, Damage, Spawnofs_XY, Spawnofs_Z, Color1, Color2, MaxDiff, Flags, PuffType, angle, slope, Range, Duration, Sparsity, DriftSpeed, SpawnClass); -} - -//========================================================================== -// -// also for monsters -// -//========================================================================== -enum -{ - CRF_DONTAIM = 0, - CRF_AIMPARALLEL = 1, - CRF_AIMDIRECT = 2, - CRF_EXPLICITANGLE = 4, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) -{ - ACTION_PARAM_START(16); - ACTION_PARAM_INT(Damage, 0); - ACTION_PARAM_INT(Spawnofs_XY, 1); - ACTION_PARAM_COLOR(Color1, 2); - ACTION_PARAM_COLOR(Color2, 3); - ACTION_PARAM_INT(Flags, 4); - ACTION_PARAM_INT(aim, 5); - ACTION_PARAM_FLOAT(MaxDiff, 6); - ACTION_PARAM_CLASS(PuffType, 7); - ACTION_PARAM_ANGLE(Spread_XY, 8); - ACTION_PARAM_ANGLE(Spread_Z, 9); - ACTION_PARAM_FIXED(Range, 10); - ACTION_PARAM_INT(Duration, 11); - ACTION_PARAM_FLOAT(Sparsity, 12); - ACTION_PARAM_FLOAT(DriftSpeed, 13); - ACTION_PARAM_CLASS(SpawnClass, 14); - ACTION_PARAM_FIXED(Spawnofs_Z, 15); - - if(Range==0) Range=8192*FRACUNIT; - if(Sparsity==0) Sparsity=1.0; - - AActor *linetarget; - - fixed_t saved_x = self->x; - fixed_t saved_y = self->y; - angle_t saved_angle = self->angle; - fixed_t saved_pitch = self->pitch; - - if (aim && self->target == NULL) - { - return; - } - // [RH] Andy Baker's stealth monsters - if (self->flags & MF_STEALTH) - { - self->visdir = 1; - } - - self->flags &= ~MF_AMBUSH; - - - if (aim) - { - self->angle = R_PointToAngle2 (self->x, - self->y, - self->target->x, - self->target->y); - } - self->pitch = P_AimLineAttack (self, self->angle, MISSILERANGE, &linetarget, ANGLE_1*60, 0, aim ? self->target : NULL); - if (linetarget == NULL && aim) - { - // We probably won't hit the target, but aim at it anyway so we don't look stupid. - FVector2 xydiff(self->target->x - self->x, self->target->y - self->y); - double zdiff = (self->target->z + (self->target->height>>1)) - - (self->z + (self->height>>1) - self->floorclip); - self->pitch = int(atan2(zdiff, xydiff.Length()) * ANGLE_180 / -M_PI); - } - // Let the aim trail behind the player - if (aim) - { - saved_angle = self->angle = R_PointToAngle2 (self->x, self->y, - self->target->x - self->target->velx * 3, - self->target->y - self->target->vely * 3); - - if (aim == CRF_AIMDIRECT) - { - // Tricky: We must offset to the angle of the current position - // but then change the angle again to ensure proper aim. - self->x += Spawnofs_XY * finecosine[self->angle]; - self->y += Spawnofs_XY * finesine[self->angle]; - Spawnofs_XY = 0; - self->angle = R_PointToAngle2 (self->x, self->y, - self->target->x - self->target->velx * 3, - self->target->y - self->target->vely * 3); - } - - if (self->target->flags & MF_SHADOW) - { - angle_t rnd = pr_crailgun.Random2() << 21; - self->angle += rnd; - saved_angle = rnd; - } - } - - angle_t angle = (self->angle - ANG90) >> ANGLETOFINESHIFT; - - angle_t angleoffset; - angle_t slopeoffset; - - if (Flags & CRF_EXPLICITANGLE) - { - angleoffset = Spread_XY; - slopeoffset = Spread_Z; - } - else - { - angleoffset = pr_crailgun.Random2() * (Spread_XY / 255); - slopeoffset = pr_crailgun.Random2() * (Spread_Z / 255); - } - - P_RailAttack (self, Damage, Spawnofs_XY, Spawnofs_Z, Color1, Color2, MaxDiff, Flags, PuffType, angleoffset, slopeoffset, Range, Duration, Sparsity, DriftSpeed, SpawnClass); - - self->x = saved_x; - self->y = saved_y; - self->angle = saved_angle; - self->pitch = saved_pitch; -} - -//=========================================================================== -// -// DoGiveInventory -// -//=========================================================================== - -static void DoGiveInventory(AActor * receiver, DECLARE_PARAMINFO) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_CLASS(mi, 0); - ACTION_PARAM_INT(amount, 1); - ACTION_PARAM_INT(setreceiver, 2); - - COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); - - bool res=true; - - if (amount==0) amount=1; - if (mi) - { - AInventory *item = static_cast(Spawn (mi, 0, 0, 0, NO_REPLACE)); - if (item->IsKindOf(RUNTIME_CLASS(AHealth))) - { - item->Amount *= amount; - } - else - { - item->Amount = amount; - } - item->flags |= MF_DROPPED; - item->ClearCounters(); - if (!item->CallTryPickup (receiver)) - { - item->Destroy (); - res = false; - } - else res = true; - } - else res = false; - ACTION_SET_RESULT(res); - -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveInventory) -{ - DoGiveInventory(self, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToTarget) -{ - DoGiveInventory(self->target, PUSH_PARAMINFO); -} - -//=========================================================================== -// -// A_TakeInventory -// -//=========================================================================== - -enum -{ - TIF_NOTAKEINFINITE = 1, -}; - -void DoTakeInventory(AActor * receiver, DECLARE_PARAMINFO) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_CLASS(item, 0); - ACTION_PARAM_INT(amount, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_INT(setreceiver, 3); - - if (!item) return; - COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); - - bool res = false; - - AInventory * inv = receiver->FindInventory(item); - - if (inv && !inv->IsKindOf(RUNTIME_CLASS(AHexenArmor))) - { - if (inv->Amount > 0) - { - res = true; - } - // Do not take ammo if the "no take infinite/take as ammo depletion" flag is set - // and infinite ammo is on - if (flags & TIF_NOTAKEINFINITE && - ((dmflags & DF_INFINITE_AMMO) || (receiver->player->cheats & CF_INFINITEAMMO)) && - inv->IsKindOf(RUNTIME_CLASS(AAmmo))) - { - // Nothing to do here, except maybe res = false;? Would it make sense? - } - else if (!amount || amount>=inv->Amount) - { - if (inv->ItemFlags&IF_KEEPDEPLETED) inv->Amount=0; - else inv->Destroy(); - } - else inv->Amount-=amount; - } - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeInventory) -{ - DoTakeInventory(self, PUSH_PARAMINFO); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromTarget) -{ - DoTakeInventory(self->target, PUSH_PARAMINFO); -} - -//=========================================================================== -// -// Common code for A_SpawnItem and A_SpawnItemEx -// -//=========================================================================== - -enum SIX_Flags -{ - SIXF_TRANSFERTRANSLATION = 1 << 0, - SIXF_ABSOLUTEPOSITION = 1 << 1, - SIXF_ABSOLUTEANGLE = 1 << 2, - SIXF_ABSOLUTEVELOCITY = 1 << 3, - SIXF_SETMASTER = 1 << 4, - SIXF_NOCHECKPOSITION = 1 << 5, - SIXF_TELEFRAG = 1 << 6, - SIXF_CLIENTSIDE = 1 << 7, // only used by Skulldronum - SIXF_TRANSFERAMBUSHFLAG = 1 << 8, - SIXF_TRANSFERPITCH = 1 << 9, - SIXF_TRANSFERPOINTERS = 1 << 10, - SIXF_USEBLOODCOLOR = 1 << 11, - SIXF_CLEARCALLERTID = 1 << 12, - SIXF_MULTIPLYSPEED = 1 << 13, - SIXF_TRANSFERSCALE = 1 << 14, - SIXF_TRANSFERSPECIAL = 1 << 15, - SIXF_CLEARCALLERSPECIAL = 1 << 16, - SIXF_TRANSFERSTENCILCOL = 1 << 17, - SIXF_TRANSFERALPHA = 1 << 18, - SIXF_TRANSFERRENDERSTYLE = 1 << 19, - SIXF_SETTARGET = 1 << 20, - SIXF_SETTRACER = 1 << 21, - SIXF_NOPOINTERS = 1 << 22, -}; - -static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) -{ - if (mo == NULL) - { - return false; - } - AActor *originator = self; - - if (!(mo->flags2 & MF2_DONTTRANSLATE)) - { - if (flags & SIXF_TRANSFERTRANSLATION) - { - mo->Translation = self->Translation; - } - else if (flags & SIXF_USEBLOODCOLOR) - { - // [XA] Use the spawning actor's BloodColor to translate the newly-spawned object. - PalEntry bloodcolor = self->GetBloodColor(); - mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a); - } - } - if (flags & SIXF_TRANSFERPOINTERS) - { - mo->target = self->target; - mo->master = self->master; // This will be overridden later if SIXF_SETMASTER is set - mo->tracer = self->tracer; - } - - mo->angle = self->angle; - if (flags & SIXF_TRANSFERPITCH) - { - mo->pitch = self->pitch; - } - while (originator && originator->isMissile()) - { - originator = originator->target; - } - - if (flags & SIXF_TELEFRAG) - { - P_TeleportMove(mo, mo->x, mo->y, mo->z, true); - // This is needed to ensure consistent behavior. - // Otherwise it will only spawn if nothing gets telefragged - flags |= SIXF_NOCHECKPOSITION; - } - if (mo->flags3 & MF3_ISMONSTER) - { - if (!(flags & SIXF_NOCHECKPOSITION) && !P_TestMobjLocation(mo)) - { - // The monster is blocked so don't spawn it at all! - mo->ClearCounters(); - mo->Destroy(); - return false; - } - else if (originator) - { - if (originator->flags3 & MF3_ISMONSTER) - { - // If this is a monster transfer all friendliness information - mo->CopyFriendliness(originator, true); - } - else if (originator->player) - { - // A player always spawns a monster friendly to him - mo->flags |= MF_FRIENDLY; - mo->SetFriendPlayer(originator->player); - - AActor * attacker=originator->player->attacker; - if (attacker) - { - if (!(attacker->flags&MF_FRIENDLY) || - (deathmatch && attacker->FriendPlayer!=0 && attacker->FriendPlayer!=mo->FriendPlayer)) - { - // Target the monster which last attacked the player - mo->LastHeard = mo->target = attacker; - } - } - } - } - } - else if (!(flags & SIXF_TRANSFERPOINTERS)) - { - // If this is a missile or something else set the target to the originator - mo->target = originator ? originator : self; - } - if (flags & SIXF_NOPOINTERS) - { - //[MC]Intentionally eliminate pointers. Overrides TRANSFERPOINTERS, but is overridden by SETMASTER/TARGET/TRACER. - mo->target = NULL; - mo->master = NULL; - mo->tracer = NULL; - } - if (flags & SIXF_SETMASTER) - { - mo->master = originator; - } - if (flags & SIXF_SETTARGET) - { - mo->target = originator; - } - if (flags & SIXF_SETTRACER) - { - mo->tracer = originator; - } - if (flags & SIXF_TRANSFERSCALE) - { - mo->scaleX = self->scaleX; - mo->scaleY = self->scaleY; - } - if (flags & SIXF_TRANSFERAMBUSHFLAG) - { - mo->flags = (mo->flags & ~MF_AMBUSH) | (self->flags & MF_AMBUSH); - } - if (flags & SIXF_CLEARCALLERTID) - { - self->RemoveFromHash(); - self->tid = 0; - } - if (flags & SIXF_TRANSFERSPECIAL) - { - mo->special = self->special; - memcpy(mo->args, self->args, sizeof(self->args)); - } - if (flags & SIXF_CLEARCALLERSPECIAL) - { - self->special = 0; - memset(self->args, 0, sizeof(self->args)); - } - if (flags & SIXF_TRANSFERSTENCILCOL) - { - mo->fillcolor = self->fillcolor; - } - if (flags & SIXF_TRANSFERALPHA) - { - mo->alpha = self->alpha; - } - if (flags & SIXF_TRANSFERRENDERSTYLE) - { - mo->RenderStyle = self->RenderStyle; - } - - return true; -} - -//=========================================================================== -// -// A_SpawnItem -// -// Spawns an item in front of the caller like Heretic's time bomb -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnItem) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_CLASS(missile, 0); - ACTION_PARAM_FIXED(distance, 1); - ACTION_PARAM_FIXED(zheight, 2); - ACTION_PARAM_BOOL(useammo, 3); - ACTION_PARAM_BOOL(transfer_translation, 4); - - if (!missile) - { - ACTION_SET_RESULT(false); - return; - } - - // Don't spawn monsters if this actor has been massacred - if (self->DamageType == NAME_Massacre && GetDefaultByType(missile)->flags3&MF3_ISMONSTER) return; - - if (distance==0) - { - // use the minimum distance that does not result in an overlap - distance=(self->radius+GetDefaultByType(missile)->radius)>>FRACBITS; - } - - if (ACTION_CALL_FROM_WEAPON()) - { - // Used from a weapon so use some ammo - AWeapon * weapon=self->player->ReadyWeapon; - - if (!weapon) return; - if (useammo && !weapon->DepleteAmmo(weapon->bAltFire)) return; - } - - AActor * mo = Spawn( missile, - self->x + FixedMul(distance, finecosine[self->angle>>ANGLETOFINESHIFT]), - self->y + FixedMul(distance, finesine[self->angle>>ANGLETOFINESHIFT]), - self->z - self->floorclip + self->GetBobOffset() + zheight, ALLOW_REPLACE); - - int flags = (transfer_translation ? SIXF_TRANSFERTRANSLATION : 0) + (useammo ? SIXF_SETMASTER : 0); - bool res = InitSpawnedItem(self, mo, flags); - ACTION_SET_RESULT(res); // for an inventory item's use state -} - -//=========================================================================== -// -// A_SpawnItemEx -// -// Enhanced spawning function -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnItemEx) -{ - ACTION_PARAM_START(11); - ACTION_PARAM_CLASS(missile, 0); - ACTION_PARAM_FIXED(xofs, 1); - ACTION_PARAM_FIXED(yofs, 2); - ACTION_PARAM_FIXED(zofs, 3); - ACTION_PARAM_FIXED(xvel, 4); - ACTION_PARAM_FIXED(yvel, 5); - ACTION_PARAM_FIXED(zvel, 6); - ACTION_PARAM_ANGLE(Angle, 7); - ACTION_PARAM_INT(flags, 8); - ACTION_PARAM_INT(chance, 9); - ACTION_PARAM_INT(tid, 10); - - if (!missile) - { - ACTION_SET_RESULT(false); - return; - } - - if (chance > 0 && pr_spawnitemex()DamageType == NAME_Massacre && GetDefaultByType(missile)->flags3&MF3_ISMONSTER) return; - - fixed_t x,y; - - if (!(flags & SIXF_ABSOLUTEANGLE)) - { - Angle += self->angle; - } - - angle_t ang = Angle >> ANGLETOFINESHIFT; - - if (flags & SIXF_ABSOLUTEPOSITION) - { - x = self->x + xofs; - y = self->y + yofs; - } - else - { - // in relative mode negative y values mean 'left' and positive ones mean 'right' - // This is the inverse orientation of the absolute mode! - x = self->x + FixedMul(xofs, finecosine[ang]) + FixedMul(yofs, finesine[ang]); - y = self->y + FixedMul(xofs, finesine[ang]) - FixedMul(yofs, finecosine[ang]); - } - - if (!(flags & SIXF_ABSOLUTEVELOCITY)) - { - // Same orientation issue here! - fixed_t newxvel = FixedMul(xvel, finecosine[ang]) + FixedMul(yvel, finesine[ang]); - yvel = FixedMul(xvel, finesine[ang]) - FixedMul(yvel, finecosine[ang]); - xvel = newxvel; - } - - AActor *mo = Spawn(missile, x, y, self->z - self->floorclip + self->GetBobOffset() + zofs, ALLOW_REPLACE); - bool res = InitSpawnedItem(self, mo, flags); - ACTION_SET_RESULT(res); // for an inventory item's use state - if (res) - { - if (tid != 0) - { - assert(mo->tid == 0); - mo->tid = tid; - mo->AddToHash(); - } - if (flags & SIXF_MULTIPLYSPEED) - { - mo->velx = FixedMul(xvel, mo->Speed); - mo->vely = FixedMul(yvel, mo->Speed); - mo->velz = FixedMul(zvel, mo->Speed); - } - else - { - mo->velx = xvel; - mo->vely = yvel; - mo->velz = zvel; - } - mo->angle = Angle; - } -} - -//=========================================================================== -// -// A_ThrowGrenade -// -// Throws a grenade (like Hexen's fighter flechette) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ThrowGrenade) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_CLASS(missile, 0); - ACTION_PARAM_FIXED(zheight, 1); - ACTION_PARAM_FIXED(xyvel, 2); - ACTION_PARAM_FIXED(zvel, 3); - ACTION_PARAM_BOOL(useammo, 4); - - if (missile == NULL) return; - - if (ACTION_CALL_FROM_WEAPON()) - { - // Used from a weapon, so use some ammo - AWeapon *weapon = self->player->ReadyWeapon; - - if (!weapon) return; - if (useammo && !weapon->DepleteAmmo(weapon->bAltFire)) return; - } - - - AActor * bo; - - bo = Spawn(missile, self->x, self->y, - self->z - self->floorclip + self->GetBobOffset() + zheight + 35*FRACUNIT + (self->player? self->player->crouchoffset : 0), - ALLOW_REPLACE); - if (bo) - { - P_PlaySpawnSound(bo, self); - if (xyvel != 0) - bo->Speed = xyvel; - bo->angle = self->angle + (((pr_grenade()&7) - 4) << 24); - - angle_t pitch = angle_t(-self->pitch) >> ANGLETOFINESHIFT; - angle_t angle = bo->angle >> ANGLETOFINESHIFT; - - // There are two vectors we are concerned about here: xy and z. We rotate - // them separately according to the shooter's pitch and then sum them to - // get the final velocity vector to shoot with. - - fixed_t xy_xyscale = FixedMul(bo->Speed, finecosine[pitch]); - fixed_t xy_velz = FixedMul(bo->Speed, finesine[pitch]); - fixed_t xy_velx = FixedMul(xy_xyscale, finecosine[angle]); - fixed_t xy_vely = FixedMul(xy_xyscale, finesine[angle]); - - pitch = angle_t(self->pitch) >> ANGLETOFINESHIFT; - fixed_t z_xyscale = FixedMul(zvel, finesine[pitch]); - fixed_t z_velz = FixedMul(zvel, finecosine[pitch]); - fixed_t z_velx = FixedMul(z_xyscale, finecosine[angle]); - fixed_t z_vely = FixedMul(z_xyscale, finesine[angle]); - - bo->velx = xy_velx + z_velx + (self->velx >> 1); - bo->vely = xy_vely + z_vely + (self->vely >> 1); - bo->velz = xy_velz + z_velz; - - bo->target = self; - P_CheckMissileSpawn (bo, self->radius); - } - else ACTION_SET_RESULT(false); -} - - -//=========================================================================== -// -// A_Recoil -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Recoil) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_FIXED(xyvel, 0); - - angle_t angle = self->angle + ANG180; - angle >>= ANGLETOFINESHIFT; - self->velx += FixedMul (xyvel, finecosine[angle]); - self->vely += FixedMul (xyvel, finesine[angle]); -} - - -//=========================================================================== -// -// A_SelectWeapon -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SelectWeapon) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_CLASS(cls, 0); - - if (cls == NULL || self->player == NULL) - { - ACTION_SET_RESULT(false); - return; - } - - AWeapon * weaponitem = static_cast(self->FindInventory(cls)); - - if (weaponitem != NULL && weaponitem->IsKindOf(RUNTIME_CLASS(AWeapon))) - { - if (self->player->ReadyWeapon != weaponitem) - { - self->player->PendingWeapon = weaponitem; - } - } - else ACTION_SET_RESULT(false); - -} - - -//=========================================================================== -// -// A_Print -// -//=========================================================================== -EXTERN_CVAR(Float, con_midtime) - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Print) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_STRING(text, 0); - ACTION_PARAM_FLOAT(time, 1); - ACTION_PARAM_NAME(fontname, 2); - - if (text[0] == '$') text = GStrings(text+1); - if (self->CheckLocalView (consoleplayer) || - (self->target!=NULL && self->target->CheckLocalView (consoleplayer))) - { - float saved = con_midtime; - FFont *font = NULL; - - if (fontname != NAME_None) - { - font = V_GetFont(fontname); - } - if (time > 0) - { - con_midtime = time; - } - - FString formatted = strbin1(text); - C_MidPrint(font != NULL ? font : SmallFont, formatted.GetChars()); - con_midtime = saved; - } - ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! -} - -//=========================================================================== -// -// A_PrintBold -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PrintBold) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_STRING(text, 0); - ACTION_PARAM_FLOAT(time, 1); - ACTION_PARAM_NAME(fontname, 2); - - float saved = con_midtime; - FFont *font = NULL; - - if (text[0] == '$') text = GStrings(text+1); - if (fontname != NAME_None) - { - font = V_GetFont(fontname); - } - if (time > 0) - { - con_midtime = time; - } - - FString formatted = strbin1(text); - C_MidPrintBold(font != NULL ? font : SmallFont, formatted.GetChars()); - con_midtime = saved; - ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! -} - -//=========================================================================== -// -// A_Log -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Log) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STRING(text, 0); - - if (text[0] == '$') text = GStrings(text+1); - FString formatted = strbin1(text); - Printf("%s\n", formatted.GetChars()); - ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! -} - -//========================================================================= -// -// A_LogInt -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogInt) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(num, 0); - Printf("%d\n", num); - ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! -} - -//=========================================================================== -// -// A_SetTranslucent -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTranslucent) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_FIXED(alpha, 0); - ACTION_PARAM_INT(mode, 1); - - mode = mode == 0 ? STYLE_Translucent : mode == 2 ? STYLE_Fuzzy : STYLE_Add; - - self->RenderStyle.Flags &= ~STYLEF_Alpha1; - self->alpha = clamp(alpha, 0, FRACUNIT); - self->RenderStyle = ERenderStyle(mode); -} - -//=========================================================================== -// -// A_FadeIn -// -// Fades the actor in -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeIn) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_FIXED(reduce, 0); - - if (reduce == 0) - { - reduce = FRACUNIT/10; - } - self->RenderStyle.Flags &= ~STYLEF_Alpha1; - self->alpha += reduce; - // Should this clamp alpha to 1.0? -} - -//=========================================================================== -// -// A_FadeOut -// -// fades the actor out and destroys it when done -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeOut) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_FIXED(reduce, 0); - ACTION_PARAM_BOOL(remove, 1); - - if (reduce == 0) - { - reduce = FRACUNIT/10; - } - self->RenderStyle.Flags &= ~STYLEF_Alpha1; - self->alpha -= reduce; - if (self->alpha <= 0 && remove) - { - self->Destroy(); - } -} - -//=========================================================================== -// -// A_FadeTo -// -// fades the actor to a specified transparency by a specified amount and -// destroys it if so desired -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_FIXED(target, 0); - ACTION_PARAM_FIXED(amount, 1); - ACTION_PARAM_BOOL(remove, 2); - - self->RenderStyle.Flags &= ~STYLEF_Alpha1; - - if (self->alpha > target) - { - self->alpha -= amount; - - if (self->alpha < target) - { - self->alpha = target; - } - } - else if (self->alpha < target) - { - self->alpha += amount; - - if (self->alpha > target) - { - self->alpha = target; - } - } - if (self->alpha == target && remove) - { - self->Destroy(); - } -} - -//=========================================================================== -// -// A_Scale(float scalex, optional float scaley) -// -// Scales the actor's graphics. If scaley is 0, use scalex. -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetScale) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_FIXED(scalex, 0); - ACTION_PARAM_FIXED(scaley, 1); - - self->scaleX = scalex; - self->scaleY = scaley ? scaley : scalex; -} - -//=========================================================================== -// -// A_SetMass(int mass) -// -// Sets the actor's mass. -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetMass) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(mass, 0); - - self->Mass = mass; -} - -//=========================================================================== -// -// A_SpawnDebris -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnDebris) -{ - int i; - AActor * mo; - - ACTION_PARAM_START(4); - ACTION_PARAM_CLASS(debris, 0); - ACTION_PARAM_BOOL(transfer_translation, 1); - ACTION_PARAM_FIXED(mult_h, 2); - ACTION_PARAM_FIXED(mult_v, 3); - - if (debris == NULL) return; - - // only positive values make sense here - if (mult_v<=0) mult_v=FRACUNIT; - if (mult_h<=0) mult_h=FRACUNIT; - - for (i = 0; i < GetDefaultByType(debris)->health; i++) - { - mo = Spawn(debris, self->x+((pr_spawndebris()-128)<<12), - self->y + ((pr_spawndebris()-128)<<12), - self->z + (pr_spawndebris()*self->height/256+self->GetBobOffset()), ALLOW_REPLACE); - if (mo) - { - if (transfer_translation) - { - mo->Translation = self->Translation; - } - if (i < mo->GetClass()->ActorInfo->NumOwnedStates) - { - mo->SetState(mo->GetClass()->ActorInfo->OwnedStates + i); - } - mo->velz = FixedMul(mult_v, ((pr_spawndebris()&7)+5)*FRACUNIT); - mo->velx = FixedMul(mult_h, pr_spawndebris.Random2()<<(FRACBITS-6)); - mo->vely = FixedMul(mult_h, pr_spawndebris.Random2()<<(FRACBITS-6)); - } - } -} - - -//=========================================================================== -// -// A_CheckSight -// jumps if no player can see this actor -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSight) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - for (int i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i]) - { - // Always check sight from each player. - if (P_CheckSight(players[i].mo, self, SF_IGNOREVISIBILITY)) - { - return; - } - // If a player is viewing from a non-player, then check that too. - if (players[i].camera != NULL && players[i].camera->player == NULL && - P_CheckSight(players[i].camera, self, SF_IGNOREVISIBILITY)) - { - return; - } - } - } - - ACTION_JUMP(jump); -} - -//=========================================================================== -// -// A_CheckSightOrRange -// Jumps if this actor is out of range of all players *and* out of sight. -// Useful for maps with many multi-actor special effects. -// -//=========================================================================== -static bool DoCheckSightOrRange(AActor *self, AActor *camera, double range) -{ - if (camera == NULL) - { - return false; - } - // Check distance first, since it's cheaper than checking sight. - double dx = self->x - camera->x; - double dy = self->y - camera->y; - double dz; - fixed_t eyez = (camera->z + camera->height - (camera->height>>2)); // same eye height as P_CheckSight - if (eyez > self->z + self->height) - { - dz = self->z + self->height - eyez; - } - else if (eyez < self->z) - { - dz = self->z - eyez; - } - else - { - dz = 0; - } - if ((dx*dx) + (dy*dy) + (dz*dz) <= range) - { // Within range - return true; - } - - // Now check LOS. - if (P_CheckSight(camera, self, SF_IGNOREVISIBILITY)) - { // Visible - return true; - } - return false; -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSightOrRange) -{ - ACTION_PARAM_START(2); - double range = EvalExpressionF(ParameterIndex+0, self); - ACTION_PARAM_STATE(jump, 1); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - range = range * range * (double(FRACUNIT) * FRACUNIT); // no need for square roots - for (int i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i]) - { - // Always check from each player. - if (DoCheckSightOrRange(self, players[i].mo, range)) - { - return; - } - // If a player is viewing from a non-player, check that too. - if (players[i].camera != NULL && players[i].camera->player == NULL && - DoCheckSightOrRange(self, players[i].camera, range)) - { - return; - } - } - } - ACTION_JUMP(jump); -} - -//=========================================================================== -// -// A_CheckRange -// Jumps if this actor is out of range of all players. -// -//=========================================================================== -static bool DoCheckRange(AActor *self, AActor *camera, double range) -{ - if (camera == NULL) - { - return false; - } - // Check distance first, since it's cheaper than checking sight. - double dx = self->x - camera->x; - double dy = self->y - camera->y; - double dz; - fixed_t eyez = (camera->z + camera->height - (camera->height>>2)); // same eye height as P_CheckSight - if (eyez > self->z + self->height){ - dz = self->z + self->height - eyez; - } - else if (eyez < self->z){ - dz = self->z - eyez; - } - else{ - dz = 0; - } - if ((dx*dx) + (dy*dy) + (dz*dz) <= range){ - // Within range - return true; - } - return false; -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckRange) -{ - ACTION_PARAM_START(2); - double range = EvalExpressionF(ParameterIndex+0, self); - ACTION_PARAM_STATE(jump, 1); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - range = range * range * (double(FRACUNIT) * FRACUNIT); // no need for square roots - for (int i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i]) - { - // Always check from each player. - if (DoCheckRange(self, players[i].mo, range)) - { - return; - } - // If a player is viewing from a non-player, check that too. - if (players[i].camera != NULL && players[i].camera->player == NULL && - DoCheckRange(self, players[i].camera, range)) - { - return; - } - } - } - ACTION_JUMP(jump); -} - - -//=========================================================================== -// -// Inventory drop -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropInventory) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_CLASS(drop, 0); - - if (drop) - { - AInventory * inv = self->FindInventory(drop); - if (inv) - { - self->DropInventory(inv); - } - } -} - - -//=========================================================================== -// -// A_SetBlend -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetBlend) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_COLOR(color, 0); - ACTION_PARAM_FLOAT(alpha, 1); - ACTION_PARAM_INT(tics, 2); - ACTION_PARAM_COLOR(color2, 3); - - if (color == MAKEARGB(255,255,255,255)) color=0; - if (color2 == MAKEARGB(255,255,255,255)) color2=0; - if (!color2.a) - color2 = color; - - new DFlashFader(color.r/255.0f, color.g/255.0f, color.b/255.0f, alpha, - color2.r/255.0f, color2.g/255.0f, color2.b/255.0f, 0, - (float)tics/TICRATE, self); -} - - -//=========================================================================== -// -// A_JumpIf -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIf) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_BOOL(expression, 0); - ACTION_PARAM_STATE(jump, 1); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - if (expression) ACTION_JUMP(jump); - -} - -//=========================================================================== -// -// A_CountdownArg -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CountdownArg) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(cnt, 0); - ACTION_PARAM_STATE(state, 1); - - if (cnt<0 || cnt>=5) return; - if (!self->args[cnt]--) - { - if (self->flags&MF_MISSILE) - { - P_ExplodeMissile(self, NULL, NULL); - } - else if (self->flags&MF_SHOOTABLE) - { - P_DamageMobj (self, NULL, NULL, self->health, NAME_None, DMG_FORCED); - } - else - { - // can't use "Death" as default parameter with current DECORATE parser. - if (state == NULL) state = self->FindState(NAME_Death); - self->SetState(state); - } - } - -} - -//============================================================================ -// -// A_Burst -// -//============================================================================ - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Burst) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_CLASS(chunk, 0); - - int i, numChunks; - AActor * mo; - - if (chunk == NULL) return; - - self->velx = self->vely = self->velz = 0; - self->height = self->GetDefault()->height; - - // [RH] In Hexen, this creates a random number of shards (range [24,56]) - // with no relation to the size of the self shattering. I think it should - // base the number of shards on the size of the dead thing, so bigger - // things break up into more shards than smaller things. - // An self with radius 20 and height 64 creates ~40 chunks. - numChunks = MAX (4, (self->radius>>FRACBITS)*(self->height>>FRACBITS)/32); - i = (pr_burst.Random2()) % (numChunks/4); - for (i = MAX (24, numChunks + i); i >= 0; i--) - { - mo = Spawn(chunk, - self->x + (((pr_burst()-128)*self->radius)>>7), - self->y + (((pr_burst()-128)*self->radius)>>7), - self->z + (pr_burst()*self->height/255 + self->GetBobOffset()), ALLOW_REPLACE); - - if (mo) - { - mo->velz = FixedDiv(mo->z - self->z, self->height)<<2; - mo->velx = pr_burst.Random2 () << (FRACBITS-7); - mo->vely = pr_burst.Random2 () << (FRACBITS-7); - mo->RenderStyle = self->RenderStyle; - mo->alpha = self->alpha; - mo->CopyFriendliness(self, true); - } - } - - // [RH] Do some stuff to make this more useful outside Hexen - if (self->flags4 & MF4_BOSSDEATH) - { - CALL_ACTION(A_BossDeath, self); - } - A_Unblock(self, true); - - self->Destroy (); -} - -//=========================================================================== -// -// A_CheckFloor -// [GRB] Jumps if actor is standing on floor -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFloor) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - if (self->z <= self->floorz) - { - ACTION_JUMP(jump); - } - -} - -//=========================================================================== -// -// A_CheckCeiling -// [GZ] Totally copied on A_CheckFloor, jumps if actor touches ceiling -// - -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckCeiling) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - ACTION_SET_RESULT(false); - if (self->z+self->height >= self->ceilingz) // Height needs to be counted - { - ACTION_JUMP(jump); - } - -} - -//=========================================================================== -// -// A_Stop -// resets all velocity of the actor to 0 -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_Stop) -{ - self->velx = self->vely = self->velz = 0; - if (self->player && self->player->mo == self && !(self->player->cheats & CF_PREDICTING)) - { - self->player->mo->PlayIdle(); - self->player->velx = self->player->vely = 0; - } -} - -static void CheckStopped(AActor *self) -{ - if (self->player != NULL && - self->player->mo == self && - !(self->player->cheats & CF_PREDICTING) && - !(self->velx | self->vely | self->velz)) - { - self->player->mo->PlayIdle(); - self->player->velx = self->player->vely = 0; - } -} - -//=========================================================================== -// -// A_Respawn -// -//=========================================================================== - -extern void AF_A_RestoreSpecialPosition(DECLARE_PARAMINFO); - -enum RS_Flags -{ - RSF_FOG=1, - RSF_KEEPTARGET=2, - RSF_TELEFRAG=4, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Respawn) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(flags, 0); - - bool oktorespawn = false; - - self->flags |= MF_SOLID; - self->height = self->GetDefault()->height; - CALL_ACTION(A_RestoreSpecialPosition, self); - - if (flags & RSF_TELEFRAG) - { - // [KS] DIE DIE DIE DIE erm *ahem* =) - oktorespawn = P_TeleportMove(self, self->x, self->y, self->z, true); - if (oktorespawn) - { // Need to do this over again, since P_TeleportMove() will redo - // it with the proper point-on-side calculation. - self->UnlinkFromWorld(); - self->LinkToWorld(true); - sector_t *sec = self->Sector; - self->dropoffz = - self->floorz = sec->floorplane.ZatPoint(self->x, self->y); - self->ceilingz = sec->ceilingplane.ZatPoint(self->x, self->y); - P_FindFloorCeiling(self, FFCF_ONLYSPAWNPOS); - } - } - else - { - oktorespawn = P_CheckPosition(self, self->x, self->y, true); - } - - if (oktorespawn) - { - AActor *defs = self->GetDefault(); - self->health = defs->health; - - // [KS] Don't keep target, because it could be self if the monster committed suicide - // ...Actually it's better off an option, so you have better control over monster behavior. - if (!(flags & RSF_KEEPTARGET)) - { - self->target = NULL; - self->LastHeard = NULL; - self->lastenemy = NULL; - } - else - { - // Don't attack yourself (Re: "Marine targets itself after suicide") - if (self->target == self) self->target = NULL; - if (self->lastenemy == self) self->lastenemy = NULL; - } - - self->flags = (defs->flags & ~MF_FRIENDLY) | (self->flags & MF_FRIENDLY); - self->flags2 = defs->flags2; - self->flags3 = (defs->flags3 & ~(MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)) | (self->flags3 & (MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)); - self->flags4 = (defs->flags4 & ~MF4_NOHATEPLAYERS) | (self->flags4 & MF4_NOHATEPLAYERS); - self->flags5 = defs->flags5; - self->SetState (self->SpawnState); - self->renderflags &= ~RF_INVISIBLE; - - if (flags & RSF_FOG) - { - Spawn (self->x, self->y, self->z + TELEFOGHEIGHT, ALLOW_REPLACE); - } - if (self->CountsAsKill()) - { - level.total_monsters++; - } - } - else - { - self->flags &= ~MF_SOLID; - } -} - - -//========================================================================== -// -// A_PlayerSkinCheck -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlayerSkinCheck) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_STATE(jump, 0); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - if (self->player != NULL && - skins[self->player->userinfo.GetSkin()].othergame) - { - ACTION_JUMP(jump); - } -} - -//=========================================================================== -// -// A_SetGravity -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetGravity) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_FIXED(val, 0); - - self->gravity = clamp (val, 0, FRACUNIT*10); -} - - -// [KS] *** Start of my modifications *** - -//=========================================================================== -// -// A_ClearTarget -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_ClearTarget) -{ - self->target = NULL; - self->LastHeard = NULL; - self->lastenemy = NULL; -} - -//========================================================================== -// -// A_CheckLOF (state jump, int flags = CRF_AIM_VERT|CRF_AIM_HOR, -// fixed range = 0, angle angle = 0, angle pitch = 0, -// fixed offsetheight = 32, fixed offsetwidth = 0, -// int ptr_target = AAPTR_DEFAULT (target) ) -// -//========================================================================== - -enum CLOF_flags -{ - CLOFF_NOAIM_VERT = 0x1, - CLOFF_NOAIM_HORZ = 0x2, - - CLOFF_JUMPENEMY = 0x4, - CLOFF_JUMPFRIEND = 0x8, - CLOFF_JUMPOBJECT = 0x10, - CLOFF_JUMPNONHOSTILE = 0x20, - - CLOFF_SKIPENEMY = 0x40, - CLOFF_SKIPFRIEND = 0x80, - CLOFF_SKIPOBJECT = 0x100, - CLOFF_SKIPNONHOSTILE = 0x200, - - CLOFF_MUSTBESHOOTABLE = 0x400, - - CLOFF_SKIPTARGET = 0x800, - CLOFF_ALLOWNULL = 0x1000, - CLOFF_CHECKPARTIAL = 0x2000, - - CLOFF_MUSTBEGHOST = 0x4000, - CLOFF_IGNOREGHOST = 0x8000, - - CLOFF_MUSTBESOLID = 0x10000, - CLOFF_BEYONDTARGET = 0x20000, - - CLOFF_FROMBASE = 0x40000, - CLOFF_MUL_HEIGHT = 0x80000, - CLOFF_MUL_WIDTH = 0x100000, - - CLOFF_JUMP_ON_MISS = 0x200000, - CLOFF_AIM_VERT_NOOFFSET = 0x400000, -}; - -struct LOFData -{ - AActor *Self; - AActor *Target; - int Flags; - bool BadActor; -}; - -ETraceStatus CheckLOFTraceFunc(FTraceResults &trace, void *userdata) -{ - LOFData *data = (LOFData *)userdata; - int flags = data->Flags; - - if (trace.HitType != TRACE_HitActor) - { - return TRACE_Stop; - } - if (trace.Actor == data->Target) - { - if (flags & CLOFF_SKIPTARGET) - { - if (flags & CLOFF_BEYONDTARGET) - { - return TRACE_Skip; - } - return TRACE_Abort; - } - return TRACE_Stop; - } - if (flags & CLOFF_MUSTBESHOOTABLE) - { // all shootability checks go here - if (!(trace.Actor->flags & MF_SHOOTABLE)) - { - return TRACE_Skip; - } - if (trace.Actor->flags2 & MF2_NONSHOOTABLE) - { - return TRACE_Skip; - } - } - if ((flags & CLOFF_MUSTBESOLID) && !(trace.Actor->flags & MF_SOLID)) - { - return TRACE_Skip; - } - if (flags & CLOFF_MUSTBEGHOST) - { - if (!(trace.Actor->flags3 & MF3_GHOST)) - { - return TRACE_Skip; - } - } - else if (flags & CLOFF_IGNOREGHOST) - { - if (trace.Actor->flags3 & MF3_GHOST) - { - return TRACE_Skip; - } - } - if ( - ((flags & CLOFF_JUMPENEMY) && data->Self->IsHostile(trace.Actor)) || - ((flags & CLOFF_JUMPFRIEND) && data->Self->IsFriend(trace.Actor)) || - ((flags & CLOFF_JUMPOBJECT) && !(trace.Actor->flags3 & MF3_ISMONSTER)) || - ((flags & CLOFF_JUMPNONHOSTILE) && (trace.Actor->flags3 & MF3_ISMONSTER) && !data->Self->IsHostile(trace.Actor)) - ) - { - return TRACE_Stop; - } - if ( - ((flags & CLOFF_SKIPENEMY) && data->Self->IsHostile(trace.Actor)) || - ((flags & CLOFF_SKIPFRIEND) && data->Self->IsFriend(trace.Actor)) || - ((flags & CLOFF_SKIPOBJECT) && !(trace.Actor->flags3 & MF3_ISMONSTER)) || - ((flags & CLOFF_SKIPNONHOSTILE) && (trace.Actor->flags3 & MF3_ISMONSTER) && !data->Self->IsHostile(trace.Actor)) - ) - { - return TRACE_Skip; - } - data->BadActor = true; - return TRACE_Abort; -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF) -{ - // Check line of fire - - /* - Not accounted for / I don't know how it works: FLOORCLIP - */ - - AActor *target; - fixed_t - x1, y1, z1, - vx, vy, vz; - - ACTION_PARAM_START(9); - - ACTION_PARAM_STATE(jump, 0); - ACTION_PARAM_INT(flags, 1); - ACTION_PARAM_FIXED(range, 2); - ACTION_PARAM_FIXED(minrange, 3); - { - ACTION_PARAM_ANGLE(angle, 4); - ACTION_PARAM_ANGLE(pitch, 5); - ACTION_PARAM_FIXED(offsetheight, 6); - ACTION_PARAM_FIXED(offsetwidth, 7); - ACTION_PARAM_INT(ptr_target, 8); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - target = COPY_AAPTR(self, ptr_target == AAPTR_DEFAULT ? AAPTR_TARGET|AAPTR_PLAYER_GETTARGET|AAPTR_NULL : ptr_target); // no player-support by default - - if (flags & CLOFF_MUL_HEIGHT) - { - if (self->player != NULL) - { - // Synced with hitscan: self->player->mo->height is strangely conscientious about getting the right actor for player - offsetheight = FixedMul(offsetheight, FixedMul (self->player->mo->height, self->player->crouchfactor)); - } - else - { - offsetheight = FixedMul(offsetheight, self->height); - } - } - if (flags & CLOFF_MUL_WIDTH) - { - offsetwidth = FixedMul(self->radius, offsetwidth); - } - - x1 = self->x; - y1 = self->y; - z1 = self->z + offsetheight - self->floorclip; - - if (!(flags & CLOFF_FROMBASE)) - { // default to hitscan origin - - // Synced with hitscan: self->height is strangely NON-conscientious about getting the right actor for player - z1 += (self->height >> 1); - if (self->player != NULL) - { - z1 += FixedMul (self->player->mo->AttackZOffset, self->player->crouchfactor); - } - else - { - z1 += 8*FRACUNIT; - } - } - - if (target) - { - FVector2 xyvec(target->x - x1, target->y - y1); - fixed_t distance = P_AproxDistance((fixed_t)xyvec.Length(), target->z - z1); - - if (range && !(flags & CLOFF_CHECKPARTIAL)) - { - if (distance > range) return; - } - - { - angle_t ang; - - if (flags & CLOFF_NOAIM_HORZ) - { - ang = self->angle; - } - else ang = R_PointToAngle2 (x1, y1, target->x, target->y); - - angle += ang; - - ang >>= ANGLETOFINESHIFT; - x1 += FixedMul(offsetwidth, finesine[ang]); - y1 -= FixedMul(offsetwidth, finecosine[ang]); - } - - if (flags & CLOFF_NOAIM_VERT) - { - pitch += self->pitch; - } - else if (flags & CLOFF_AIM_VERT_NOOFFSET) - { - pitch += R_PointToAngle2 (0,0, (fixed_t)xyvec.Length(), target->z - z1 + offsetheight + target->height / 2); - } - else - { - pitch += R_PointToAngle2 (0,0, (fixed_t)xyvec.Length(), target->z - z1 + target->height / 2); - } - } - else if (flags & CLOFF_ALLOWNULL) - { - angle += self->angle; - pitch += self->pitch; - - angle_t ang = self->angle >> ANGLETOFINESHIFT; - x1 += FixedMul(offsetwidth, finesine[ang]); - y1 -= FixedMul(offsetwidth, finecosine[ang]); - } - else return; - - angle >>= ANGLETOFINESHIFT; - pitch = (0-pitch)>>ANGLETOFINESHIFT; - - vx = FixedMul (finecosine[pitch], finecosine[angle]); - vy = FixedMul (finecosine[pitch], finesine[angle]); - vz = -finesine[pitch]; - } - - /* Variable set: - - jump, flags, target - x1,y1,z1 (trace point of origin) - vx,vy,vz (trace unit vector) - range - */ - - sector_t *sec = P_PointInSector(x1, y1); - - if (range == 0) - { - range = (self->player != NULL) ? PLAYERMISSILERANGE : MISSILERANGE; - } - - FTraceResults trace; - LOFData lof_data; - - lof_data.Self = self; - lof_data.Target = target; - lof_data.Flags = flags; - lof_data.BadActor = false; - - Trace(x1, y1, z1, sec, vx, vy, vz, range, 0xFFFFFFFF, ML_BLOCKEVERYTHING, self, trace, 0, - CheckLOFTraceFunc, &lof_data); - - if (trace.HitType == TRACE_HitActor || - ((flags & CLOFF_JUMP_ON_MISS) && !lof_data.BadActor && trace.HitType != TRACE_HitNone)) - { - if (minrange > 0 && trace.Distance < minrange) - { - return; - } - ACTION_JUMP(jump); - } -} - -//========================================================================== -// -// A_JumpIfTargetInLOS (state label, optional fixed fov, optional int flags, -// optional fixed dist_max, optional fixed dist_close) -// -// Jumps if the actor can see its target, or if the player has a linetarget. -// ProjectileTarget affects how projectiles are treated. If set, it will use -// the target of the projectile for seekers, and ignore the target for -// normal projectiles. If not set, it will use the missile's owner instead -// (the default). ProjectileTarget is now flag JLOSF_PROJECTILE. dist_max -// sets the maximum distance that actor can see, 0 means forever. dist_close -// uses special behavior if certain flags are set, 0 means no checks. -// -//========================================================================== - -enum JLOS_flags -{ - JLOSF_PROJECTILE=1, - JLOSF_NOSIGHT=2, - JLOSF_CLOSENOFOV=4, - JLOSF_CLOSENOSIGHT=8, - JLOSF_CLOSENOJUMP=16, - JLOSF_DEADNOJUMP=32, - JLOSF_CHECKMASTER=64, - JLOSF_TARGETLOS=128, - JLOSF_FLIPFOV=256, - JLOSF_ALLYNOJUMP=512, - JLOSF_COMBATANTONLY=1024, - JLOSF_NOAUTOAIM=2048, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_STATE(jump, 0); - ACTION_PARAM_ANGLE(fov, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_FIXED(dist_max, 3); - ACTION_PARAM_FIXED(dist_close, 4); - - angle_t an; - AActor *target, *viewport; - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - bool doCheckSight; - - if (!self->player) - { - if (flags & JLOSF_CHECKMASTER) - { - target = self->master; - } - else if (self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE)) - { - if (self->flags2 & MF2_SEEKERMISSILE) - target = self->tracer; - else - target = NULL; - } - else - { - target = self->target; - } - - if (!target) return; // [KS] Let's not call P_CheckSight unnecessarily in this case. - - if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0)) return; - - doCheckSight = !(flags & JLOSF_NOSIGHT); - } - else - { - // Does the player aim at something that can be shot? - P_AimLineAttack(self, self->angle, MISSILERANGE, &target, (flags & JLOSF_NOAUTOAIM) ? ANGLE_1/2 : 0); - - if (!target) return; - - switch (flags & (JLOSF_TARGETLOS|JLOSF_FLIPFOV)) - { - case JLOSF_TARGETLOS|JLOSF_FLIPFOV: - // target makes sight check, player makes fov check; player has verified fov - fov = 0; - // fall-through - case JLOSF_TARGETLOS: - doCheckSight = !(flags & JLOSF_NOSIGHT); // The target is responsible for sight check and fov - break; - default: - // player has verified sight and fov - fov = 0; - // fall-through - case JLOSF_FLIPFOV: // Player has verified sight, but target must verify fov - doCheckSight = false; - break; - } - } - - // [FDARI] If target is not a combatant, don't jump - if ( (flags & JLOSF_COMBATANTONLY) && (!target->player) && !(target->flags3 & MF3_ISMONSTER)) return; - - // [FDARI] If actors share team, don't jump - if ((flags & JLOSF_ALLYNOJUMP) && self->IsFriend(target)) return; - - fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y); - distance = P_AproxDistance(distance, target->z - self->z); - - if (dist_max && (distance > dist_max)) return; - - if (dist_close && (distance < dist_close)) - { - if (flags & JLOSF_CLOSENOJUMP) - return; - - if (flags & JLOSF_CLOSENOFOV) - fov = 0; - - if (flags & JLOSF_CLOSENOSIGHT) - doCheckSight = false; - } - - if (flags & JLOSF_TARGETLOS) { viewport = target; target = self; } - else { viewport = self; } - - if (doCheckSight && !P_CheckSight (viewport, target, SF_IGNOREVISIBILITY)) - return; - - if (flags & JLOSF_FLIPFOV) - { - if (viewport == self) { viewport = target; target = self; } - else { target = viewport; viewport = self; } - } - - if (fov && (fov < ANGLE_MAX)) - { - an = R_PointToAngle2 (viewport->x, - viewport->y, - target->x, - target->y) - - viewport->angle; - - if (an > (fov / 2) && an < (ANGLE_MAX - (fov / 2))) - { - return; // [KS] Outside of FOV - return - } - - } - - ACTION_JUMP(jump); -} - - -//========================================================================== -// -// A_JumpIfInTargetLOS (state label, optional fixed fov, optional int flags -// optional fixed dist_max, optional fixed dist_close) -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_STATE(jump, 0); - ACTION_PARAM_ANGLE(fov, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_FIXED(dist_max, 3); - ACTION_PARAM_FIXED(dist_close, 4); - - angle_t an; - AActor *target; - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - if (flags & JLOSF_CHECKMASTER) - { - target = self->master; - } - else if (self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE)) - { - if (self->flags2 & MF2_SEEKERMISSILE) - target = self->tracer; - else - target = NULL; - } - else - { - target = self->target; - } - - if (!target) return; // [KS] Let's not call P_CheckSight unnecessarily in this case. - - if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0)) return; - - fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y); - distance = P_AproxDistance(distance, target->z - self->z); - - if (dist_max && (distance > dist_max)) return; - - bool doCheckSight = !(flags & JLOSF_NOSIGHT); - - if (dist_close && (distance < dist_close)) - { - if (flags & JLOSF_CLOSENOJUMP) - return; - - if (flags & JLOSF_CLOSENOFOV) - fov = 0; - - if (flags & JLOSF_CLOSENOSIGHT) - doCheckSight = false; - } - - if (fov && (fov < ANGLE_MAX)) - { - an = R_PointToAngle2 (target->x, - target->y, - self->x, - self->y) - - target->angle; - - if (an > (fov / 2) && an < (ANGLE_MAX - (fov / 2))) - { - return; // [KS] Outside of FOV - return - } - } - - if (doCheckSight && !P_CheckSight (target, self, SF_IGNOREVISIBILITY)) - return; - - ACTION_JUMP(jump); -} - -//=========================================================================== -// -// Modified code pointer from Skulltag -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckForReload) -{ - if ( self->player == NULL || self->player->ReadyWeapon == NULL ) - return; - - ACTION_PARAM_START(2); - ACTION_PARAM_INT(count, 0); - ACTION_PARAM_STATE(jump, 1); - ACTION_PARAM_BOOL(dontincrement, 2) - - if (count <= 0) return; - - AWeapon *weapon = self->player->ReadyWeapon; - - int ReloadCounter = weapon->ReloadCounter; - if(!dontincrement || ReloadCounter != 0) - ReloadCounter = (weapon->ReloadCounter+1) % count; - else // 0 % 1 = 1? So how do we check if the weapon was never fired? We should only do this when we're not incrementing the counter though. - ReloadCounter = 1; - - // If we have not made our last shot... - if (ReloadCounter != 0) - { - // Go back to the refire frames, instead of continuing on to the reload frames. - ACTION_JUMP(jump); - } - else - { - // We need to reload. However, don't reload if we're out of ammo. - weapon->CheckAmmo( false, false ); - } - - if(!dontincrement) - weapon->ReloadCounter = ReloadCounter; -} - -//=========================================================================== -// -// Resets the counter for the above function -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION(AActor, A_ResetReloadCounter) -{ - if ( self->player == NULL || self->player->ReadyWeapon == NULL ) - return; - - AWeapon *weapon = self->player->ReadyWeapon; - weapon->ReloadCounter = 0; -} - -//=========================================================================== -// -// A_ChangeFlag -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_STRING(flagname, 0); - ACTION_PARAM_BOOL(expression, 1); - - const char *dot = strchr (flagname, '.'); - FFlagDef *fd; - const PClass *cls = self->GetClass(); - - if (dot != NULL) - { - FString part1(flagname, dot-flagname); - fd = FindFlag (cls, part1, dot+1); - } - else - { - fd = FindFlag (cls, flagname, NULL); - } - - if (fd != NULL) - { - bool kill_before, kill_after; - INTBOOL item_before, item_after; - INTBOOL secret_before, secret_after; - - kill_before = self->CountsAsKill(); - item_before = self->flags & MF_COUNTITEM; - secret_before = self->flags5 & MF5_COUNTSECRET; - - if (fd->structoffset == -1) - { - HandleDeprecatedFlags(self, cls->ActorInfo, expression, fd->flagbit); - } - else - { - DWORD *flagp = (DWORD*) (((char*)self) + fd->structoffset); - - // If these 2 flags get changed we need to update the blockmap and sector links. - bool linkchange = flagp == &self->flags && (fd->flagbit == MF_NOBLOCKMAP || fd->flagbit == MF_NOSECTOR); - - if (linkchange) self->UnlinkFromWorld(); - ModActorFlag(self, fd, expression); - if (linkchange) self->LinkToWorld(); - } - kill_after = self->CountsAsKill(); - item_after = self->flags & MF_COUNTITEM; - secret_after = self->flags5 & MF5_COUNTSECRET; - // Was this monster previously worth a kill but no longer is? - // Or vice versa? - if (kill_before != kill_after) - { - if (kill_after) - { // It counts as a kill now. - level.total_monsters++; - } - else - { // It no longer counts as a kill. - level.total_monsters--; - } - } - // same for items - if (item_before != item_after) - { - if (item_after) - { // It counts as an item now. - level.total_items++; - } - else - { // It no longer counts as an item - level.total_items--; - } - } - // and secretd - if (secret_before != secret_after) - { - if (secret_after) - { // It counts as an secret now. - level.total_secrets++; - } - else - { // It no longer counts as an secret - level.total_secrets--; - } - } - } - else - { - Printf("Unknown flag '%s' in '%s'\n", flagname, cls->TypeName.GetChars()); - } -} - -//=========================================================================== -// -// A_CheckFlag -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFlag) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_STRING(flagname, 0); - ACTION_PARAM_STATE(jumpto, 1); - ACTION_PARAM_INT(checkpointer, 2); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - AActor *owner; - - COPY_AAPTR_NOT_NULL(self, owner, checkpointer); - - if (CheckActorFlag(owner, flagname)) - { - ACTION_JUMP(jumpto); - } -} - - -//=========================================================================== -// -// A_RemoveMaster -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RemoveMaster) -{ - if (self->master != NULL) - { - P_RemoveThing(self->master); - } -} - -//=========================================================================== -// -// A_RemoveChildren -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveChildren) -{ - TThinkerIterator it; - AActor *mo; - ACTION_PARAM_START(1); - ACTION_PARAM_BOOL(removeall,0); - - while ((mo = it.Next()) != NULL) - { - if (mo->master == self && (mo->health <= 0 || removeall)) - { - P_RemoveThing(mo); - } - } -} - -//=========================================================================== -// -// A_RemoveSiblings -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) -{ - TThinkerIterator it; - AActor *mo; - ACTION_PARAM_START(1); - ACTION_PARAM_BOOL(removeall,0); - - if (self->master != NULL) - { - while ((mo = it.Next()) != NULL) - { - if (mo->master == self->master && mo != self && (mo->health <= 0 || removeall)) - { - P_RemoveThing(mo); - } - } - } -} - -//=========================================================================== -// -// A_RaiseMaster -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RaiseMaster) -{ - if (self->master != NULL) - { - P_Thing_Raise(self->master); - } -} - -//=========================================================================== -// -// A_RaiseChildren -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RaiseChildren) -{ - TThinkerIterator it; - AActor *mo; - - while ((mo = it.Next()) != NULL) - { - if (mo->master == self) - { - P_Thing_Raise(mo); - } - } -} - -//=========================================================================== -// -// A_RaiseSiblings -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings) -{ - TThinkerIterator it; - AActor *mo; - - if (self->master != NULL) - { - while ((mo = it.Next()) != NULL) - { - if (mo->master == self->master && mo != self) - { - P_Thing_Raise(mo); - } - } - } -} - -//=========================================================================== -// -// A_MonsterRefire -// -// Keep firing unless target got out of sight -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_MonsterRefire) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(prob, 0); - ACTION_PARAM_STATE(jump, 1); - - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - A_FaceTarget (self); - - if (pr_monsterrefire() < prob) - return; - - if (!self->target - || P_HitFriend (self) - || self->target->health <= 0 - || !P_CheckSight (self, self->target, SF_SEEPASTBLOCKEVERYTHING|SF_SEEPASTSHOOTABLELINES) ) - { - ACTION_JUMP(jump); - } -} - -//=========================================================================== -// -// A_SetAngle -// -// Set actor's angle (in degrees). -// -//=========================================================================== -enum -{ - SPF_FORCECLAMP = 1, // players always clamp - SPF_INTERPOLATE = 2, -}; - - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetAngle) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_ANGLE(angle, 0); - ACTION_PARAM_INT(flags, 1) - self->SetAngle(angle, !!(flags & SPF_INTERPOLATE)); -} - -//=========================================================================== -// -// A_SetPitch -// -// Set actor's pitch (in degrees). -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPitch) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_ANGLE(pitch, 0); - ACTION_PARAM_INT(flags, 1); - - if (self->player != NULL || (flags & SPF_FORCECLAMP)) - { // clamp the pitch we set - int min, max; - - if (self->player != NULL) - { - min = self->player->MinPitch; - max = self->player->MaxPitch; - } - else - { - min = -ANGLE_90 + (1 << ANGLETOFINESHIFT); - max = ANGLE_90 - (1 << ANGLETOFINESHIFT); - } - pitch = clamp(pitch, min, max); - } - self->SetPitch(pitch, !!(flags & SPF_INTERPOLATE)); -} - -//=========================================================================== -// -// A_ScaleVelocity -// -// Scale actor's velocity. -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ScaleVelocity) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_FIXED(scale, 0); - - INTBOOL was_moving = self->velx | self->vely | self->velz; - - self->velx = FixedMul(self->velx, scale); - self->vely = FixedMul(self->vely, scale); - self->velz = FixedMul(self->velz, scale); - - // If the actor was previously moving but now is not, and is a player, - // update its player variables. (See A_Stop.) - if (was_moving) - { - CheckStopped(self); - } -} - -//=========================================================================== -// -// A_ChangeVelocity -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeVelocity) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_FIXED(x, 0); - ACTION_PARAM_FIXED(y, 1); - ACTION_PARAM_FIXED(z, 2); - ACTION_PARAM_INT(flags, 3); - - INTBOOL was_moving = self->velx | self->vely | self->velz; - - fixed_t vx = x, vy = y, vz = z; - fixed_t sina = finesine[self->angle >> ANGLETOFINESHIFT]; - fixed_t cosa = finecosine[self->angle >> ANGLETOFINESHIFT]; - - if (flags & 1) // relative axes - make x, y relative to actor's current angle - { - vx = DMulScale16(x, cosa, -y, sina); - vy = DMulScale16(x, sina, y, cosa); - } - if (flags & 2) // discard old velocity - replace old velocity with new velocity - { - self->velx = vx; - self->vely = vy; - self->velz = vz; - } - else // add new velocity to old velocity - { - self->velx += vx; - self->vely += vy; - self->velz += vz; - } - - if (was_moving) - { - CheckStopped(self); - } -} - -//=========================================================================== -// -// A_SetArg -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetArg) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(pos, 0); - ACTION_PARAM_INT(value, 1); - - // Set the value of the specified arg - if ((size_t)pos < countof(self->args)) - { - self->args[pos] = value; - } -} - -//=========================================================================== -// -// A_SetSpecial -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpecial) -{ - ACTION_PARAM_START(6); - ACTION_PARAM_INT(spec, 0); - ACTION_PARAM_INT(arg0, 1); - ACTION_PARAM_INT(arg1, 2); - ACTION_PARAM_INT(arg2, 3); - ACTION_PARAM_INT(arg3, 4); - ACTION_PARAM_INT(arg4, 5); - - self->special = spec; - self->args[0] = arg0; - self->args[1] = arg1; - self->args[2] = arg2; - self->args[3] = arg3; - self->args[4] = arg4; -} - -//=========================================================================== -// -// A_SetUserVar -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(varname, 0); - ACTION_PARAM_INT(value, 1); - - PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true); - PSymbolVariable *var; - - if (sym == NULL || sym->SymbolType != SYM_Variable || - !(var = static_cast(sym))->bUserVar || - var->ValueType.Type != VAL_Int) - { - Printf("%s is not a user variable in class %s\n", varname.GetChars(), - self->GetClass()->TypeName.GetChars()); - return; - } - // Set the value of the specified user variable. - *(int *)(reinterpret_cast(self) + var->offset) = value; -} - -//=========================================================================== -// -// A_SetUserArray -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_NAME(varname, 0); - ACTION_PARAM_INT(pos, 1); - ACTION_PARAM_INT(value, 2); - - PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true); - PSymbolVariable *var; - - if (sym == NULL || sym->SymbolType != SYM_Variable || - !(var = static_cast(sym))->bUserVar || - var->ValueType.Type != VAL_Array || var->ValueType.BaseType != VAL_Int) - { - Printf("%s is not a user array in class %s\n", varname.GetChars(), - self->GetClass()->TypeName.GetChars()); - return; - } - if (pos < 0 || pos >= var->ValueType.size) - { - Printf("%d is out of bounds in array %s in class %s\n", pos, varname.GetChars(), - self->GetClass()->TypeName.GetChars()); - return; - } - // Set the value of the specified user array at index pos. - ((int *)(reinterpret_cast(self) + var->offset))[pos] = value; -} - -//=========================================================================== -// -// A_Teleport(optional state teleportstate, optional class targettype, -// optional class fogtype, optional int flags, optional fixed mindist, -// optional fixed maxdist) -// -// Attempts to teleport to a targettype at least mindist away and at most -// maxdist away (0 means unlimited). If successful, spawn a fogtype at old -// location and place calling actor in teleportstate. -// -//=========================================================================== -enum T_Flags -{ - TF_TELEFRAG = 1, // Allow telefrag in order to teleport. - TF_RANDOMDECIDE = 2, // Randomly fail based on health. (A_Srcr2Decide) -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport) -{ - ACTION_PARAM_START(6); - ACTION_PARAM_STATE(TeleportState, 0); - ACTION_PARAM_CLASS(TargetType, 1); - ACTION_PARAM_CLASS(FogType, 2); - ACTION_PARAM_INT(Flags, 3); - ACTION_PARAM_FIXED(MinDist, 4); - ACTION_PARAM_FIXED(MaxDist, 5); - - // Randomly choose not to teleport like A_Srcr2Decide. - if (Flags & TF_RANDOMDECIDE) - { - static const int chance[] = - { - 192, 120, 120, 120, 64, 64, 32, 16, 0 - }; - - unsigned int chanceindex = self->health / ((self->SpawnHealth()/8 == 0) ? 1 : self->SpawnHealth()/8); - - if (chanceindex >= countof(chance)) - { - chanceindex = countof(chance) - 1; - } - - if (pr_teleport() >= chance[chanceindex]) return; - } - - if (TeleportState == NULL) - { - // Default to Teleport. - TeleportState = self->FindState("Teleport"); - // If still nothing, then return. - if (!TeleportState) return; - } - - DSpotState *state = DSpotState::GetSpotState(); - if (state == NULL) return; - - if (!TargetType) TargetType = PClass::FindClass("BossSpot"); - - AActor * spot = state->GetSpotWithMinMaxDistance(TargetType, self->x, self->y, MinDist, MaxDist); - if (spot == NULL) return; - - fixed_t prevX = self->x; - fixed_t prevY = self->y; - fixed_t prevZ = self->z; - if (P_TeleportMove (self, spot->x, spot->y, spot->z, Flags & TF_TELEFRAG)) - { - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - - if (FogType) - { - Spawn(FogType, prevX, prevY, prevZ, ALLOW_REPLACE); - } - - ACTION_JUMP(TeleportState); - - self->z = self->floorz; - self->angle = spot->angle; - self->velx = self->vely = self->velz = 0; - } -} - -//=========================================================================== -// -// A_Turn -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Turn) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_ANGLE(angle, 0); - self->angle += angle; -} - -//=========================================================================== -// -// A_Quake -// -//=========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Quake) -{ - ACTION_PARAM_START(5); - ACTION_PARAM_INT(intensity, 0); - ACTION_PARAM_INT(duration, 1); - ACTION_PARAM_INT(damrad, 2); - ACTION_PARAM_INT(tremrad, 3); - ACTION_PARAM_SOUND(sound, 4); - P_StartQuake(self, 0, intensity, duration, damrad, tremrad, sound); -} - -//=========================================================================== -// -// A_Weave -// -//=========================================================================== - -void A_Weave(AActor *self, int xyspeed, int zspeed, fixed_t xydist, fixed_t zdist) -{ - fixed_t newX, newY; - int weaveXY, weaveZ; - int angle; - fixed_t dist; - - weaveXY = self->WeaveIndexXY & 63; - weaveZ = self->WeaveIndexZ & 63; - angle = (self->angle + ANG90) >> ANGLETOFINESHIFT; - - if (xydist != 0 && xyspeed != 0) - { - dist = MulScale13(finesine[weaveXY << BOBTOFINESHIFT], xydist); - newX = self->x - FixedMul (finecosine[angle], dist); - newY = self->y - FixedMul (finesine[angle], dist); - weaveXY = (weaveXY + xyspeed) & 63; - dist = MulScale13(finesine[weaveXY << BOBTOFINESHIFT], xydist); - newX += FixedMul (finecosine[angle], dist); - newY += FixedMul (finesine[angle], dist); - if (!(self->flags5 & MF5_NOINTERACTION)) - { - P_TryMove (self, newX, newY, true); - } - else - { - self->UnlinkFromWorld (); - self->flags |= MF_NOBLOCKMAP; - self->x = newX; - self->y = newY; - self->LinkToWorld (); - } - self->WeaveIndexXY = weaveXY; - } - if (zdist != 0 && zspeed != 0) - { - self->z -= MulScale13(finesine[weaveZ << BOBTOFINESHIFT], zdist); - weaveZ = (weaveZ + zspeed) & 63; - self->z += MulScale13(finesine[weaveZ << BOBTOFINESHIFT], zdist); - self->WeaveIndexZ = weaveZ; - } -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Weave) -{ - ACTION_PARAM_START(4); - ACTION_PARAM_INT(xspeed, 0); - ACTION_PARAM_INT(yspeed, 1); - ACTION_PARAM_FIXED(xdist, 2); - ACTION_PARAM_FIXED(ydist, 3); - A_Weave(self, xspeed, yspeed, xdist, ydist); -} - - - - -//=========================================================================== -// -// A_LineEffect -// -// This allows linedef effects to be activated inside deh frames. -// -//=========================================================================== - - -void P_TranslateLineDef (line_t *ld, maplinedef_t *mld); -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LineEffect) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_INT(special, 0); - ACTION_PARAM_INT(tag, 1); - - line_t junk; maplinedef_t oldjunk; - bool res = false; - if (!(self->flags6 & MF6_LINEDONE)) // Unless already used up - { - if ((oldjunk.special = special)) // Linedef type - { - oldjunk.tag = tag; // Sector tag for linedef - P_TranslateLineDef(&junk, &oldjunk); // Turn into native type - res = !!P_ExecuteSpecial(junk.special, NULL, self, false, junk.args[0], - junk.args[1], junk.args[2], junk.args[3], junk.args[4]); - if (res && !(junk.flags & ML_REPEAT_SPECIAL)) // If only once, - self->flags6 |= MF6_LINEDONE; // no more for this thing - } - } - ACTION_SET_RESULT(res); -} - -//========================================================================== -// -// A Wolf3D-style attack codepointer -// -//========================================================================== -enum WolfAttackFlags -{ - WAF_NORANDOM = 1, - WAF_USEPUFF = 2, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_WolfAttack) -{ - ACTION_PARAM_START(9); - ACTION_PARAM_INT(flags, 0); - ACTION_PARAM_SOUND(sound, 1); - ACTION_PARAM_FIXED(snipe, 2); - ACTION_PARAM_INT(maxdamage, 3); - ACTION_PARAM_INT(blocksize, 4); - ACTION_PARAM_INT(pointblank, 5); - ACTION_PARAM_INT(longrange, 6); - ACTION_PARAM_FIXED(runspeed, 7); - ACTION_PARAM_CLASS(pufftype, 8); - - if (!self->target) - return; - - // Enemy can't see target - if (!P_CheckSight(self, self->target)) - return; - - A_FaceTarget (self); - - - // Target can dodge if it can see enemy - angle_t angle = R_PointToAngle2(self->target->x, self->target->y, self->x, self->y) - self->target->angle; - angle >>= 24; - bool dodge = (P_CheckSight(self->target, self) && (angle>226 || angle<30)); - - // Distance check is simplistic - fixed_t dx = abs (self->x - self->target->x); - fixed_t dy = abs (self->y - self->target->y); - fixed_t dz; - fixed_t dist = dx > dy ? dx : dy; - - // Some enemies are more precise - dist = FixedMul(dist, snipe); - - // Convert distance into integer number of blocks - dist >>= FRACBITS; - dist /= blocksize; - - // Now for the speed accuracy thingie - fixed_t speed = FixedMul(self->target->velx, self->target->velx) - + FixedMul(self->target->vely, self->target->vely) - + FixedMul(self->target->velz, self->target->velz); - int hitchance = speed < runspeed ? 256 : 160; - - // Distance accuracy (factoring dodge) - hitchance -= dist * (dodge ? 16 : 8); - - // While we're here, we may as well do something for this: - if (self->target->flags & MF_SHADOW) - { - hitchance >>= 2; - } - - // The attack itself - if (pr_cabullet() < hitchance) - { - // Compute position for spawning blood/puff - dx = self->target->x; - dy = self->target->y; - dz = self->target->z + (self->target->height>>1); - angle = R_PointToAngle2(dx, dy, self->x, self->y); - - dx += FixedMul(self->target->radius, finecosine[angle>>ANGLETOFINESHIFT]); - dy += FixedMul(self->target->radius, finesine[angle>>ANGLETOFINESHIFT]); - - int damage = flags & WAF_NORANDOM ? maxdamage : (1 + (pr_cabullet() % maxdamage)); - if (dist >= pointblank) - damage >>= 1; - if (dist >= longrange) - damage >>= 1; - FName mod = NAME_None; - bool spawnblood = !((self->target->flags & MF_NOBLOOD) - || (self->target->flags2 & (MF2_INVULNERABLE|MF2_DORMANT))); - if (flags & WAF_USEPUFF && pufftype) - { - AActor * dpuff = GetDefaultByType(pufftype->GetReplacement()); - mod = dpuff->DamageType; - - if (dpuff->flags2 & MF2_THRUGHOST && self->target->flags3 & MF3_GHOST) - damage = 0; - - if ((0 && dpuff->flags3 & MF3_PUFFONACTORS) || !spawnblood) - { - spawnblood = false; - P_SpawnPuff(self, pufftype, dx, dy, dz, angle, 0); - } - } - else if (self->target->flags3 & MF3_GHOST) - damage >>= 2; - if (damage) - { - int newdam = P_DamageMobj(self->target, self, self, damage, mod, DMG_THRUSTLESS); - if (spawnblood) - { - P_SpawnBlood(dx, dy, dz, angle, newdam > 0 ? newdam : damage, self->target); - P_TraceBleed(newdam > 0 ? newdam : damage, self->target, R_PointToAngle2(self->x, self->y, dx, dy), 0); - } - } - } - - // And finally, let's play the sound - S_Sound (self, CHAN_WEAPON, sound, 1, ATTN_NORM); -} - - -//========================================================================== -// -// A_Warp -// -//========================================================================== - -enum WARPF -{ - WARPF_ABSOLUTEOFFSET = 0x1, - WARPF_ABSOLUTEANGLE = 0x2, - WARPF_USECALLERANGLE = 0x4, - - WARPF_NOCHECKPOSITION = 0x8, - - WARPF_INTERPOLATE = 0x10, - WARPF_WARPINTERPOLATION = 0x20, - WARPF_COPYINTERPOLATION = 0x40, - - WARPF_STOP = 0x80, - WARPF_TOFLOOR = 0x100, - WARPF_TESTONLY = 0x200, - WARPF_ABSOLUTEPOSITION = 0x400, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) -{ - ACTION_PARAM_START(7); - - ACTION_PARAM_INT(destination_selector, 0); - ACTION_PARAM_FIXED(xofs, 1); - ACTION_PARAM_FIXED(yofs, 2); - ACTION_PARAM_FIXED(zofs, 3); - ACTION_PARAM_ANGLE(angle, 4); - ACTION_PARAM_INT(flags, 5); - ACTION_PARAM_STATE(success_state, 6); - - fixed_t - - oldx, - oldy, - oldz; - - AActor *reference = COPY_AAPTR(self, destination_selector); - - if (!reference) - { - ACTION_SET_RESULT(false); - return; - } - - if (!(flags & WARPF_ABSOLUTEANGLE)) - { - angle += (flags & WARPF_USECALLERANGLE) ? self->angle : reference->angle; - } - if (!(flags & WARPF_ABSOLUTEPOSITION)) - { - if (!(flags & WARPF_ABSOLUTEOFFSET)) - { - angle_t fineangle = angle >> ANGLETOFINESHIFT; - oldx = xofs; - - // (borrowed from A_SpawnItemEx, assumed workable) - // in relative mode negative y values mean 'left' and positive ones mean 'right' - // This is the inverse orientation of the absolute mode! - - xofs = FixedMul(oldx, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); - yofs = FixedMul(oldx, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); - } - - oldx = self->x; - oldy = self->y; - oldz = self->z; - - if (flags & WARPF_TOFLOOR) - { - // set correct xy - - self->SetOrigin( - reference->x + xofs, - reference->y + yofs, - reference->z); - - // now the caller's floorz should be appropriate for the assigned xy-position - // assigning position again with - - if (zofs) - { - // extra unlink, link and environment calculation - self->SetOrigin( - self->x, - self->y, - self->floorz + zofs); - } - else - { - // if there is no offset, there should be no ill effect from moving down to the - // already identified floor - - // A_Teleport does the same thing anyway - self->z = self->floorz; - } - } - else - { - self->SetOrigin( - reference->x + xofs, - reference->y + yofs, - reference->z + zofs); - } - } - else //[MC] The idea behind "absolute" is meant to be "absolute". Override everything, just like A_SpawnItemEx's. - { - if (flags & WARPF_TOFLOOR) - { - self->SetOrigin(xofs, yofs, self->floorz + zofs); - } - else - { - self->SetOrigin(xofs, yofs, zofs); - } - } - - if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(self)) - { - if (flags & WARPF_TESTONLY) - { - self->SetOrigin(oldx, oldy, oldz); - } - else - { - self->angle = angle; - - if (flags & WARPF_STOP) - { - self->velx = 0; - self->vely = 0; - self->velz = 0; - } - - if (flags & WARPF_WARPINTERPOLATION) - { - self->PrevX += self->x - oldx; - self->PrevY += self->y - oldy; - self->PrevZ += self->z - oldz; - } - else if (flags & WARPF_COPYINTERPOLATION) - { - self->PrevX = self->x + reference->PrevX - reference->x; - self->PrevY = self->y + reference->PrevY - reference->y; - self->PrevZ = self->z + reference->PrevZ - reference->z; - } - else if (! (flags & WARPF_INTERPOLATE)) - { - self->PrevX = self->x; - self->PrevY = self->y; - self->PrevZ = self->z; - } - } - - if (success_state) - { - ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! - // in this case, you have the statejump to help you handle all the success anyway. - ACTION_JUMP(success_state); - return; - } - - ACTION_SET_RESULT(true); - } - else - { - self->SetOrigin(oldx, oldy, oldz); - ACTION_SET_RESULT(false); - } - -} - -//========================================================================== -// -// ACS_Named* stuff - -// -// These are exactly like their un-named line special equivalents, except -// they take strings instead of integers to indicate which script to run. -// Some of these probably aren't very useful, but they are included for -// the sake of completeness. -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecuteWithResult) -{ - ACTION_PARAM_START(5); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(arg1, 1); - ACTION_PARAM_INT(arg2, 2); - ACTION_PARAM_INT(arg3, 3); - ACTION_PARAM_INT(arg4, 4); - - bool res = !!P_ExecuteSpecial(ACS_ExecuteWithResult, NULL, self, false, -scriptname, arg1, arg2, arg3, arg4); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecute) -{ - ACTION_PARAM_START(5); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - ACTION_PARAM_INT(arg1, 2); - ACTION_PARAM_INT(arg2, 3); - ACTION_PARAM_INT(arg3, 4); - - bool res = !!P_ExecuteSpecial(ACS_Execute, NULL, self, false, -scriptname, mapnum, arg1, arg2, arg3); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecuteAlways) -{ - ACTION_PARAM_START(5); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - ACTION_PARAM_INT(arg1, 2); - ACTION_PARAM_INT(arg2, 3); - ACTION_PARAM_INT(arg3, 4); - - bool res = !!P_ExecuteSpecial(ACS_ExecuteAlways, NULL, self, false, -scriptname, mapnum, arg1, arg2, arg3); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedLockedExecute) -{ - ACTION_PARAM_START(5); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - ACTION_PARAM_INT(arg1, 2); - ACTION_PARAM_INT(arg2, 3); - ACTION_PARAM_INT(lock, 4); - - bool res = !!P_ExecuteSpecial(ACS_LockedExecute, NULL, self, false, -scriptname, mapnum, arg1, arg2, lock); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedLockedExecuteDoor) -{ - ACTION_PARAM_START(5); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - ACTION_PARAM_INT(arg1, 2); - ACTION_PARAM_INT(arg2, 3); - ACTION_PARAM_INT(lock, 4); - - bool res = !!P_ExecuteSpecial(ACS_LockedExecuteDoor, NULL, self, false, -scriptname, mapnum, arg1, arg2, lock); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedSuspend) -{ - ACTION_PARAM_START(2); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - - bool res = !!P_ExecuteSpecial(ACS_Suspend, NULL, self, false, -scriptname, mapnum, 0, 0, 0); - - ACTION_SET_RESULT(res); -} - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedTerminate) -{ - ACTION_PARAM_START(2); - - ACTION_PARAM_NAME(scriptname, 0); - ACTION_PARAM_INT(mapnum, 1); - - bool res = !!P_ExecuteSpecial(ACS_Terminate, NULL, self, false, -scriptname, mapnum, 0, 0, 0); - - ACTION_SET_RESULT(res); -} - - -//========================================================================== -// -// A_RadiusGive -// -// Uses code roughly similar to A_Explode (but without all the compatibility -// baggage and damage computation code to give an item to all eligible mobjs -// in range. -// -//========================================================================== -enum RadiusGiveFlags -{ - RGF_GIVESELF = 1, - RGF_PLAYERS = 2, - RGF_MONSTERS = 4, - RGF_OBJECTS = 8, - RGF_VOODOO = 16, - RGF_CORPSES = 32, - RGF_MASK = 63, - RGF_NOTARGET = 64, - RGF_NOTRACER = 128, - RGF_NOMASTER = 256, - RGF_CUBE = 512, -}; - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) -{ - ACTION_PARAM_START(7); - ACTION_PARAM_CLASS(item, 0); - ACTION_PARAM_FIXED(distance, 1); - ACTION_PARAM_INT(flags, 2); - ACTION_PARAM_INT(amount, 3); - - // We need a valid item, valid targets, and a valid range - if (item == NULL || (flags & RGF_MASK) == 0 || distance <= 0) - { - return; - } - if (amount == 0) - { - amount = 1; - } - FBlockThingsIterator it(FBoundingBox(self->x, self->y, distance)); - double distsquared = double(distance) * double(distance); - - AActor *thing; - while ((thing = it.Next())) - { - // Don't give to inventory items - if (thing->flags & MF_SPECIAL) - { - continue; - } - // Avoid giving to self unless requested - if (thing == self && !(flags & RGF_GIVESELF)) - { - continue; - } - // Avoiding special pointers if requested - if (((thing == self->target) && (flags & RGF_NOTARGET)) || - ((thing == self->tracer) && (flags & RGF_NOTRACER)) || - ((thing == self->master) && (flags & RGF_NOMASTER))) - { - continue; - } - // Don't give to dead thing unless requested - if (thing->flags & MF_CORPSE) - { - if (!(flags & RGF_CORPSES)) - { - continue; - } - } - else if (thing->health <= 0 || thing->flags6 & MF6_KILLED) - { - continue; - } - // Players, monsters, and other shootable objects - if (thing->player) - { - if ((thing->player->mo == thing) && !(flags & RGF_PLAYERS)) - { - continue; - } - if ((thing->player->mo != thing) && !(flags & RGF_VOODOO)) - { - continue; - } - } - else if (thing->flags3 & MF3_ISMONSTER) - { - if (!(flags & RGF_MONSTERS)) - { - continue; - } - } - else if (thing->flags & MF_SHOOTABLE || thing->flags6 & MF6_VULNERABLE) - { - if (!(flags & RGF_OBJECTS)) - { - continue; - } - } - else - { - continue; - } - - if (flags & RGF_CUBE) - { // check if inside a cube - if (abs(thing->x - self->x) > distance || - abs(thing->y - self->y) > distance || - abs((thing->z + thing->height/2) - (self->z + self->height/2)) > distance) - { - continue; - } - } - else - { // check if inside a sphere - TVector3 tpos(thing->x, thing->y, thing->z + thing->height/2); - TVector3 spos(self->x, self->y, self->z + self->height/2); - if ((tpos - spos).LengthSquared() > distsquared) - { - continue; - } - } - fixed_t dz = abs ((thing->z + thing->height/2) - (self->z + self->height/2)); - - if (P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY)) - { // OK to give; target is in direct path - AInventory *gift = static_cast(Spawn (item, 0, 0, 0, NO_REPLACE)); - if (gift->IsKindOf(RUNTIME_CLASS(AHealth))) - { - gift->Amount *= amount; - } - else - { - gift->Amount = amount; - } - gift->flags |= MF_DROPPED; - gift->ClearCounters(); - if (!gift->CallTryPickup (thing)) - { - gift->Destroy (); - } - } - } -} - - -//========================================================================== -// -// A_SetTics -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTics) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_INT(tics_to_set, 0); - - if (stateowner != self && self->player != NULL && stateowner->IsKindOf(RUNTIME_CLASS(AWeapon))) - { // Is this a weapon? Need to check psp states for a match, then. Blah. - for (int i = 0; i < NUMPSPRITES; ++i) - { - if (self->player->psprites[i].state == CallingState) - { - self->player->psprites[i].tics = tics_to_set; - return; - } - } - } - // Just set tics for self. - self->tics = tics_to_set; -} - -//========================================================================== -// -// A_SetDamageType -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetDamageType) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_NAME(damagetype, 0); - - self->DamageType = damagetype; -} - -//========================================================================== -// -// A_DropItem -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropItem) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_CLASS(spawntype, 0); - ACTION_PARAM_INT(amount, 1); - ACTION_PARAM_INT(chance, 2); - - P_DropItem(self, spawntype, amount, chance); -} - -//========================================================================== -// -// A_SetSpeed -// -//========================================================================== - -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) -{ - ACTION_PARAM_START(1); - ACTION_PARAM_FIXED(speed, 0); - - self->Speed = speed; -} - -//=========================================================================== -// -// Common A_Damage handler -// -// A_Damage* (int amount, str damagetype, int flags) -// Damages the specified actor by the specified amount. Negative values heal. -// -//=========================================================================== - -enum DMSS -{ - DMSS_FOILINVUL = 1, - DMSS_AFFECTARMOR = 2, - DMSS_KILL = 4, -}; - -static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags) -{ - if ((amount > 0) || (flags & DMSS_KILL)) - { - if (!(dmgtarget->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) - { - if (flags & DMSS_KILL) - { - P_DamageMobj(dmgtarget, self, self, dmgtarget->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); - } - if (flags & DMSS_AFFECTARMOR) - { - P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL); - } - else - { - //[MC] DMG_FOILINVUL is needed for making the damage occur on the actor. - P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); - } - } - } - else if (amount < 0) - { - amount = -amount; - P_GiveBody(dmgtarget, amount); - } -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - DoDamage(self, self, amount, DamageType, flags); -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - if (self->target != NULL) DoDamage(self->target, self, amount, DamageType, flags); -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - if (self->tracer != NULL) DoDamage(self->tracer, self, amount, DamageType, flags); -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - if (self->master != NULL) DoDamage(self->master, self, amount, DamageType, flags); -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - TThinkerIterator it; - AActor * mo; - - while ( (mo = it.Next()) ) - { - if (mo->master == self) DoDamage(mo, self, amount, DamageType, flags); - } -} - -//=========================================================================== -// -// -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) -{ - ACTION_PARAM_START(3); - ACTION_PARAM_INT(amount, 0); - ACTION_PARAM_NAME(DamageType, 1); - ACTION_PARAM_INT(flags, 2); - - TThinkerIterator it; - AActor * mo; - - if (self->master != NULL) - { - while ((mo = it.Next())) - { - if (mo->master == self->master && mo != self) DoDamage(mo, self, amount, DamageType, flags); - } - } -} - - -//=========================================================================== -// -// A_Kill*(damagetype, int flags) -// -//=========================================================================== -enum KILS -{ - KILS_FOILINVUL = 1 << 0, - KILS_KILLMISSILES = 1 << 1, - KILS_NOMONSTERS = 1 << 2, -}; - -static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags) -{ - if ((killtarget->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) - { - //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! - //Check to see if it's invulnerable. Disregarded if foilinvul is on, but never works on a missile with NODAMAGE - //since that's the whole point of it. - if ((!(killtarget->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(killtarget->flags5 & MF5_NODAMAGE)) - { - P_ExplodeMissile(self->target, NULL, NULL); - } - } - if (!(flags & KILS_NOMONSTERS)) - { - if (flags & KILS_FOILINVUL) - { - P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); - } - else - { - P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); - } - } -} - - - -//=========================================================================== -// -// A_KillTarget(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - - if (self->target != NULL) DoKill(self->target, self, damagetype, flags); -} - -//=========================================================================== -// -// A_KillTracer(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - - if (self->tracer != NULL) DoKill(self->tracer, self, damagetype, flags); -} - -//=========================================================================== -// -// A_KillMaster(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - - if (self->master != NULL) DoKill(self->master, self, damagetype, flags); -} - -//=========================================================================== -// -// A_KillChildren(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - - TThinkerIterator it; - AActor *mo; - - while ( (mo = it.Next()) ) - { - if (mo->master == self) DoKill(mo, self, damagetype, flags); - } -} - -//=========================================================================== -// -// A_KillSiblings(damagetype, int flags) -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) -{ - ACTION_PARAM_START(2); - ACTION_PARAM_NAME(damagetype, 0); - ACTION_PARAM_INT(flags, 1); - - TThinkerIterator it; - AActor *mo; - - if (self->master != NULL) - { - while ( (mo = it.Next()) ) - { - if (mo->master == self->master && mo != self) DoKill(mo, self, damagetype, flags); - } - } -} - - -//=========================================================================== -// -// A_RemoveTarget -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) -{ - if (self->target != NULL) - { - P_RemoveThing(self->target); - } -} - -//=========================================================================== -// -// A_RemoveTracer -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RemoveTracer) -{ - if (self->tracer != NULL) - { - P_RemoveThing(self->tracer); - } +/* +** thingdef.cpp +** +** Code pointers for Actor definitions +** +**--------------------------------------------------------------------------- +** Copyright 2002-2006 Christoph Oelckers +** Copyright 2004-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be +** covered by the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or (at +** your option) any later version. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "gi.h" +#include "g_level.h" +#include "actor.h" +#include "info.h" +#include "sc_man.h" +#include "tarray.h" +#include "w_wad.h" +#include "templates.h" +#include "r_defs.h" +#include "a_pickups.h" +#include "s_sound.h" +#include "cmdlib.h" +#include "p_lnspec.h" +#include "p_enemy.h" +#include "a_action.h" +#include "decallib.h" +#include "m_random.h" +#include "i_system.h" +#include "p_local.h" +#include "c_console.h" +#include "doomerrors.h" +#include "a_sharedglobal.h" +#include "thingdef/thingdef.h" +#include "v_video.h" +#include "v_font.h" +#include "doomstat.h" +#include "v_palette.h" +#include "g_shared/a_specialspot.h" +#include "actorptrselect.h" +#include "m_bbox.h" +#include "r_data/r_translate.h" +#include "p_trace.h" +#include "gstrings.h" + + +static FRandom pr_camissile ("CustomActorfire"); +static FRandom pr_camelee ("CustomMelee"); +static FRandom pr_cabullet ("CustomBullet"); +static FRandom pr_cajump ("CustomJump"); +static FRandom pr_cwbullet ("CustomWpBullet"); +static FRandom pr_cwjump ("CustomWpJump"); +static FRandom pr_cwpunch ("CustomWpPunch"); +static FRandom pr_grenade ("ThrowGrenade"); +static 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"); + +//========================================================================== +// +// ACustomInventory :: CallStateChain +// +// Executes the code pointers in a chain of states +// until there is no next state +// +//========================================================================== + +bool ACustomInventory::CallStateChain (AActor *actor, FState * State) +{ + StateCallData StateCall; + bool result = false; + int counter = 0; + + while (State != NULL) + { + // Assume success. The code pointer will set this to false if necessary + StateCall.State = State; + StateCall.Result = true; + if (State->CallAction(actor, this, &StateCall)) + { + // collect all the results. Even one successful call signifies overall success. + result |= StateCall.Result; + } + + + // Since there are no delays it is a good idea to check for infinite loops here! + counter++; + if (counter >= 10000) break; + + if (StateCall.State == State) + { + // Abort immediately if the state jumps to itself! + if (State == State->GetNextState()) + { + return false; + } + + // If both variables are still the same there was no jump + // so we must advance to the next state. + State = State->GetNextState(); + } + else + { + State = StateCall.State; + } + } + return result; +} + +//========================================================================== +// +// A_RearrangePointers +// +// Allow an actor to change its relationship to other actors by +// copying pointers freely between TARGET MASTER and TRACER. +// Can also assign null value, but does not duplicate A_ClearTarget. +// +//========================================================================== + + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RearrangePointers) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_INT(ptr_target, 0); + ACTION_PARAM_INT(ptr_master, 1); + ACTION_PARAM_INT(ptr_tracer, 2); + ACTION_PARAM_INT(flags, 3); + + // Rearrange pointers internally + + // Fetch all values before modification, so that all fields can get original values + AActor + *gettarget = self->target, + *getmaster = self->master, + *gettracer = self->tracer; + + switch (ptr_target) // pick the new target + { + case AAPTR_MASTER: + self->target = getmaster; + if (!(PTROP_UNSAFETARGET & flags)) VerifyTargetChain(self); + break; + case AAPTR_TRACER: + self->target = gettracer; + if (!(PTROP_UNSAFETARGET & flags)) VerifyTargetChain(self); + break; + case AAPTR_NULL: + self->target = NULL; + // THIS IS NOT "A_ClearTarget", so no other targeting info is removed + break; + } + + // presently permitting non-monsters to set master + switch (ptr_master) // pick the new master + { + case AAPTR_TARGET: + self->master = gettarget; + if (!(PTROP_UNSAFEMASTER & flags)) VerifyMasterChain(self); + break; + case AAPTR_TRACER: + self->master = gettracer; + if (!(PTROP_UNSAFEMASTER & flags)) VerifyMasterChain(self); + break; + case AAPTR_NULL: + self->master = NULL; + break; + } + + switch (ptr_tracer) // pick the new tracer + { + case AAPTR_TARGET: + self->tracer = gettarget; + break; // no verification deemed necessary; the engine never follows a tracer chain(?) + case AAPTR_MASTER: + self->tracer = getmaster; + break; // no verification deemed necessary; the engine never follows a tracer chain(?) + case AAPTR_NULL: + self->tracer = NULL; + break; + } +} + +//========================================================================== +// +// A_TransferPointer +// +// Copy one pointer (MASTER, TARGET or TRACER) from this actor (SELF), +// or from this actor's MASTER, TARGET or TRACER. +// +// You can copy any one of that actor's pointers +// +// Assign the copied pointer to any one pointer in SELF, +// MASTER, TARGET or TRACER. +// +// Any attempt to make an actor point to itself will replace the pointer +// with a null value. +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TransferPointer) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(ptr_source, 0); + ACTION_PARAM_INT(ptr_recepient, 1); + ACTION_PARAM_INT(ptr_sourcefield, 2); + ACTION_PARAM_INT(ptr_recepientfield, 3); + ACTION_PARAM_INT(flags, 4); + + AActor *source, *recepient; + + // Exchange pointers with actors to whom you have pointers (or with yourself, if you must) + + source = COPY_AAPTR(self, ptr_source); + COPY_AAPTR_NOT_NULL(self, recepient, ptr_recepient); // pick an actor to store the provided pointer value + + // convert source from dataprovider to data + + source = COPY_AAPTR(source, ptr_sourcefield); + + if (source == recepient) source = NULL; // The recepient should not acquire a pointer to itself; will write NULL + + if (ptr_recepientfield == AAPTR_DEFAULT) ptr_recepientfield = ptr_sourcefield; // If default: Write to same field as data was read from + + ASSIGN_AAPTR(recepient, ptr_recepientfield, source, flags); +} + +//========================================================================== +// +// A_CopyFriendliness +// +// Join forces with one of the actors you are pointing to (MASTER by default) +// +// Normal CopyFriendliness reassigns health. This function will not. +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CopyFriendliness) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(ptr_source, 0); + + if (self->player) return; + + AActor *source; + COPY_AAPTR_NOT_NULL(self, source, ptr_source); + self->CopyFriendliness(source, false, false); // No change in current target or health +} + +//========================================================================== +// +// Simple flag changers +// +//========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_SetSolid) +{ + self->flags |= MF_SOLID; +} + +DEFINE_ACTION_FUNCTION(AActor, A_UnsetSolid) +{ + self->flags &= ~MF_SOLID; +} + +DEFINE_ACTION_FUNCTION(AActor, A_SetFloat) +{ + self->flags |= MF_FLOAT; +} + +DEFINE_ACTION_FUNCTION(AActor, A_UnsetFloat) +{ + self->flags &= ~(MF_FLOAT|MF_INFLOAT); +} + +//========================================================================== +// +// Customizable attack functions which use actor parameters. +// +//========================================================================== +static void DoAttack (AActor *self, bool domelee, bool domissile, + int MeleeDamage, FSoundID MeleeSound, const PClass *MissileType,fixed_t MissileHeight) +{ + if (self->target == NULL) return; + + A_FaceTarget (self); + if (domelee && MeleeDamage>0 && self->CheckMeleeRange ()) + { + int damage = pr_camelee.HitDice(MeleeDamage); + if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); + int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee); + P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); + } + else if (domissile && MissileType != NULL) + { + // This seemingly senseless code is needed for proper aiming. + self->z += MissileHeight + self->GetBobOffset() - 32*FRACUNIT; + AActor *missile = P_SpawnMissileXYZ (self->x, self->y, self->z + 32*FRACUNIT, self, self->target, MissileType, false); + self->z -= MissileHeight + self->GetBobOffset() - 32*FRACUNIT; + + if (missile) + { + // automatic handling of seeker missiles + if (missile->flags2&MF2_SEEKERMISSILE) + { + missile->tracer=self->target; + } + P_CheckMissileSpawn(missile, self->radius); + } + } +} + +DEFINE_ACTION_FUNCTION(AActor, A_MeleeAttack) +{ + int MeleeDamage = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeDamage, 0); + FSoundID MeleeSound = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeSound, 0); + DoAttack(self, true, false, MeleeDamage, MeleeSound, NULL, 0); +} + +DEFINE_ACTION_FUNCTION(AActor, A_MissileAttack) +{ + const PClass *MissileType=PClass::FindClass((ENamedName) self->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None)); + fixed_t MissileHeight= self->GetClass()->Meta.GetMetaFixed (ACMETA_MissileHeight, 32*FRACUNIT); + DoAttack(self, false, true, 0, 0, MissileType, MissileHeight); +} + +DEFINE_ACTION_FUNCTION(AActor, A_ComboAttack) +{ + int MeleeDamage = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeDamage, 0); + FSoundID MeleeSound = self->GetClass()->Meta.GetMetaInt (ACMETA_MeleeSound, 0); + const PClass *MissileType=PClass::FindClass((ENamedName) self->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None)); + fixed_t MissileHeight= self->GetClass()->Meta.GetMetaFixed (ACMETA_MissileHeight, 32*FRACUNIT); + DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BasicAttack) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_INT(MeleeDamage, 0); + ACTION_PARAM_SOUND(MeleeSound, 1); + ACTION_PARAM_CLASS(MissileType, 2); + ACTION_PARAM_FIXED(MissileHeight, 3); + + if (MissileType == NULL) return; + DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); +} + +//========================================================================== +// +// Custom sound functions. +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlaySound) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_SOUND(soundid, 0); + ACTION_PARAM_INT(channel, 1); + ACTION_PARAM_FLOAT(volume, 2); + ACTION_PARAM_BOOL(looping, 3); + ACTION_PARAM_FLOAT(attenuation, 4); + + if (!looping) + { + S_Sound (self, channel, soundid, volume, attenuation); + } + else + { + if (!S_IsActorPlayingSomething (self, channel&7, soundid)) + { + S_Sound (self, channel | CHAN_LOOP, soundid, volume, attenuation); + } + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StopSound) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(slot, 0); + + S_StopSound(self, slot); +} + +//========================================================================== +// +// These come from a time when DECORATE constants did not exist yet and +// the sound interface was less flexible. As a result the parameters are +// not optimal and these functions have been deprecated in favor of extending +// A_PlaySound and A_StopSound. +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlayWeaponSound) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_SOUND(soundid, 0); + + S_Sound (self, CHAN_WEAPON, soundid, 1, ATTN_NORM); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlaySoundEx) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_SOUND(soundid, 0); + ACTION_PARAM_NAME(channel, 1); + ACTION_PARAM_BOOL(looping, 2); + ACTION_PARAM_INT(attenuation_raw, 3); + + float attenuation; + switch (attenuation_raw) + { + case -1: attenuation = ATTN_STATIC; break; // drop off rapidly + default: + case 0: attenuation = ATTN_NORM; break; // normal + case 1: + case 2: attenuation = ATTN_NONE; break; // full volume + } + + if (channel < NAME_Auto || channel > NAME_SoundSlot7) + { + channel = NAME_Auto; + } + + if (!looping) + { + S_Sound (self, int(channel) - NAME_Auto, soundid, 1, attenuation); + } + else + { + if (!S_IsActorPlayingSomething (self, int(channel) - NAME_Auto, soundid)) + { + S_Sound (self, (int(channel) - NAME_Auto) | CHAN_LOOP, soundid, 1, attenuation); + } + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StopSoundEx) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_NAME(channel, 0); + + if (channel > NAME_Auto && channel <= NAME_SoundSlot7) + { + S_StopSound (self, int(channel) - NAME_Auto); + } +} + +//========================================================================== +// +// Generic seeker missile function +// +//========================================================================== +static FRandom pr_seekermissile ("SeekerMissile"); +enum +{ + SMF_LOOK = 1, + SMF_PRECISE = 2, + SMF_CURSPEED = 4, +}; +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SeekerMissile) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(ang1, 0); + ACTION_PARAM_INT(ang2, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_INT(chance, 3); + ACTION_PARAM_INT(distance, 4); + + if ((flags & SMF_LOOK) && (self->tracer == 0) && (pr_seekermissile()tracer = P_RoughMonsterSearch (self, distance, true); + } + if (!P_SeekerMissile(self, clamp(ang1, 0, 90) * ANGLE_1, clamp(ang2, 0, 90) * ANGLE_1, !!(flags & SMF_PRECISE), !!(flags & SMF_CURSPEED))) + { + if (flags & SMF_LOOK) + { // This monster is no longer seekable, so let us look for another one next time. + self->tracer = NULL; + } + } +} + +//========================================================================== +// +// Hitscan attack with a customizable amount of bullets (specified in damage) +// +//========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_BulletAttack) +{ + int i; + int bangle; + int slope; + + if (!self->target) return; + + A_FaceTarget (self); + bangle = self->angle; + + slope = P_AimLineAttack (self, bangle, MISSILERANGE); + + S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM); + for (i = self->GetMissileDamage (0, 1); i > 0; --i) + { + int angle = bangle + (pr_cabullet.Random2() << 20); + int damage = ((pr_cabullet()%5)+1)*3; + P_LineAttack(self, angle, MISSILERANGE, slope, damage, + NAME_Hitscan, NAME_BulletPuff); + } +} + + +//========================================================================== +// +// Do the state jump +// +//========================================================================== +static void DoJump(AActor * self, FState * CallingState, FState *jumpto, StateCallData *statecall) +{ + if (jumpto == NULL) return; + + if (statecall != NULL) + { + statecall->State = jumpto; + } + else if (self->player != NULL && CallingState == self->player->psprites[ps_weapon].state) + { + P_SetPsprite(self->player, ps_weapon, jumpto); + } + else if (self->player != NULL && CallingState == self->player->psprites[ps_flash].state) + { + P_SetPsprite(self->player, ps_flash, jumpto); + } + else if (CallingState == self->state) + { + self->SetState (jumpto); + } + else + { + // something went very wrong. This should never happen. + assert(false); + } +} + +// This is just to avoid having to directly reference the internally defined +// CallingState and statecall parameters in the code below. +#define ACTION_JUMP(offset) DoJump(self, CallingState, offset, statecall) + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Jump) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(count, 0); + ACTION_PARAM_INT(maxchance, 1); + + if (count >= 2 && (maxchance >= 256 || pr_cajump() < maxchance)) + { + int jumps = 2 + (count == 2? 0 : (pr_cajump() % (count - 1))); + ACTION_PARAM_STATE(jumpto, jumps); + ACTION_JUMP(jumpto); + } + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfHealthLower) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(health, 0); + ACTION_PARAM_STATE(jump, 1); + ACTION_PARAM_INT(ptr_selector, 2); + + AActor *measured; + + measured = COPY_AAPTR(self, ptr_selector); + + if (measured && measured->health < health) + { + ACTION_JUMP(jump); + } + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetOutsideMeleeRange) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + if (!self->CheckMeleeRange()) + { + ACTION_JUMP(jump); + } + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInsideMeleeRange) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + if (self->CheckMeleeRange()) + { + ACTION_JUMP(jump); + } + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! +} +//========================================================================== +// +// State jump function +// +//========================================================================== +void DoJumpIfCloser(AActor *target, DECLARE_PARAMINFO) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_FIXED(dist, 0); + ACTION_PARAM_STATE(jump, 1); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + // No target - no jump + if (target != NULL && P_AproxDistance(self->x-target->x, self->y-target->y) < dist && + ( (self->z > target->z && self->z - (target->z + target->height) < dist) || + (self->z <=target->z && target->z - (self->z + self->height) < dist) + ) + ) + { + ACTION_JUMP(jump); + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfCloser) +{ + AActor *target; + + if (!self->player) + { + target = self->target; + } + else + { + // Does the player aim at something that can be shot? + P_BulletSlope(self, &target); + } + DoJumpIfCloser(target, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTracerCloser) +{ + DoJumpIfCloser(self->tracer, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfMasterCloser) +{ + DoJumpIfCloser(self->master, PUSH_PARAMINFO); +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +void DoJumpIfInventory(AActor * owner, DECLARE_PARAMINFO) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_CLASS(Type, 0); + ACTION_PARAM_INT(ItemAmount, 1); + ACTION_PARAM_STATE(JumpOffset, 2); + ACTION_PARAM_INT(setowner, 3); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + if (!Type) return; + COPY_AAPTR_NOT_NULL(owner, owner, setowner); // returns if owner ends up being NULL + + AInventory *Item = owner->FindInventory(Type); + + if (Item) + { + if (ItemAmount > 0) + { + if (Item->Amount >= ItemAmount) + ACTION_JUMP(JumpOffset); + } + else if (Item->Amount >= Item->MaxAmount) + { + ACTION_JUMP(JumpOffset); + } + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInventory) +{ + DoJumpIfInventory(self, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetInventory) +{ + DoJumpIfInventory(self->target, PUSH_PARAMINFO); +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfArmorType) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_NAME(Type, 0); + ACTION_PARAM_STATE(JumpOffset, 1); + ACTION_PARAM_INT(amount, 2); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + ABasicArmor * armor = (ABasicArmor *) self->FindInventory(NAME_BasicArmor); + + if (armor && armor->ArmorType == Type && armor->Amount >= amount) + ACTION_JUMP(JumpOffset); +} + +//========================================================================== +// +// Parameterized version of A_Explode +// +//========================================================================== + +enum +{ + XF_HURTSOURCE = 1, + XF_NOTMISSILE = 4, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Explode) +{ + ACTION_PARAM_START(8); + ACTION_PARAM_INT(damage, 0); + ACTION_PARAM_INT(distance, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_BOOL(alert, 3); + ACTION_PARAM_INT(fulldmgdistance, 4); + ACTION_PARAM_INT(nails, 5); + ACTION_PARAM_INT(naildamage, 6); + ACTION_PARAM_CLASS(pufftype, 7); + + if (damage < 0) // get parameters from metadata + { + damage = self->GetClass()->Meta.GetMetaInt (ACMETA_ExplosionDamage, 128); + distance = self->GetClass()->Meta.GetMetaInt (ACMETA_ExplosionRadius, damage); + flags = !self->GetClass()->Meta.GetMetaInt (ACMETA_DontHurtShooter); + alert = false; + } + else + { + if (distance <= 0) distance = damage; + } + // NailBomb effect, from SMMU but not from its source code: instead it was implemented and + // generalized from the documentation at http://www.doomworld.com/eternity/engine/codeptrs.html + + if (nails) + { + angle_t ang; + for (int i = 0; i < nails; i++) + { + ang = i*(ANGLE_MAX/nails); + // Comparing the results of a test wad with Eternity, it seems A_NailBomb does not aim + P_LineAttack (self, ang, MISSILERANGE, 0, + //P_AimLineAttack (self, ang, MISSILERANGE), + naildamage, NAME_Hitscan, pufftype); + } + } + + P_RadiusAttack (self, self->target, damage, distance, self->DamageType, flags, fulldmgdistance); + P_CheckSplash(self, distance<target != NULL && self->target->player != NULL) + { + validcount++; + P_RecursiveSound (self->Sector, self->target, false, 0); + } +} + +//========================================================================== +// +// A_RadiusThrust +// +//========================================================================== + +enum +{ + RTF_AFFECTSOURCE = 1, + RTF_NOIMPACTDAMAGE = 2, + RTF_NOTMISSILE = 4, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusThrust) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(force, 0); + ACTION_PARAM_INT(distance, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_INT(fullthrustdistance, 3); + + bool sourcenothrust = false; + + if (force == 0) force = 128; + if (distance <= 0) distance = abs(force); + + // Temporarily negate MF2_NODMGTHRUST on the shooter, since it renders this function useless. + if (!(flags & RTF_NOTMISSILE) && self->target != NULL && self->target->flags2 & MF2_NODMGTHRUST) + { + sourcenothrust = true; + self->target->flags2 &= ~MF2_NODMGTHRUST; + } + + P_RadiusAttack (self, self->target, force, distance, self->DamageType, flags | RADF_NODAMAGE, fullthrustdistance); + P_CheckSplash(self, distance << FRACBITS); + + if (sourcenothrust) + { + self->target->flags2 |= MF2_NODMGTHRUST; + } +} + +//========================================================================== +// +// Execute a line special / script +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CallSpecial) +{ + ACTION_PARAM_START(6); + ACTION_PARAM_INT(special, 0); + ACTION_PARAM_INT(arg1, 1); + ACTION_PARAM_INT(arg2, 2); + ACTION_PARAM_INT(arg3, 3); + ACTION_PARAM_INT(arg4, 4); + ACTION_PARAM_INT(arg5, 5); + + bool res = !!P_ExecuteSpecial(special, NULL, self, false, arg1, arg2, arg3, arg4, arg5); + + ACTION_SET_RESULT(res); +} + +//========================================================================== +// +// The ultimate code pointer: Fully customizable missiles! +// +//========================================================================== +enum CM_Flags +{ + CMF_AIMMODE = 3, + CMF_TRACKOWNER = 4, + CMF_CHECKTARGETDEAD = 8, + + CMF_ABSOLUTEPITCH = 16, + CMF_OFFSETPITCH = 32, + CMF_SAVEPITCH = 64, + + CMF_ABSOLUTEANGLE = 128 +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMissile) +{ + ACTION_PARAM_START(6); + ACTION_PARAM_CLASS(ti, 0); + ACTION_PARAM_FIXED(SpawnHeight, 1); + ACTION_PARAM_INT(Spawnofs_XY, 2); + ACTION_PARAM_ANGLE(Angle, 3); + ACTION_PARAM_INT(flags, 4); + ACTION_PARAM_ANGLE(pitch, 5); + + int aimmode = flags & CMF_AIMMODE; + + AActor * targ; + AActor * missile; + + if (self->target != NULL || aimmode==2) + { + if (ti) + { + angle_t ang = (self->angle - ANGLE_90) >> ANGLETOFINESHIFT; + fixed_t x = Spawnofs_XY * finecosine[ang]; + fixed_t y = Spawnofs_XY * finesine[ang]; + fixed_t z = SpawnHeight + self->GetBobOffset() - 32*FRACUNIT + (self->player? self->player->crouchoffset : 0); + + switch (aimmode) + { + case 0: + default: + // same adjustment as above (in all 3 directions this time) - for better aiming! + self->x += x; + self->y += y; + self->z += z; + missile = P_SpawnMissileXYZ(self->x, self->y, self->z + 32*FRACUNIT, self, self->target, ti, false); + self->x -= x; + self->y -= y; + self->z -= z; + break; + + case 1: + missile = P_SpawnMissileXYZ(self->x+x, self->y+y, self->z + self->GetBobOffset() + SpawnHeight, self, self->target, ti, false); + break; + + case 2: + self->x += x; + self->y += y; + missile = P_SpawnMissileAngleZSpeed(self, self->z + self->GetBobOffset() + SpawnHeight, ti, self->angle, 0, GetDefaultByType(ti)->Speed, self, false); + self->x -= x; + self->y -= y; + + flags |= CMF_ABSOLUTEPITCH; + + break; + } + + if (missile) + { + // Use the actual velocity instead of the missile's Speed property + // so that this can handle missiles with a high vertical velocity + // component properly. + + fixed_t missilespeed; + + if ( (CMF_ABSOLUTEPITCH|CMF_OFFSETPITCH) & flags) + { + if (CMF_OFFSETPITCH & flags) + { + FVector2 velocity (missile->velx, missile->vely); + pitch += R_PointToAngle2(0,0, (fixed_t)velocity.Length(), missile->velz); + } + ang = pitch >> ANGLETOFINESHIFT; + missilespeed = abs(FixedMul(finecosine[ang], missile->Speed)); + missile->velz = FixedMul(finesine[ang], missile->Speed); + } + else + { + FVector2 velocity (missile->velx, missile->vely); + missilespeed = (fixed_t)velocity.Length(); + } + + if (CMF_SAVEPITCH & flags) + { + missile->pitch = pitch; + // In aimmode 0 and 1 without absolutepitch or offsetpitch, the pitch parameter + // contains the unapplied parameter. In that case, it is set as pitch without + // otherwise affecting the spawned actor. + } + + missile->angle = (CMF_ABSOLUTEANGLE & flags) ? Angle : missile->angle + Angle ; + + ang = missile->angle >> ANGLETOFINESHIFT; + missile->velx = FixedMul (missilespeed, finecosine[ang]); + missile->vely = FixedMul (missilespeed, finesine[ang]); + + // handle projectile shooting projectiles - track the + // links back to a real owner + if (self->isMissile(!!(flags & CMF_TRACKOWNER))) + { + AActor * owner=self ;//->target; + while (owner->isMissile(!!(flags & CMF_TRACKOWNER)) && owner->target) owner=owner->target; + targ=owner; + missile->target=owner; + // automatic handling of seeker missiles + if (self->flags & missile->flags2 & MF2_SEEKERMISSILE) + { + missile->tracer=self->tracer; + } + } + else if (missile->flags2&MF2_SEEKERMISSILE) + { + // automatic handling of seeker missiles + missile->tracer=self->target; + } + // we must redo the spectral check here because the owner is set after spawning so the FriendPlayer value may be wrong + if (missile->flags4 & MF4_SPECTRAL) + { + if (missile->target != NULL) + { + missile->SetFriendPlayer(missile->target->player); + } + else + { + missile->FriendPlayer = 0; + } + } + P_CheckMissileSpawn(missile, self->radius); + } + } + } + else if (flags & CMF_CHECKTARGETDEAD) + { + // Target is dead and the attack shall be aborted. + if (self->SeeState != NULL && (self->health > 0 || !(self->flags3 & MF3_ISMONSTER))) self->SetState(self->SeeState); + } +} + +//========================================================================== +// +// An even more customizable hitscan attack +// +//========================================================================== +enum CBA_Flags +{ + CBAF_AIMFACING = 1, + CBAF_NORANDOM = 2, + CBAF_EXPLICITANGLE = 4, + CBAF_NOPITCH = 8, + CBAF_NORANDOMPUFFZ = 16, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack) +{ + ACTION_PARAM_START(7); + ACTION_PARAM_ANGLE(Spread_XY, 0); + ACTION_PARAM_ANGLE(Spread_Z, 1); + ACTION_PARAM_INT(NumBullets, 2); + ACTION_PARAM_INT(DamagePerBullet, 3); + ACTION_PARAM_CLASS(pufftype, 4); + ACTION_PARAM_FIXED(Range, 5); + ACTION_PARAM_INT(Flags, 6); + + if(Range==0) Range=MISSILERANGE; + + int i; + int bangle; + int bslope = 0; + int laflags = (Flags & CBAF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; + + if (self->target || (Flags & CBAF_AIMFACING)) + { + if (!(Flags & CBAF_AIMFACING)) A_FaceTarget (self); + bangle = self->angle; + + if (!pufftype) pufftype = PClass::FindClass(NAME_BulletPuff); + + if (!(Flags & CBAF_NOPITCH)) bslope = P_AimLineAttack (self, bangle, MISSILERANGE); + + S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM); + for (i=0 ; itarget) + return; + + A_FaceTarget (self); + if (self->CheckMeleeRange ()) + { + if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); + int newdam = P_DamageMobj (self->target, self, self, damage, DamageType); + if (bleed) P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); + } + else + { + if (MissSound) S_Sound (self, CHAN_WEAPON, MissSound, 1, ATTN_NORM); + } +} + +//========================================================================== +// +// A fully customizable combo attack +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomComboAttack) +{ + ACTION_PARAM_START(6); + ACTION_PARAM_CLASS(ti, 0); + ACTION_PARAM_FIXED(SpawnHeight, 1); + ACTION_PARAM_INT(damage, 2); + ACTION_PARAM_SOUND(MeleeSound, 3); + ACTION_PARAM_NAME(DamageType, 4); + ACTION_PARAM_BOOL(bleed, 5); + + if (!self->target) + return; + + A_FaceTarget (self); + if (self->CheckMeleeRange ()) + { + if (DamageType==NAME_None) DamageType = NAME_Melee; // Melee is the default type + if (MeleeSound) S_Sound (self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); + int newdam = P_DamageMobj (self->target, self, self, damage, DamageType); + if (bleed) P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self); + } + else if (ti) + { + // This seemingly senseless code is needed for proper aiming. + self->z += SpawnHeight + self->GetBobOffset() - 32*FRACUNIT; + AActor *missile = P_SpawnMissileXYZ (self->x, self->y, self->z + 32*FRACUNIT, self, self->target, ti, false); + self->z -= SpawnHeight + self->GetBobOffset() - 32*FRACUNIT; + + if (missile) + { + // automatic handling of seeker missiles + if (missile->flags2&MF2_SEEKERMISSILE) + { + missile->tracer=self->target; + } + P_CheckMissileSpawn(missile, self->radius); + } + } +} + +//========================================================================== +// +// State jump function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfNoAmmo) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + if (!ACTION_CALL_FROM_WEAPON()) return; + + if (!self->player->ReadyWeapon->CheckAmmo(self->player->ReadyWeapon->bAltFire, false, true)) + { + ACTION_JUMP(jump); + } + +} + + +//========================================================================== +// +// An even more customizable hitscan attack +// +//========================================================================== +enum FB_Flags +{ + FBF_USEAMMO = 1, + FBF_NORANDOM = 2, + FBF_EXPLICITANGLE = 4, + FBF_NOPITCH = 8, + FBF_NOFLASH = 16, + FBF_NORANDOMPUFFZ = 32, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets) +{ + ACTION_PARAM_START(7); + ACTION_PARAM_ANGLE(Spread_XY, 0); + ACTION_PARAM_ANGLE(Spread_Z, 1); + ACTION_PARAM_INT(NumberOfBullets, 2); + ACTION_PARAM_INT(DamagePerBullet, 3); + ACTION_PARAM_CLASS(PuffType, 4); + ACTION_PARAM_INT(Flags, 5); + ACTION_PARAM_FIXED(Range, 6); + + if (!self->player) return; + + player_t * player=self->player; + AWeapon * weapon=player->ReadyWeapon; + + int i; + int bangle; + int bslope = 0; + int laflags = (Flags & FBF_NORANDOMPUFFZ)? LAF_NORANDOMPUFFZ : 0; + + if ((Flags & FBF_USEAMMO) && weapon) + { + if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo + } + + if (Range == 0) Range = PLAYERMISSILERANGE; + + if (!(Flags & FBF_NOFLASH)) static_cast(self)->PlayAttacking2 (); + + if (!(Flags & FBF_NOPITCH)) bslope = P_BulletSlope(self); + bangle = self->angle; + + if (!PuffType) PuffType = PClass::FindClass(NAME_BulletPuff); + + if (weapon != NULL) + { + S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); + } + + if ((NumberOfBullets==1 && !player->refire) || NumberOfBullets==0) + { + int damage = DamagePerBullet; + + if (!(Flags & FBF_NORANDOM)) + damage *= ((pr_cwbullet()%3)+1); + + P_LineAttack(self, bangle, Range, bslope, damage, NAME_Hitscan, PuffType, laflags); + } + else + { + if (NumberOfBullets == -1) NumberOfBullets = 1; + for (i=0 ; iplayer) return; + + + player_t *player=self->player; + AWeapon * weapon=player->ReadyWeapon; + AActor *linetarget; + + if (UseAmmo && weapon) + { + if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo + } + + if (ti) + { + angle_t ang = (self->angle - ANGLE_90) >> ANGLETOFINESHIFT; + fixed_t x = SpawnOfs_XY * finecosine[ang]; + fixed_t y = SpawnOfs_XY * finesine[ang]; + fixed_t z = SpawnHeight; + fixed_t shootangle = self->angle; + + if (Flags & FPF_AIMATANGLE) shootangle += Angle; + + // Temporarily adjusts the pitch + fixed_t SavedPlayerPitch = self->pitch; + self->pitch -= pitch; + AActor * misl=P_SpawnPlayerMissile (self, x, y, z, ti, shootangle, &linetarget); + self->pitch = SavedPlayerPitch; + + // automatic handling of seeker missiles + if (misl) + { + if (Flags & FPF_TRANSFERTRANSLATION) misl->Translation = self->Translation; + if (linetarget && misl->flags2&MF2_SEEKERMISSILE) misl->tracer=linetarget; + if (!(Flags & FPF_AIMATANGLE)) + { + // This original implementation is to aim straight ahead and then offset + // the angle from the resulting direction. + FVector3 velocity(misl->velx, misl->vely, 0); + fixed_t missilespeed = (fixed_t)velocity.Length(); + misl->angle += Angle; + angle_t an = misl->angle >> ANGLETOFINESHIFT; + misl->velx = FixedMul (missilespeed, finecosine[an]); + misl->vely = FixedMul (missilespeed, finesine[an]); + } + } + } +} + + +//========================================================================== +// +// A_CustomPunch +// +// Berserk is not handled here. That can be done with A_CheckIfInventory +// +//========================================================================== + +enum +{ + CPF_USEAMMO = 1, + CPF_DAGGER = 2, + CPF_PULLIN = 4, + CPF_NORANDOMPUFFZ = 8, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(Damage, 0); + ACTION_PARAM_BOOL(norandom, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_CLASS(PuffType, 3); + ACTION_PARAM_FIXED(Range, 4); + ACTION_PARAM_FIXED(LifeSteal, 5); + + if (!self->player) return; + + player_t *player=self->player; + AWeapon * weapon=player->ReadyWeapon; + + + angle_t angle; + int pitch; + AActor * linetarget; + int actualdamage; + + if (!norandom) Damage *= (pr_cwpunch()%8+1); + + angle = self->angle + (pr_cwpunch.Random2() << 18); + if (Range == 0) Range = MELEERANGE; + pitch = P_AimLineAttack (self, angle, Range, &linetarget); + + // only use ammo when actually hitting something! + if ((flags & CPF_USEAMMO) && linetarget && weapon) + { + if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo + } + + if (!PuffType) PuffType = PClass::FindClass(NAME_BulletPuff); + int puffFlags = LAF_ISMELEEATTACK | ((flags & CPF_NORANDOMPUFFZ) ? LAF_NORANDOMPUFFZ : 0); + + P_LineAttack (self, angle, Range, pitch, Damage, NAME_Melee, PuffType, puffFlags, &linetarget, &actualdamage); + + // turn to face target + if (linetarget) + { + if (LifeSteal && !(linetarget->flags5 & MF5_DONTDRAIN)) + P_GiveBody (self, (actualdamage * LifeSteal) >> FRACBITS); + + if (weapon != NULL) + { + S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); + } + + self->angle = R_PointToAngle2 (self->x, + self->y, + linetarget->x, + linetarget->y); + + if (flags & CPF_PULLIN) self->flags |= MF_JUSTATTACKED; + if (flags & CPF_DAGGER) P_DaggerAlert (self, linetarget); + } +} + + +//========================================================================== +// +// customizable railgun attack function +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RailAttack) +{ + ACTION_PARAM_START(16); + ACTION_PARAM_INT(Damage, 0); + ACTION_PARAM_INT(Spawnofs_XY, 1); + ACTION_PARAM_BOOL(UseAmmo, 2); + ACTION_PARAM_COLOR(Color1, 3); + ACTION_PARAM_COLOR(Color2, 4); + ACTION_PARAM_INT(Flags, 5); + ACTION_PARAM_FLOAT(MaxDiff, 6); + ACTION_PARAM_CLASS(PuffType, 7); + ACTION_PARAM_ANGLE(Spread_XY, 8); + ACTION_PARAM_ANGLE(Spread_Z, 9); + ACTION_PARAM_FIXED(Range, 10); + ACTION_PARAM_INT(Duration, 11); + ACTION_PARAM_FLOAT(Sparsity, 12); + ACTION_PARAM_FLOAT(DriftSpeed, 13); + ACTION_PARAM_CLASS(SpawnClass, 14); + ACTION_PARAM_FIXED(Spawnofs_Z, 15); + + if(Range==0) Range=8192*FRACUNIT; + if(Sparsity==0) Sparsity=1.0; + + if (!self->player) return; + + AWeapon * weapon=self->player->ReadyWeapon; + + // only use ammo when actually hitting something! + if (UseAmmo) + { + if (!weapon->DepleteAmmo(weapon->bAltFire, true)) return; // out of ammo + } + + angle_t angle; + angle_t slope; + + if (Flags & RAF_EXPLICITANGLE) + { + angle = Spread_XY; + slope = Spread_Z; + } + else + { + angle = pr_crailgun.Random2() * (Spread_XY / 255); + slope = pr_crailgun.Random2() * (Spread_Z / 255); + } + + P_RailAttack (self, Damage, Spawnofs_XY, Spawnofs_Z, Color1, Color2, MaxDiff, Flags, PuffType, angle, slope, Range, Duration, Sparsity, DriftSpeed, SpawnClass); +} + +//========================================================================== +// +// also for monsters +// +//========================================================================== +enum +{ + CRF_DONTAIM = 0, + CRF_AIMPARALLEL = 1, + CRF_AIMDIRECT = 2, + CRF_EXPLICITANGLE = 4, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) +{ + ACTION_PARAM_START(16); + ACTION_PARAM_INT(Damage, 0); + ACTION_PARAM_INT(Spawnofs_XY, 1); + ACTION_PARAM_COLOR(Color1, 2); + ACTION_PARAM_COLOR(Color2, 3); + ACTION_PARAM_INT(Flags, 4); + ACTION_PARAM_INT(aim, 5); + ACTION_PARAM_FLOAT(MaxDiff, 6); + ACTION_PARAM_CLASS(PuffType, 7); + ACTION_PARAM_ANGLE(Spread_XY, 8); + ACTION_PARAM_ANGLE(Spread_Z, 9); + ACTION_PARAM_FIXED(Range, 10); + ACTION_PARAM_INT(Duration, 11); + ACTION_PARAM_FLOAT(Sparsity, 12); + ACTION_PARAM_FLOAT(DriftSpeed, 13); + ACTION_PARAM_CLASS(SpawnClass, 14); + ACTION_PARAM_FIXED(Spawnofs_Z, 15); + + if(Range==0) Range=8192*FRACUNIT; + if(Sparsity==0) Sparsity=1.0; + + AActor *linetarget; + + fixed_t saved_x = self->x; + fixed_t saved_y = self->y; + angle_t saved_angle = self->angle; + fixed_t saved_pitch = self->pitch; + + if (aim && self->target == NULL) + { + return; + } + // [RH] Andy Baker's stealth monsters + if (self->flags & MF_STEALTH) + { + self->visdir = 1; + } + + self->flags &= ~MF_AMBUSH; + + + if (aim) + { + self->angle = R_PointToAngle2 (self->x, + self->y, + self->target->x, + self->target->y); + } + self->pitch = P_AimLineAttack (self, self->angle, MISSILERANGE, &linetarget, ANGLE_1*60, 0, aim ? self->target : NULL); + if (linetarget == NULL && aim) + { + // We probably won't hit the target, but aim at it anyway so we don't look stupid. + FVector2 xydiff(self->target->x - self->x, self->target->y - self->y); + double zdiff = (self->target->z + (self->target->height>>1)) - + (self->z + (self->height>>1) - self->floorclip); + self->pitch = int(atan2(zdiff, xydiff.Length()) * ANGLE_180 / -M_PI); + } + // Let the aim trail behind the player + if (aim) + { + saved_angle = self->angle = R_PointToAngle2 (self->x, self->y, + self->target->x - self->target->velx * 3, + self->target->y - self->target->vely * 3); + + if (aim == CRF_AIMDIRECT) + { + // Tricky: We must offset to the angle of the current position + // but then change the angle again to ensure proper aim. + self->x += Spawnofs_XY * finecosine[self->angle]; + self->y += Spawnofs_XY * finesine[self->angle]; + Spawnofs_XY = 0; + self->angle = R_PointToAngle2 (self->x, self->y, + self->target->x - self->target->velx * 3, + self->target->y - self->target->vely * 3); + } + + if (self->target->flags & MF_SHADOW) + { + angle_t rnd = pr_crailgun.Random2() << 21; + self->angle += rnd; + saved_angle = rnd; + } + } + + angle_t angle = (self->angle - ANG90) >> ANGLETOFINESHIFT; + + angle_t angleoffset; + angle_t slopeoffset; + + if (Flags & CRF_EXPLICITANGLE) + { + angleoffset = Spread_XY; + slopeoffset = Spread_Z; + } + else + { + angleoffset = pr_crailgun.Random2() * (Spread_XY / 255); + slopeoffset = pr_crailgun.Random2() * (Spread_Z / 255); + } + + P_RailAttack (self, Damage, Spawnofs_XY, Spawnofs_Z, Color1, Color2, MaxDiff, Flags, PuffType, angleoffset, slopeoffset, Range, Duration, Sparsity, DriftSpeed, SpawnClass); + + self->x = saved_x; + self->y = saved_y; + self->angle = saved_angle; + self->pitch = saved_pitch; +} + +//=========================================================================== +// +// DoGiveInventory +// +//=========================================================================== + +static void DoGiveInventory(AActor * receiver, DECLARE_PARAMINFO) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_CLASS(mi, 0); + ACTION_PARAM_INT(amount, 1); + ACTION_PARAM_INT(setreceiver, 2); + + COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); + + bool res=true; + + if (amount==0) amount=1; + if (mi) + { + AInventory *item = static_cast(Spawn (mi, 0, 0, 0, NO_REPLACE)); + if (item->IsKindOf(RUNTIME_CLASS(AHealth))) + { + item->Amount *= amount; + } + else + { + item->Amount = amount; + } + item->flags |= MF_DROPPED; + item->ClearCounters(); + if (!item->CallTryPickup (receiver)) + { + item->Destroy (); + res = false; + } + else res = true; + } + else res = false; + ACTION_SET_RESULT(res); + +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveInventory) +{ + DoGiveInventory(self, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToTarget) +{ + DoGiveInventory(self->target, PUSH_PARAMINFO); +} + +//=========================================================================== +// +// A_TakeInventory +// +//=========================================================================== + +enum +{ + TIF_NOTAKEINFINITE = 1, +}; + +void DoTakeInventory(AActor * receiver, DECLARE_PARAMINFO) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_CLASS(item, 0); + ACTION_PARAM_INT(amount, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_INT(setreceiver, 3); + + if (!item) return; + COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); + + bool res = false; + + AInventory * inv = receiver->FindInventory(item); + + if (inv && !inv->IsKindOf(RUNTIME_CLASS(AHexenArmor))) + { + if (inv->Amount > 0) + { + res = true; + } + // Do not take ammo if the "no take infinite/take as ammo depletion" flag is set + // and infinite ammo is on + if (flags & TIF_NOTAKEINFINITE && + ((dmflags & DF_INFINITE_AMMO) || (receiver->player->cheats & CF_INFINITEAMMO)) && + inv->IsKindOf(RUNTIME_CLASS(AAmmo))) + { + // Nothing to do here, except maybe res = false;? Would it make sense? + } + else if (!amount || amount>=inv->Amount) + { + if (inv->ItemFlags&IF_KEEPDEPLETED) inv->Amount=0; + else inv->Destroy(); + } + else inv->Amount-=amount; + } + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeInventory) +{ + DoTakeInventory(self, PUSH_PARAMINFO); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromTarget) +{ + DoTakeInventory(self->target, PUSH_PARAMINFO); +} + +//=========================================================================== +// +// Common code for A_SpawnItem and A_SpawnItemEx +// +//=========================================================================== + +enum SIX_Flags +{ + SIXF_TRANSFERTRANSLATION = 1 << 0, + SIXF_ABSOLUTEPOSITION = 1 << 1, + SIXF_ABSOLUTEANGLE = 1 << 2, + SIXF_ABSOLUTEVELOCITY = 1 << 3, + SIXF_SETMASTER = 1 << 4, + SIXF_NOCHECKPOSITION = 1 << 5, + SIXF_TELEFRAG = 1 << 6, + SIXF_CLIENTSIDE = 1 << 7, // only used by Skulldronum + SIXF_TRANSFERAMBUSHFLAG = 1 << 8, + SIXF_TRANSFERPITCH = 1 << 9, + SIXF_TRANSFERPOINTERS = 1 << 10, + SIXF_USEBLOODCOLOR = 1 << 11, + SIXF_CLEARCALLERTID = 1 << 12, + SIXF_MULTIPLYSPEED = 1 << 13, + SIXF_TRANSFERSCALE = 1 << 14, + SIXF_TRANSFERSPECIAL = 1 << 15, + SIXF_CLEARCALLERSPECIAL = 1 << 16, + SIXF_TRANSFERSTENCILCOL = 1 << 17, + SIXF_TRANSFERALPHA = 1 << 18, + SIXF_TRANSFERRENDERSTYLE = 1 << 19, + SIXF_SETTARGET = 1 << 20, + SIXF_SETTRACER = 1 << 21, + SIXF_NOPOINTERS = 1 << 22, +}; + +static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) +{ + if (mo == NULL) + { + return false; + } + AActor *originator = self; + + if (!(mo->flags2 & MF2_DONTTRANSLATE)) + { + if (flags & SIXF_TRANSFERTRANSLATION) + { + mo->Translation = self->Translation; + } + else if (flags & SIXF_USEBLOODCOLOR) + { + // [XA] Use the spawning actor's BloodColor to translate the newly-spawned object. + PalEntry bloodcolor = self->GetBloodColor(); + mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a); + } + } + if (flags & SIXF_TRANSFERPOINTERS) + { + mo->target = self->target; + mo->master = self->master; // This will be overridden later if SIXF_SETMASTER is set + mo->tracer = self->tracer; + } + + mo->angle = self->angle; + if (flags & SIXF_TRANSFERPITCH) + { + mo->pitch = self->pitch; + } + while (originator && originator->isMissile()) + { + originator = originator->target; + } + + if (flags & SIXF_TELEFRAG) + { + P_TeleportMove(mo, mo->x, mo->y, mo->z, true); + // This is needed to ensure consistent behavior. + // Otherwise it will only spawn if nothing gets telefragged + flags |= SIXF_NOCHECKPOSITION; + } + if (mo->flags3 & MF3_ISMONSTER) + { + if (!(flags & SIXF_NOCHECKPOSITION) && !P_TestMobjLocation(mo)) + { + // The monster is blocked so don't spawn it at all! + mo->ClearCounters(); + mo->Destroy(); + return false; + } + else if (originator && !(flags & SIXF_NOPOINTERS)) + { + if (originator->flags3 & MF3_ISMONSTER) + { + // If this is a monster transfer all friendliness information + mo->CopyFriendliness(originator, true); + } + else if (originator->player) + { + // A player always spawns a monster friendly to him + mo->flags |= MF_FRIENDLY; + mo->SetFriendPlayer(originator->player); + + AActor * attacker=originator->player->attacker; + if (attacker) + { + if (!(attacker->flags&MF_FRIENDLY) || + (deathmatch && attacker->FriendPlayer!=0 && attacker->FriendPlayer!=mo->FriendPlayer)) + { + // Target the monster which last attacked the player + mo->LastHeard = mo->target = attacker; + } + } + } + } + } + else if (!(flags & SIXF_TRANSFERPOINTERS)) + { + // If this is a missile or something else set the target to the originator + mo->target = originator ? originator : self; + } + if (flags & SIXF_NOPOINTERS) + { + //[MC]Intentionally eliminate pointers. Overrides TRANSFERPOINTERS, but is overridden by SETMASTER/TARGET/TRACER. + mo->LastHeard = NULL; //Sanity check. + mo->target = NULL; + mo->master = NULL; + mo->tracer = NULL; + } + if (flags & SIXF_SETMASTER) + { + mo->master = originator; + } + if (flags & SIXF_SETTARGET) + { + mo->target = originator; + } + if (flags & SIXF_SETTRACER) + { + mo->tracer = originator; + } + if (flags & SIXF_TRANSFERSCALE) + { + mo->scaleX = self->scaleX; + mo->scaleY = self->scaleY; + } + if (flags & SIXF_TRANSFERAMBUSHFLAG) + { + mo->flags = (mo->flags & ~MF_AMBUSH) | (self->flags & MF_AMBUSH); + } + if (flags & SIXF_CLEARCALLERTID) + { + self->RemoveFromHash(); + self->tid = 0; + } + if (flags & SIXF_TRANSFERSPECIAL) + { + mo->special = self->special; + memcpy(mo->args, self->args, sizeof(self->args)); + } + if (flags & SIXF_CLEARCALLERSPECIAL) + { + self->special = 0; + memset(self->args, 0, sizeof(self->args)); + } + if (flags & SIXF_TRANSFERSTENCILCOL) + { + mo->fillcolor = self->fillcolor; + } + if (flags & SIXF_TRANSFERALPHA) + { + mo->alpha = self->alpha; + } + if (flags & SIXF_TRANSFERRENDERSTYLE) + { + mo->RenderStyle = self->RenderStyle; + } + + return true; +} + +//=========================================================================== +// +// A_SpawnItem +// +// Spawns an item in front of the caller like Heretic's time bomb +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnItem) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_CLASS(missile, 0); + ACTION_PARAM_FIXED(distance, 1); + ACTION_PARAM_FIXED(zheight, 2); + ACTION_PARAM_BOOL(useammo, 3); + ACTION_PARAM_BOOL(transfer_translation, 4); + + if (!missile) + { + ACTION_SET_RESULT(false); + return; + } + + // Don't spawn monsters if this actor has been massacred + if (self->DamageType == NAME_Massacre && GetDefaultByType(missile)->flags3&MF3_ISMONSTER) return; + + if (distance==0) + { + // use the minimum distance that does not result in an overlap + distance=(self->radius+GetDefaultByType(missile)->radius)>>FRACBITS; + } + + if (ACTION_CALL_FROM_WEAPON()) + { + // Used from a weapon so use some ammo + AWeapon * weapon=self->player->ReadyWeapon; + + if (!weapon) return; + if (useammo && !weapon->DepleteAmmo(weapon->bAltFire)) return; + } + + AActor * mo = Spawn( missile, + self->x + FixedMul(distance, finecosine[self->angle>>ANGLETOFINESHIFT]), + self->y + FixedMul(distance, finesine[self->angle>>ANGLETOFINESHIFT]), + self->z - self->floorclip + self->GetBobOffset() + zheight, ALLOW_REPLACE); + + int flags = (transfer_translation ? SIXF_TRANSFERTRANSLATION : 0) + (useammo ? SIXF_SETMASTER : 0); + bool res = InitSpawnedItem(self, mo, flags); + ACTION_SET_RESULT(res); // for an inventory item's use state +} + +//=========================================================================== +// +// A_SpawnItemEx +// +// Enhanced spawning function +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnItemEx) +{ + ACTION_PARAM_START(11); + ACTION_PARAM_CLASS(missile, 0); + ACTION_PARAM_FIXED(xofs, 1); + ACTION_PARAM_FIXED(yofs, 2); + ACTION_PARAM_FIXED(zofs, 3); + ACTION_PARAM_FIXED(xvel, 4); + ACTION_PARAM_FIXED(yvel, 5); + ACTION_PARAM_FIXED(zvel, 6); + ACTION_PARAM_ANGLE(Angle, 7); + ACTION_PARAM_INT(flags, 8); + ACTION_PARAM_INT(chance, 9); + ACTION_PARAM_INT(tid, 10); + + if (!missile) + { + ACTION_SET_RESULT(false); + return; + } + + if (chance > 0 && pr_spawnitemex()DamageType == NAME_Massacre && GetDefaultByType(missile)->flags3&MF3_ISMONSTER) return; + + fixed_t x,y; + + if (!(flags & SIXF_ABSOLUTEANGLE)) + { + Angle += self->angle; + } + + angle_t ang = Angle >> ANGLETOFINESHIFT; + + if (flags & SIXF_ABSOLUTEPOSITION) + { + x = self->x + xofs; + y = self->y + yofs; + } + else + { + // in relative mode negative y values mean 'left' and positive ones mean 'right' + // This is the inverse orientation of the absolute mode! + x = self->x + FixedMul(xofs, finecosine[ang]) + FixedMul(yofs, finesine[ang]); + y = self->y + FixedMul(xofs, finesine[ang]) - FixedMul(yofs, finecosine[ang]); + } + + if (!(flags & SIXF_ABSOLUTEVELOCITY)) + { + // Same orientation issue here! + fixed_t newxvel = FixedMul(xvel, finecosine[ang]) + FixedMul(yvel, finesine[ang]); + yvel = FixedMul(xvel, finesine[ang]) - FixedMul(yvel, finecosine[ang]); + xvel = newxvel; + } + + AActor *mo = Spawn(missile, x, y, self->z - self->floorclip + self->GetBobOffset() + zofs, ALLOW_REPLACE); + bool res = InitSpawnedItem(self, mo, flags); + ACTION_SET_RESULT(res); // for an inventory item's use state + if (res) + { + if (tid != 0) + { + assert(mo->tid == 0); + mo->tid = tid; + mo->AddToHash(); + } + if (flags & SIXF_MULTIPLYSPEED) + { + mo->velx = FixedMul(xvel, mo->Speed); + mo->vely = FixedMul(yvel, mo->Speed); + mo->velz = FixedMul(zvel, mo->Speed); + } + else + { + mo->velx = xvel; + mo->vely = yvel; + mo->velz = zvel; + } + mo->angle = Angle; + } +} + +//=========================================================================== +// +// A_ThrowGrenade +// +// Throws a grenade (like Hexen's fighter flechette) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ThrowGrenade) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_CLASS(missile, 0); + ACTION_PARAM_FIXED(zheight, 1); + ACTION_PARAM_FIXED(xyvel, 2); + ACTION_PARAM_FIXED(zvel, 3); + ACTION_PARAM_BOOL(useammo, 4); + + if (missile == NULL) return; + + if (ACTION_CALL_FROM_WEAPON()) + { + // Used from a weapon, so use some ammo + AWeapon *weapon = self->player->ReadyWeapon; + + if (!weapon) return; + if (useammo && !weapon->DepleteAmmo(weapon->bAltFire)) return; + } + + + AActor * bo; + + bo = Spawn(missile, self->x, self->y, + self->z - self->floorclip + self->GetBobOffset() + zheight + 35*FRACUNIT + (self->player? self->player->crouchoffset : 0), + ALLOW_REPLACE); + if (bo) + { + P_PlaySpawnSound(bo, self); + if (xyvel != 0) + bo->Speed = xyvel; + bo->angle = self->angle + (((pr_grenade()&7) - 4) << 24); + + angle_t pitch = angle_t(-self->pitch) >> ANGLETOFINESHIFT; + angle_t angle = bo->angle >> ANGLETOFINESHIFT; + + // There are two vectors we are concerned about here: xy and z. We rotate + // them separately according to the shooter's pitch and then sum them to + // get the final velocity vector to shoot with. + + fixed_t xy_xyscale = FixedMul(bo->Speed, finecosine[pitch]); + fixed_t xy_velz = FixedMul(bo->Speed, finesine[pitch]); + fixed_t xy_velx = FixedMul(xy_xyscale, finecosine[angle]); + fixed_t xy_vely = FixedMul(xy_xyscale, finesine[angle]); + + pitch = angle_t(self->pitch) >> ANGLETOFINESHIFT; + fixed_t z_xyscale = FixedMul(zvel, finesine[pitch]); + fixed_t z_velz = FixedMul(zvel, finecosine[pitch]); + fixed_t z_velx = FixedMul(z_xyscale, finecosine[angle]); + fixed_t z_vely = FixedMul(z_xyscale, finesine[angle]); + + bo->velx = xy_velx + z_velx + (self->velx >> 1); + bo->vely = xy_vely + z_vely + (self->vely >> 1); + bo->velz = xy_velz + z_velz; + + bo->target = self; + P_CheckMissileSpawn (bo, self->radius); + } + else ACTION_SET_RESULT(false); +} + + +//=========================================================================== +// +// A_Recoil +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Recoil) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_FIXED(xyvel, 0); + + angle_t angle = self->angle + ANG180; + angle >>= ANGLETOFINESHIFT; + self->velx += FixedMul (xyvel, finecosine[angle]); + self->vely += FixedMul (xyvel, finesine[angle]); +} + + +//=========================================================================== +// +// A_SelectWeapon +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SelectWeapon) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_CLASS(cls, 0); + + if (cls == NULL || self->player == NULL) + { + ACTION_SET_RESULT(false); + return; + } + + AWeapon * weaponitem = static_cast(self->FindInventory(cls)); + + if (weaponitem != NULL && weaponitem->IsKindOf(RUNTIME_CLASS(AWeapon))) + { + if (self->player->ReadyWeapon != weaponitem) + { + self->player->PendingWeapon = weaponitem; + } + } + else ACTION_SET_RESULT(false); + +} + + +//=========================================================================== +// +// A_Print +// +//=========================================================================== +EXTERN_CVAR(Float, con_midtime) + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Print) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_STRING(text, 0); + ACTION_PARAM_FLOAT(time, 1); + ACTION_PARAM_NAME(fontname, 2); + + if (text[0] == '$') text = GStrings(text+1); + if (self->CheckLocalView (consoleplayer) || + (self->target!=NULL && self->target->CheckLocalView (consoleplayer))) + { + float saved = con_midtime; + FFont *font = NULL; + + if (fontname != NAME_None) + { + font = V_GetFont(fontname); + } + if (time > 0) + { + con_midtime = time; + } + + FString formatted = strbin1(text); + C_MidPrint(font != NULL ? font : SmallFont, formatted.GetChars()); + con_midtime = saved; + } + ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! +} + +//=========================================================================== +// +// A_PrintBold +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PrintBold) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_STRING(text, 0); + ACTION_PARAM_FLOAT(time, 1); + ACTION_PARAM_NAME(fontname, 2); + + float saved = con_midtime; + FFont *font = NULL; + + if (text[0] == '$') text = GStrings(text+1); + if (fontname != NAME_None) + { + font = V_GetFont(fontname); + } + if (time > 0) + { + con_midtime = time; + } + + FString formatted = strbin1(text); + C_MidPrintBold(font != NULL ? font : SmallFont, formatted.GetChars()); + con_midtime = saved; + ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! +} + +//=========================================================================== +// +// A_Log +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Log) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STRING(text, 0); + + if (text[0] == '$') text = GStrings(text+1); + FString formatted = strbin1(text); + Printf("%s\n", formatted.GetChars()); + ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! +} + +//========================================================================= +// +// A_LogInt +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogInt) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(num, 0); + Printf("%d\n", num); + ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains! +} + +//=========================================================================== +// +// A_SetTranslucent +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTranslucent) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_FIXED(alpha, 0); + ACTION_PARAM_INT(mode, 1); + + mode = mode == 0 ? STYLE_Translucent : mode == 2 ? STYLE_Fuzzy : STYLE_Add; + + self->RenderStyle.Flags &= ~STYLEF_Alpha1; + self->alpha = clamp(alpha, 0, FRACUNIT); + self->RenderStyle = ERenderStyle(mode); +} + +//=========================================================================== +// +// A_FadeIn +// +// Fades the actor in +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeIn) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_FIXED(reduce, 0); + + if (reduce == 0) + { + reduce = FRACUNIT/10; + } + self->RenderStyle.Flags &= ~STYLEF_Alpha1; + self->alpha += reduce; + // Should this clamp alpha to 1.0? +} + +//=========================================================================== +// +// A_FadeOut +// +// fades the actor out and destroys it when done +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeOut) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_FIXED(reduce, 0); + ACTION_PARAM_BOOL(remove, 1); + + if (reduce == 0) + { + reduce = FRACUNIT/10; + } + self->RenderStyle.Flags &= ~STYLEF_Alpha1; + self->alpha -= reduce; + if (self->alpha <= 0 && remove) + { + self->Destroy(); + } +} + +//=========================================================================== +// +// A_FadeTo +// +// fades the actor to a specified transparency by a specified amount and +// destroys it if so desired +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_FIXED(target, 0); + ACTION_PARAM_FIXED(amount, 1); + ACTION_PARAM_BOOL(remove, 2); + + self->RenderStyle.Flags &= ~STYLEF_Alpha1; + + if (self->alpha > target) + { + self->alpha -= amount; + + if (self->alpha < target) + { + self->alpha = target; + } + } + else if (self->alpha < target) + { + self->alpha += amount; + + if (self->alpha > target) + { + self->alpha = target; + } + } + if (self->alpha == target && remove) + { + self->Destroy(); + } +} + +//=========================================================================== +// +// A_Scale(float scalex, optional float scaley) +// +// Scales the actor's graphics. If scaley is 0, use scalex. +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetScale) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_FIXED(scalex, 0); + ACTION_PARAM_FIXED(scaley, 1); + + self->scaleX = scalex; + self->scaleY = scaley ? scaley : scalex; +} + +//=========================================================================== +// +// A_SetMass(int mass) +// +// Sets the actor's mass. +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetMass) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(mass, 0); + + self->Mass = mass; +} + +//=========================================================================== +// +// A_SpawnDebris +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnDebris) +{ + int i; + AActor * mo; + + ACTION_PARAM_START(4); + ACTION_PARAM_CLASS(debris, 0); + ACTION_PARAM_BOOL(transfer_translation, 1); + ACTION_PARAM_FIXED(mult_h, 2); + ACTION_PARAM_FIXED(mult_v, 3); + + if (debris == NULL) return; + + // only positive values make sense here + if (mult_v<=0) mult_v=FRACUNIT; + if (mult_h<=0) mult_h=FRACUNIT; + + for (i = 0; i < GetDefaultByType(debris)->health; i++) + { + mo = Spawn(debris, self->x+((pr_spawndebris()-128)<<12), + self->y + ((pr_spawndebris()-128)<<12), + self->z + (pr_spawndebris()*self->height/256+self->GetBobOffset()), ALLOW_REPLACE); + if (mo) + { + if (transfer_translation) + { + mo->Translation = self->Translation; + } + if (i < mo->GetClass()->ActorInfo->NumOwnedStates) + { + mo->SetState(mo->GetClass()->ActorInfo->OwnedStates + i); + } + mo->velz = FixedMul(mult_v, ((pr_spawndebris()&7)+5)*FRACUNIT); + mo->velx = FixedMul(mult_h, pr_spawndebris.Random2()<<(FRACBITS-6)); + mo->vely = FixedMul(mult_h, pr_spawndebris.Random2()<<(FRACBITS-6)); + } + } +} + + +//=========================================================================== +// +// A_CheckSight +// jumps if no player can see this actor +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSight) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + for (int i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + // Always check sight from each player. + if (P_CheckSight(players[i].mo, self, SF_IGNOREVISIBILITY)) + { + return; + } + // If a player is viewing from a non-player, then check that too. + if (players[i].camera != NULL && players[i].camera->player == NULL && + P_CheckSight(players[i].camera, self, SF_IGNOREVISIBILITY)) + { + return; + } + } + } + + ACTION_JUMP(jump); +} + +//=========================================================================== +// +// A_CheckSightOrRange +// Jumps if this actor is out of range of all players *and* out of sight. +// Useful for maps with many multi-actor special effects. +// +//=========================================================================== +static bool DoCheckSightOrRange(AActor *self, AActor *camera, double range) +{ + if (camera == NULL) + { + return false; + } + // Check distance first, since it's cheaper than checking sight. + double dx = self->x - camera->x; + double dy = self->y - camera->y; + double dz; + fixed_t eyez = (camera->z + camera->height - (camera->height>>2)); // same eye height as P_CheckSight + if (eyez > self->z + self->height) + { + dz = self->z + self->height - eyez; + } + else if (eyez < self->z) + { + dz = self->z - eyez; + } + else + { + dz = 0; + } + if ((dx*dx) + (dy*dy) + (dz*dz) <= range) + { // Within range + return true; + } + + // Now check LOS. + if (P_CheckSight(camera, self, SF_IGNOREVISIBILITY)) + { // Visible + return true; + } + return false; +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSightOrRange) +{ + ACTION_PARAM_START(2); + double range = EvalExpressionF(ParameterIndex+0, self); + ACTION_PARAM_STATE(jump, 1); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + range = range * range * (double(FRACUNIT) * FRACUNIT); // no need for square roots + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i]) + { + // Always check from each player. + if (DoCheckSightOrRange(self, players[i].mo, range)) + { + return; + } + // If a player is viewing from a non-player, check that too. + if (players[i].camera != NULL && players[i].camera->player == NULL && + DoCheckSightOrRange(self, players[i].camera, range)) + { + return; + } + } + } + ACTION_JUMP(jump); +} + +//=========================================================================== +// +// A_CheckRange +// Jumps if this actor is out of range of all players. +// +//=========================================================================== +static bool DoCheckRange(AActor *self, AActor *camera, double range) +{ + if (camera == NULL) + { + return false; + } + // Check distance first, since it's cheaper than checking sight. + double dx = self->x - camera->x; + double dy = self->y - camera->y; + double dz; + fixed_t eyez = (camera->z + camera->height - (camera->height>>2)); // same eye height as P_CheckSight + if (eyez > self->z + self->height){ + dz = self->z + self->height - eyez; + } + else if (eyez < self->z){ + dz = self->z - eyez; + } + else{ + dz = 0; + } + if ((dx*dx) + (dy*dy) + (dz*dz) <= range){ + // Within range + return true; + } + return false; +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckRange) +{ + ACTION_PARAM_START(2); + double range = EvalExpressionF(ParameterIndex+0, self); + ACTION_PARAM_STATE(jump, 1); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + range = range * range * (double(FRACUNIT) * FRACUNIT); // no need for square roots + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i]) + { + // Always check from each player. + if (DoCheckRange(self, players[i].mo, range)) + { + return; + } + // If a player is viewing from a non-player, check that too. + if (players[i].camera != NULL && players[i].camera->player == NULL && + DoCheckRange(self, players[i].camera, range)) + { + return; + } + } + } + ACTION_JUMP(jump); +} + + +//=========================================================================== +// +// Inventory drop +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropInventory) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_CLASS(drop, 0); + + if (drop) + { + AInventory * inv = self->FindInventory(drop); + if (inv) + { + self->DropInventory(inv); + } + } +} + + +//=========================================================================== +// +// A_SetBlend +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetBlend) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_COLOR(color, 0); + ACTION_PARAM_FLOAT(alpha, 1); + ACTION_PARAM_INT(tics, 2); + ACTION_PARAM_COLOR(color2, 3); + + if (color == MAKEARGB(255,255,255,255)) color=0; + if (color2 == MAKEARGB(255,255,255,255)) color2=0; + if (!color2.a) + color2 = color; + + new DFlashFader(color.r/255.0f, color.g/255.0f, color.b/255.0f, alpha, + color2.r/255.0f, color2.g/255.0f, color2.b/255.0f, 0, + (float)tics/TICRATE, self); +} + + +//=========================================================================== +// +// A_JumpIf +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIf) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_BOOL(expression, 0); + ACTION_PARAM_STATE(jump, 1); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + if (expression) ACTION_JUMP(jump); + +} + +//=========================================================================== +// +// A_CountdownArg +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CountdownArg) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(cnt, 0); + ACTION_PARAM_STATE(state, 1); + + if (cnt<0 || cnt>=5) return; + if (!self->args[cnt]--) + { + if (self->flags&MF_MISSILE) + { + P_ExplodeMissile(self, NULL, NULL); + } + else if (self->flags&MF_SHOOTABLE) + { + P_DamageMobj (self, NULL, NULL, self->health, NAME_None, DMG_FORCED); + } + else + { + // can't use "Death" as default parameter with current DECORATE parser. + if (state == NULL) state = self->FindState(NAME_Death); + self->SetState(state); + } + } + +} + +//============================================================================ +// +// A_Burst +// +//============================================================================ + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Burst) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_CLASS(chunk, 0); + + int i, numChunks; + AActor * mo; + + if (chunk == NULL) return; + + self->velx = self->vely = self->velz = 0; + self->height = self->GetDefault()->height; + + // [RH] In Hexen, this creates a random number of shards (range [24,56]) + // with no relation to the size of the self shattering. I think it should + // base the number of shards on the size of the dead thing, so bigger + // things break up into more shards than smaller things. + // An self with radius 20 and height 64 creates ~40 chunks. + numChunks = MAX (4, (self->radius>>FRACBITS)*(self->height>>FRACBITS)/32); + i = (pr_burst.Random2()) % (numChunks/4); + for (i = MAX (24, numChunks + i); i >= 0; i--) + { + mo = Spawn(chunk, + self->x + (((pr_burst()-128)*self->radius)>>7), + self->y + (((pr_burst()-128)*self->radius)>>7), + self->z + (pr_burst()*self->height/255 + self->GetBobOffset()), ALLOW_REPLACE); + + if (mo) + { + mo->velz = FixedDiv(mo->z - self->z, self->height)<<2; + mo->velx = pr_burst.Random2 () << (FRACBITS-7); + mo->vely = pr_burst.Random2 () << (FRACBITS-7); + mo->RenderStyle = self->RenderStyle; + mo->alpha = self->alpha; + mo->CopyFriendliness(self, true); + } + } + + // [RH] Do some stuff to make this more useful outside Hexen + if (self->flags4 & MF4_BOSSDEATH) + { + CALL_ACTION(A_BossDeath, self); + } + A_Unblock(self, true); + + self->Destroy (); +} + +//=========================================================================== +// +// A_CheckFloor +// [GRB] Jumps if actor is standing on floor +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFloor) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + if (self->z <= self->floorz) + { + ACTION_JUMP(jump); + } + +} + +//=========================================================================== +// +// A_CheckCeiling +// [GZ] Totally copied on A_CheckFloor, jumps if actor touches ceiling +// + +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckCeiling) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + ACTION_SET_RESULT(false); + if (self->z+self->height >= self->ceilingz) // Height needs to be counted + { + ACTION_JUMP(jump); + } + +} + +//=========================================================================== +// +// A_Stop +// resets all velocity of the actor to 0 +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_Stop) +{ + self->velx = self->vely = self->velz = 0; + if (self->player && self->player->mo == self && !(self->player->cheats & CF_PREDICTING)) + { + self->player->mo->PlayIdle(); + self->player->velx = self->player->vely = 0; + } +} + +static void CheckStopped(AActor *self) +{ + if (self->player != NULL && + self->player->mo == self && + !(self->player->cheats & CF_PREDICTING) && + !(self->velx | self->vely | self->velz)) + { + self->player->mo->PlayIdle(); + self->player->velx = self->player->vely = 0; + } +} + +//=========================================================================== +// +// A_Respawn +// +//=========================================================================== + +extern void AF_A_RestoreSpecialPosition(DECLARE_PARAMINFO); + +enum RS_Flags +{ + RSF_FOG=1, + RSF_KEEPTARGET=2, + RSF_TELEFRAG=4, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Respawn) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(flags, 0); + + bool oktorespawn = false; + + self->flags |= MF_SOLID; + self->height = self->GetDefault()->height; + CALL_ACTION(A_RestoreSpecialPosition, self); + + if (flags & RSF_TELEFRAG) + { + // [KS] DIE DIE DIE DIE erm *ahem* =) + oktorespawn = P_TeleportMove(self, self->x, self->y, self->z, true); + if (oktorespawn) + { // Need to do this over again, since P_TeleportMove() will redo + // it with the proper point-on-side calculation. + self->UnlinkFromWorld(); + self->LinkToWorld(true); + sector_t *sec = self->Sector; + self->dropoffz = + self->floorz = sec->floorplane.ZatPoint(self->x, self->y); + self->ceilingz = sec->ceilingplane.ZatPoint(self->x, self->y); + P_FindFloorCeiling(self, FFCF_ONLYSPAWNPOS); + } + } + else + { + oktorespawn = P_CheckPosition(self, self->x, self->y, true); + } + + if (oktorespawn) + { + AActor *defs = self->GetDefault(); + self->health = defs->health; + + // [KS] Don't keep target, because it could be self if the monster committed suicide + // ...Actually it's better off an option, so you have better control over monster behavior. + if (!(flags & RSF_KEEPTARGET)) + { + self->target = NULL; + self->LastHeard = NULL; + self->lastenemy = NULL; + } + else + { + // Don't attack yourself (Re: "Marine targets itself after suicide") + if (self->target == self) self->target = NULL; + if (self->lastenemy == self) self->lastenemy = NULL; + } + + self->flags = (defs->flags & ~MF_FRIENDLY) | (self->flags & MF_FRIENDLY); + self->flags2 = defs->flags2; + self->flags3 = (defs->flags3 & ~(MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)) | (self->flags3 & (MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)); + self->flags4 = (defs->flags4 & ~MF4_NOHATEPLAYERS) | (self->flags4 & MF4_NOHATEPLAYERS); + self->flags5 = defs->flags5; + self->SetState (self->SpawnState); + self->renderflags &= ~RF_INVISIBLE; + + if (flags & RSF_FOG) + { + Spawn (self->x, self->y, self->z + TELEFOGHEIGHT, ALLOW_REPLACE); + } + if (self->CountsAsKill()) + { + level.total_monsters++; + } + } + else + { + self->flags &= ~MF_SOLID; + } +} + + +//========================================================================== +// +// A_PlayerSkinCheck +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlayerSkinCheck) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_STATE(jump, 0); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + if (self->player != NULL && + skins[self->player->userinfo.GetSkin()].othergame) + { + ACTION_JUMP(jump); + } +} + +//=========================================================================== +// +// A_SetGravity +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetGravity) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_FIXED(val, 0); + + self->gravity = clamp (val, 0, FRACUNIT*10); +} + + +// [KS] *** Start of my modifications *** + +//=========================================================================== +// +// A_ClearTarget +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION(AActor, A_ClearTarget) +{ + self->target = NULL; + self->LastHeard = NULL; + self->lastenemy = NULL; +} + +//========================================================================== +// +// A_CheckLOF (state jump, int flags = CRF_AIM_VERT|CRF_AIM_HOR, +// fixed range = 0, angle angle = 0, angle pitch = 0, +// fixed offsetheight = 32, fixed offsetwidth = 0, +// int ptr_target = AAPTR_DEFAULT (target) ) +// +//========================================================================== + +enum CLOF_flags +{ + CLOFF_NOAIM_VERT = 0x1, + CLOFF_NOAIM_HORZ = 0x2, + + CLOFF_JUMPENEMY = 0x4, + CLOFF_JUMPFRIEND = 0x8, + CLOFF_JUMPOBJECT = 0x10, + CLOFF_JUMPNONHOSTILE = 0x20, + + CLOFF_SKIPENEMY = 0x40, + CLOFF_SKIPFRIEND = 0x80, + CLOFF_SKIPOBJECT = 0x100, + CLOFF_SKIPNONHOSTILE = 0x200, + + CLOFF_MUSTBESHOOTABLE = 0x400, + + CLOFF_SKIPTARGET = 0x800, + CLOFF_ALLOWNULL = 0x1000, + CLOFF_CHECKPARTIAL = 0x2000, + + CLOFF_MUSTBEGHOST = 0x4000, + CLOFF_IGNOREGHOST = 0x8000, + + CLOFF_MUSTBESOLID = 0x10000, + CLOFF_BEYONDTARGET = 0x20000, + + CLOFF_FROMBASE = 0x40000, + CLOFF_MUL_HEIGHT = 0x80000, + CLOFF_MUL_WIDTH = 0x100000, + + CLOFF_JUMP_ON_MISS = 0x200000, + CLOFF_AIM_VERT_NOOFFSET = 0x400000, +}; + +struct LOFData +{ + AActor *Self; + AActor *Target; + int Flags; + bool BadActor; +}; + +ETraceStatus CheckLOFTraceFunc(FTraceResults &trace, void *userdata) +{ + LOFData *data = (LOFData *)userdata; + int flags = data->Flags; + + if (trace.HitType != TRACE_HitActor) + { + return TRACE_Stop; + } + if (trace.Actor == data->Target) + { + if (flags & CLOFF_SKIPTARGET) + { + if (flags & CLOFF_BEYONDTARGET) + { + return TRACE_Skip; + } + return TRACE_Abort; + } + return TRACE_Stop; + } + if (flags & CLOFF_MUSTBESHOOTABLE) + { // all shootability checks go here + if (!(trace.Actor->flags & MF_SHOOTABLE)) + { + return TRACE_Skip; + } + if (trace.Actor->flags2 & MF2_NONSHOOTABLE) + { + return TRACE_Skip; + } + } + if ((flags & CLOFF_MUSTBESOLID) && !(trace.Actor->flags & MF_SOLID)) + { + return TRACE_Skip; + } + if (flags & CLOFF_MUSTBEGHOST) + { + if (!(trace.Actor->flags3 & MF3_GHOST)) + { + return TRACE_Skip; + } + } + else if (flags & CLOFF_IGNOREGHOST) + { + if (trace.Actor->flags3 & MF3_GHOST) + { + return TRACE_Skip; + } + } + if ( + ((flags & CLOFF_JUMPENEMY) && data->Self->IsHostile(trace.Actor)) || + ((flags & CLOFF_JUMPFRIEND) && data->Self->IsFriend(trace.Actor)) || + ((flags & CLOFF_JUMPOBJECT) && !(trace.Actor->flags3 & MF3_ISMONSTER)) || + ((flags & CLOFF_JUMPNONHOSTILE) && (trace.Actor->flags3 & MF3_ISMONSTER) && !data->Self->IsHostile(trace.Actor)) + ) + { + return TRACE_Stop; + } + if ( + ((flags & CLOFF_SKIPENEMY) && data->Self->IsHostile(trace.Actor)) || + ((flags & CLOFF_SKIPFRIEND) && data->Self->IsFriend(trace.Actor)) || + ((flags & CLOFF_SKIPOBJECT) && !(trace.Actor->flags3 & MF3_ISMONSTER)) || + ((flags & CLOFF_SKIPNONHOSTILE) && (trace.Actor->flags3 & MF3_ISMONSTER) && !data->Self->IsHostile(trace.Actor)) + ) + { + return TRACE_Skip; + } + data->BadActor = true; + return TRACE_Abort; +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF) +{ + // Check line of fire + + /* + Not accounted for / I don't know how it works: FLOORCLIP + */ + + AActor *target; + fixed_t + x1, y1, z1, + vx, vy, vz; + + ACTION_PARAM_START(9); + + ACTION_PARAM_STATE(jump, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_FIXED(range, 2); + ACTION_PARAM_FIXED(minrange, 3); + { + ACTION_PARAM_ANGLE(angle, 4); + ACTION_PARAM_ANGLE(pitch, 5); + ACTION_PARAM_FIXED(offsetheight, 6); + ACTION_PARAM_FIXED(offsetwidth, 7); + ACTION_PARAM_INT(ptr_target, 8); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + target = COPY_AAPTR(self, ptr_target == AAPTR_DEFAULT ? AAPTR_TARGET|AAPTR_PLAYER_GETTARGET|AAPTR_NULL : ptr_target); // no player-support by default + + if (flags & CLOFF_MUL_HEIGHT) + { + if (self->player != NULL) + { + // Synced with hitscan: self->player->mo->height is strangely conscientious about getting the right actor for player + offsetheight = FixedMul(offsetheight, FixedMul (self->player->mo->height, self->player->crouchfactor)); + } + else + { + offsetheight = FixedMul(offsetheight, self->height); + } + } + if (flags & CLOFF_MUL_WIDTH) + { + offsetwidth = FixedMul(self->radius, offsetwidth); + } + + x1 = self->x; + y1 = self->y; + z1 = self->z + offsetheight - self->floorclip; + + if (!(flags & CLOFF_FROMBASE)) + { // default to hitscan origin + + // Synced with hitscan: self->height is strangely NON-conscientious about getting the right actor for player + z1 += (self->height >> 1); + if (self->player != NULL) + { + z1 += FixedMul (self->player->mo->AttackZOffset, self->player->crouchfactor); + } + else + { + z1 += 8*FRACUNIT; + } + } + + if (target) + { + FVector2 xyvec(target->x - x1, target->y - y1); + fixed_t distance = P_AproxDistance((fixed_t)xyvec.Length(), target->z - z1); + + if (range && !(flags & CLOFF_CHECKPARTIAL)) + { + if (distance > range) return; + } + + { + angle_t ang; + + if (flags & CLOFF_NOAIM_HORZ) + { + ang = self->angle; + } + else ang = R_PointToAngle2 (x1, y1, target->x, target->y); + + angle += ang; + + ang >>= ANGLETOFINESHIFT; + x1 += FixedMul(offsetwidth, finesine[ang]); + y1 -= FixedMul(offsetwidth, finecosine[ang]); + } + + if (flags & CLOFF_NOAIM_VERT) + { + pitch += self->pitch; + } + else if (flags & CLOFF_AIM_VERT_NOOFFSET) + { + pitch += R_PointToAngle2 (0,0, (fixed_t)xyvec.Length(), target->z - z1 + offsetheight + target->height / 2); + } + else + { + pitch += R_PointToAngle2 (0,0, (fixed_t)xyvec.Length(), target->z - z1 + target->height / 2); + } + } + else if (flags & CLOFF_ALLOWNULL) + { + angle += self->angle; + pitch += self->pitch; + + angle_t ang = self->angle >> ANGLETOFINESHIFT; + x1 += FixedMul(offsetwidth, finesine[ang]); + y1 -= FixedMul(offsetwidth, finecosine[ang]); + } + else return; + + angle >>= ANGLETOFINESHIFT; + pitch = (0-pitch)>>ANGLETOFINESHIFT; + + vx = FixedMul (finecosine[pitch], finecosine[angle]); + vy = FixedMul (finecosine[pitch], finesine[angle]); + vz = -finesine[pitch]; + } + + /* Variable set: + + jump, flags, target + x1,y1,z1 (trace point of origin) + vx,vy,vz (trace unit vector) + range + */ + + sector_t *sec = P_PointInSector(x1, y1); + + if (range == 0) + { + range = (self->player != NULL) ? PLAYERMISSILERANGE : MISSILERANGE; + } + + FTraceResults trace; + LOFData lof_data; + + lof_data.Self = self; + lof_data.Target = target; + lof_data.Flags = flags; + lof_data.BadActor = false; + + Trace(x1, y1, z1, sec, vx, vy, vz, range, 0xFFFFFFFF, ML_BLOCKEVERYTHING, self, trace, 0, + CheckLOFTraceFunc, &lof_data); + + if (trace.HitType == TRACE_HitActor || + ((flags & CLOFF_JUMP_ON_MISS) && !lof_data.BadActor && trace.HitType != TRACE_HitNone)) + { + if (minrange > 0 && trace.Distance < minrange) + { + return; + } + ACTION_JUMP(jump); + } +} + +//========================================================================== +// +// A_JumpIfTargetInLOS (state label, optional fixed fov, optional int flags, +// optional fixed dist_max, optional fixed dist_close) +// +// Jumps if the actor can see its target, or if the player has a linetarget. +// ProjectileTarget affects how projectiles are treated. If set, it will use +// the target of the projectile for seekers, and ignore the target for +// normal projectiles. If not set, it will use the missile's owner instead +// (the default). ProjectileTarget is now flag JLOSF_PROJECTILE. dist_max +// sets the maximum distance that actor can see, 0 means forever. dist_close +// uses special behavior if certain flags are set, 0 means no checks. +// +//========================================================================== + +enum JLOS_flags +{ + JLOSF_PROJECTILE=1, + JLOSF_NOSIGHT=2, + JLOSF_CLOSENOFOV=4, + JLOSF_CLOSENOSIGHT=8, + JLOSF_CLOSENOJUMP=16, + JLOSF_DEADNOJUMP=32, + JLOSF_CHECKMASTER=64, + JLOSF_TARGETLOS=128, + JLOSF_FLIPFOV=256, + JLOSF_ALLYNOJUMP=512, + JLOSF_COMBATANTONLY=1024, + JLOSF_NOAUTOAIM=2048, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_STATE(jump, 0); + ACTION_PARAM_ANGLE(fov, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_FIXED(dist_max, 3); + ACTION_PARAM_FIXED(dist_close, 4); + + angle_t an; + AActor *target, *viewport; + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + bool doCheckSight; + + if (!self->player) + { + if (flags & JLOSF_CHECKMASTER) + { + target = self->master; + } + else if (self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE)) + { + if (self->flags2 & MF2_SEEKERMISSILE) + target = self->tracer; + else + target = NULL; + } + else + { + target = self->target; + } + + if (!target) return; // [KS] Let's not call P_CheckSight unnecessarily in this case. + + if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0)) return; + + doCheckSight = !(flags & JLOSF_NOSIGHT); + } + else + { + // Does the player aim at something that can be shot? + P_AimLineAttack(self, self->angle, MISSILERANGE, &target, (flags & JLOSF_NOAUTOAIM) ? ANGLE_1/2 : 0); + + if (!target) return; + + switch (flags & (JLOSF_TARGETLOS|JLOSF_FLIPFOV)) + { + case JLOSF_TARGETLOS|JLOSF_FLIPFOV: + // target makes sight check, player makes fov check; player has verified fov + fov = 0; + // fall-through + case JLOSF_TARGETLOS: + doCheckSight = !(flags & JLOSF_NOSIGHT); // The target is responsible for sight check and fov + break; + default: + // player has verified sight and fov + fov = 0; + // fall-through + case JLOSF_FLIPFOV: // Player has verified sight, but target must verify fov + doCheckSight = false; + break; + } + } + + // [FDARI] If target is not a combatant, don't jump + if ( (flags & JLOSF_COMBATANTONLY) && (!target->player) && !(target->flags3 & MF3_ISMONSTER)) return; + + // [FDARI] If actors share team, don't jump + if ((flags & JLOSF_ALLYNOJUMP) && self->IsFriend(target)) return; + + fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y); + distance = P_AproxDistance(distance, target->z - self->z); + + if (dist_max && (distance > dist_max)) return; + + if (dist_close && (distance < dist_close)) + { + if (flags & JLOSF_CLOSENOJUMP) + return; + + if (flags & JLOSF_CLOSENOFOV) + fov = 0; + + if (flags & JLOSF_CLOSENOSIGHT) + doCheckSight = false; + } + + if (flags & JLOSF_TARGETLOS) { viewport = target; target = self; } + else { viewport = self; } + + if (doCheckSight && !P_CheckSight (viewport, target, SF_IGNOREVISIBILITY)) + return; + + if (flags & JLOSF_FLIPFOV) + { + if (viewport == self) { viewport = target; target = self; } + else { target = viewport; viewport = self; } + } + + if (fov && (fov < ANGLE_MAX)) + { + an = R_PointToAngle2 (viewport->x, + viewport->y, + target->x, + target->y) + - viewport->angle; + + if (an > (fov / 2) && an < (ANGLE_MAX - (fov / 2))) + { + return; // [KS] Outside of FOV - return + } + + } + + ACTION_JUMP(jump); +} + + +//========================================================================== +// +// A_JumpIfInTargetLOS (state label, optional fixed fov, optional int flags +// optional fixed dist_max, optional fixed dist_close) +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_STATE(jump, 0); + ACTION_PARAM_ANGLE(fov, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_FIXED(dist_max, 3); + ACTION_PARAM_FIXED(dist_close, 4); + + angle_t an; + AActor *target; + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + if (flags & JLOSF_CHECKMASTER) + { + target = self->master; + } + else if (self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE)) + { + if (self->flags2 & MF2_SEEKERMISSILE) + target = self->tracer; + else + target = NULL; + } + else + { + target = self->target; + } + + if (!target) return; // [KS] Let's not call P_CheckSight unnecessarily in this case. + + if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0)) return; + + fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y); + distance = P_AproxDistance(distance, target->z - self->z); + + if (dist_max && (distance > dist_max)) return; + + bool doCheckSight = !(flags & JLOSF_NOSIGHT); + + if (dist_close && (distance < dist_close)) + { + if (flags & JLOSF_CLOSENOJUMP) + return; + + if (flags & JLOSF_CLOSENOFOV) + fov = 0; + + if (flags & JLOSF_CLOSENOSIGHT) + doCheckSight = false; + } + + if (fov && (fov < ANGLE_MAX)) + { + an = R_PointToAngle2 (target->x, + target->y, + self->x, + self->y) + - target->angle; + + if (an > (fov / 2) && an < (ANGLE_MAX - (fov / 2))) + { + return; // [KS] Outside of FOV - return + } + } + + if (doCheckSight && !P_CheckSight (target, self, SF_IGNOREVISIBILITY)) + return; + + ACTION_JUMP(jump); +} + +//=========================================================================== +// +// Modified code pointer from Skulltag +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckForReload) +{ + if ( self->player == NULL || self->player->ReadyWeapon == NULL ) + return; + + ACTION_PARAM_START(2); + ACTION_PARAM_INT(count, 0); + ACTION_PARAM_STATE(jump, 1); + ACTION_PARAM_BOOL(dontincrement, 2) + + if (count <= 0) return; + + AWeapon *weapon = self->player->ReadyWeapon; + + int ReloadCounter = weapon->ReloadCounter; + if(!dontincrement || ReloadCounter != 0) + ReloadCounter = (weapon->ReloadCounter+1) % count; + else // 0 % 1 = 1? So how do we check if the weapon was never fired? We should only do this when we're not incrementing the counter though. + ReloadCounter = 1; + + // If we have not made our last shot... + if (ReloadCounter != 0) + { + // Go back to the refire frames, instead of continuing on to the reload frames. + ACTION_JUMP(jump); + } + else + { + // We need to reload. However, don't reload if we're out of ammo. + weapon->CheckAmmo( false, false ); + } + + if(!dontincrement) + weapon->ReloadCounter = ReloadCounter; +} + +//=========================================================================== +// +// Resets the counter for the above function +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION(AActor, A_ResetReloadCounter) +{ + if ( self->player == NULL || self->player->ReadyWeapon == NULL ) + return; + + AWeapon *weapon = self->player->ReadyWeapon; + weapon->ReloadCounter = 0; +} + +//=========================================================================== +// +// A_ChangeFlag +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_STRING(flagname, 0); + ACTION_PARAM_BOOL(expression, 1); + + const char *dot = strchr (flagname, '.'); + FFlagDef *fd; + const PClass *cls = self->GetClass(); + + if (dot != NULL) + { + FString part1(flagname, dot-flagname); + fd = FindFlag (cls, part1, dot+1); + } + else + { + fd = FindFlag (cls, flagname, NULL); + } + + if (fd != NULL) + { + bool kill_before, kill_after; + INTBOOL item_before, item_after; + INTBOOL secret_before, secret_after; + + kill_before = self->CountsAsKill(); + item_before = self->flags & MF_COUNTITEM; + secret_before = self->flags5 & MF5_COUNTSECRET; + + if (fd->structoffset == -1) + { + HandleDeprecatedFlags(self, cls->ActorInfo, expression, fd->flagbit); + } + else + { + DWORD *flagp = (DWORD*) (((char*)self) + fd->structoffset); + + // If these 2 flags get changed we need to update the blockmap and sector links. + bool linkchange = flagp == &self->flags && (fd->flagbit == MF_NOBLOCKMAP || fd->flagbit == MF_NOSECTOR); + + if (linkchange) self->UnlinkFromWorld(); + ModActorFlag(self, fd, expression); + if (linkchange) self->LinkToWorld(); + } + kill_after = self->CountsAsKill(); + item_after = self->flags & MF_COUNTITEM; + secret_after = self->flags5 & MF5_COUNTSECRET; + // Was this monster previously worth a kill but no longer is? + // Or vice versa? + if (kill_before != kill_after) + { + if (kill_after) + { // It counts as a kill now. + level.total_monsters++; + } + else + { // It no longer counts as a kill. + level.total_monsters--; + } + } + // same for items + if (item_before != item_after) + { + if (item_after) + { // It counts as an item now. + level.total_items++; + } + else + { // It no longer counts as an item + level.total_items--; + } + } + // and secretd + if (secret_before != secret_after) + { + if (secret_after) + { // It counts as an secret now. + level.total_secrets++; + } + else + { // It no longer counts as an secret + level.total_secrets--; + } + } + } + else + { + Printf("Unknown flag '%s' in '%s'\n", flagname, cls->TypeName.GetChars()); + } +} + +//=========================================================================== +// +// A_CheckFlag +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFlag) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_STRING(flagname, 0); + ACTION_PARAM_STATE(jumpto, 1); + ACTION_PARAM_INT(checkpointer, 2); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + AActor *owner; + + COPY_AAPTR_NOT_NULL(self, owner, checkpointer); + + if (CheckActorFlag(owner, flagname)) + { + ACTION_JUMP(jumpto); + } +} + + +//=========================================================================== +// +// A_RemoveMaster +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RemoveMaster) +{ + if (self->master != NULL) + { + P_RemoveThing(self->master); + } +} + +//=========================================================================== +// +// A_RemoveChildren +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveChildren) +{ + TThinkerIterator it; + AActor *mo; + ACTION_PARAM_START(1); + ACTION_PARAM_BOOL(removeall,0); + + while ((mo = it.Next()) != NULL) + { + if (mo->master == self && (mo->health <= 0 || removeall)) + { + P_RemoveThing(mo); + } + } +} + +//=========================================================================== +// +// A_RemoveSiblings +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) +{ + TThinkerIterator it; + AActor *mo; + ACTION_PARAM_START(1); + ACTION_PARAM_BOOL(removeall,0); + + if (self->master != NULL) + { + while ((mo = it.Next()) != NULL) + { + if (mo->master == self->master && mo != self && (mo->health <= 0 || removeall)) + { + P_RemoveThing(mo); + } + } + } +} + +//=========================================================================== +// +// A_RaiseMaster +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RaiseMaster) +{ + if (self->master != NULL) + { + P_Thing_Raise(self->master); + } +} + +//=========================================================================== +// +// A_RaiseChildren +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RaiseChildren) +{ + TThinkerIterator it; + AActor *mo; + + while ((mo = it.Next()) != NULL) + { + if (mo->master == self) + { + P_Thing_Raise(mo); + } + } +} + +//=========================================================================== +// +// A_RaiseSiblings +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings) +{ + TThinkerIterator it; + AActor *mo; + + if (self->master != NULL) + { + while ((mo = it.Next()) != NULL) + { + if (mo->master == self->master && mo != self) + { + P_Thing_Raise(mo); + } + } + } +} + +//=========================================================================== +// +// A_MonsterRefire +// +// Keep firing unless target got out of sight +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_MonsterRefire) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(prob, 0); + ACTION_PARAM_STATE(jump, 1); + + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + A_FaceTarget (self); + + if (pr_monsterrefire() < prob) + return; + + if (!self->target + || P_HitFriend (self) + || self->target->health <= 0 + || !P_CheckSight (self, self->target, SF_SEEPASTBLOCKEVERYTHING|SF_SEEPASTSHOOTABLELINES) ) + { + ACTION_JUMP(jump); + } +} + +//=========================================================================== +// +// A_SetAngle +// +// Set actor's angle (in degrees). +// +//=========================================================================== +enum +{ + SPF_FORCECLAMP = 1, // players always clamp + SPF_INTERPOLATE = 2, +}; + + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetAngle) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_ANGLE(angle, 0); + ACTION_PARAM_INT(flags, 1) + self->SetAngle(angle, !!(flags & SPF_INTERPOLATE)); +} + +//=========================================================================== +// +// A_SetPitch +// +// Set actor's pitch (in degrees). +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPitch) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_ANGLE(pitch, 0); + ACTION_PARAM_INT(flags, 1); + + if (self->player != NULL || (flags & SPF_FORCECLAMP)) + { // clamp the pitch we set + int min, max; + + if (self->player != NULL) + { + min = self->player->MinPitch; + max = self->player->MaxPitch; + } + else + { + min = -ANGLE_90 + (1 << ANGLETOFINESHIFT); + max = ANGLE_90 - (1 << ANGLETOFINESHIFT); + } + pitch = clamp(pitch, min, max); + } + self->SetPitch(pitch, !!(flags & SPF_INTERPOLATE)); +} + +//=========================================================================== +// +// A_ScaleVelocity +// +// Scale actor's velocity. +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ScaleVelocity) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_FIXED(scale, 0); + + INTBOOL was_moving = self->velx | self->vely | self->velz; + + self->velx = FixedMul(self->velx, scale); + self->vely = FixedMul(self->vely, scale); + self->velz = FixedMul(self->velz, scale); + + // If the actor was previously moving but now is not, and is a player, + // update its player variables. (See A_Stop.) + if (was_moving) + { + CheckStopped(self); + } +} + +//=========================================================================== +// +// A_ChangeVelocity +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeVelocity) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_FIXED(x, 0); + ACTION_PARAM_FIXED(y, 1); + ACTION_PARAM_FIXED(z, 2); + ACTION_PARAM_INT(flags, 3); + + INTBOOL was_moving = self->velx | self->vely | self->velz; + + fixed_t vx = x, vy = y, vz = z; + fixed_t sina = finesine[self->angle >> ANGLETOFINESHIFT]; + fixed_t cosa = finecosine[self->angle >> ANGLETOFINESHIFT]; + + if (flags & 1) // relative axes - make x, y relative to actor's current angle + { + vx = DMulScale16(x, cosa, -y, sina); + vy = DMulScale16(x, sina, y, cosa); + } + if (flags & 2) // discard old velocity - replace old velocity with new velocity + { + self->velx = vx; + self->vely = vy; + self->velz = vz; + } + else // add new velocity to old velocity + { + self->velx += vx; + self->vely += vy; + self->velz += vz; + } + + if (was_moving) + { + CheckStopped(self); + } +} + +//=========================================================================== +// +// A_SetArg +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetArg) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(pos, 0); + ACTION_PARAM_INT(value, 1); + + // Set the value of the specified arg + if ((size_t)pos < countof(self->args)) + { + self->args[pos] = value; + } +} + +//=========================================================================== +// +// A_SetSpecial +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpecial) +{ + ACTION_PARAM_START(6); + ACTION_PARAM_INT(spec, 0); + ACTION_PARAM_INT(arg0, 1); + ACTION_PARAM_INT(arg1, 2); + ACTION_PARAM_INT(arg2, 3); + ACTION_PARAM_INT(arg3, 4); + ACTION_PARAM_INT(arg4, 5); + + self->special = spec; + self->args[0] = arg0; + self->args[1] = arg1; + self->args[2] = arg2; + self->args[3] = arg3; + self->args[4] = arg4; +} + +//=========================================================================== +// +// A_SetUserVar +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(varname, 0); + ACTION_PARAM_INT(value, 1); + + PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true); + PSymbolVariable *var; + + if (sym == NULL || sym->SymbolType != SYM_Variable || + !(var = static_cast(sym))->bUserVar || + var->ValueType.Type != VAL_Int) + { + Printf("%s is not a user variable in class %s\n", varname.GetChars(), + self->GetClass()->TypeName.GetChars()); + return; + } + // Set the value of the specified user variable. + *(int *)(reinterpret_cast(self) + var->offset) = value; +} + +//=========================================================================== +// +// A_SetUserArray +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_NAME(varname, 0); + ACTION_PARAM_INT(pos, 1); + ACTION_PARAM_INT(value, 2); + + PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true); + PSymbolVariable *var; + + if (sym == NULL || sym->SymbolType != SYM_Variable || + !(var = static_cast(sym))->bUserVar || + var->ValueType.Type != VAL_Array || var->ValueType.BaseType != VAL_Int) + { + Printf("%s is not a user array in class %s\n", varname.GetChars(), + self->GetClass()->TypeName.GetChars()); + return; + } + if (pos < 0 || pos >= var->ValueType.size) + { + Printf("%d is out of bounds in array %s in class %s\n", pos, varname.GetChars(), + self->GetClass()->TypeName.GetChars()); + return; + } + // Set the value of the specified user array at index pos. + ((int *)(reinterpret_cast(self) + var->offset))[pos] = value; +} + +//=========================================================================== +// +// A_Teleport(optional state teleportstate, optional class targettype, +// optional class fogtype, optional int flags, optional fixed mindist, +// optional fixed maxdist) +// +// Attempts to teleport to a targettype at least mindist away and at most +// maxdist away (0 means unlimited). If successful, spawn a fogtype at old +// location and place calling actor in teleportstate. +// +//=========================================================================== +enum T_Flags +{ + TF_TELEFRAG = 1, // Allow telefrag in order to teleport. + TF_RANDOMDECIDE = 2, // Randomly fail based on health. (A_Srcr2Decide) +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport) +{ + ACTION_PARAM_START(6); + ACTION_PARAM_STATE(TeleportState, 0); + ACTION_PARAM_CLASS(TargetType, 1); + ACTION_PARAM_CLASS(FogType, 2); + ACTION_PARAM_INT(Flags, 3); + ACTION_PARAM_FIXED(MinDist, 4); + ACTION_PARAM_FIXED(MaxDist, 5); + + // Randomly choose not to teleport like A_Srcr2Decide. + if (Flags & TF_RANDOMDECIDE) + { + static const int chance[] = + { + 192, 120, 120, 120, 64, 64, 32, 16, 0 + }; + + unsigned int chanceindex = self->health / ((self->SpawnHealth()/8 == 0) ? 1 : self->SpawnHealth()/8); + + if (chanceindex >= countof(chance)) + { + chanceindex = countof(chance) - 1; + } + + if (pr_teleport() >= chance[chanceindex]) return; + } + + if (TeleportState == NULL) + { + // Default to Teleport. + TeleportState = self->FindState("Teleport"); + // If still nothing, then return. + if (!TeleportState) return; + } + + DSpotState *state = DSpotState::GetSpotState(); + if (state == NULL) return; + + if (!TargetType) TargetType = PClass::FindClass("BossSpot"); + + AActor * spot = state->GetSpotWithMinMaxDistance(TargetType, self->x, self->y, MinDist, MaxDist); + if (spot == NULL) return; + + fixed_t prevX = self->x; + fixed_t prevY = self->y; + fixed_t prevZ = self->z; + if (P_TeleportMove (self, spot->x, spot->y, spot->z, Flags & TF_TELEFRAG)) + { + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + + if (FogType) + { + Spawn(FogType, prevX, prevY, prevZ, ALLOW_REPLACE); + } + + ACTION_JUMP(TeleportState); + + self->z = self->floorz; + self->angle = spot->angle; + self->velx = self->vely = self->velz = 0; + } +} + +//=========================================================================== +// +// A_Turn +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Turn) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_ANGLE(angle, 0); + self->angle += angle; +} + +//=========================================================================== +// +// A_Quake +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Quake) +{ + ACTION_PARAM_START(5); + ACTION_PARAM_INT(intensity, 0); + ACTION_PARAM_INT(duration, 1); + ACTION_PARAM_INT(damrad, 2); + ACTION_PARAM_INT(tremrad, 3); + ACTION_PARAM_SOUND(sound, 4); + P_StartQuake(self, 0, intensity, duration, damrad, tremrad, sound); +} + +//=========================================================================== +// +// A_Weave +// +//=========================================================================== + +void A_Weave(AActor *self, int xyspeed, int zspeed, fixed_t xydist, fixed_t zdist) +{ + fixed_t newX, newY; + int weaveXY, weaveZ; + int angle; + fixed_t dist; + + weaveXY = self->WeaveIndexXY & 63; + weaveZ = self->WeaveIndexZ & 63; + angle = (self->angle + ANG90) >> ANGLETOFINESHIFT; + + if (xydist != 0 && xyspeed != 0) + { + dist = MulScale13(finesine[weaveXY << BOBTOFINESHIFT], xydist); + newX = self->x - FixedMul (finecosine[angle], dist); + newY = self->y - FixedMul (finesine[angle], dist); + weaveXY = (weaveXY + xyspeed) & 63; + dist = MulScale13(finesine[weaveXY << BOBTOFINESHIFT], xydist); + newX += FixedMul (finecosine[angle], dist); + newY += FixedMul (finesine[angle], dist); + if (!(self->flags5 & MF5_NOINTERACTION)) + { + P_TryMove (self, newX, newY, true); + } + else + { + self->UnlinkFromWorld (); + self->flags |= MF_NOBLOCKMAP; + self->x = newX; + self->y = newY; + self->LinkToWorld (); + } + self->WeaveIndexXY = weaveXY; + } + if (zdist != 0 && zspeed != 0) + { + self->z -= MulScale13(finesine[weaveZ << BOBTOFINESHIFT], zdist); + weaveZ = (weaveZ + zspeed) & 63; + self->z += MulScale13(finesine[weaveZ << BOBTOFINESHIFT], zdist); + self->WeaveIndexZ = weaveZ; + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Weave) +{ + ACTION_PARAM_START(4); + ACTION_PARAM_INT(xspeed, 0); + ACTION_PARAM_INT(yspeed, 1); + ACTION_PARAM_FIXED(xdist, 2); + ACTION_PARAM_FIXED(ydist, 3); + A_Weave(self, xspeed, yspeed, xdist, ydist); +} + + + + +//=========================================================================== +// +// A_LineEffect +// +// This allows linedef effects to be activated inside deh frames. +// +//=========================================================================== + + +void P_TranslateLineDef (line_t *ld, maplinedef_t *mld); +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LineEffect) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(special, 0); + ACTION_PARAM_INT(tag, 1); + + line_t junk; maplinedef_t oldjunk; + bool res = false; + if (!(self->flags6 & MF6_LINEDONE)) // Unless already used up + { + if ((oldjunk.special = special)) // Linedef type + { + oldjunk.tag = tag; // Sector tag for linedef + P_TranslateLineDef(&junk, &oldjunk); // Turn into native type + res = !!P_ExecuteSpecial(junk.special, NULL, self, false, junk.args[0], + junk.args[1], junk.args[2], junk.args[3], junk.args[4]); + if (res && !(junk.flags & ML_REPEAT_SPECIAL)) // If only once, + self->flags6 |= MF6_LINEDONE; // no more for this thing + } + } + ACTION_SET_RESULT(res); +} + +//========================================================================== +// +// A Wolf3D-style attack codepointer +// +//========================================================================== +enum WolfAttackFlags +{ + WAF_NORANDOM = 1, + WAF_USEPUFF = 2, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_WolfAttack) +{ + ACTION_PARAM_START(9); + ACTION_PARAM_INT(flags, 0); + ACTION_PARAM_SOUND(sound, 1); + ACTION_PARAM_FIXED(snipe, 2); + ACTION_PARAM_INT(maxdamage, 3); + ACTION_PARAM_INT(blocksize, 4); + ACTION_PARAM_INT(pointblank, 5); + ACTION_PARAM_INT(longrange, 6); + ACTION_PARAM_FIXED(runspeed, 7); + ACTION_PARAM_CLASS(pufftype, 8); + + if (!self->target) + return; + + // Enemy can't see target + if (!P_CheckSight(self, self->target)) + return; + + A_FaceTarget (self); + + + // Target can dodge if it can see enemy + angle_t angle = R_PointToAngle2(self->target->x, self->target->y, self->x, self->y) - self->target->angle; + angle >>= 24; + bool dodge = (P_CheckSight(self->target, self) && (angle>226 || angle<30)); + + // Distance check is simplistic + fixed_t dx = abs (self->x - self->target->x); + fixed_t dy = abs (self->y - self->target->y); + fixed_t dz; + fixed_t dist = dx > dy ? dx : dy; + + // Some enemies are more precise + dist = FixedMul(dist, snipe); + + // Convert distance into integer number of blocks + dist >>= FRACBITS; + dist /= blocksize; + + // Now for the speed accuracy thingie + fixed_t speed = FixedMul(self->target->velx, self->target->velx) + + FixedMul(self->target->vely, self->target->vely) + + FixedMul(self->target->velz, self->target->velz); + int hitchance = speed < runspeed ? 256 : 160; + + // Distance accuracy (factoring dodge) + hitchance -= dist * (dodge ? 16 : 8); + + // While we're here, we may as well do something for this: + if (self->target->flags & MF_SHADOW) + { + hitchance >>= 2; + } + + // The attack itself + if (pr_cabullet() < hitchance) + { + // Compute position for spawning blood/puff + dx = self->target->x; + dy = self->target->y; + dz = self->target->z + (self->target->height>>1); + angle = R_PointToAngle2(dx, dy, self->x, self->y); + + dx += FixedMul(self->target->radius, finecosine[angle>>ANGLETOFINESHIFT]); + dy += FixedMul(self->target->radius, finesine[angle>>ANGLETOFINESHIFT]); + + int damage = flags & WAF_NORANDOM ? maxdamage : (1 + (pr_cabullet() % maxdamage)); + if (dist >= pointblank) + damage >>= 1; + if (dist >= longrange) + damage >>= 1; + FName mod = NAME_None; + bool spawnblood = !((self->target->flags & MF_NOBLOOD) + || (self->target->flags2 & (MF2_INVULNERABLE|MF2_DORMANT))); + if (flags & WAF_USEPUFF && pufftype) + { + AActor * dpuff = GetDefaultByType(pufftype->GetReplacement()); + mod = dpuff->DamageType; + + if (dpuff->flags2 & MF2_THRUGHOST && self->target->flags3 & MF3_GHOST) + damage = 0; + + if ((0 && dpuff->flags3 & MF3_PUFFONACTORS) || !spawnblood) + { + spawnblood = false; + P_SpawnPuff(self, pufftype, dx, dy, dz, angle, 0); + } + } + else if (self->target->flags3 & MF3_GHOST) + damage >>= 2; + if (damage) + { + int newdam = P_DamageMobj(self->target, self, self, damage, mod, DMG_THRUSTLESS); + if (spawnblood) + { + P_SpawnBlood(dx, dy, dz, angle, newdam > 0 ? newdam : damage, self->target); + P_TraceBleed(newdam > 0 ? newdam : damage, self->target, R_PointToAngle2(self->x, self->y, dx, dy), 0); + } + } + } + + // And finally, let's play the sound + S_Sound (self, CHAN_WEAPON, sound, 1, ATTN_NORM); +} + + +//========================================================================== +// +// A_Warp +// +//========================================================================== + +enum WARPF +{ + WARPF_ABSOLUTEOFFSET = 0x1, + WARPF_ABSOLUTEANGLE = 0x2, + WARPF_USECALLERANGLE = 0x4, + + WARPF_NOCHECKPOSITION = 0x8, + + WARPF_INTERPOLATE = 0x10, + WARPF_WARPINTERPOLATION = 0x20, + WARPF_COPYINTERPOLATION = 0x40, + + WARPF_STOP = 0x80, + WARPF_TOFLOOR = 0x100, + WARPF_TESTONLY = 0x200, + WARPF_ABSOLUTEPOSITION = 0x400, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) +{ + ACTION_PARAM_START(7); + + ACTION_PARAM_INT(destination_selector, 0); + ACTION_PARAM_FIXED(xofs, 1); + ACTION_PARAM_FIXED(yofs, 2); + ACTION_PARAM_FIXED(zofs, 3); + ACTION_PARAM_ANGLE(angle, 4); + ACTION_PARAM_INT(flags, 5); + ACTION_PARAM_STATE(success_state, 6); + + fixed_t + + oldx, + oldy, + oldz; + + AActor *reference = COPY_AAPTR(self, destination_selector); + + if (!reference) + { + ACTION_SET_RESULT(false); + return; + } + + if (!(flags & WARPF_ABSOLUTEANGLE)) + { + angle += (flags & WARPF_USECALLERANGLE) ? self->angle : reference->angle; + } + if (!(flags & WARPF_ABSOLUTEPOSITION)) + { + if (!(flags & WARPF_ABSOLUTEOFFSET)) + { + angle_t fineangle = angle >> ANGLETOFINESHIFT; + oldx = xofs; + + // (borrowed from A_SpawnItemEx, assumed workable) + // in relative mode negative y values mean 'left' and positive ones mean 'right' + // This is the inverse orientation of the absolute mode! + + xofs = FixedMul(oldx, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); + yofs = FixedMul(oldx, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); + } + + oldx = self->x; + oldy = self->y; + oldz = self->z; + + if (flags & WARPF_TOFLOOR) + { + // set correct xy + + self->SetOrigin( + reference->x + xofs, + reference->y + yofs, + reference->z); + + // now the caller's floorz should be appropriate for the assigned xy-position + // assigning position again with + + if (zofs) + { + // extra unlink, link and environment calculation + self->SetOrigin( + self->x, + self->y, + self->floorz + zofs); + } + else + { + // if there is no offset, there should be no ill effect from moving down to the + // already identified floor + + // A_Teleport does the same thing anyway + self->z = self->floorz; + } + } + else + { + self->SetOrigin( + reference->x + xofs, + reference->y + yofs, + reference->z + zofs); + } + } + else //[MC] The idea behind "absolute" is meant to be "absolute". Override everything, just like A_SpawnItemEx's. + { + if (flags & WARPF_TOFLOOR) + { + self->SetOrigin(xofs, yofs, self->floorz + zofs); + } + else + { + self->SetOrigin(xofs, yofs, zofs); + } + } + + if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(self)) + { + if (flags & WARPF_TESTONLY) + { + self->SetOrigin(oldx, oldy, oldz); + } + else + { + self->angle = angle; + + if (flags & WARPF_STOP) + { + self->velx = 0; + self->vely = 0; + self->velz = 0; + } + + if (flags & WARPF_WARPINTERPOLATION) + { + self->PrevX += self->x - oldx; + self->PrevY += self->y - oldy; + self->PrevZ += self->z - oldz; + } + else if (flags & WARPF_COPYINTERPOLATION) + { + self->PrevX = self->x + reference->PrevX - reference->x; + self->PrevY = self->y + reference->PrevY - reference->y; + self->PrevZ = self->z + reference->PrevZ - reference->z; + } + else if (! (flags & WARPF_INTERPOLATE)) + { + self->PrevX = self->x; + self->PrevY = self->y; + self->PrevZ = self->z; + } + } + + if (success_state) + { + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + // in this case, you have the statejump to help you handle all the success anyway. + ACTION_JUMP(success_state); + return; + } + + ACTION_SET_RESULT(true); + } + else + { + self->SetOrigin(oldx, oldy, oldz); + ACTION_SET_RESULT(false); + } + +} + +//========================================================================== +// +// ACS_Named* stuff + +// +// These are exactly like their un-named line special equivalents, except +// they take strings instead of integers to indicate which script to run. +// Some of these probably aren't very useful, but they are included for +// the sake of completeness. +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecuteWithResult) +{ + ACTION_PARAM_START(5); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(arg1, 1); + ACTION_PARAM_INT(arg2, 2); + ACTION_PARAM_INT(arg3, 3); + ACTION_PARAM_INT(arg4, 4); + + bool res = !!P_ExecuteSpecial(ACS_ExecuteWithResult, NULL, self, false, -scriptname, arg1, arg2, arg3, arg4); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecute) +{ + ACTION_PARAM_START(5); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + ACTION_PARAM_INT(arg1, 2); + ACTION_PARAM_INT(arg2, 3); + ACTION_PARAM_INT(arg3, 4); + + bool res = !!P_ExecuteSpecial(ACS_Execute, NULL, self, false, -scriptname, mapnum, arg1, arg2, arg3); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedExecuteAlways) +{ + ACTION_PARAM_START(5); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + ACTION_PARAM_INT(arg1, 2); + ACTION_PARAM_INT(arg2, 3); + ACTION_PARAM_INT(arg3, 4); + + bool res = !!P_ExecuteSpecial(ACS_ExecuteAlways, NULL, self, false, -scriptname, mapnum, arg1, arg2, arg3); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedLockedExecute) +{ + ACTION_PARAM_START(5); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + ACTION_PARAM_INT(arg1, 2); + ACTION_PARAM_INT(arg2, 3); + ACTION_PARAM_INT(lock, 4); + + bool res = !!P_ExecuteSpecial(ACS_LockedExecute, NULL, self, false, -scriptname, mapnum, arg1, arg2, lock); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedLockedExecuteDoor) +{ + ACTION_PARAM_START(5); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + ACTION_PARAM_INT(arg1, 2); + ACTION_PARAM_INT(arg2, 3); + ACTION_PARAM_INT(lock, 4); + + bool res = !!P_ExecuteSpecial(ACS_LockedExecuteDoor, NULL, self, false, -scriptname, mapnum, arg1, arg2, lock); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedSuspend) +{ + ACTION_PARAM_START(2); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + + bool res = !!P_ExecuteSpecial(ACS_Suspend, NULL, self, false, -scriptname, mapnum, 0, 0, 0); + + ACTION_SET_RESULT(res); +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedTerminate) +{ + ACTION_PARAM_START(2); + + ACTION_PARAM_NAME(scriptname, 0); + ACTION_PARAM_INT(mapnum, 1); + + bool res = !!P_ExecuteSpecial(ACS_Terminate, NULL, self, false, -scriptname, mapnum, 0, 0, 0); + + ACTION_SET_RESULT(res); +} + + +//========================================================================== +// +// A_RadiusGive +// +// Uses code roughly similar to A_Explode (but without all the compatibility +// baggage and damage computation code to give an item to all eligible mobjs +// in range. +// +//========================================================================== +enum RadiusGiveFlags +{ + RGF_GIVESELF = 1, + RGF_PLAYERS = 2, + RGF_MONSTERS = 4, + RGF_OBJECTS = 8, + RGF_VOODOO = 16, + RGF_CORPSES = 32, + RGF_MASK = 63, + RGF_NOTARGET = 64, + RGF_NOTRACER = 128, + RGF_NOMASTER = 256, + RGF_CUBE = 512, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) +{ + ACTION_PARAM_START(7); + ACTION_PARAM_CLASS(item, 0); + ACTION_PARAM_FIXED(distance, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_INT(amount, 3); + + // We need a valid item, valid targets, and a valid range + if (item == NULL || (flags & RGF_MASK) == 0 || distance <= 0) + { + return; + } + if (amount == 0) + { + amount = 1; + } + FBlockThingsIterator it(FBoundingBox(self->x, self->y, distance)); + double distsquared = double(distance) * double(distance); + + AActor *thing; + while ((thing = it.Next())) + { + // Don't give to inventory items + if (thing->flags & MF_SPECIAL) + { + continue; + } + // Avoid giving to self unless requested + if (thing == self && !(flags & RGF_GIVESELF)) + { + continue; + } + // Avoiding special pointers if requested + if (((thing == self->target) && (flags & RGF_NOTARGET)) || + ((thing == self->tracer) && (flags & RGF_NOTRACER)) || + ((thing == self->master) && (flags & RGF_NOMASTER))) + { + continue; + } + // Don't give to dead thing unless requested + if (thing->flags & MF_CORPSE) + { + if (!(flags & RGF_CORPSES)) + { + continue; + } + } + else if (thing->health <= 0 || thing->flags6 & MF6_KILLED) + { + continue; + } + // Players, monsters, and other shootable objects + if (thing->player) + { + if ((thing->player->mo == thing) && !(flags & RGF_PLAYERS)) + { + continue; + } + if ((thing->player->mo != thing) && !(flags & RGF_VOODOO)) + { + continue; + } + } + else if (thing->flags3 & MF3_ISMONSTER) + { + if (!(flags & RGF_MONSTERS)) + { + continue; + } + } + else if (thing->flags & MF_SHOOTABLE || thing->flags6 & MF6_VULNERABLE) + { + if (!(flags & RGF_OBJECTS)) + { + continue; + } + } + else + { + continue; + } + + if (flags & RGF_CUBE) + { // check if inside a cube + if (abs(thing->x - self->x) > distance || + abs(thing->y - self->y) > distance || + abs((thing->z + thing->height/2) - (self->z + self->height/2)) > distance) + { + continue; + } + } + else + { // check if inside a sphere + TVector3 tpos(thing->x, thing->y, thing->z + thing->height/2); + TVector3 spos(self->x, self->y, self->z + self->height/2); + if ((tpos - spos).LengthSquared() > distsquared) + { + continue; + } + } + fixed_t dz = abs ((thing->z + thing->height/2) - (self->z + self->height/2)); + + if (P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY)) + { // OK to give; target is in direct path + AInventory *gift = static_cast(Spawn (item, 0, 0, 0, NO_REPLACE)); + if (gift->IsKindOf(RUNTIME_CLASS(AHealth))) + { + gift->Amount *= amount; + } + else + { + gift->Amount = amount; + } + gift->flags |= MF_DROPPED; + gift->ClearCounters(); + if (!gift->CallTryPickup (thing)) + { + gift->Destroy (); + } + } + } +} + + +//========================================================================== +// +// A_SetTics +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTics) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(tics_to_set, 0); + + if (stateowner != self && self->player != NULL && stateowner->IsKindOf(RUNTIME_CLASS(AWeapon))) + { // Is this a weapon? Need to check psp states for a match, then. Blah. + for (int i = 0; i < NUMPSPRITES; ++i) + { + if (self->player->psprites[i].state == CallingState) + { + self->player->psprites[i].tics = tics_to_set; + return; + } + } + } + // Just set tics for self. + self->tics = tics_to_set; +} + +//========================================================================== +// +// A_SetDamageType +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetDamageType) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_NAME(damagetype, 0); + + self->DamageType = damagetype; +} + +//========================================================================== +// +// A_DropItem +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropItem) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_CLASS(spawntype, 0); + ACTION_PARAM_INT(amount, 1); + ACTION_PARAM_INT(chance, 2); + + P_DropItem(self, spawntype, amount, chance); +} + +//========================================================================== +// +// A_SetSpeed +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_FIXED(speed, 0); + + self->Speed = speed; +} + +//=========================================================================== +// +// Common A_Damage handler +// +// A_Damage* (int amount, str damagetype, int flags) +// Damages the specified actor by the specified amount. Negative values heal. +// +//=========================================================================== + +enum DMSS +{ + DMSS_FOILINVUL = 1, + DMSS_AFFECTARMOR = 2, + DMSS_KILL = 4, +}; + +static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags) +{ + if ((amount > 0) || (flags & DMSS_KILL)) + { + if (!(dmgtarget->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) + { + if (flags & DMSS_KILL) + { + P_DamageMobj(dmgtarget, self, self, dmgtarget->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); + } + if (flags & DMSS_AFFECTARMOR) + { + P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL); + } + else + { + //[MC] DMG_FOILINVUL is needed for making the damage occur on the actor. + P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } + } + } + else if (amount < 0) + { + amount = -amount; + P_GiveBody(dmgtarget, amount); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSelf) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + DoDamage(self, self, amount, DamageType, flags); +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTarget) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + if (self->target != NULL) DoDamage(self->target, self, amount, DamageType, flags); +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageTracer) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + if (self->tracer != NULL) DoDamage(self->tracer, self, amount, DamageType, flags); +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageMaster) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + if (self->master != NULL) DoDamage(self->master, self, amount, DamageType, flags); +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageChildren) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + TThinkerIterator it; + AActor * mo; + + while ( (mo = it.Next()) ) + { + if (mo->master == self) DoDamage(mo, self, amount, DamageType, flags); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DamageSiblings) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_INT(amount, 0); + ACTION_PARAM_NAME(DamageType, 1); + ACTION_PARAM_INT(flags, 2); + + TThinkerIterator it; + AActor * mo; + + if (self->master != NULL) + { + while ((mo = it.Next())) + { + if (mo->master == self->master && mo != self) DoDamage(mo, self, amount, DamageType, flags); + } + } +} + + +//=========================================================================== +// +// A_Kill*(damagetype, int flags) +// +//=========================================================================== +enum KILS +{ + KILS_FOILINVUL = 1 << 0, + KILS_KILLMISSILES = 1 << 1, + KILS_NOMONSTERS = 1 << 2, +}; + +static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags) +{ + if ((killtarget->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) + { + //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! + //Check to see if it's invulnerable. Disregarded if foilinvul is on, but never works on a missile with NODAMAGE + //since that's the whole point of it. + if ((!(killtarget->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(killtarget->flags5 & MF5_NODAMAGE)) + { + P_ExplodeMissile(self->target, NULL, NULL); + } + } + if (!(flags & KILS_NOMONSTERS)) + { + if (flags & KILS_FOILINVUL) + { + P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); + } + else + { + P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); + } + } +} + + + +//=========================================================================== +// +// A_KillTarget(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTarget) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + if (self->target != NULL) DoKill(self->target, self, damagetype, flags); +} + +//=========================================================================== +// +// A_KillTracer(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillTracer) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + if (self->tracer != NULL) DoKill(self->tracer, self, damagetype, flags); +} + +//=========================================================================== +// +// A_KillMaster(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + if (self->master != NULL) DoKill(self->master, self, damagetype, flags); +} + +//=========================================================================== +// +// A_KillChildren(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + TThinkerIterator it; + AActor *mo; + + while ( (mo = it.Next()) ) + { + if (mo->master == self) DoKill(mo, self, damagetype, flags); + } +} + +//=========================================================================== +// +// A_KillSiblings(damagetype, int flags) +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(damagetype, 0); + ACTION_PARAM_INT(flags, 1); + + TThinkerIterator it; + AActor *mo; + + if (self->master != NULL) + { + while ( (mo = it.Next()) ) + { + if (mo->master == self->master && mo != self) DoKill(mo, self, damagetype, flags); + } + } +} + + +//=========================================================================== +// +// A_RemoveTarget +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) +{ + if (self->target != NULL) + { + P_RemoveThing(self->target); + } +} + +//=========================================================================== +// +// A_RemoveTracer +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RemoveTracer) +{ + if (self->tracer != NULL) + { + P_RemoveThing(self->tracer); + } } \ No newline at end of file From c962c56079480acff87afc078d23a4019b304492 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 28 Sep 2014 01:04:18 +0200 Subject: [PATCH 35/47] - removed accidentally committed .gitattributes file. --- .gitattributes | 63 -------------------------------------------------- 1 file changed, 63 deletions(-) delete mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 1ff0c42304..0000000000 --- a/.gitattributes +++ /dev/null @@ -1,63 +0,0 @@ -############################################################################### -# Set default behavior to automatically normalize line endings. -############################################################################### -* text=auto - -############################################################################### -# Set default behavior for command prompt diff. -# -# This is need for earlier builds of msysgit that does not have it on by -# default for csharp files. -# Note: This is only used by command line -############################################################################### -#*.cs diff=csharp - -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -#*.sln merge=binary -#*.csproj merge=binary -#*.vbproj merge=binary -#*.vcxproj merge=binary -#*.vcproj merge=binary -#*.dbproj merge=binary -#*.fsproj merge=binary -#*.lsproj merge=binary -#*.wixproj merge=binary -#*.modelproj merge=binary -#*.sqlproj merge=binary -#*.wwaproj merge=binary - -############################################################################### -# behavior for image files -# -# image files are treated as binary by default. -############################################################################### -#*.jpg binary -#*.png binary -#*.gif binary - -############################################################################### -# diff behavior for common document formats -# -# Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the -# entries below. -############################################################################### -#*.doc diff=astextplain -#*.DOC diff=astextplain -#*.docx diff=astextplain -#*.DOCX diff=astextplain -#*.dot diff=astextplain -#*.DOT diff=astextplain -#*.pdf diff=astextplain -#*.PDF diff=astextplain -#*.rtf diff=astextplain -#*.RTF diff=astextplain From a2f7b86a0f460c88821ddfb2463d2c9de252cbd2 Mon Sep 17 00:00:00 2001 From: fdari Date: Sun, 28 Sep 2014 11:52:37 +0200 Subject: [PATCH 36/47] IsPointerEqual (ACS and Decorate) Decorate: IsPointerEqual(int aaptr_selector1, int aaptr_selector2) ACS: IsPointerEqual(int aaptr_selector1, int aaptr_selector2, int tid1 = 0, int tid2 = 0) Compare the pointers values returned by two pointer select operations. Returns true if they both resolve to the same value. Null values can be explicitly tested using IsPointerEqual(AAPTR_NULL, ...) ACS: IsPointerEqual(int aaptr1, int aaptr2, int tid1 = 0, int tid2 = 0) This function lets you compare pointers from other actors than the activator, using tids. Tid1 determines the actor used to resolve aaptr1, Tid2 does the same for aaptr2. If tid1 and tid2 are equal, the same actor will be used for resolving both pointers (that could always happen randomly; this way you know it will happen). --- src/namedef.h | 1 + src/p_acs.cpp | 17 +++++++++++++++ src/thingdef/thingdef_function.cpp | 35 +++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/namedef.h b/src/namedef.h index 1e7fbd0d3a..5c65ab6559 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -299,6 +299,7 @@ xx(ACS_NamedExecuteWithResult) xx(CallACS) xx(Sqrt) xx(CheckClass) +xx(IsPointerEqual) // Various actor names which are used internally xx(MapSpot) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 983728c8dc..9fa5079ff2 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4373,6 +4373,7 @@ enum EACSFunctions ACSF_GetArmorInfo, ACSF_DropInventory, ACSF_PickActor, + ACSF_IsPointerEqual, /* Zandronum's - these must be skipped when we reach 99! -100:ResetMap(0), @@ -5630,6 +5631,22 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) } break; + case ACSF_IsPointerEqual: + { + int tid1 = 0, tid2 = 0; + switch (argCount) + { + case 4: tid2 = args[3]; + case 3: tid1 = args[2]; + } + + actor = SingleActorFromTID(tid1, activator); + AActor * actor2 = tid2 == tid1 ? actor : SingleActorFromTID(tid2, activator); + + return COPY_AAPTR(actor, args[0]) == COPY_AAPTR(actor2, args[1]); + } + break; + default: break; } diff --git a/src/thingdef/thingdef_function.cpp b/src/thingdef/thingdef_function.cpp index 9bf80dcaaf..fcc9360170 100644 --- a/src/thingdef/thingdef_function.cpp +++ b/src/thingdef/thingdef_function.cpp @@ -260,4 +260,37 @@ class FxGlobalFunctionCall_CheckClass : public FxGlobalFunctionCall } }; -GLOBALFUNCTION_ADDER(CheckClass); \ No newline at end of file +GLOBALFUNCTION_ADDER(CheckClass); + +//========================================================================== +// +// Function: ispointerequal +// +//========================================================================== + +class FxGlobalFunctionCall_IsPointerEqual : public FxGlobalFunctionCall +{ + public: + GLOBALFUNCTION_DEFINE(IsPointerEqual); + + FxExpression *Resolve(FCompileContext& ctx) + { + CHECKRESOLVED(); + + if (!ResolveArgs(ctx, 2, 2, true)) + return NULL; + + ValueType = VAL_Int; + return this; + } + + ExpVal EvalExpression(AActor *self) + { + ExpVal ret; + ret.Type = VAL_Int; + ret.Int = COPY_AAPTR(self, (*ArgList)[0]->EvalExpression(self).GetInt()) == COPY_AAPTR(self, (*ArgList)[1]->EvalExpression(self).GetInt()); + return ret; + } +}; + +GLOBALFUNCTION_ADDER(IsPointerEqual); \ No newline at end of file From c4f0f95ec8bc0320f0d50be2b55bdc55bc016843 Mon Sep 17 00:00:00 2001 From: fdari Date: Sun, 28 Sep 2014 15:06:52 +0200 Subject: [PATCH 37/47] Get linetarget (aim target) from any actor (not just player): AAPTR_LINETARGET --- src/actorptrselect.cpp | 7 +++++++ src/actorptrselect.h | 3 ++- wadsrc/static/actors/constants.txt | 31 +++++++++++++++--------------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/actorptrselect.cpp b/src/actorptrselect.cpp index f1f50eeb5e..774067550d 100644 --- a/src/actorptrselect.cpp +++ b/src/actorptrselect.cpp @@ -56,6 +56,13 @@ AActor *COPY_AAPTR(AActor *origin, int selector) case AAPTR_TRACER: return origin->tracer; case AAPTR_FRIENDPLAYER: return origin->FriendPlayer ? AAPTR_RESOLVE_PLAYERNUM(origin->FriendPlayer - 1) : NULL; + + case AAPTR_GET_LINETARGET: + { + AActor *gettarget = NULL; + P_BulletSlope(origin, &gettarget); + return gettarget; + } } } diff --git a/src/actorptrselect.h b/src/actorptrselect.h index 46e1aa54d6..bfc88fb3c0 100644 --- a/src/actorptrselect.h +++ b/src/actorptrselect.h @@ -36,12 +36,13 @@ enum AAPTR AAPTR_PLAYER8 = 0x2000, AAPTR_FRIENDPLAYER = 0x4000, + AAPTR_GET_LINETARGET = 0x8000, AAPTR_PLAYER_SELECTORS = AAPTR_PLAYER_GETTARGET|AAPTR_PLAYER_GETCONVERSATION, AAPTR_GENERAL_SELECTORS = - AAPTR_TARGET|AAPTR_MASTER|AAPTR_TRACER|AAPTR_FRIENDPLAYER, + AAPTR_TARGET|AAPTR_MASTER|AAPTR_TRACER|AAPTR_FRIENDPLAYER|AAPTR_GET_LINETARGET, AAPTR_STATIC_SELECTORS = AAPTR_PLAYER1|AAPTR_PLAYER2|AAPTR_PLAYER3|AAPTR_PLAYER4| diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 3419561f8b..428f92d148 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -279,24 +279,25 @@ Const Int BLOCKF_USE = 128; // Pointer constants, bitfield-enabled Const Int AAPTR_DEFAULT = 0; -Const Int AAPTR_NULL = 1; -Const Int AAPTR_TARGET = 2; -Const Int AAPTR_MASTER = 4; -Const Int AAPTR_TRACER = 8; +Const Int AAPTR_NULL = 0x1; +Const Int AAPTR_TARGET = 0x2; +Const Int AAPTR_MASTER = 0x4; +Const Int AAPTR_TRACER = 0x8; -Const Int AAPTR_PLAYER_GETTARGET = 16; -Const Int AAPTR_PLAYER_GETCONVERSATION = 32; +Const Int AAPTR_PLAYER_GETTARGET = 0x10; +Const Int AAPTR_PLAYER_GETCONVERSATION = 0x20; -Const Int AAPTR_PLAYER1 = 64; -Const Int AAPTR_PLAYER2 = 128; -Const Int AAPTR_PLAYER3 = 256; -Const Int AAPTR_PLAYER4 = 512; -Const Int AAPTR_PLAYER5 = 1024; -Const Int AAPTR_PLAYER6 = 2048; -Const Int AAPTR_PLAYER7 = 4096; -Const Int AAPTR_PLAYER8 = 8192; +Const Int AAPTR_PLAYER1 = 0x40; +Const Int AAPTR_PLAYER2 = 0x80; +Const Int AAPTR_PLAYER3 = 0x100; +Const Int AAPTR_PLAYER4 = 0x200; +Const Int AAPTR_PLAYER5 = 0x400; +Const Int AAPTR_PLAYER6 = 0x800; +Const Int AAPTR_PLAYER7 = 0x1000; +Const Int AAPTR_PLAYER8 = 0x2000; -Const Int AAPTR_FRIENDPLAYER = 16384; +Const Int AAPTR_FRIENDPLAYER = 0x4000; +Const Int AAPTR_LINETARGET = 0x8000; // Pointer operation flags From 43b86288c76e50c1eac79257714b5e3243b55be1 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sun, 28 Sep 2014 08:15:00 -0500 Subject: [PATCH 38/47] - Added A_Remove(int pointer, int flags). - RMVF_MISSILES removes missiles. - RMVF_NOMONSTERS ignores monsters. - RMVF_MISC includes non-monsters and missiles. - RMVF_EVERYTHING disregards all checks and remove it. These flags now apply to the following in addition to the new function: -A_RemoveTarget -A_RemoveMaster -A_RemoveTracer -A_RemoveChildren -A_RemoveSiblings --- src/thingdef/thingdef_codeptr.cpp | 181 +++++++++++++++++++---------- wadsrc/static/actors/actor.txt | 11 +- wadsrc/static/actors/constants.txt | 9 ++ 3 files changed, 133 insertions(+), 68 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 4266c01c76..13b5a0cb31 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -3671,65 +3671,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFlag) } } - -//=========================================================================== -// -// A_RemoveMaster -// -//=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RemoveMaster) -{ - if (self->master != NULL) - { - P_RemoveThing(self->master); - } -} - -//=========================================================================== -// -// A_RemoveChildren -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveChildren) -{ - TThinkerIterator it; - AActor *mo; - ACTION_PARAM_START(1); - ACTION_PARAM_BOOL(removeall,0); - - while ((mo = it.Next()) != NULL) - { - if (mo->master == self && (mo->health <= 0 || removeall)) - { - P_RemoveThing(mo); - } - } -} - -//=========================================================================== -// -// A_RemoveSiblings -// -//=========================================================================== -DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) -{ - TThinkerIterator it; - AActor *mo; - ACTION_PARAM_START(1); - ACTION_PARAM_BOOL(removeall,0); - - if (self->master != NULL) - { - while ((mo = it.Next()) != NULL) - { - if (mo->master == self->master && mo != self && (mo->health <= 0 || removeall)) - { - P_RemoveThing(mo); - } - } - } -} - //=========================================================================== // // A_RaiseMaster @@ -5063,7 +5004,6 @@ static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags } - //=========================================================================== // // A_KillTarget(damagetype, int flags) @@ -5149,6 +5089,39 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) } } +//=========================================================================== +// +// DoRemove +// +//=========================================================================== + +enum RMVF_flags +{ + RMVF_MISSILES = 1 << 0, + RMVF_NOMONSTERS = 1 << 1, + RMVF_MISC = 1 << 2, + RMVF_EVERYTHING = 1 << 3, +}; + +static void DoRemove(AActor *removetarget, int flags) +{ + if ((flags & RMVF_EVERYTHING)) + { + P_RemoveThing(removetarget); + } + if ((flags & RMVF_MISC) && !((removetarget->flags3 & MF3_ISMONSTER) && (removetarget->flags & MF_MISSILE))) + { + P_RemoveThing(removetarget); + } + if ((removetarget->flags3 & MF3_ISMONSTER) && !(flags & RMVF_NOMONSTERS)) + { + P_RemoveThing(removetarget); + } + if ((removetarget->flags & MF_MISSILE) && (flags & RMVF_MISSILES)) + { + P_RemoveThing(removetarget); + } +} //=========================================================================== // @@ -5157,7 +5130,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) //=========================================================================== DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) { - if (self->target != NULL) + if ((self->target != NULL) && (self->tracer->flags3 & MF3_ISMONSTER)) { P_RemoveThing(self->target); } @@ -5170,8 +5143,90 @@ DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) //=========================================================================== DEFINE_ACTION_FUNCTION(AActor, A_RemoveTracer) { - if (self->tracer != NULL) + if ((self->tracer != NULL) && (self->tracer->flags3 & MF3_ISMONSTER)) { P_RemoveThing(self->tracer); } -} \ No newline at end of file +} + +//=========================================================================== +// +// A_RemoveMaster +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveMaster) +{ + ACTION_PARAM_START(1); + ACTION_PARAM_INT(flags, 0); + if (self->master != NULL) + { + DoRemove(self->master, flags); + } +} + +//=========================================================================== +// +// A_RemoveChildren +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveChildren) +{ + TThinkerIterator it; + AActor *mo; + ACTION_PARAM_START(2); + ACTION_PARAM_BOOL(removeall, 0); + ACTION_PARAM_INT(flags, 1); + + while ((mo = it.Next()) != NULL) + { + if (mo->master == self && (mo->health <= 0 || removeall)) + { + DoRemove(mo, flags); + } + } +} + +//=========================================================================== +// +// A_RemoveSiblings +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveSiblings) +{ + TThinkerIterator it; + AActor *mo; + ACTION_PARAM_START(2); + ACTION_PARAM_BOOL(removeall, 0); + ACTION_PARAM_INT(flags, 1); + + if (self->master != NULL) + { + while ((mo = it.Next()) != NULL) + { + if (mo->master == self->master && mo != self && (mo->health <= 0 || removeall)) + { + DoRemove(mo, flags); + } + } + } +} + +//=========================================================================== +// +// A_Remove +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Remove) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_INT(removee, 0); + ACTION_PARAM_INT(flags, 1); + + AActor *reference = COPY_AAPTR(self, removee); + + if (reference != NULL) + { + DoRemove(reference, flags); + } +} + diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index d404f23a2d..280321ad09 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -234,9 +234,9 @@ ACTOR Actor native //: Thinker action native A_ChangeFlag(string flagname, bool value); action native A_CheckFlag(string flagname, state label, int check_pointer = AAPTR_DEFAULT); action native A_JumpIf(bool expression, state label); - action native A_RemoveMaster(); - action native A_RemoveChildren(bool removeall = false); - action native A_RemoveSiblings(bool removeall = false); + action native A_RemoveMaster(int flags = 0); + action native A_RemoveChildren(bool removeall = false, int flags = 0); + action native A_RemoveSiblings(bool removeall = false, int flags = 0); action native A_KillMaster(name damagetype = "none", int flags = 0); action native A_KillChildren(name damagetype = "none", int flags = 0); action native A_KillSiblings(name damagetype = "none", int flags = 0); @@ -309,8 +309,9 @@ ACTOR Actor native //: Thinker action native A_DamageTracer(int amount, name damagetype = "none", int flags = 0); action native A_KillTarget(name damagetype = "none", int flags = 0); action native A_KillTracer(name damagetype = "none", int flags = 0); - action native A_RemoveTarget(); - action native A_RemoveTracer(); + action native A_RemoveTarget(int flags = 0); + action native A_RemoveTracer(int flags = 0); + action native A_Remove(int removee, int flags = 0); action native A_CheckSightOrRange(float distance, state label); action native A_CheckRange(float distance, state label); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 3419561f8b..41d69a157b 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -380,6 +380,15 @@ const int AMF_TARGETEMITTER = 1; const int AMF_TARGETNONPLAYER = 2; const int AMF_EMITFROMTARGET = 4; +// Flags for A_Remove* +enum +{ + RMVF_MISSILES = 1 << 0, + RMVF_NOMONSTERS = 1 << 1, + RMVF_MISC = 1 << 2, + RMVF_EVERYTHING = 1 << 3 +}; + // This is only here to provide one global variable for testing. native int testglobalvar; From 96c6e7d9bfe5b466c85e01e6bee65cf62dad22c2 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sun, 28 Sep 2014 08:20:27 -0500 Subject: [PATCH 39/47] Minor fix; didn't mean to include that flag check. --- src/thingdef/thingdef_codeptr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 13b5a0cb31..e724a1ba27 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5130,7 +5130,7 @@ static void DoRemove(AActor *removetarget, int flags) //=========================================================================== DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) { - if ((self->target != NULL) && (self->tracer->flags3 & MF3_ISMONSTER)) + if ((self->target != NULL)) { P_RemoveThing(self->target); } @@ -5143,7 +5143,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) //=========================================================================== DEFINE_ACTION_FUNCTION(AActor, A_RemoveTracer) { - if ((self->tracer != NULL) && (self->tracer->flags3 & MF3_ISMONSTER)) + if ((self->tracer != NULL)) { P_RemoveThing(self->tracer); } From 770547e6618588a47c1c5d4b65bd3603cd59873b Mon Sep 17 00:00:00 2001 From: Teemu Piippo Date: Sun, 28 Sep 2014 17:17:19 +0300 Subject: [PATCH 40/47] - added udmf key midtex3dimpassible which causes the midtex to behave like a finite-height impassible line; practically this means the mid texture lets projectiles pass through it. --- specs/udmf_zdoom.txt | 51 +++++++++++++++++++++++--------------------- src/doomdata.h | 1 + src/namedef.h | 3 ++- src/p_3dmidtex.cpp | 7 ++++++ src/p_udmf.cpp | 11 +++++++++- 5 files changed, 47 insertions(+), 26 deletions(-) diff --git a/specs/udmf_zdoom.txt b/specs/udmf_zdoom.txt index 1128b2e55d..cbb5b902ce 100644 --- a/specs/udmf_zdoom.txt +++ b/specs/udmf_zdoom.txt @@ -92,30 +92,33 @@ Note: All fields default to false unless mentioned otherwise. linedef { - alpha = ; // Translucency of this line, default is 1.0 - renderstyle = ; // Render style, can be "translucent" or "add", - // default is "translucent". - playeruseback = ; // New SPAC flag, true = player can use from back side. - anycross = ; // New SPAC flag, true = any non-projectile - // crossing will trigger this line - monsteractivate = ; // Monsters can trigger this line. - // For compatibility only because this flag's - // semantics can not be fully reproduced with - // explicit trigger flags. - blockplayers = ; // Line blocks players' movement. - blockeverything = ; // Line blocks everything. - firstsideonly = ; // Line can only be triggered from the front side. - zoneboundary = ; // Line is a boundary for sound reverb zones. - clipmidtex = ; // Line's mid textures are clipped to floor and ceiling. - wrapmidtex = ; // Line's mid textures are wrapped. - midtex3d = ; // Actors can walk on mid texture. - checkswitchrange = ;// Switches can only be activated when vertically reachable. - blockprojectiles = ;// Line blocks all projectiles - blockuse = ; // Line blocks all use actions - blocksight = ; // Line blocks monster line of sight - blockhitscan = ; // Line blocks hitscan attacks - locknumber = ; // Line special is locked - arg0str = ; // Alternate string-based version of arg0 + alpha = ; // Translucency of this line, default is 1.0 + renderstyle = ; // Render style, can be "translucent" or "add", + // default is "translucent". + playeruseback = ; // New SPAC flag, true = player can use from back side. + anycross = ; // New SPAC flag, true = any non-projectile + // crossing will trigger this line + monsteractivate = ; // Monsters can trigger this line. + // For compatibility only because this flag's + // semantics can not be fully reproduced with + // explicit trigger flags. + blockplayers = ; // Line blocks players' movement. + blockeverything = ; // Line blocks everything. + firstsideonly = ; // Line can only be triggered from the front side. + zoneboundary = ; // Line is a boundary for sound reverb zones. + clipmidtex = ; // Line's mid textures are clipped to floor and ceiling. + wrapmidtex = ; // Line's mid textures are wrapped. + midtex3d = ; // Actors can walk on mid texture. + midtex3dimpassible = ;// Used in conjuction with midtex3d - causes the mid + // texture to behave like an impassible line (projectiles + // pass through it). + checkswitchrange = ; // Switches can only be activated when vertically reachable. + blockprojectiles = ; // Line blocks all projectiles + blockuse = ; // Line blocks all use actions + blocksight = ; // Line blocks monster line of sight + blockhitscan = ; // Line blocks hitscan attacks + locknumber = ; // Line special is locked + arg0str = ; // Alternate string-based version of arg0 transparent = ; // true = line is a Strife transparent line (alpha 0.25) diff --git a/src/doomdata.h b/src/doomdata.h index 215c2a263c..f190be37d1 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -162,6 +162,7 @@ enum ELineFlags ML_BLOCKUSE = 0x02000000, // blocks all use actions through this line ML_BLOCKSIGHT = 0x04000000, // blocks monster line of sight ML_BLOCKHITSCAN = 0x08000000, // blocks hitscan attacks + ML_3DMIDTEX_IMPASS = 0x10000000, // [TP] if 3D midtex, behaves like a height-restricted ML_BLOCKING }; diff --git a/src/namedef.h b/src/namedef.h index 1e7fbd0d3a..4706fa5008 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -419,6 +419,7 @@ xx(Passuse) xx(Repeatspecial) xx(Conversation) xx(Locknumber) +xx(Midtex3dimpassible) xx(Playercross) xx(Playeruse) @@ -597,4 +598,4 @@ xx(NeverSwitchOnPickup) xx(MoveBob) xx(StillBob) xx(PlayerClass) -xx(Wi_NoAutostartMap) \ No newline at end of file +xx(Wi_NoAutostartMap) diff --git a/src/p_3dmidtex.cpp b/src/p_3dmidtex.cpp index ccb6f0359d..ac6d7aca79 100644 --- a/src/p_3dmidtex.cpp +++ b/src/p_3dmidtex.cpp @@ -258,6 +258,13 @@ bool P_GetMidTexturePosition(const line_t *line, int sideno, fixed_t *ptextop, f bool P_LineOpening_3dMidtex(AActor *thing, const line_t *linedef, FLineOpening &open, bool restrict) { + // [TP] Impassible-like 3dmidtextures do not block missiles + if ((linedef->flags & ML_3DMIDTEX_IMPASS) + && (thing->flags & MF_MISSILE || thing->BounceFlags & BOUNCE_MBF)) + { + return false; + } + fixed_t tt, tb; open.abovemidtex = false; diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 7588199b4d..473cdcd814 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -1030,11 +1030,16 @@ public: Flag(ld->flags, ML_BLOCKHITSCAN, key); continue; - // [Dusk] lock number + // [TP] Locks the special with a key case NAME_Locknumber: ld->locknumber = CheckInt(key); continue; + // [TP] Causes a 3d midtex to behave like an impassible line + case NAME_Midtex3dimpassible: + Flag(ld->flags, ML_3DMIDTEX_IMPASS, key); + continue; + default: break; } @@ -1081,6 +1086,10 @@ public: { ld->args[1] = -FName(arg1str); } + if ((ld->flags & ML_3DMIDTEX_IMPASS) && !(ld->flags & ML_3DMIDTEX)) // [TP] + { + Printf ("Line %d has midtex3dimpassible without midtex3d.\n", index); + } } //=========================================================================== From 3050ea9a6d3a85f89bcb27c5254e26cf0304a3b7 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 2 Oct 2014 11:27:22 -0500 Subject: [PATCH 41/47] - Fixed: A_RemoveTracer/Target didn't take flags into account. --- src/thingdef/thingdef_codeptr.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index e724a1ba27..e3dbbfbb7a 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5128,11 +5128,13 @@ static void DoRemove(AActor *removetarget, int flags) // A_RemoveTarget // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTarget) { - if ((self->target != NULL)) + ACTION_PARAM_START(1); + ACTION_PARAM_INT(flags, 0); + if (self->master != NULL) { - P_RemoveThing(self->target); + DoRemove(self->target, flags); } } @@ -5141,11 +5143,13 @@ DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) // A_RemoveTracer // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RemoveTracer) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTracer) { - if ((self->tracer != NULL)) + ACTION_PARAM_START(1); + ACTION_PARAM_INT(flags, 0); + if (self->master != NULL) { - P_RemoveThing(self->tracer); + DoRemove(self->tracer, flags); } } From 5030832df0141a7598f5a6d4363f3febf3711829 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 2 Oct 2014 11:48:07 -0500 Subject: [PATCH 42/47] - Added DMSS_NOFACTOR for all A_Damage functions. --- src/thingdef/thingdef_codeptr.cpp | 35 +++++++++++++++++++++++------- wadsrc/static/actors/constants.txt | 1 + 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index e724a1ba27..65888d0d56 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4830,6 +4830,7 @@ enum DMSS DMSS_FOILINVUL = 1, DMSS_AFFECTARMOR = 2, DMSS_KILL = 4, + DMSS_NOFACTOR = 8, }; static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags) @@ -4844,12 +4845,26 @@ static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageTy } if (flags & DMSS_AFFECTARMOR) { - P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL); + if (flags & DMSS_NOFACTOR) + { + P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_FACTOR); + } + else + { + P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL); + } } else { + if (flags & DMSS_NOFACTOR) + { + P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR | DMG_NO_FACTOR); + } //[MC] DMG_FOILINVUL is needed for making the damage occur on the actor. - P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + else + { + P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); + } } } } @@ -5128,11 +5143,13 @@ static void DoRemove(AActor *removetarget, int flags) // A_RemoveTarget // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTarget) { - if ((self->target != NULL)) + ACTION_PARAM_START(1); + ACTION_PARAM_INT(flags, 0); + if (self->master != NULL) { - P_RemoveThing(self->target); + DoRemove(self->target, flags); } } @@ -5141,11 +5158,13 @@ DEFINE_ACTION_FUNCTION(AActor, A_RemoveTarget) // A_RemoveTracer // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RemoveTracer) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RemoveTracer) { - if ((self->tracer != NULL)) + ACTION_PARAM_START(1); + ACTION_PARAM_INT(flags, 0); + if (self->master != NULL) { - P_RemoveThing(self->tracer); + DoRemove(self->tracer, flags); } } diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 1fe8e75cb1..57de8e6ad8 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -375,6 +375,7 @@ const int KILS_NOMONSTERS = 4; const int DMSS_FOILINVUL = 1; const int DMSS_AFFECTARMOR = 2; const int DMSS_KILL = 4; +const int DMSS_NOFACTOR = 8; // Flags for A_AlertMonsters const int AMF_TARGETEMITTER = 1; From c6e1ea864f38cb1f267f33fdd5ade5a9903de8f3 Mon Sep 17 00:00:00 2001 From: ChillyDoom Date: Thu, 2 Oct 2014 21:03:15 +0100 Subject: [PATCH 43/47] - Added scoreboard toggling. --- src/ct_chat.cpp | 3 ++- src/hu_scores.cpp | 8 ++++++++ src/hu_stuff.h | 2 ++ wadsrc/static/menudef.txt | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/ct_chat.cpp b/src/ct_chat.cpp index c0c1f13f0a..697d72c583 100644 --- a/src/ct_chat.cpp +++ b/src/ct_chat.cpp @@ -273,7 +273,8 @@ void CT_Drawer (void) if (players[consoleplayer].camera != NULL && (Button_ShowScores.bDown || - players[consoleplayer].camera->health <= 0) && + players[consoleplayer].camera->health <= 0 || + SB_ForceActive) && // Don't draw during intermission, since it has its own scoreboard in wi_stuff.cpp. gamestate != GS_INTERMISSION) { diff --git a/src/hu_scores.cpp b/src/hu_scores.cpp index 8b9a7d0ad9..fe2735ede9 100644 --- a/src/hu_scores.cpp +++ b/src/hu_scores.cpp @@ -49,6 +49,7 @@ #include "hu_stuff.h" #include "gstrings.h" #include "d_net.h" +#include "c_dispatch.h" // MACROS ------------------------------------------------------------------ @@ -117,6 +118,8 @@ int STACK_ARGS compareteams (const void *arg1, const void *arg2) return diff; } +bool SB_ForceActive = false; + // PRIVATE DATA DEFINITIONS ------------------------------------------------ // CODE -------------------------------------------------------------------- @@ -492,3 +495,8 @@ int HU_GetRowColor(player_t *player, bool highlight) } } } + +CCMD (togglescoreboard) +{ + SB_ForceActive = !SB_ForceActive; +} diff --git a/src/hu_stuff.h b/src/hu_stuff.h index dc22a2adcc..eb2dc573b5 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -52,6 +52,8 @@ void HU_GetPlayerWidths(int &maxnamewidth, int &maxscorewidth, int &maxiconheigh void HU_DrawColorBar(int x, int y, int height, int playernum); int HU_GetRowColor(player_t *player, bool hightlight); +extern bool SB_ForceActive; + // Sorting routines int STACK_ARGS comparepoints(const void *arg1, const void *arg2); diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 6d313faf14..36ff3d8aba 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -458,6 +458,7 @@ OptionMenu "CustomizeControls" Control "Run", "+speed" Control "Strafe", "+strafe" Control "Show Scoreboard", "+showscores" + Control "Toggle Scoreboard", "togglescoreboard" StaticText "" StaticText "Chat", 1 Control "Say", "messagemode" From f54a59fdf8e6d54f6cae8b9bd92f90b81bbf26c3 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Thu, 2 Oct 2014 17:00:17 -0500 Subject: [PATCH 44/47] - Added JLOSF_CHECKTRACER for A_JumpIfTargetInLOS. - CHECKTRACER doesn't need to be a missile or have the SEEKERMISSILE flag. --- src/thingdef/thingdef_codeptr.cpp | 29 +- wadsrc/static/actors/constants.txt | 796 +++++++++++++++-------------- 2 files changed, 415 insertions(+), 410 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 65888d0d56..45dd9ff49d 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -3278,18 +3278,19 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF) enum JLOS_flags { - JLOSF_PROJECTILE=1, - JLOSF_NOSIGHT=2, - JLOSF_CLOSENOFOV=4, - JLOSF_CLOSENOSIGHT=8, - JLOSF_CLOSENOJUMP=16, - JLOSF_DEADNOJUMP=32, - JLOSF_CHECKMASTER=64, - JLOSF_TARGETLOS=128, - JLOSF_FLIPFOV=256, - JLOSF_ALLYNOJUMP=512, - JLOSF_COMBATANTONLY=1024, - JLOSF_NOAUTOAIM=2048, + JLOSF_PROJECTILE = 1, + JLOSF_NOSIGHT = 1 << 1, + JLOSF_CLOSENOFOV = 1 << 2, + JLOSF_CLOSENOSIGHT = 1 << 3, + JLOSF_CLOSENOJUMP = 1 << 4, + JLOSF_DEADNOJUMP = 1 << 5, + JLOSF_CHECKMASTER = 1 << 6, + JLOSF_TARGETLOS = 1 << 7, + JLOSF_FLIPFOV = 1 << 8, + JLOSF_ALLYNOJUMP = 1 << 9, + JLOSF_COMBATANTONLY = 1 << 10, + JLOSF_NOAUTOAIM = 1 << 11, + JLOSF_CHECKTRACER = 1 << 12, }; DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS) @@ -3314,9 +3315,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS) { target = self->master; } - else if (self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE)) + else if ((self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE)) || (flags & JLOSF_CHECKTRACER)) { - if (self->flags2 & MF2_SEEKERMISSILE) + if ((self->flags2 & MF2_SEEKERMISSILE) || (flags & JLOSF_CHECKTRACER)) target = self->tracer; else target = NULL; diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 57de8e6ad8..5e9eb2d6d2 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -1,396 +1,400 @@ - -// Flags for A_PainAttack -const int PAF_NOSKULLATTACK = 1; -const int PAF_AIMFACING = 2; -const int PAF_NOTARGET = 4; - -// Flags for A_VileAttack -const int VAF_DMGTYPEAPPLYTODIRECT = 1; - -// Flags for A_Saw -const int SF_NORANDOM = 1; -const int SF_RANDOMLIGHTMISS = 2; -const int SF_RANDOMLIGHTHIT = 4; -const int SF_RANDOMLIGHTBOTH = 6; -const int SF_NOUSEAMMOMISS = 8; -const int SF_NOUSEAMMO = 16; -const int SF_NOPULLIN = 32; -const int SF_NOTURN = 64; - -// Flags for A_CustomMissile -const int CMF_AIMOFFSET = 1; -const int CMF_AIMDIRECTION = 2; -const int CMF_TRACKOWNER = 4; -const int CMF_CHECKTARGETDEAD = 8; -const int CMF_ABSOLUTEPITCH = 16; -const int CMF_OFFSETPITCH = 32; -const int CMF_SAVEPITCH = 64; -const int CMF_ABSOLUTEANGLE = 128; - -// Flags for A_CustomBulletAttack -const int CBAF_AIMFACING = 1; -const int CBAF_NORANDOM = 2; -const int CBAF_EXPLICITANGLE = 4; -const int CBAF_NOPITCH = 8; -const int CBAF_NORANDOMPUFFZ = 16; - -// Flags for A_GunFlash -const int GFF_NOEXTCHANGE = 1; - -// Flags for A_FireBullets -const int FBF_USEAMMO = 1; -const int FBF_NORANDOM = 2; -const int FBF_EXPLICITANGLE = 4; -const int FBF_NOPITCH = 8; -const int FBF_NOFLASH = 16; -const int FBF_NORANDOMPUFFZ = 32; - -// Flags for A_SpawnItemEx -const int SXF_TRANSFERTRANSLATION = 1 << 0; -const int SXF_ABSOLUTEPOSITION = 1 << 1; -const int SXF_ABSOLUTEANGLE = 1 << 2; -const int SXF_ABSOLUTEMOMENTUM = 1 << 3; //Since "momentum" is declared to be deprecated in the expressions, for compatibility -const int SXF_ABSOLUTEVELOCITY = 1 << 3; //purposes, this was made. It does the same thing though. Do not change the value. -const int SXF_SETMASTER = 1 << 4; -const int SXF_NOCHECKPOSITION = 1 << 5; -const int SXF_TELEFRAG = 1 << 6; -const int SXF_CLIENTSIDE = 1 << 7; // only used by Skulltag -const int SXF_TRANSFERAMBUSHFLAG = 1 << 8; -const int SXF_TRANSFERPITCH = 1 << 9; -const int SXF_TRANSFERPOINTERS = 1 << 10; -const int SXF_USEBLOODCOLOR = 1 << 11; -const int SXF_CLEARCALLERTID = 1 << 12; -const int SXF_MULTIPLYSPEED = 1 << 13; -const int SXF_TRANSFERSCALE = 1 << 14; -const int SXF_TRANSFERSPECIAL = 1 << 15; -const int SXF_CLEARCALLERSPECIAL = 1 << 16; -const int SXF_TRANSFERSTENCILCOL = 1 << 17; -const int SXF_TRANSFERALPHA = 1 << 18; -const int SXF_TRANSFERRENDERSTYLE = 1 << 19; -const int SXF_SETTARGET = 1 << 20; -const int SXF_SETTRACER = 1 << 21; -const int SXF_NOPOINTERS = 1 << 22; - -// Flags for A_Chase -const int CHF_FASTCHASE = 1; -const int CHF_NOPLAYACTIVE = 2; -const int CHF_NIGHTMAREFAST = 4; -const int CHF_RESURRECT = 8; -const int CHF_DONTMOVE = 16; - -// Flags for A_LookEx -const int LOF_NOSIGHTCHECK = 1; -const int LOF_NOSOUNDCHECK = 2; -const int LOF_DONTCHASEGOAL = 4; -const int LOF_NOSEESOUND = 8; -const int LOF_FULLVOLSEESOUND = 16; -const int LOF_NOJUMP = 32; - -// Flags for A_Respawn -const int RSF_FOG = 1; -const int RSF_KEEPTARGET = 2; -const int RSF_TELEFRAG = 4; - -// Flags for A_JumpIfTargetInLOS and A_JumpIfInTargetLOS -const int JLOSF_PROJECTILE = 1; -const int JLOSF_NOSIGHT = 2; -const int JLOSF_CLOSENOFOV = 4; -const int JLOSF_CLOSENOSIGHT = 8; -const int JLOSF_CLOSENOJUMP = 16; -const int JLOSF_DEADNOJUMP = 32; -const int JLOSF_CHECKMASTER = 64; -const int JLOSF_TARGETLOS = 128; -const int JLOSF_FLIPFOV = 256; -const int JLOSF_ALLYNOJUMP = 512; -const int JLOSF_COMBATANTONLY = 1024; -const int JLOSF_NOAUTOAIM = 2048; - -// Flags for A_ChangeVelocity -const int CVF_RELATIVE = 1; -const int CVF_REPLACE = 2; - -// Flags for A_WeaponReady -const int WRF_NOBOB = 1; -const int WRF_NOSWITCH = 2; -const int WRF_NOPRIMARY = 4; -const int WRF_NOSECONDARY = 8; -const int WRF_NOFIRE = WRF_NOPRIMARY | WRF_NOSECONDARY; -const int WRF_ALLOWRELOAD = 16; -const int WRF_ALLOWZOOM = 32; -const int WRF_DISABLESWITCH = 64; - -// Morph constants -const int MRF_ADDSTAMINA = 1; -const int MRF_FULLHEALTH = 2; -const int MRF_UNDOBYTOMEOFPOWER = 4; -const int MRF_UNDOBYCHAOSDEVICE = 8; -const int MRF_FAILNOTELEFRAG = 16; -const int MRF_FAILNOLAUGH = 32; -const int MRF_WHENINVULNERABLE = 64; -const int MRF_LOSEACTUALWEAPON = 128; -const int MRF_NEWTIDBEHAVIOUR = 256; -const int MRF_UNDOBYDEATH = 512; -const int MRF_UNDOBYDEATHFORCED = 1024; -const int MRF_UNDOBYDEATHSAVES = 2048; - -// Flags for A_RailAttack and A_CustomRailgun -const int RGF_SILENT = 1; -const int RGF_NOPIERCING = 2; -const int RGF_EXPLICITANGLE = 4; -const int RGF_FULLBRIGHT = 8; -const int RGF_CENTERZ = 16; - -// Flags for A_Mushroom -const int MSF_Standard = 0; -const int MSF_Classic = 1; -const int MSF_DontHurt = 2; - -// Flags for A_Explode -const int XF_HURTSOURCE = 1; -const int XF_NOTMISSILE = 4; - -// Flags for A_RadiusThrust -const int RTF_AFFECTSOURCE = 1; -const int RTF_NOIMPACTDAMAGE = 2; -const int RTF_NOTMISSILE = 4; - -// Flags for A_Blast -const int BF_USEAMMO = 1; -const int BF_DONTWARN = 2; -const int BF_AFFECTBOSSES = 4; -const int BF_NOIMPACTDAMAGE = 8; - -// Flags for A_SeekerMissile -const int SMF_LOOK = 1; -const int SMF_PRECISE = 2; -const int SMF_CURSPEED = 4; - -// Flags for A_CustomPunch -const int CPF_USEAMMO = 1; -const int CPF_DAGGER = 2; -const int CPF_PULLIN = 4; -const int CPF_NORANDOMPUFFZ = 8; - -// Flags for A_CustomMissile -const int FPF_AIMATANGLE = 1; -const int FPF_TRANSFERTRANSLATION = 2; - -// Flags for A_Teleport -const int TF_TELEFRAG = 1;const int TF_RANDOMDECIDE = 2; - -// Flags for A_WolfAttack -const int WAF_NORANDOM = 1; -const int WAF_USEPUFF = 2; - -// Flags for A_RadiusGive -enum -{ - RGF_GIVESELF = 1, - RGF_PLAYERS = 2, - RGF_MONSTERS = 4, - RGF_OBJECTS = 8, - RGF_VOODOO = 16, - RGF_CORPSES = 32, - RGF_NOTARGET = 64, - RGF_NOTRACER = 128, - RGF_NOMASTER = 256, - RGF_CUBE = 512, -}; - -// Activation flags -enum -{ - THINGSPEC_Default = 0, - THINGSPEC_ThingActs = 1, - THINGSPEC_ThingTargets = 2, - THINGSPEC_TriggerTargets = 4, - THINGSPEC_MonsterTrigger = 8, - THINGSPEC_MissileTrigger = 16, - THINGSPEC_ClearSpecial = 32, - THINGSPEC_NoDeathSpecial = 64, - THINGSPEC_TriggerActs = 128, -}; -// Shorter aliases for same -const int AF_Default = 0; -const int AF_ThingActs = 1; -const int AF_ThingTargets = 2; -const int AF_TriggerTargets = 4; -const int AF_MonsterTrigger = 8; -const int AF_MissileTrigger = 16; -const int AF_ClearSpecial = 32; -const int AF_NoDeathSpecial = 64; -const int AF_TriggerActs = 128; - -// Flags for A_TakeInventory and A_TakeFromTarget -const int TIF_NOTAKEINFINITE = 1; - -// constants for A_PlaySound -enum -{ - CHAN_AUTO = 0, - CHAN_WEAPON = 1, - CHAN_VOICE = 2, - CHAN_ITEM = 3, - CHAN_BODY = 4, - CHAN_5 = 5, - CHAN_6 = 6, - CHAN_7 = 7, - - // modifier flags - CHAN_LISTENERZ = 8, - CHAN_MAYBE_LOCAL = 16, - CHAN_UI = 32, - CHAN_NOPAUSE = 64 -}; - -// sound attenuation values -const float ATTN_NONE = 0; -const float ATTN_NORM = 1; -const float ATTN_IDLE = 1.001; -const float ATTN_STATIC = 3; - -// For SetPlayerProprty action special -Const Int PROP_FROZEN = 0; -Const Int PROP_NOTARGET = 1; -Const Int PROP_INSTANTWEAPONSWITCH = 2; -Const Int PROP_FLY = 3; -Const Int PROP_TOTALLYFROZEN = 4; -Const Int PROP_INVULNERABILITY = 5; // (Deprecated) -Const Int PROP_STRENGTH = 6; // (Deprecated) -Const Int PROP_INVISIBILITY = 7; // (Deprecated) -Const Int PROP_RADIATIONSUIT = 8; // (Deprecated) -Const Int PROP_ALLMAP = 9; // (Deprecated) -Const Int PROP_INFRARED = 10; // (Deprecated) -Const Int PROP_WEAPONLEVEL2 = 11; // (Deprecated) -Const Int PROP_FLIGHT = 12; // (Deprecated) -Const Int PROP_SPEED = 15; // (Deprecated) -Const Int PROP_BUDDHA = 16; - -// Line_SetBlocking -Const Int BLOCKF_CREATURES = 1; -Const Int BLOCKF_MONSTERS = 2; -Const Int BLOCKF_PLAYERS = 4; -Const Int BLOCKF_FLOATERS = 8; -Const Int BLOCKF_PROJECTILES = 16; -Const Int BLOCKF_EVERYTHING = 32; -Const Int BLOCKF_RAILING = 64; -Const Int BLOCKF_USE = 128; - -// Pointer constants, bitfield-enabled - -Const Int AAPTR_DEFAULT = 0; -Const Int AAPTR_NULL = 0x1; -Const Int AAPTR_TARGET = 0x2; -Const Int AAPTR_MASTER = 0x4; -Const Int AAPTR_TRACER = 0x8; - -Const Int AAPTR_PLAYER_GETTARGET = 0x10; -Const Int AAPTR_PLAYER_GETCONVERSATION = 0x20; - -Const Int AAPTR_PLAYER1 = 0x40; -Const Int AAPTR_PLAYER2 = 0x80; -Const Int AAPTR_PLAYER3 = 0x100; -Const Int AAPTR_PLAYER4 = 0x200; -Const Int AAPTR_PLAYER5 = 0x400; -Const Int AAPTR_PLAYER6 = 0x800; -Const Int AAPTR_PLAYER7 = 0x1000; -Const Int AAPTR_PLAYER8 = 0x2000; - -Const Int AAPTR_FRIENDPLAYER = 0x4000; -Const Int AAPTR_LINETARGET = 0x8000; - -// Pointer operation flags - -Const Int PTROP_UNSAFETARGET = 1; -Const Int PTROP_UNSAFEMASTER = 2; -Const Int PTROP_NOSAFEGUARDS = PTROP_UNSAFETARGET|PTROP_UNSAFEMASTER; - - -// Flags for A_Warp - -Const Int WARPF_ABSOLUTEOFFSET = 0x1; -Const Int WARPF_ABSOLUTEANGLE = 0x2; -Const Int WARPF_USECALLERANGLE = 0x4; -Const Int WARPF_NOCHECKPOSITION = 0x8; -Const Int WARPF_INTERPOLATE = 0x10; -Const Int WARPF_WARPINTERPOLATION = 0x20; -Const Int WARPF_COPYINTERPOLATION = 0x40; -Const Int WARPF_STOP = 0x80; -Const Int WARPF_TOFLOOR = 0x100; -Const Int WARPF_TESTONLY = 0x200; -Const Int WAPRF_ABSOLUTEPOSITION = 0x400; - -// flags for A_SetPitch/SetAngle -const int SPF_FORCECLAMP = 1; -const int SPF_INTERPOLATE = 2; - - -// flags for A_CheckLOF - -enum -{ - CLOFF_NOAIM_VERT = 0x1, - CLOFF_NOAIM_HORZ = 0x2, - - CLOFF_JUMPENEMY = 0x4, - CLOFF_JUMPFRIEND = 0x8, - CLOFF_JUMPOBJECT = 0x10, - CLOFF_JUMPNONHOSTILE = 0x20, - - CLOFF_SKIPENEMY = 0x40, - CLOFF_SKIPFRIEND = 0x80, - CLOFF_SKIPOBJECT = 0x100, - CLOFF_SKIPNONHOSTILE = 0x200, - - CLOFF_MUSTBESHOOTABLE = 0x400, - - CLOFF_SKIPTARGET = 0x800, - CLOFF_ALLOWNULL = 0x1000, - CLOFF_CHECKPARTIAL = 0x2000, - - CLOFF_MUSTBEGHOST = 0x4000, - CLOFF_IGNOREGHOST = 0x8000, - - CLOFF_MUSTBESOLID = 0x10000, - CLOFF_BEYONDTARGET = 0x20000, - - CLOFF_FROMBASE = 0x40000, - CLOFF_MUL_HEIGHT = 0x80000, - CLOFF_MUL_WIDTH = 0x100000, - - CLOFF_JUMP_ON_MISS = 0x200000, - CLOFF_AIM_VERT_NOOFFSET = 0x400000, - - CLOFF_SKIPOBSTACLES = CLOFF_SKIPENEMY|CLOFF_SKIPFRIEND|CLOFF_SKIPOBJECT|CLOFF_SKIPNONHOSTILE, - CLOFF_NOAIM = CLOFF_NOAIM_VERT|CLOFF_NOAIM_HORZ -}; - -// Flags for A_Kill (Master/Target/Tracer/Children/Siblings) series - -const int KILS_FOILINVUL = 1; -const int KILS_KILLMISSILES = 2; -const int KILS_NOMONSTERS = 4; - -// Flags for A_Damage (Master/Target/Tracer/Children/Siblings/Self) series -const int DMSS_FOILINVUL = 1; -const int DMSS_AFFECTARMOR = 2; -const int DMSS_KILL = 4; -const int DMSS_NOFACTOR = 8; - -// Flags for A_AlertMonsters -const int AMF_TARGETEMITTER = 1; -const int AMF_TARGETNONPLAYER = 2; -const int AMF_EMITFROMTARGET = 4; - -// Flags for A_Remove* -enum -{ - RMVF_MISSILES = 1 << 0, - RMVF_NOMONSTERS = 1 << 1, - RMVF_MISC = 1 << 2, - RMVF_EVERYTHING = 1 << 3 -}; - - -// This is only here to provide one global variable for testing. -native int testglobalvar; + +// Flags for A_PainAttack +const int PAF_NOSKULLATTACK = 1; +const int PAF_AIMFACING = 2; +const int PAF_NOTARGET = 4; + +// Flags for A_VileAttack +const int VAF_DMGTYPEAPPLYTODIRECT = 1; + +// Flags for A_Saw +const int SF_NORANDOM = 1; +const int SF_RANDOMLIGHTMISS = 2; +const int SF_RANDOMLIGHTHIT = 4; +const int SF_RANDOMLIGHTBOTH = 6; +const int SF_NOUSEAMMOMISS = 8; +const int SF_NOUSEAMMO = 16; +const int SF_NOPULLIN = 32; +const int SF_NOTURN = 64; + +// Flags for A_CustomMissile +const int CMF_AIMOFFSET = 1; +const int CMF_AIMDIRECTION = 2; +const int CMF_TRACKOWNER = 4; +const int CMF_CHECKTARGETDEAD = 8; +const int CMF_ABSOLUTEPITCH = 16; +const int CMF_OFFSETPITCH = 32; +const int CMF_SAVEPITCH = 64; +const int CMF_ABSOLUTEANGLE = 128; + +// Flags for A_CustomBulletAttack +const int CBAF_AIMFACING = 1; +const int CBAF_NORANDOM = 2; +const int CBAF_EXPLICITANGLE = 4; +const int CBAF_NOPITCH = 8; +const int CBAF_NORANDOMPUFFZ = 16; + +// Flags for A_GunFlash +const int GFF_NOEXTCHANGE = 1; + +// Flags for A_FireBullets +const int FBF_USEAMMO = 1; +const int FBF_NORANDOM = 2; +const int FBF_EXPLICITANGLE = 4; +const int FBF_NOPITCH = 8; +const int FBF_NOFLASH = 16; +const int FBF_NORANDOMPUFFZ = 32; + +// Flags for A_SpawnItemEx +const int SXF_TRANSFERTRANSLATION = 1 << 0; +const int SXF_ABSOLUTEPOSITION = 1 << 1; +const int SXF_ABSOLUTEANGLE = 1 << 2; +const int SXF_ABSOLUTEMOMENTUM = 1 << 3; //Since "momentum" is declared to be deprecated in the expressions, for compatibility +const int SXF_ABSOLUTEVELOCITY = 1 << 3; //purposes, this was made. It does the same thing though. Do not change the value. +const int SXF_SETMASTER = 1 << 4; +const int SXF_NOCHECKPOSITION = 1 << 5; +const int SXF_TELEFRAG = 1 << 6; +const int SXF_CLIENTSIDE = 1 << 7; // only used by Skulltag +const int SXF_TRANSFERAMBUSHFLAG = 1 << 8; +const int SXF_TRANSFERPITCH = 1 << 9; +const int SXF_TRANSFERPOINTERS = 1 << 10; +const int SXF_USEBLOODCOLOR = 1 << 11; +const int SXF_CLEARCALLERTID = 1 << 12; +const int SXF_MULTIPLYSPEED = 1 << 13; +const int SXF_TRANSFERSCALE = 1 << 14; +const int SXF_TRANSFERSPECIAL = 1 << 15; +const int SXF_CLEARCALLERSPECIAL = 1 << 16; +const int SXF_TRANSFERSTENCILCOL = 1 << 17; +const int SXF_TRANSFERALPHA = 1 << 18; +const int SXF_TRANSFERRENDERSTYLE = 1 << 19; +const int SXF_SETTARGET = 1 << 20; +const int SXF_SETTRACER = 1 << 21; +const int SXF_NOPOINTERS = 1 << 22; + +// Flags for A_Chase +const int CHF_FASTCHASE = 1; +const int CHF_NOPLAYACTIVE = 2; +const int CHF_NIGHTMAREFAST = 4; +const int CHF_RESURRECT = 8; +const int CHF_DONTMOVE = 16; + +// Flags for A_LookEx +const int LOF_NOSIGHTCHECK = 1; +const int LOF_NOSOUNDCHECK = 2; +const int LOF_DONTCHASEGOAL = 4; +const int LOF_NOSEESOUND = 8; +const int LOF_FULLVOLSEESOUND = 16; +const int LOF_NOJUMP = 32; + +// Flags for A_Respawn +const int RSF_FOG = 1; +const int RSF_KEEPTARGET = 2; +const int RSF_TELEFRAG = 4; + +// Flags for A_JumpIfTargetInLOS and A_JumpIfInTargetLOS +enum +{ + JLOSF_PROJECTILE = 1, + JLOSF_NOSIGHT = 1 << 1, + JLOSF_CLOSENOFOV = 1 << 2, + JLOSF_CLOSENOSIGHT = 1 << 3, + JLOSF_CLOSENOJUMP = 1 << 4, + JLOSF_DEADNOJUMP = 1 << 5, + JLOSF_CHECKMASTER = 1 << 6, + JLOSF_TARGETLOS = 1 << 7, + JLOSF_FLIPFOV = 1 << 8, + JLOSF_ALLYNOJUMP = 1 << 9, + JLOSF_COMBATANTONLY = 1 << 10, + JLOSF_NOAUTOAIM = 1 << 11, + JLOSF_CHECKTRACER = 1 << 12, +}; + +// Flags for A_ChangeVelocity +const int CVF_RELATIVE = 1; +const int CVF_REPLACE = 2; + +// Flags for A_WeaponReady +const int WRF_NOBOB = 1; +const int WRF_NOSWITCH = 2; +const int WRF_NOPRIMARY = 4; +const int WRF_NOSECONDARY = 8; +const int WRF_NOFIRE = WRF_NOPRIMARY | WRF_NOSECONDARY; +const int WRF_ALLOWRELOAD = 16; +const int WRF_ALLOWZOOM = 32; +const int WRF_DISABLESWITCH = 64; + +// Morph constants +const int MRF_ADDSTAMINA = 1; +const int MRF_FULLHEALTH = 2; +const int MRF_UNDOBYTOMEOFPOWER = 4; +const int MRF_UNDOBYCHAOSDEVICE = 8; +const int MRF_FAILNOTELEFRAG = 16; +const int MRF_FAILNOLAUGH = 32; +const int MRF_WHENINVULNERABLE = 64; +const int MRF_LOSEACTUALWEAPON = 128; +const int MRF_NEWTIDBEHAVIOUR = 256; +const int MRF_UNDOBYDEATH = 512; +const int MRF_UNDOBYDEATHFORCED = 1024; +const int MRF_UNDOBYDEATHSAVES = 2048; + +// Flags for A_RailAttack and A_CustomRailgun +const int RGF_SILENT = 1; +const int RGF_NOPIERCING = 2; +const int RGF_EXPLICITANGLE = 4; +const int RGF_FULLBRIGHT = 8; +const int RGF_CENTERZ = 16; + +// Flags for A_Mushroom +const int MSF_Standard = 0; +const int MSF_Classic = 1; +const int MSF_DontHurt = 2; + +// Flags for A_Explode +const int XF_HURTSOURCE = 1; +const int XF_NOTMISSILE = 4; + +// Flags for A_RadiusThrust +const int RTF_AFFECTSOURCE = 1; +const int RTF_NOIMPACTDAMAGE = 2; +const int RTF_NOTMISSILE = 4; + +// Flags for A_Blast +const int BF_USEAMMO = 1; +const int BF_DONTWARN = 2; +const int BF_AFFECTBOSSES = 4; +const int BF_NOIMPACTDAMAGE = 8; + +// Flags for A_SeekerMissile +const int SMF_LOOK = 1; +const int SMF_PRECISE = 2; +const int SMF_CURSPEED = 4; + +// Flags for A_CustomPunch +const int CPF_USEAMMO = 1; +const int CPF_DAGGER = 2; +const int CPF_PULLIN = 4; +const int CPF_NORANDOMPUFFZ = 8; + +// Flags for A_CustomMissile +const int FPF_AIMATANGLE = 1; +const int FPF_TRANSFERTRANSLATION = 2; + +// Flags for A_Teleport +const int TF_TELEFRAG = 1;const int TF_RANDOMDECIDE = 2; + +// Flags for A_WolfAttack +const int WAF_NORANDOM = 1; +const int WAF_USEPUFF = 2; + +// Flags for A_RadiusGive +enum +{ + RGF_GIVESELF = 1, + RGF_PLAYERS = 2, + RGF_MONSTERS = 4, + RGF_OBJECTS = 8, + RGF_VOODOO = 16, + RGF_CORPSES = 32, + RGF_NOTARGET = 64, + RGF_NOTRACER = 128, + RGF_NOMASTER = 256, + RGF_CUBE = 512, +}; + +// Activation flags +enum +{ + THINGSPEC_Default = 0, + THINGSPEC_ThingActs = 1, + THINGSPEC_ThingTargets = 2, + THINGSPEC_TriggerTargets = 4, + THINGSPEC_MonsterTrigger = 8, + THINGSPEC_MissileTrigger = 16, + THINGSPEC_ClearSpecial = 32, + THINGSPEC_NoDeathSpecial = 64, + THINGSPEC_TriggerActs = 128, +}; +// Shorter aliases for same +const int AF_Default = 0; +const int AF_ThingActs = 1; +const int AF_ThingTargets = 2; +const int AF_TriggerTargets = 4; +const int AF_MonsterTrigger = 8; +const int AF_MissileTrigger = 16; +const int AF_ClearSpecial = 32; +const int AF_NoDeathSpecial = 64; +const int AF_TriggerActs = 128; + +// Flags for A_TakeInventory and A_TakeFromTarget +const int TIF_NOTAKEINFINITE = 1; + +// constants for A_PlaySound +enum +{ + CHAN_AUTO = 0, + CHAN_WEAPON = 1, + CHAN_VOICE = 2, + CHAN_ITEM = 3, + CHAN_BODY = 4, + CHAN_5 = 5, + CHAN_6 = 6, + CHAN_7 = 7, + + // modifier flags + CHAN_LISTENERZ = 8, + CHAN_MAYBE_LOCAL = 16, + CHAN_UI = 32, + CHAN_NOPAUSE = 64 +}; + +// sound attenuation values +const float ATTN_NONE = 0; +const float ATTN_NORM = 1; +const float ATTN_IDLE = 1.001; +const float ATTN_STATIC = 3; + +// For SetPlayerProprty action special +Const Int PROP_FROZEN = 0; +Const Int PROP_NOTARGET = 1; +Const Int PROP_INSTANTWEAPONSWITCH = 2; +Const Int PROP_FLY = 3; +Const Int PROP_TOTALLYFROZEN = 4; +Const Int PROP_INVULNERABILITY = 5; // (Deprecated) +Const Int PROP_STRENGTH = 6; // (Deprecated) +Const Int PROP_INVISIBILITY = 7; // (Deprecated) +Const Int PROP_RADIATIONSUIT = 8; // (Deprecated) +Const Int PROP_ALLMAP = 9; // (Deprecated) +Const Int PROP_INFRARED = 10; // (Deprecated) +Const Int PROP_WEAPONLEVEL2 = 11; // (Deprecated) +Const Int PROP_FLIGHT = 12; // (Deprecated) +Const Int PROP_SPEED = 15; // (Deprecated) +Const Int PROP_BUDDHA = 16; + +// Line_SetBlocking +Const Int BLOCKF_CREATURES = 1; +Const Int BLOCKF_MONSTERS = 2; +Const Int BLOCKF_PLAYERS = 4; +Const Int BLOCKF_FLOATERS = 8; +Const Int BLOCKF_PROJECTILES = 16; +Const Int BLOCKF_EVERYTHING = 32; +Const Int BLOCKF_RAILING = 64; +Const Int BLOCKF_USE = 128; + +// Pointer constants, bitfield-enabled + +Const Int AAPTR_DEFAULT = 0; +Const Int AAPTR_NULL = 0x1; +Const Int AAPTR_TARGET = 0x2; +Const Int AAPTR_MASTER = 0x4; +Const Int AAPTR_TRACER = 0x8; + +Const Int AAPTR_PLAYER_GETTARGET = 0x10; +Const Int AAPTR_PLAYER_GETCONVERSATION = 0x20; + +Const Int AAPTR_PLAYER1 = 0x40; +Const Int AAPTR_PLAYER2 = 0x80; +Const Int AAPTR_PLAYER3 = 0x100; +Const Int AAPTR_PLAYER4 = 0x200; +Const Int AAPTR_PLAYER5 = 0x400; +Const Int AAPTR_PLAYER6 = 0x800; +Const Int AAPTR_PLAYER7 = 0x1000; +Const Int AAPTR_PLAYER8 = 0x2000; + +Const Int AAPTR_FRIENDPLAYER = 0x4000; +Const Int AAPTR_LINETARGET = 0x8000; + +// Pointer operation flags + +Const Int PTROP_UNSAFETARGET = 1; +Const Int PTROP_UNSAFEMASTER = 2; +Const Int PTROP_NOSAFEGUARDS = PTROP_UNSAFETARGET|PTROP_UNSAFEMASTER; + + +// Flags for A_Warp + +Const Int WARPF_ABSOLUTEOFFSET = 0x1; +Const Int WARPF_ABSOLUTEANGLE = 0x2; +Const Int WARPF_USECALLERANGLE = 0x4; +Const Int WARPF_NOCHECKPOSITION = 0x8; +Const Int WARPF_INTERPOLATE = 0x10; +Const Int WARPF_WARPINTERPOLATION = 0x20; +Const Int WARPF_COPYINTERPOLATION = 0x40; +Const Int WARPF_STOP = 0x80; +Const Int WARPF_TOFLOOR = 0x100; +Const Int WARPF_TESTONLY = 0x200; +Const Int WAPRF_ABSOLUTEPOSITION = 0x400; + +// flags for A_SetPitch/SetAngle +const int SPF_FORCECLAMP = 1; +const int SPF_INTERPOLATE = 2; + + +// flags for A_CheckLOF + +enum +{ + CLOFF_NOAIM_VERT = 0x1, + CLOFF_NOAIM_HORZ = 0x2, + + CLOFF_JUMPENEMY = 0x4, + CLOFF_JUMPFRIEND = 0x8, + CLOFF_JUMPOBJECT = 0x10, + CLOFF_JUMPNONHOSTILE = 0x20, + + CLOFF_SKIPENEMY = 0x40, + CLOFF_SKIPFRIEND = 0x80, + CLOFF_SKIPOBJECT = 0x100, + CLOFF_SKIPNONHOSTILE = 0x200, + + CLOFF_MUSTBESHOOTABLE = 0x400, + + CLOFF_SKIPTARGET = 0x800, + CLOFF_ALLOWNULL = 0x1000, + CLOFF_CHECKPARTIAL = 0x2000, + + CLOFF_MUSTBEGHOST = 0x4000, + CLOFF_IGNOREGHOST = 0x8000, + + CLOFF_MUSTBESOLID = 0x10000, + CLOFF_BEYONDTARGET = 0x20000, + + CLOFF_FROMBASE = 0x40000, + CLOFF_MUL_HEIGHT = 0x80000, + CLOFF_MUL_WIDTH = 0x100000, + + CLOFF_JUMP_ON_MISS = 0x200000, + CLOFF_AIM_VERT_NOOFFSET = 0x400000, + + CLOFF_SKIPOBSTACLES = CLOFF_SKIPENEMY|CLOFF_SKIPFRIEND|CLOFF_SKIPOBJECT|CLOFF_SKIPNONHOSTILE, + CLOFF_NOAIM = CLOFF_NOAIM_VERT|CLOFF_NOAIM_HORZ +}; + +// Flags for A_Kill (Master/Target/Tracer/Children/Siblings) series + +const int KILS_FOILINVUL = 1; +const int KILS_KILLMISSILES = 2; +const int KILS_NOMONSTERS = 4; + +// Flags for A_Damage (Master/Target/Tracer/Children/Siblings/Self) series +const int DMSS_FOILINVUL = 1; +const int DMSS_AFFECTARMOR = 2; +const int DMSS_KILL = 4; +const int DMSS_NOFACTOR = 8; + +// Flags for A_AlertMonsters +const int AMF_TARGETEMITTER = 1; +const int AMF_TARGETNONPLAYER = 2; +const int AMF_EMITFROMTARGET = 4; + +// Flags for A_Remove* +enum +{ + RMVF_MISSILES = 1 << 0, + RMVF_NOMONSTERS = 1 << 1, + RMVF_MISC = 1 << 2, + RMVF_EVERYTHING = 1 << 3, +}; + + +// This is only here to provide one global variable for testing. +native int testglobalvar; From 3eaebabdf114c4ae9d6d8590e4e8681692af704d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 6 Oct 2014 17:40:02 +0200 Subject: [PATCH 45/47] - fixed: EV_Teleport must not access the thing's player pointer before ensuring that 'thing' is a valid pointer. --- src/p_teleport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index b64c04a5ae..60308d8ed7 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -328,7 +328,6 @@ static AActor *SelectTeleDest (int tid, int tag, bool norandom) bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, bool fog, bool sourceFog, bool keepOrientation, bool haltVelocity, bool keepHeight) { - bool predicting = (thing->player && (thing->player->cheats & CF_PREDICTING)); AActor *searcher; fixed_t z; @@ -341,6 +340,7 @@ bool EV_Teleport (int tid, int tag, line_t *line, int side, AActor *thing, bool { // Teleport function called with an invalid actor return false; } + bool predicting = (thing->player && (thing->player->cheats & CF_PREDICTING)); if (thing->flags2 & MF2_NOTELEPORT) { return false; From 47029a3efca0b61f05477a05ab28f518a61507c6 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Tue, 7 Oct 2014 11:33:08 -0500 Subject: [PATCH 46/47] DoKill bug -Fixed: DoKill did not target 'killtarget' for missiles, causing crashes and making the function not work properly. --- src/thingdef/thingdef_codeptr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 65888d0d56..c513878fcd 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5002,7 +5002,7 @@ static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags //since that's the whole point of it. if ((!(killtarget->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(killtarget->flags5 & MF5_NODAMAGE)) { - P_ExplodeMissile(self->target, NULL, NULL); + P_ExplodeMissile(killtarget, NULL, NULL); } } if (!(flags & KILS_NOMONSTERS)) From f766a1ab388d40769e299b733d0b72e8d6484b09 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Sat, 11 Oct 2014 16:15:42 -0500 Subject: [PATCH 47/47] - Added SXF_ORIGINATOR. - Only useful for missiles. By default, missiles cannot set themselves as the master when spawning actors. --- src/thingdef/thingdef_codeptr.cpp | 15 +- wadsrc/static/actors/constants.txt | 793 +++++++++++++++-------------- 2 files changed, 406 insertions(+), 402 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index c513878fcd..2d53b40529 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1768,6 +1768,7 @@ enum SIX_Flags SIXF_SETTARGET = 1 << 20, SIXF_SETTRACER = 1 << 21, SIXF_NOPOINTERS = 1 << 22, + SIXF_ORIGINATOR = 1 << 23, }; static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) @@ -1803,11 +1804,13 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) { mo->pitch = self->pitch; } - while (originator && originator->isMissile()) + if (!(flags & SIXF_ORIGINATOR)) { - originator = originator->target; - } - + while (originator && originator->isMissile()) + { + originator = originator->target; + } + } if (flags & SIXF_TELEFRAG) { P_TeleportMove(mo, mo->x, mo->y, mo->z, true); @@ -5112,9 +5115,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) enum RMVF_flags { - RMVF_MISSILES = 1 << 0, + RMVF_MISSILES = 1 << 0, RMVF_NOMONSTERS = 1 << 1, - RMVF_MISC = 1 << 2, + RMVF_MISC = 1 << 2, RMVF_EVERYTHING = 1 << 3, }; diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 57de8e6ad8..553b231129 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -1,396 +1,397 @@ - -// Flags for A_PainAttack -const int PAF_NOSKULLATTACK = 1; -const int PAF_AIMFACING = 2; -const int PAF_NOTARGET = 4; - -// Flags for A_VileAttack -const int VAF_DMGTYPEAPPLYTODIRECT = 1; - -// Flags for A_Saw -const int SF_NORANDOM = 1; -const int SF_RANDOMLIGHTMISS = 2; -const int SF_RANDOMLIGHTHIT = 4; -const int SF_RANDOMLIGHTBOTH = 6; -const int SF_NOUSEAMMOMISS = 8; -const int SF_NOUSEAMMO = 16; -const int SF_NOPULLIN = 32; -const int SF_NOTURN = 64; - -// Flags for A_CustomMissile -const int CMF_AIMOFFSET = 1; -const int CMF_AIMDIRECTION = 2; -const int CMF_TRACKOWNER = 4; -const int CMF_CHECKTARGETDEAD = 8; -const int CMF_ABSOLUTEPITCH = 16; -const int CMF_OFFSETPITCH = 32; -const int CMF_SAVEPITCH = 64; -const int CMF_ABSOLUTEANGLE = 128; - -// Flags for A_CustomBulletAttack -const int CBAF_AIMFACING = 1; -const int CBAF_NORANDOM = 2; -const int CBAF_EXPLICITANGLE = 4; -const int CBAF_NOPITCH = 8; -const int CBAF_NORANDOMPUFFZ = 16; - -// Flags for A_GunFlash -const int GFF_NOEXTCHANGE = 1; - -// Flags for A_FireBullets -const int FBF_USEAMMO = 1; -const int FBF_NORANDOM = 2; -const int FBF_EXPLICITANGLE = 4; -const int FBF_NOPITCH = 8; -const int FBF_NOFLASH = 16; -const int FBF_NORANDOMPUFFZ = 32; - -// Flags for A_SpawnItemEx -const int SXF_TRANSFERTRANSLATION = 1 << 0; -const int SXF_ABSOLUTEPOSITION = 1 << 1; -const int SXF_ABSOLUTEANGLE = 1 << 2; -const int SXF_ABSOLUTEMOMENTUM = 1 << 3; //Since "momentum" is declared to be deprecated in the expressions, for compatibility -const int SXF_ABSOLUTEVELOCITY = 1 << 3; //purposes, this was made. It does the same thing though. Do not change the value. -const int SXF_SETMASTER = 1 << 4; -const int SXF_NOCHECKPOSITION = 1 << 5; -const int SXF_TELEFRAG = 1 << 6; -const int SXF_CLIENTSIDE = 1 << 7; // only used by Skulltag -const int SXF_TRANSFERAMBUSHFLAG = 1 << 8; -const int SXF_TRANSFERPITCH = 1 << 9; -const int SXF_TRANSFERPOINTERS = 1 << 10; -const int SXF_USEBLOODCOLOR = 1 << 11; -const int SXF_CLEARCALLERTID = 1 << 12; -const int SXF_MULTIPLYSPEED = 1 << 13; -const int SXF_TRANSFERSCALE = 1 << 14; -const int SXF_TRANSFERSPECIAL = 1 << 15; -const int SXF_CLEARCALLERSPECIAL = 1 << 16; -const int SXF_TRANSFERSTENCILCOL = 1 << 17; -const int SXF_TRANSFERALPHA = 1 << 18; -const int SXF_TRANSFERRENDERSTYLE = 1 << 19; -const int SXF_SETTARGET = 1 << 20; -const int SXF_SETTRACER = 1 << 21; -const int SXF_NOPOINTERS = 1 << 22; - -// Flags for A_Chase -const int CHF_FASTCHASE = 1; -const int CHF_NOPLAYACTIVE = 2; -const int CHF_NIGHTMAREFAST = 4; -const int CHF_RESURRECT = 8; -const int CHF_DONTMOVE = 16; - -// Flags for A_LookEx -const int LOF_NOSIGHTCHECK = 1; -const int LOF_NOSOUNDCHECK = 2; -const int LOF_DONTCHASEGOAL = 4; -const int LOF_NOSEESOUND = 8; -const int LOF_FULLVOLSEESOUND = 16; -const int LOF_NOJUMP = 32; - -// Flags for A_Respawn -const int RSF_FOG = 1; -const int RSF_KEEPTARGET = 2; -const int RSF_TELEFRAG = 4; - -// Flags for A_JumpIfTargetInLOS and A_JumpIfInTargetLOS -const int JLOSF_PROJECTILE = 1; -const int JLOSF_NOSIGHT = 2; -const int JLOSF_CLOSENOFOV = 4; -const int JLOSF_CLOSENOSIGHT = 8; -const int JLOSF_CLOSENOJUMP = 16; -const int JLOSF_DEADNOJUMP = 32; -const int JLOSF_CHECKMASTER = 64; -const int JLOSF_TARGETLOS = 128; -const int JLOSF_FLIPFOV = 256; -const int JLOSF_ALLYNOJUMP = 512; -const int JLOSF_COMBATANTONLY = 1024; -const int JLOSF_NOAUTOAIM = 2048; - -// Flags for A_ChangeVelocity -const int CVF_RELATIVE = 1; -const int CVF_REPLACE = 2; - -// Flags for A_WeaponReady -const int WRF_NOBOB = 1; -const int WRF_NOSWITCH = 2; -const int WRF_NOPRIMARY = 4; -const int WRF_NOSECONDARY = 8; -const int WRF_NOFIRE = WRF_NOPRIMARY | WRF_NOSECONDARY; -const int WRF_ALLOWRELOAD = 16; -const int WRF_ALLOWZOOM = 32; -const int WRF_DISABLESWITCH = 64; - -// Morph constants -const int MRF_ADDSTAMINA = 1; -const int MRF_FULLHEALTH = 2; -const int MRF_UNDOBYTOMEOFPOWER = 4; -const int MRF_UNDOBYCHAOSDEVICE = 8; -const int MRF_FAILNOTELEFRAG = 16; -const int MRF_FAILNOLAUGH = 32; -const int MRF_WHENINVULNERABLE = 64; -const int MRF_LOSEACTUALWEAPON = 128; -const int MRF_NEWTIDBEHAVIOUR = 256; -const int MRF_UNDOBYDEATH = 512; -const int MRF_UNDOBYDEATHFORCED = 1024; -const int MRF_UNDOBYDEATHSAVES = 2048; - -// Flags for A_RailAttack and A_CustomRailgun -const int RGF_SILENT = 1; -const int RGF_NOPIERCING = 2; -const int RGF_EXPLICITANGLE = 4; -const int RGF_FULLBRIGHT = 8; -const int RGF_CENTERZ = 16; - -// Flags for A_Mushroom -const int MSF_Standard = 0; -const int MSF_Classic = 1; -const int MSF_DontHurt = 2; - -// Flags for A_Explode -const int XF_HURTSOURCE = 1; -const int XF_NOTMISSILE = 4; - -// Flags for A_RadiusThrust -const int RTF_AFFECTSOURCE = 1; -const int RTF_NOIMPACTDAMAGE = 2; -const int RTF_NOTMISSILE = 4; - -// Flags for A_Blast -const int BF_USEAMMO = 1; -const int BF_DONTWARN = 2; -const int BF_AFFECTBOSSES = 4; -const int BF_NOIMPACTDAMAGE = 8; - -// Flags for A_SeekerMissile -const int SMF_LOOK = 1; -const int SMF_PRECISE = 2; -const int SMF_CURSPEED = 4; - -// Flags for A_CustomPunch -const int CPF_USEAMMO = 1; -const int CPF_DAGGER = 2; -const int CPF_PULLIN = 4; -const int CPF_NORANDOMPUFFZ = 8; - -// Flags for A_CustomMissile -const int FPF_AIMATANGLE = 1; -const int FPF_TRANSFERTRANSLATION = 2; - -// Flags for A_Teleport -const int TF_TELEFRAG = 1;const int TF_RANDOMDECIDE = 2; - -// Flags for A_WolfAttack -const int WAF_NORANDOM = 1; -const int WAF_USEPUFF = 2; - -// Flags for A_RadiusGive -enum -{ - RGF_GIVESELF = 1, - RGF_PLAYERS = 2, - RGF_MONSTERS = 4, - RGF_OBJECTS = 8, - RGF_VOODOO = 16, - RGF_CORPSES = 32, - RGF_NOTARGET = 64, - RGF_NOTRACER = 128, - RGF_NOMASTER = 256, - RGF_CUBE = 512, -}; - -// Activation flags -enum -{ - THINGSPEC_Default = 0, - THINGSPEC_ThingActs = 1, - THINGSPEC_ThingTargets = 2, - THINGSPEC_TriggerTargets = 4, - THINGSPEC_MonsterTrigger = 8, - THINGSPEC_MissileTrigger = 16, - THINGSPEC_ClearSpecial = 32, - THINGSPEC_NoDeathSpecial = 64, - THINGSPEC_TriggerActs = 128, -}; -// Shorter aliases for same -const int AF_Default = 0; -const int AF_ThingActs = 1; -const int AF_ThingTargets = 2; -const int AF_TriggerTargets = 4; -const int AF_MonsterTrigger = 8; -const int AF_MissileTrigger = 16; -const int AF_ClearSpecial = 32; -const int AF_NoDeathSpecial = 64; -const int AF_TriggerActs = 128; - -// Flags for A_TakeInventory and A_TakeFromTarget -const int TIF_NOTAKEINFINITE = 1; - -// constants for A_PlaySound -enum -{ - CHAN_AUTO = 0, - CHAN_WEAPON = 1, - CHAN_VOICE = 2, - CHAN_ITEM = 3, - CHAN_BODY = 4, - CHAN_5 = 5, - CHAN_6 = 6, - CHAN_7 = 7, - - // modifier flags - CHAN_LISTENERZ = 8, - CHAN_MAYBE_LOCAL = 16, - CHAN_UI = 32, - CHAN_NOPAUSE = 64 -}; - -// sound attenuation values -const float ATTN_NONE = 0; -const float ATTN_NORM = 1; -const float ATTN_IDLE = 1.001; -const float ATTN_STATIC = 3; - -// For SetPlayerProprty action special -Const Int PROP_FROZEN = 0; -Const Int PROP_NOTARGET = 1; -Const Int PROP_INSTANTWEAPONSWITCH = 2; -Const Int PROP_FLY = 3; -Const Int PROP_TOTALLYFROZEN = 4; -Const Int PROP_INVULNERABILITY = 5; // (Deprecated) -Const Int PROP_STRENGTH = 6; // (Deprecated) -Const Int PROP_INVISIBILITY = 7; // (Deprecated) -Const Int PROP_RADIATIONSUIT = 8; // (Deprecated) -Const Int PROP_ALLMAP = 9; // (Deprecated) -Const Int PROP_INFRARED = 10; // (Deprecated) -Const Int PROP_WEAPONLEVEL2 = 11; // (Deprecated) -Const Int PROP_FLIGHT = 12; // (Deprecated) -Const Int PROP_SPEED = 15; // (Deprecated) -Const Int PROP_BUDDHA = 16; - -// Line_SetBlocking -Const Int BLOCKF_CREATURES = 1; -Const Int BLOCKF_MONSTERS = 2; -Const Int BLOCKF_PLAYERS = 4; -Const Int BLOCKF_FLOATERS = 8; -Const Int BLOCKF_PROJECTILES = 16; -Const Int BLOCKF_EVERYTHING = 32; -Const Int BLOCKF_RAILING = 64; -Const Int BLOCKF_USE = 128; - -// Pointer constants, bitfield-enabled - -Const Int AAPTR_DEFAULT = 0; -Const Int AAPTR_NULL = 0x1; -Const Int AAPTR_TARGET = 0x2; -Const Int AAPTR_MASTER = 0x4; -Const Int AAPTR_TRACER = 0x8; - -Const Int AAPTR_PLAYER_GETTARGET = 0x10; -Const Int AAPTR_PLAYER_GETCONVERSATION = 0x20; - -Const Int AAPTR_PLAYER1 = 0x40; -Const Int AAPTR_PLAYER2 = 0x80; -Const Int AAPTR_PLAYER3 = 0x100; -Const Int AAPTR_PLAYER4 = 0x200; -Const Int AAPTR_PLAYER5 = 0x400; -Const Int AAPTR_PLAYER6 = 0x800; -Const Int AAPTR_PLAYER7 = 0x1000; -Const Int AAPTR_PLAYER8 = 0x2000; - -Const Int AAPTR_FRIENDPLAYER = 0x4000; -Const Int AAPTR_LINETARGET = 0x8000; - -// Pointer operation flags - -Const Int PTROP_UNSAFETARGET = 1; -Const Int PTROP_UNSAFEMASTER = 2; -Const Int PTROP_NOSAFEGUARDS = PTROP_UNSAFETARGET|PTROP_UNSAFEMASTER; - - -// Flags for A_Warp - -Const Int WARPF_ABSOLUTEOFFSET = 0x1; -Const Int WARPF_ABSOLUTEANGLE = 0x2; -Const Int WARPF_USECALLERANGLE = 0x4; -Const Int WARPF_NOCHECKPOSITION = 0x8; -Const Int WARPF_INTERPOLATE = 0x10; -Const Int WARPF_WARPINTERPOLATION = 0x20; -Const Int WARPF_COPYINTERPOLATION = 0x40; -Const Int WARPF_STOP = 0x80; -Const Int WARPF_TOFLOOR = 0x100; -Const Int WARPF_TESTONLY = 0x200; -Const Int WAPRF_ABSOLUTEPOSITION = 0x400; - -// flags for A_SetPitch/SetAngle -const int SPF_FORCECLAMP = 1; -const int SPF_INTERPOLATE = 2; - - -// flags for A_CheckLOF - -enum -{ - CLOFF_NOAIM_VERT = 0x1, - CLOFF_NOAIM_HORZ = 0x2, - - CLOFF_JUMPENEMY = 0x4, - CLOFF_JUMPFRIEND = 0x8, - CLOFF_JUMPOBJECT = 0x10, - CLOFF_JUMPNONHOSTILE = 0x20, - - CLOFF_SKIPENEMY = 0x40, - CLOFF_SKIPFRIEND = 0x80, - CLOFF_SKIPOBJECT = 0x100, - CLOFF_SKIPNONHOSTILE = 0x200, - - CLOFF_MUSTBESHOOTABLE = 0x400, - - CLOFF_SKIPTARGET = 0x800, - CLOFF_ALLOWNULL = 0x1000, - CLOFF_CHECKPARTIAL = 0x2000, - - CLOFF_MUSTBEGHOST = 0x4000, - CLOFF_IGNOREGHOST = 0x8000, - - CLOFF_MUSTBESOLID = 0x10000, - CLOFF_BEYONDTARGET = 0x20000, - - CLOFF_FROMBASE = 0x40000, - CLOFF_MUL_HEIGHT = 0x80000, - CLOFF_MUL_WIDTH = 0x100000, - - CLOFF_JUMP_ON_MISS = 0x200000, - CLOFF_AIM_VERT_NOOFFSET = 0x400000, - - CLOFF_SKIPOBSTACLES = CLOFF_SKIPENEMY|CLOFF_SKIPFRIEND|CLOFF_SKIPOBJECT|CLOFF_SKIPNONHOSTILE, - CLOFF_NOAIM = CLOFF_NOAIM_VERT|CLOFF_NOAIM_HORZ -}; - -// Flags for A_Kill (Master/Target/Tracer/Children/Siblings) series - -const int KILS_FOILINVUL = 1; -const int KILS_KILLMISSILES = 2; -const int KILS_NOMONSTERS = 4; - -// Flags for A_Damage (Master/Target/Tracer/Children/Siblings/Self) series -const int DMSS_FOILINVUL = 1; -const int DMSS_AFFECTARMOR = 2; -const int DMSS_KILL = 4; -const int DMSS_NOFACTOR = 8; - -// Flags for A_AlertMonsters -const int AMF_TARGETEMITTER = 1; -const int AMF_TARGETNONPLAYER = 2; -const int AMF_EMITFROMTARGET = 4; - -// Flags for A_Remove* -enum -{ - RMVF_MISSILES = 1 << 0, - RMVF_NOMONSTERS = 1 << 1, - RMVF_MISC = 1 << 2, - RMVF_EVERYTHING = 1 << 3 -}; - - -// This is only here to provide one global variable for testing. -native int testglobalvar; + +// Flags for A_PainAttack +const int PAF_NOSKULLATTACK = 1; +const int PAF_AIMFACING = 2; +const int PAF_NOTARGET = 4; + +// Flags for A_VileAttack +const int VAF_DMGTYPEAPPLYTODIRECT = 1; + +// Flags for A_Saw +const int SF_NORANDOM = 1; +const int SF_RANDOMLIGHTMISS = 2; +const int SF_RANDOMLIGHTHIT = 4; +const int SF_RANDOMLIGHTBOTH = 6; +const int SF_NOUSEAMMOMISS = 8; +const int SF_NOUSEAMMO = 16; +const int SF_NOPULLIN = 32; +const int SF_NOTURN = 64; + +// Flags for A_CustomMissile +const int CMF_AIMOFFSET = 1; +const int CMF_AIMDIRECTION = 2; +const int CMF_TRACKOWNER = 4; +const int CMF_CHECKTARGETDEAD = 8; +const int CMF_ABSOLUTEPITCH = 16; +const int CMF_OFFSETPITCH = 32; +const int CMF_SAVEPITCH = 64; +const int CMF_ABSOLUTEANGLE = 128; + +// Flags for A_CustomBulletAttack +const int CBAF_AIMFACING = 1; +const int CBAF_NORANDOM = 2; +const int CBAF_EXPLICITANGLE = 4; +const int CBAF_NOPITCH = 8; +const int CBAF_NORANDOMPUFFZ = 16; + +// Flags for A_GunFlash +const int GFF_NOEXTCHANGE = 1; + +// Flags for A_FireBullets +const int FBF_USEAMMO = 1; +const int FBF_NORANDOM = 2; +const int FBF_EXPLICITANGLE = 4; +const int FBF_NOPITCH = 8; +const int FBF_NOFLASH = 16; +const int FBF_NORANDOMPUFFZ = 32; + +// Flags for A_SpawnItemEx +const int SXF_TRANSFERTRANSLATION = 1 << 0; +const int SXF_ABSOLUTEPOSITION = 1 << 1; +const int SXF_ABSOLUTEANGLE = 1 << 2; +const int SXF_ABSOLUTEMOMENTUM = 1 << 3; //Since "momentum" is declared to be deprecated in the expressions, for compatibility +const int SXF_ABSOLUTEVELOCITY = 1 << 3; //purposes, this was made. It does the same thing though. Do not change the value. +const int SXF_SETMASTER = 1 << 4; +const int SXF_NOCHECKPOSITION = 1 << 5; +const int SXF_TELEFRAG = 1 << 6; +const int SXF_CLIENTSIDE = 1 << 7; // only used by Skulltag +const int SXF_TRANSFERAMBUSHFLAG = 1 << 8; +const int SXF_TRANSFERPITCH = 1 << 9; +const int SXF_TRANSFERPOINTERS = 1 << 10; +const int SXF_USEBLOODCOLOR = 1 << 11; +const int SXF_CLEARCALLERTID = 1 << 12; +const int SXF_MULTIPLYSPEED = 1 << 13; +const int SXF_TRANSFERSCALE = 1 << 14; +const int SXF_TRANSFERSPECIAL = 1 << 15; +const int SXF_CLEARCALLERSPECIAL = 1 << 16; +const int SXF_TRANSFERSTENCILCOL = 1 << 17; +const int SXF_TRANSFERALPHA = 1 << 18; +const int SXF_TRANSFERRENDERSTYLE = 1 << 19; +const int SXF_SETTARGET = 1 << 20; +const int SXF_SETTRACER = 1 << 21; +const int SXF_NOPOINTERS = 1 << 22; +const int SXF_ORIGINATOR = 1 << 23; + +// Flags for A_Chase +const int CHF_FASTCHASE = 1; +const int CHF_NOPLAYACTIVE = 2; +const int CHF_NIGHTMAREFAST = 4; +const int CHF_RESURRECT = 8; +const int CHF_DONTMOVE = 16; + +// Flags for A_LookEx +const int LOF_NOSIGHTCHECK = 1; +const int LOF_NOSOUNDCHECK = 2; +const int LOF_DONTCHASEGOAL = 4; +const int LOF_NOSEESOUND = 8; +const int LOF_FULLVOLSEESOUND = 16; +const int LOF_NOJUMP = 32; + +// Flags for A_Respawn +const int RSF_FOG = 1; +const int RSF_KEEPTARGET = 2; +const int RSF_TELEFRAG = 4; + +// Flags for A_JumpIfTargetInLOS and A_JumpIfInTargetLOS +const int JLOSF_PROJECTILE = 1; +const int JLOSF_NOSIGHT = 2; +const int JLOSF_CLOSENOFOV = 4; +const int JLOSF_CLOSENOSIGHT = 8; +const int JLOSF_CLOSENOJUMP = 16; +const int JLOSF_DEADNOJUMP = 32; +const int JLOSF_CHECKMASTER = 64; +const int JLOSF_TARGETLOS = 128; +const int JLOSF_FLIPFOV = 256; +const int JLOSF_ALLYNOJUMP = 512; +const int JLOSF_COMBATANTONLY = 1024; +const int JLOSF_NOAUTOAIM = 2048; + +// Flags for A_ChangeVelocity +const int CVF_RELATIVE = 1; +const int CVF_REPLACE = 2; + +// Flags for A_WeaponReady +const int WRF_NOBOB = 1; +const int WRF_NOSWITCH = 2; +const int WRF_NOPRIMARY = 4; +const int WRF_NOSECONDARY = 8; +const int WRF_NOFIRE = WRF_NOPRIMARY | WRF_NOSECONDARY; +const int WRF_ALLOWRELOAD = 16; +const int WRF_ALLOWZOOM = 32; +const int WRF_DISABLESWITCH = 64; + +// Morph constants +const int MRF_ADDSTAMINA = 1; +const int MRF_FULLHEALTH = 2; +const int MRF_UNDOBYTOMEOFPOWER = 4; +const int MRF_UNDOBYCHAOSDEVICE = 8; +const int MRF_FAILNOTELEFRAG = 16; +const int MRF_FAILNOLAUGH = 32; +const int MRF_WHENINVULNERABLE = 64; +const int MRF_LOSEACTUALWEAPON = 128; +const int MRF_NEWTIDBEHAVIOUR = 256; +const int MRF_UNDOBYDEATH = 512; +const int MRF_UNDOBYDEATHFORCED = 1024; +const int MRF_UNDOBYDEATHSAVES = 2048; + +// Flags for A_RailAttack and A_CustomRailgun +const int RGF_SILENT = 1; +const int RGF_NOPIERCING = 2; +const int RGF_EXPLICITANGLE = 4; +const int RGF_FULLBRIGHT = 8; +const int RGF_CENTERZ = 16; + +// Flags for A_Mushroom +const int MSF_Standard = 0; +const int MSF_Classic = 1; +const int MSF_DontHurt = 2; + +// Flags for A_Explode +const int XF_HURTSOURCE = 1; +const int XF_NOTMISSILE = 4; + +// Flags for A_RadiusThrust +const int RTF_AFFECTSOURCE = 1; +const int RTF_NOIMPACTDAMAGE = 2; +const int RTF_NOTMISSILE = 4; + +// Flags for A_Blast +const int BF_USEAMMO = 1; +const int BF_DONTWARN = 2; +const int BF_AFFECTBOSSES = 4; +const int BF_NOIMPACTDAMAGE = 8; + +// Flags for A_SeekerMissile +const int SMF_LOOK = 1; +const int SMF_PRECISE = 2; +const int SMF_CURSPEED = 4; + +// Flags for A_CustomPunch +const int CPF_USEAMMO = 1; +const int CPF_DAGGER = 2; +const int CPF_PULLIN = 4; +const int CPF_NORANDOMPUFFZ = 8; + +// Flags for A_CustomMissile +const int FPF_AIMATANGLE = 1; +const int FPF_TRANSFERTRANSLATION = 2; + +// Flags for A_Teleport +const int TF_TELEFRAG = 1;const int TF_RANDOMDECIDE = 2; + +// Flags for A_WolfAttack +const int WAF_NORANDOM = 1; +const int WAF_USEPUFF = 2; + +// Flags for A_RadiusGive +enum +{ + RGF_GIVESELF = 1, + RGF_PLAYERS = 2, + RGF_MONSTERS = 4, + RGF_OBJECTS = 8, + RGF_VOODOO = 16, + RGF_CORPSES = 32, + RGF_NOTARGET = 64, + RGF_NOTRACER = 128, + RGF_NOMASTER = 256, + RGF_CUBE = 512, +}; + +// Activation flags +enum +{ + THINGSPEC_Default = 0, + THINGSPEC_ThingActs = 1, + THINGSPEC_ThingTargets = 2, + THINGSPEC_TriggerTargets = 4, + THINGSPEC_MonsterTrigger = 8, + THINGSPEC_MissileTrigger = 16, + THINGSPEC_ClearSpecial = 32, + THINGSPEC_NoDeathSpecial = 64, + THINGSPEC_TriggerActs = 128, +}; +// Shorter aliases for same +const int AF_Default = 0; +const int AF_ThingActs = 1; +const int AF_ThingTargets = 2; +const int AF_TriggerTargets = 4; +const int AF_MonsterTrigger = 8; +const int AF_MissileTrigger = 16; +const int AF_ClearSpecial = 32; +const int AF_NoDeathSpecial = 64; +const int AF_TriggerActs = 128; + +// Flags for A_TakeInventory and A_TakeFromTarget +const int TIF_NOTAKEINFINITE = 1; + +// constants for A_PlaySound +enum +{ + CHAN_AUTO = 0, + CHAN_WEAPON = 1, + CHAN_VOICE = 2, + CHAN_ITEM = 3, + CHAN_BODY = 4, + CHAN_5 = 5, + CHAN_6 = 6, + CHAN_7 = 7, + + // modifier flags + CHAN_LISTENERZ = 8, + CHAN_MAYBE_LOCAL = 16, + CHAN_UI = 32, + CHAN_NOPAUSE = 64 +}; + +// sound attenuation values +const float ATTN_NONE = 0; +const float ATTN_NORM = 1; +const float ATTN_IDLE = 1.001; +const float ATTN_STATIC = 3; + +// For SetPlayerProprty action special +Const Int PROP_FROZEN = 0; +Const Int PROP_NOTARGET = 1; +Const Int PROP_INSTANTWEAPONSWITCH = 2; +Const Int PROP_FLY = 3; +Const Int PROP_TOTALLYFROZEN = 4; +Const Int PROP_INVULNERABILITY = 5; // (Deprecated) +Const Int PROP_STRENGTH = 6; // (Deprecated) +Const Int PROP_INVISIBILITY = 7; // (Deprecated) +Const Int PROP_RADIATIONSUIT = 8; // (Deprecated) +Const Int PROP_ALLMAP = 9; // (Deprecated) +Const Int PROP_INFRARED = 10; // (Deprecated) +Const Int PROP_WEAPONLEVEL2 = 11; // (Deprecated) +Const Int PROP_FLIGHT = 12; // (Deprecated) +Const Int PROP_SPEED = 15; // (Deprecated) +Const Int PROP_BUDDHA = 16; + +// Line_SetBlocking +Const Int BLOCKF_CREATURES = 1; +Const Int BLOCKF_MONSTERS = 2; +Const Int BLOCKF_PLAYERS = 4; +Const Int BLOCKF_FLOATERS = 8; +Const Int BLOCKF_PROJECTILES = 16; +Const Int BLOCKF_EVERYTHING = 32; +Const Int BLOCKF_RAILING = 64; +Const Int BLOCKF_USE = 128; + +// Pointer constants, bitfield-enabled + +Const Int AAPTR_DEFAULT = 0; +Const Int AAPTR_NULL = 0x1; +Const Int AAPTR_TARGET = 0x2; +Const Int AAPTR_MASTER = 0x4; +Const Int AAPTR_TRACER = 0x8; + +Const Int AAPTR_PLAYER_GETTARGET = 0x10; +Const Int AAPTR_PLAYER_GETCONVERSATION = 0x20; + +Const Int AAPTR_PLAYER1 = 0x40; +Const Int AAPTR_PLAYER2 = 0x80; +Const Int AAPTR_PLAYER3 = 0x100; +Const Int AAPTR_PLAYER4 = 0x200; +Const Int AAPTR_PLAYER5 = 0x400; +Const Int AAPTR_PLAYER6 = 0x800; +Const Int AAPTR_PLAYER7 = 0x1000; +Const Int AAPTR_PLAYER8 = 0x2000; + +Const Int AAPTR_FRIENDPLAYER = 0x4000; +Const Int AAPTR_LINETARGET = 0x8000; + +// Pointer operation flags + +Const Int PTROP_UNSAFETARGET = 1; +Const Int PTROP_UNSAFEMASTER = 2; +Const Int PTROP_NOSAFEGUARDS = PTROP_UNSAFETARGET|PTROP_UNSAFEMASTER; + + +// Flags for A_Warp + +Const Int WARPF_ABSOLUTEOFFSET = 0x1; +Const Int WARPF_ABSOLUTEANGLE = 0x2; +Const Int WARPF_USECALLERANGLE = 0x4; +Const Int WARPF_NOCHECKPOSITION = 0x8; +Const Int WARPF_INTERPOLATE = 0x10; +Const Int WARPF_WARPINTERPOLATION = 0x20; +Const Int WARPF_COPYINTERPOLATION = 0x40; +Const Int WARPF_STOP = 0x80; +Const Int WARPF_TOFLOOR = 0x100; +Const Int WARPF_TESTONLY = 0x200; +Const Int WAPRF_ABSOLUTEPOSITION = 0x400; + +// flags for A_SetPitch/SetAngle +const int SPF_FORCECLAMP = 1; +const int SPF_INTERPOLATE = 2; + + +// flags for A_CheckLOF + +enum +{ + CLOFF_NOAIM_VERT = 0x1, + CLOFF_NOAIM_HORZ = 0x2, + + CLOFF_JUMPENEMY = 0x4, + CLOFF_JUMPFRIEND = 0x8, + CLOFF_JUMPOBJECT = 0x10, + CLOFF_JUMPNONHOSTILE = 0x20, + + CLOFF_SKIPENEMY = 0x40, + CLOFF_SKIPFRIEND = 0x80, + CLOFF_SKIPOBJECT = 0x100, + CLOFF_SKIPNONHOSTILE = 0x200, + + CLOFF_MUSTBESHOOTABLE = 0x400, + + CLOFF_SKIPTARGET = 0x800, + CLOFF_ALLOWNULL = 0x1000, + CLOFF_CHECKPARTIAL = 0x2000, + + CLOFF_MUSTBEGHOST = 0x4000, + CLOFF_IGNOREGHOST = 0x8000, + + CLOFF_MUSTBESOLID = 0x10000, + CLOFF_BEYONDTARGET = 0x20000, + + CLOFF_FROMBASE = 0x40000, + CLOFF_MUL_HEIGHT = 0x80000, + CLOFF_MUL_WIDTH = 0x100000, + + CLOFF_JUMP_ON_MISS = 0x200000, + CLOFF_AIM_VERT_NOOFFSET = 0x400000, + + CLOFF_SKIPOBSTACLES = CLOFF_SKIPENEMY|CLOFF_SKIPFRIEND|CLOFF_SKIPOBJECT|CLOFF_SKIPNONHOSTILE, + CLOFF_NOAIM = CLOFF_NOAIM_VERT|CLOFF_NOAIM_HORZ +}; + +// Flags for A_Kill (Master/Target/Tracer/Children/Siblings) series + +const int KILS_FOILINVUL = 1; +const int KILS_KILLMISSILES = 2; +const int KILS_NOMONSTERS = 4; + +// Flags for A_Damage (Master/Target/Tracer/Children/Siblings/Self) series +const int DMSS_FOILINVUL = 1; +const int DMSS_AFFECTARMOR = 2; +const int DMSS_KILL = 4; +const int DMSS_NOFACTOR = 8; + +// Flags for A_AlertMonsters +const int AMF_TARGETEMITTER = 1; +const int AMF_TARGETNONPLAYER = 2; +const int AMF_EMITFROMTARGET = 4; + +// Flags for A_Remove* +enum +{ + RMVF_MISSILES = 1 << 0, + RMVF_NOMONSTERS = 1 << 1, + RMVF_MISC = 1 << 2, + RMVF_EVERYTHING = 1 << 3, +}; + + +// This is only here to provide one global variable for testing. +native int testglobalvar;