Add Hexen line activation flags

- Lines can be set to activate when crossing or bumping into them, with distinctions for players, enemies, and missiles+items.
- A new flag has been added to determine if a line special can activate more than once.
- Finish Line + Respawn Line are now handled like other specials. This means that:
    - They follow the new line activation rules (so you can potentially have a finish line that you have to bump instead of cross)
    - More importantly, they can be called as functions in ACS. (Player_FinishLine and Player_Respawn)
- Fixed linedef flags not being saved in save games.
This commit is contained in:
Sally Coolatta 2022-12-31 13:38:05 -05:00
parent b93ffbef2f
commit c27139dbcb
13 changed files with 477 additions and 205 deletions

View file

@ -25,7 +25,7 @@ terminator = ";";
extrawordchars = "#"; // Extra characters to be treated as a part of a word by the Script Editor
//keywordhelp = "http://www.zdoom.org/wiki/index.php?title=%K";
snippetsdir = "acs";
scripttype = 1; //0 = unknown script, 1 = acc, 2 = modeldef, 3 = decorate
scripttype = "ACS";
keywords
{
@ -71,78 +71,6 @@ keywords
Ends a collaspable text region. See also: #region\n
Ignored by the compiler.";
// Keywords
if = "if (<expression>)";
else = "else";
for = "for (<initialize>; <condition>; <iterate>)";
do = "do";
while = "while (<expression>)";
until = "until (<expression>)";
break = "break";
continue = "continue";
switch = "switch (<expression>)";
case = "case <expression>:";
default = "default:";
const = "const";
function = "function <return> <identifier> ([arg], [...])";
script = "script <identifier> ([arg], [...])";
return = "return <value>\n
Return value for functions.\n
Does not work in scripts.";
restart = "restart\n
Restarts the current script from the beginning.\n
Does not work in functions.";
suspend = "suspend\n
Pauses the current script. It can be resumed by\n
activating the same script again.\n
Does not work in functions.";
terminate = "terminate\n
Ends the current script early.\n
Does not work in functions.";
special = "special <expressions>\n
Defines special actions for the ACS compiler.\n
Serves little to no purpose for level scripts.";
world = "world <type> <index>:<identifier>\n
Sets a wrapper for a hub scope variable.\n
Unimplemented in Ring Racers.";
global = "global <type> <index>:<identifier>\n
Sets a wrapper for a global scope variable.\n
Unimplemented in Ring Racers.";
// Types
void = "void";
bool = "bool";
int = "int";
str = "str";
fixed = "fixed";
// Script modifiers
OPEN = "script <identifier> OPEN\n
Makes a script automatically run when the level is initially loaded.";
ENTER = "script <identifier> ENTER\n
Makes a script automatically run when a player enters the game.\n
The activator is set to said player for this script.";
RESPAWN = "script <identifier> RESPAWN\n
Makes a script automatically run when a player respawns.\n
The activator is set to said player for this script.";
DEATH = "script <identifier> DEATH\n
Makes a script automatically run when a player dies.\n
The activator is set to said player for this script.";
LAP = "script <identifier> LAP\n
Makes a script automatically run whenever a player crosses the finish line.\n
The activator is set to said player for this script.";
// Specials
Sector_CopyHeights = "void Sector_CopyHeights(int tag1, int tag2, int plane, [bool texture])\n
Sets the plane heights of the tagged sectors to match another sector.\n
@ -666,6 +594,17 @@ keywords
- tag: The sector tag to search for waypoints in.\n
- enable: If true, enable the waypoints. Otherwise, disable the waypoints.";
Player_FinishLine = "void Player_FinishLine([bool flip])\n
Makes the activating player gain or lose a lap, depending on the activating side.\n
Runs the LAP script type when incrementing.\n
- flip: Flip which side of the activating line is used to increment laps.\n
If there was no activating line, then this will increment when false, and decrement when true.";
Player_Respawn = "void Player_Respawn([bool frontonly])\n
Makes the activating player respawn.\n
- frontonly: If true, then only respawn when crossing the front side.\n
Has no effect if this was not activated from a line.";
// Functions
Delay = "void Delay(int tics)\n
Pauses the current script.\n
@ -836,6 +775,46 @@ keywords
For logging to only the activator, use Log.";
}
properties
{
// Keywords
if;
else;
for;
do;
while;
until;
break;
continue;
switch;
case;
default;
const;
function;
script;
return;
restart;
suspend;
terminate;
world;
global;
special;
// Types
void;
bool;
int;
str;
fixed;
// Script modifiers
OPEN;
ENTER;
RESPAWN;
DEATH;
LAP;
}
constants
{
TRUE;

View file

@ -63,6 +63,31 @@ linedefflags_udmf
transfer = "FOF Transfer";
}
linedefactivations
{
1 = "When player crosses";
2 = "When enemy crosses";
4 = "When projectile crosses";
8 = "When player bumps";
16 = "When enemy bumps";
32 = "On projectile impact";
}
linedefactivations_udmf
{
repeatspecial
{
name = "Repeatable action";
istrigger = false;
}
playercross = "When player crosses";
playerpush = "When player bumps";
monstercross = "When enemy crosses";
monsterpush = "When enemy bumps";
missilecross = "When projectile crosses";
impact = "On projectile impact";
}
linedefrenderstyles
{
translucent = "Translucent";

View file

@ -152,6 +152,9 @@ special
int 499:Sector_ToggleWaypoints(2),
int 2001:Player_FinishLine(0, 1),
int 2003:Player_RespawnLine(0, 1),
// Internal functions have negative values
// [type] [ID]:[function name]([required args], [type of each arg, including optional ones])
/*

View file

@ -149,6 +149,30 @@ enum
// Transfers FOF properties.
ML_TFERLINE = 0x00008000,
// Special action is repeatable.
ML_REPEATSPECIAL = 0x00010000,
};
enum
{
// Activates when crossed by a player.
SPAC_CROSS = 0x00000001,
// Activates when crossed by an enemy.
SPAC_CROSSMONSTER = 0x00000002,
// Activates when crossed by a projectile.
SPAC_CROSSMISSILE = 0x00000004,
// Activates when bumped by a player.
SPAC_PUSH = 0x00000008,
// Activates when bumped by an enemy.
SPAC_PUSHMONSTER = 0x00000010,
// Activates when bumped by a missile.
SPAC_IMPACT = 0x00000020,
};
// Sector definition, from editing.

View file

@ -112,6 +112,7 @@ enum line_e {
line_dy,
line_angle,
line_flags,
line_activation,
line_special,
line_tag,
line_taglist,
@ -138,6 +139,7 @@ static const char *const line_opt[] = {
"dy",
"angle",
"flags",
"activation",
"special",
"tag",
"taglist",
@ -950,6 +952,9 @@ static int line_get(lua_State *L)
case line_flags:
lua_pushinteger(L, line->flags);
return 1;
case line_activation:
lua_pushinteger(L, line->activation);
return 1;
case line_special:
lua_pushinteger(L, line->special);
return 1;

View file

@ -249,20 +249,11 @@ static boolean P_SpecialIsLinedefCrossType(line_t *ld)
{
boolean linedefcrossspecial = false;
switch (ld->special)
// Take anything with any cross type for now,
// we'll have to filter it down later...
if (ld->activation & (SPAC_CROSS | SPAC_CROSSMONSTER | SPAC_CROSSMISSILE))
{
case 2001: // Finish line
case 2003: // Respawn line
{
linedefcrossspecial = true;
}
break;
default:
{
linedefcrossspecial = false;
}
break;
linedefcrossspecial = P_CanActivateSpecial(ld->special);
}
return linedefcrossspecial;

View file

@ -1618,6 +1618,8 @@ void P_XYMovement(mobj_t *mo)
else if (P_MobjWasRemoved(mo))
return;
P_PushSpecialLine(tm.blockingline, mo);
if (mo->flags & MF_MISSILE)
{
// explode a missile
@ -5215,6 +5217,23 @@ boolean P_IsKartItem(INT32 type)
}
}
boolean K_IsMissileOrKartItem(mobj_t *mo)
{
if (mo->flags & MF_MISSILE)
{
// It's already a missile!
return true;
}
if (mo->type == MT_SPB)
{
// Not considered a field item, so manually include.
return true;
}
return P_IsKartFieldItem(mo->type);
}
// This item can die in death sectors. There may be some
// special conditions for items that don't switch types...
// TODO: just make a general function for things that should

View file

@ -506,6 +506,7 @@ void P_AddCachedAction(mobj_t *mobj, INT32 statenum);
// kartitem stuff: Returns true if the specified 'type' is one of the kart item constants we want in the kitemcap list
boolean P_IsKartFieldItem(INT32 type);
boolean P_IsKartItem(INT32 type);
boolean K_IsMissileOrKartItem(mobj_t *mo);
boolean P_CanDeleteKartItem(INT32 type);
void P_AddKartItem(mobj_t *thing); // needs to be called in k_kart.c
void P_RunKartItems(void);

View file

@ -1147,6 +1147,10 @@ static void P_NetUnArchiveTubeWaypoints(savebuffer_t *save)
#define LD_ARGS 0x10
#define LD_STRINGARGS 0x20
#define LD_EXECUTORDELAY 0x40
#define LD_DIFF3 0x80
// diff3 flags
#define LD_ACTIVATION 0x01
static boolean P_AreArgsEqual(const line_t *li, const line_t *spawnli)
{
@ -1540,11 +1544,14 @@ static void ArchiveLines(savebuffer_t *save)
const line_t *spawnli = spawnlines;
const side_t *si;
const side_t *spawnsi;
UINT8 diff, diff2; // no diff3
UINT8 diff, diff2, diff3;
for (i = 0; i < numlines; i++, spawnli++, li++)
{
diff = diff2 = 0;
diff = diff2 = diff3 = 0;
if (li->flags != spawnli->flags)
diff |= LD_FLAG;
if (li->special != spawnli->special)
diff |= LD_SPECIAL;
@ -1561,6 +1568,9 @@ static void ArchiveLines(savebuffer_t *save)
if (li->executordelay != spawnli->executordelay)
diff2 |= LD_EXECUTORDELAY;
if (li->activation != spawnli->activation)
diff3 |= LD_ACTIVATION;
if (li->sidenum[0] != 0xffff)
{
si = &sides[li->sidenum[0]];
@ -1589,6 +1599,9 @@ static void ArchiveLines(savebuffer_t *save)
diff2 |= LD_S2MIDTEX;
}
if (diff3)
diff2 |= LD_DIFF3;
if (diff2)
diff |= LD_DIFF2;
@ -1598,6 +1611,8 @@ static void ArchiveLines(savebuffer_t *save)
WRITEUINT8(save->p, diff);
if (diff & LD_DIFF2)
WRITEUINT8(save->p, diff2);
if (diff2 & LD_DIFF3)
WRITEUINT8(save->p, diff3);
if (diff & LD_FLAG)
WRITEUINT32(save->p, li->flags);
if (diff & LD_SPECIAL)
@ -1651,6 +1666,8 @@ static void ArchiveLines(savebuffer_t *save)
}
if (diff2 & LD_EXECUTORDELAY)
WRITEINT32(save->p, li->executordelay);
if (diff3 & LD_ACTIVATION)
WRITEUINT32(save->p, li->activation);
}
}
WRITEUINT16(save->p, 0xffff);
@ -1661,7 +1678,7 @@ static void UnArchiveLines(savebuffer_t *save)
UINT16 i;
line_t *li;
side_t *si;
UINT8 diff, diff2; // no diff3
UINT8 diff, diff2, diff3;
for (;;)
{
@ -1680,6 +1697,11 @@ static void UnArchiveLines(savebuffer_t *save)
else
diff2 = 0;
if (diff2 & LD_DIFF3)
diff3 = READUINT8(save->p);
else
diff3 = 0;
if (diff & LD_FLAG)
li->flags = READUINT32(save->p);
if (diff & LD_SPECIAL)
@ -1735,6 +1757,8 @@ static void UnArchiveLines(savebuffer_t *save)
}
if (diff2 & LD_EXECUTORDELAY)
li->executordelay = READINT32(save->p);
if (diff3 & LD_ACTIVATION)
li->activation = READUINT32(save->p);
}
}

View file

@ -1646,6 +1646,21 @@ static void ParseTextmapLinedefParameter(UINT32 i, const char *param, const char
lines[i].flags |= ML_NOTBOUNCY;
else if (fastcmp(param, "transfer") && fastcmp("true", val))
lines[i].flags |= ML_TFERLINE;
else if (fastcmp(param, "repeatspecial") && fastcmp("true", val))
lines[i].flags |= ML_REPEATSPECIAL;
// Activation flags
else if (fastcmp(param, "playercross") && fastcmp("true", val))
lines[i].activation |= SPAC_CROSS;
else if (fastcmp(param, "monstercross") && fastcmp("true", val))
lines[i].activation |= SPAC_CROSSMONSTER;
else if (fastcmp(param, "missilecross") && fastcmp("true", val))
lines[i].activation |= SPAC_CROSSMISSILE;
else if (fastcmp(param, "playerpush") && fastcmp("true", val))
lines[i].activation |= SPAC_PUSH;
else if (fastcmp(param, "monsterpush") && fastcmp("true", val))
lines[i].activation |= SPAC_PUSHMONSTER;
else if (fastcmp(param, "impact") && fastcmp("true", val))
lines[i].activation |= SPAC_IMPACT;
}
static void ParseTextmapThingParameter(UINT32 i, const char *param, const char *val)
@ -5814,10 +5829,14 @@ static void P_ConvertBinaryLinedefTypes(void)
lines[i].blendmode = AST_FOG;
break;
case 2001: //Finish line
lines[i].activation |= SPAC_CROSS;
lines[i].flags |= ML_REPEATSPECIAL;
if (lines[i].flags & ML_NOCLIMB)
lines[i].args[0] |= TMCFF_FLIP;
break;
case 2003: //Respawn line
lines[i].activation |= SPAC_CROSS;
lines[i].flags |= ML_REPEATSPECIAL;
if (lines[i].flags & ML_NOCLIMB)
lines[i].args[0] |= TMCRF_FRONTONLY;
break;

View file

@ -1376,9 +1376,26 @@ static boolean P_CheckPushables(line_t *triggerline, sector_t *caller)
}
}
boolean P_CanActivateSpecial(INT16 special)
{
switch (special)
{
case 2001: // Finish line
case 2003: // Respawn line
{
return true;
}
default:
{
// Linedef executors
return (special >= 400 && special < 500);
}
}
}
static void P_ActivateLinedefExecutor(line_t *line, mobj_t *actor, sector_t *caller)
{
if (line->special < 400 || line->special >= 500)
if (P_CanActivateSpecial(line->special) == false)
return;
if (line->executordelay)
@ -2073,68 +2090,183 @@ static void K_HandleLapDecrement(player_t *player)
}
}
static void P_LineSpecialWasActivated(line_t *line)
{
if (!(line->flags & ML_REPEATSPECIAL))
{
line->special = 0;
}
}
static boolean P_AllowSpecialCross(line_t *line, mobj_t *thing)
{
if (P_CanActivateSpecial(line->special) == false)
{
// No special to even activate.
return false;
}
if (thing->player != NULL)
{
return !!(line->activation & SPAC_CROSS);
}
else if ((thing->flags & (MF_ENEMY|MF_BOSS)) != 0)
{
return !!(line->activation & SPAC_CROSSMONSTER);
}
else if (K_IsMissileOrKartItem(thing) == true)
{
return !!(line->activation & SPAC_CROSSMISSILE);
}
// No activation flags for you.
return false;
}
//
// P_CrossSpecialLine - TRIGGER
// Called every time a thing origin is about
// to cross a line with specific specials
// Kart - Only used for the finish line currently
//
void P_CrossSpecialLine(line_t *line, INT32 side, mobj_t *thing)
{
// only used for the players currently
if (!(thing && thing->player && !thing->player->spectator && !(thing->player->pflags & PF_NOCONTEST)))
return;
{
player_t *player = thing->player;
player_t *player = NULL;
activator_t *activator = NULL;
boolean result = false;
if (thing == NULL || P_MobjWasRemoved(thing) == true || thing->health <= 0)
{
// Invalid mobj.
return;
}
player = thing->player;
if (player != NULL)
{
if (player->spectator == true)
{
// Ignore spectators.
return;
}
if (player->pflags & PF_NOCONTEST)
{
// Ignore NO CONTEST.
return;
}
// Tripwire effect
if (P_IsLineTripWire(line))
{
K_ApplyTripWire(player, TRIPSTATE_PASSED);
}
switch (line->special)
{
case 2001: // Finish Line
{
if ((gametyperules & GTR_CIRCUIT) && !(player->exiting) && !(player->pflags & PF_HITFINISHLINE))
{
if (((line->args[0] & TMCFF_FLIP) && (side == 0))
|| (!(line->args[0] & TMCFF_FLIP) && (side == 1))) // crossed from behind to infront
{
K_HandleLapIncrement(player);
ACS_RunLapScript(thing, line);
}
else
{
K_HandleLapDecrement(player);
}
player->pflags |= PF_HITFINISHLINE;
if (P_AllowSpecialCross(line, thing) == false)
{
// This special can't be activated this way.
return;
}
}
break;
case 2003: // Respawn Line
{
/* No Climb: only trigger from front side */
if
(
player->respawn.state == RESPAWNST_NONE &&
(!(line->args[0] & TMCRF_FRONTONLY) || side == 0)
)
{
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_DEATHPIT);
}
}
break;
activator = Z_Calloc(sizeof(activator_t), PU_LEVEL, NULL);
I_Assert(activator != NULL);
default:
P_SetTarget(&activator->mo, thing);
activator->line = line;
activator->side = side;
activator->sector = (side != 0) ? line->backsector : line->frontsector;
activator->fromLineSpecial = true;
result = P_ProcessSpecial(activator, line->special, line->args, line->stringargs);
Z_Free(activator);
if (result == true)
{
// Do nothing
P_LineSpecialWasActivated(line);
}
break;
}
static boolean P_AllowSpecialPush(line_t *line, mobj_t *thing)
{
if (P_CanActivateSpecial(line->special) == false)
{
// No special to even activate.
return false;
}
if (thing->player != NULL)
{
return !!(line->activation & SPAC_PUSH);
}
else if ((thing->flags & (MF_ENEMY|MF_BOSS)) != 0)
{
return !!(line->activation & SPAC_PUSHMONSTER);
}
else if (K_IsMissileOrKartItem(thing) == true)
{
return !!(line->activation & SPAC_IMPACT);
}
// No activation flags for you.
return false;
}
//
// P_PushSpecialLine - TRIGGER
// Called every time a thing origin is blocked
// by a line with specific specials
//
void P_PushSpecialLine(line_t *line, mobj_t *thing)
{
player_t *player = NULL;
activator_t *activator = NULL;
boolean result = false;
if (thing == NULL || P_MobjWasRemoved(thing) == true || thing->health <= 0)
{
// Invalid mobj.
return;
}
player = thing->player;
if (player != NULL)
{
if (player->spectator == true)
{
// Ignore spectators.
return;
}
if (player->pflags & PF_NOCONTEST)
{
// Ignore NO CONTEST.
return;
}
}
if (P_AllowSpecialPush(line, thing) == false)
{
// This special can't be activated this way.
return;
}
activator = Z_Calloc(sizeof(activator_t), PU_LEVEL, NULL);
I_Assert(activator != NULL);
P_SetTarget(&activator->mo, thing);
activator->line = line;
activator->side = P_PointOnLineSide(thing->x, thing->y, line);
activator->sector = (activator->side != 0) ? line->backsector : line->frontsector;
activator->fromLineSpecial = true;
result = P_ProcessSpecial(activator, line->special, line->args, line->stringargs);
Z_Free(activator);
if (result == true)
{
P_LineSpecialWasActivated(line);
}
}
@ -2221,6 +2353,9 @@ static mobj_t* P_FindObjectTypeFromTag(mobjtype_t type, mtag_t tag)
*/
static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
{
// This is an old function purely for linedef executor
// backwards compatibility.
activator_t *activator = NULL;
if (line == NULL)
@ -2239,26 +2374,23 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
P_ProcessSpecial(activator, line->special, line->args, line->stringargs);
Z_Free(activator);
// Intentionally no P_LineSpecialWasActivated call.
}
void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char **stringargs)
boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char **stringargs)
{
line_t *line = activator->line; // If called from a linedef executor, this is the control sector linedef. If from a script, then it's the actual activator.
mobj_t *mo = activator->mo;
sector_t *callsec = activator->sector;
line_t *const line = activator->line; // If called from a linedef executor, this is the control sector linedef. If from a script, then it's the actual activator.
UINT8 const side = activator->side;
mobj_t *const mo = activator->mo;
sector_t *const callsec = activator->sector;
// All of these conditions being met means this is a binary map using a linedef executor.
const boolean backwardsCompat = (!udmf && activator->fromLineSpecial && line != NULL);
boolean const backwardsCompat = (!udmf && activator->fromLineSpecial && line != NULL);
INT32 secnum = -1;
//
// TODO: Too many specials are tied to a linedef existing,
// even after UDMF cleanups. We'll need to remove all of the
// (line == NULL) guards if we want these to be useful for ACS.
//
// note: only commands with linedef types >= 400 && < 500 can be used
// note: only specials that P_CanActivateSpecial returns true on can be used
switch (special)
{
case 400: // Copy tagged sector's heights/flats
@ -2270,7 +2402,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (line == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 400 Executor: No frontsector to copy planes from!\n");
return;
return false;
}
copySector = line->frontsector;
}
@ -2280,7 +2412,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (destsec == -1)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 400 Executor: No sector to copy planes from (tag %d)!\n", args[0]);
return;
return false;
}
copySector = &sectors[destsec];
}
@ -2319,7 +2451,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (line == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 402 Executor: No frontsector to copy light level from!\n");
return;
return false;
}
copySector = line->frontsector;
}
@ -2329,7 +2461,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (destsec == -1)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 402 Executor: No sector to copy light level from (tag %d)!\n", args[0]);
return;
return false;
}
copySector = &sectors[destsec];
}
@ -2380,7 +2512,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (line == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 403 Executor: No frontsector to copy planes from!\n");
return;
return false;
}
copySector = line->frontsector;
}
@ -2390,7 +2522,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (destsec == -1)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 403 Executor: No sector to copy planes from (tag %d)!\n", args[0]);
return;
return false;
}
copySector = &sectors[destsec];
}
@ -2450,7 +2582,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (line == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 408 Executor: No frontsector to copy flats from!\n");
return;
return false;
}
copySector = line->frontsector;
}
@ -2460,7 +2592,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (destsec == -1)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 408 Executor: No sector to copy flats from (tag %d)!\n", args[0]);
return;
return false;
}
copySector = &sectors[destsec];
}
@ -2509,7 +2641,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (line == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 410 Executor: No linedef to change frontsector tag of!\n");
return;
return false;
}
editLine = line;
}
@ -2519,7 +2651,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (destline == -1)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 408 Executor: No linedef to change frontsector tag of (tag %d)!\n", args[0]);
return;
return false;
}
editLine = &lines[destline];
}
@ -2576,7 +2708,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
mobj_t *dest;
if (!mo) // nothing to teleport
return;
return false;
if (args[1] & TMT_RELATIVE) // Relative silent teleport
{
@ -2617,7 +2749,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
dest = P_FindObjectTypeFromTag(MT_TELEPORTMAN, args[0]);
if (!dest)
return;
return false;
angle = (args[1] & TMT_KEEPANGLE) ? mo->angle : dest->angle;
silent = !!(args[1] & TMT_SILENT);
@ -2764,11 +2896,11 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
INT32 aim;
if ((!mo || !mo->player) && !titlemapinaction) // only players have views, and title screens
return;
return false;
altview = P_FindObjectTypeFromTag(MT_ALTVIEWMAN, args[0]);
if (!altview || !altview->spawnpoint)
return;
return false;
// If titlemap, set the camera ref for title's thinker
// This is not revoked until overwritten; awayviewtics is ignored
@ -2818,7 +2950,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
case 426: // Moves the mobj to its sector's soundorg and on the floor, and stops it
if (!mo)
return;
return false;
if (args[0])
{
@ -2882,7 +3014,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
case 433: // Flip/flop gravity. Works on pushables, too!
if (!mo)
return;
return false;
if (args[0])
mo->flags2 &= ~MF2_OBJECTFLIP;
@ -2935,7 +3067,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (!sec->ffloors)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 436: Target sector #%d has no FOFs.\n", secnum);
return;
return false;
}
for (rover = sec->ffloors; rover; rover = rover->next)
@ -2951,7 +3083,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (!foundrover)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 436: Can't find a FOF control sector with tag %d\n", foftag);
return;
return false;
}
}
}
@ -2988,7 +3120,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (line == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 439 Executor: No activating line to copy textures from!\n");
return;
return false;
}
copyLine = line;
}
@ -2998,7 +3130,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (origline == -1)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 439 Executor: No tagged line to copy textures from (tag %d)!\n", args[0]);
return;
return false;
}
copyLine = &lines[origline];
}
@ -3149,7 +3281,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (!sec->ffloors)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 445 Executor: Target sector #%d has no FOFs.\n", secnum);
return;
return false;
}
for (rover = sec->ffloors; rover; rover = rover->next)
@ -3178,7 +3310,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (!foundrover)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 445 Executor: Can't find a FOF control sector with tag %d\n", foftag);
return;
return false;
}
}
}
@ -3207,7 +3339,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (!sec->ffloors)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 446 Executor: Target sector #%d has no FOFs.\n", secnum);
return;
return false;
}
for (rover = sec->ffloors; rover; rover = rover->next)
@ -3226,7 +3358,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (!foundrover)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 446 Executor: Can't find a FOF control sector with tag %d\n", foftag);
return;
return false;
}
}
}
@ -3251,7 +3383,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (line == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 447 Executor: Can't find frontsector with source colormap!\n");
return;
return false;
}
source = line->frontsector->extra_colormap;
}
@ -3261,7 +3393,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (sourcesec == -1)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 447 Executor: Can't find sector with source colormap (tag %d)!\n", args[1]);
return;
return false;
}
source = sectors[sourcesec].extra_colormap;
}
@ -3416,7 +3548,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (!sec->ffloors)
{
CONS_Debug(DBG_GAMELOGIC, "Line type 452 Executor: Target sector #%d has no FOFs.\n", secnum);
return;
return false;
}
for (rover = sec->ffloors; rover; rover = rover->next)
@ -3454,7 +3586,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (!foundrover)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 452 Executor: Can't find a FOF control sector with tag %d\n", foftag);
return;
return false;
}
}
break;
@ -3478,7 +3610,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (!sec->ffloors)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 453 Executor: Target sector #%d has no FOFs.\n", secnum);
return;
return false;
}
for (rover = sec->ffloors; rover; rover = rover->next)
@ -3542,7 +3674,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (!foundrover)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 453 Executor: Can't find a FOF control sector with tag %d\n", foftag);
return;
return false;
}
}
break;
@ -3563,7 +3695,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (!sec->ffloors)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 454 Executor: Target sector #%d has no FOFs.\n", secnum);
return;
return false;
}
for (rover = sec->ffloors; rover; rover = rover->next)
@ -3580,7 +3712,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (!foundrover)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 454 Executor: Can't find a FOF control sector with tag %d\n", foftag);
return;
return false;
}
}
break;
@ -3601,7 +3733,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (line == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 455 Executor: Can't find frontsector with destination colormap!\n");
return;
return false;
}
dest = line->frontsector->extra_colormap;
}
@ -3611,7 +3743,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (destsec == -1)
{
CONS_Debug(DBG_GAMELOGIC, "Special type 455 Executor: Can't find sector with destination colormap (tag %d)!\n", args[1]);
return;
return false;
}
dest = sectors[destsec].extra_colormap;
}
@ -3718,7 +3850,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
anchormo = P_FindObjectTypeFromTag(MT_ANGLEMAN, args[0]);
if (!anchormo)
return;
return false;
mo->eflags |= MFE_TRACERANGLE;
P_SetTarget(&mo->tracer, anchormo);
@ -3773,7 +3905,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
{
// Don't award rings while SPB is targetting you
if (mo->player->pflags & PF_RINGLOCK)
return;
return false;
if (delay <= 0 || !(leveltime % delay))
{
@ -3837,7 +3969,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
INT32 color = stringargs[0] ? get_number(stringargs[0]) : SKINCOLOR_NONE;
if (color < 0 || color >= numskincolors)
return;
return false;
var1 = 0;
var2 = color;
@ -4005,7 +4137,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (!stringargs[0])
{
CONS_Debug(DBG_GAMELOGIC, "Linedef type 475: No script name given\n");
return;
return false;
}
ACS_Execute(stringargs[0], args, NUMLINEARGS, activator);
@ -4014,7 +4146,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (!stringargs[0])
{
CONS_Debug(DBG_GAMELOGIC, "Linedef type 476: No script name given\n");
return;
return false;
}
ACS_ExecuteAlways(stringargs[0], args, NUMLINEARGS, activator);
@ -4023,7 +4155,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (!stringargs[0])
{
CONS_Debug(DBG_GAMELOGIC, "Linedef type 477: No script name given\n");
return;
return false;
}
ACS_Suspend(stringargs[0]);
@ -4032,7 +4164,7 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
if (!stringargs[0])
{
CONS_Debug(DBG_GAMELOGIC, "Linedef type 478: No script name given\n");
return;
return false;
}
ACS_Terminate(stringargs[0]);
@ -4097,9 +4229,56 @@ void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char *
}
break;
case 2001: // Finish Line
{
if (mo->player == NULL)
{
return false;
}
if ((gametyperules & GTR_CIRCUIT) && (mo->player->exiting == 0) && !(mo->player->pflags & PF_HITFINISHLINE))
{
if (((line->args[0] & TMCFF_FLIP) && (side == 0))
|| (!(line->args[0] & TMCFF_FLIP) && (side == 1))) // crossed from behind to infront
{
K_HandleLapIncrement(mo->player);
ACS_RunLapScript(mo, line);
}
else
{
K_HandleLapDecrement(mo->player);
}
mo->player->pflags |= PF_HITFINISHLINE;
}
}
break;
case 2003: // Respawn Line
{
if (mo->player == NULL)
{
return false;
}
/* No Climb: only trigger from front side */
if
(
mo->player->respawn.state == RESPAWNST_NONE &&
(!(line->args[0] & TMCRF_FRONTONLY) || side == 0)
)
{
P_DamageMobj(mo, NULL, NULL, 1, DMG_DEATHPIT);
}
}
break;
default:
break;
}
return true;
}
static void P_SetupSignObject(mobj_t *sign, mobj_t *pmo, boolean error)

View file

@ -557,7 +557,8 @@ fixed_t P_FindHighestCeilingSurrounding(sector_t *sec);
INT32 P_FindMinSurroundingLight(sector_t *sector, INT32 max);
void P_CrossSpecialLine(line_t *ld, INT32 side, mobj_t *thing);
void P_CrossSpecialLine(line_t *line, INT32 side, mobj_t *thing);
void P_PushSpecialLine(line_t *line, mobj_t *thing);
//
// Special activation info
@ -572,7 +573,8 @@ struct activator_t
boolean fromLineSpecial; // Backwards compat for ACS
};
void P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char **stringargs);
boolean P_CanActivateSpecial(INT16 special);
boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char **stringargs);
void P_SetupSignExit(player_t *player);

View file

@ -525,6 +525,7 @@ struct line_t
// Animation related.
UINT32 flags;
UINT32 activation;
INT16 special;
taglist_t tags;
INT32 args[NUMLINEARGS];