From 6c038e2918b835c5f46bff374eb0cf141c24611b Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 22 Feb 2018 21:49:36 +0000 Subject: [PATCH 01/79] Cache all the act number graphicss at game startup rather than at every start/end of a level I also added a few helpful functions for drawing the act numbers themselves --- src/hu_stuff.c | 8 ++++++++ src/hu_stuff.h | 1 + src/st_stuff.c | 12 ++++-------- src/v_video.c | 20 ++++++++++++++++++++ src/v_video.h | 2 ++ src/y_inter.c | 13 ++++--------- 6 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 28b20f61e..484daaf49 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -69,6 +69,7 @@ patch_t *nightsnum[10]; // 0-9 // Level title and credits fonts patch_t *lt_font[LT_FONTSIZE]; patch_t *cred_font[CRED_FONTSIZE]; +patch_t *ttlnum[20]; // act numbers (0-19) static player_t *plr; boolean chat_on; // entering a chat message? @@ -238,6 +239,13 @@ void HU_LoadGraphics(void) tallminus = (patch_t *)W_CachePatchName("STTMINUS", PU_HUDGFX); tallinfin = (patch_t *)W_CachePatchName("STTINFIN", PU_HUDGFX); + // cache act numbers for level titles + for (i = 0; i < 20; i++) + { + sprintf(buffer, "TTL%.2d", i); + ttlnum[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); + } + // cache the crosshairs, don't bother to know which one is being used, // just cache all 3, they're so small anyway. for (i = 0; i < HU_CROSSHAIRS; i++) diff --git a/src/hu_stuff.h b/src/hu_stuff.h index 2dbeb556d..b7fa0abb0 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -63,6 +63,7 @@ extern patch_t *tallnum[10]; extern patch_t *nightsnum[10]; extern patch_t *lt_font[LT_FONTSIZE]; extern patch_t *cred_font[CRED_FONTSIZE]; +extern patch_t *ttlnum[20]; extern patch_t *emeraldpics[7]; extern patch_t *tinyemeraldpics[7]; extern patch_t *rflagico; diff --git a/src/st_stuff.c b/src/st_stuff.c index 437b6758a..ddb3e2738 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -77,7 +77,6 @@ static patch_t *race1; static patch_t *race2; static patch_t *race3; static patch_t *racego; -static patch_t *ttlnum; static patch_t *nightslink; static patch_t *curweapon; static patch_t *normring; @@ -992,13 +991,10 @@ static void ST_drawLevelTitle(void) if (!(timeinmap > 2 && timeinmap-3 < 110)) return; + lvlttlxpos = ((BASEVIDWIDTH/2) - (V_LevelNameWidth(lvlttl)/2)); + if (actnum > 0) - { - ttlnum = W_CachePatchName(va("TTL%.2d", actnum), PU_CACHE); - lvlttlxpos = ((BASEVIDWIDTH/2) - (V_LevelNameWidth(lvlttl)/2)) - SHORT(ttlnum->width); - } - else - lvlttlxpos = ((BASEVIDWIDTH/2) - (V_LevelNameWidth(lvlttl)/2)); + lvlttlxpos -= V_LevelActNumWidth(actnum); ttlnumxpos = lvlttlxpos + V_LevelNameWidth(lvlttl); zonexpos = ttlnumxpos - V_LevelNameWidth(M_GetText("ZONE")); @@ -1026,7 +1022,7 @@ static void ST_drawLevelTitle(void) } if (actnum) - V_DrawScaledPatch(ttlnumxpos, zoney, 0, ttlnum); + V_DrawLevelActNum(ttlnumxpos, zoney, 0, actnum); V_DrawLevelTitle(lvlttlxpos, lvlttly, 0, lvlttl); diff --git a/src/v_video.c b/src/v_video.c index 20fe9229e..5d9b9e841 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1819,6 +1819,16 @@ void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits) } while (--digits); } +// Draw an act number for a level title +// Todo: actually draw two-digit numbers as two act num patches +void V_DrawLevelActNum(INT32 x, INT32 y, INT32 flags, INT32 num) +{ + if (num < 0 || num > 19) + return; // not supported + + V_DrawScaledPatch(x, y, flags, ttlnum[num]); +} + // Write a string using the credit font // NOTE: the text is centered for screens larger than the base width // @@ -1983,6 +1993,16 @@ INT32 V_LevelNameHeight(const char *string) return w; } +// For ST_drawLevelTitle +// Returns the width of the act num patch +INT32 V_LevelActNumWidth(INT32 num) +{ + if (num < 0 || num > 19) + return 0; // not a valid number + + return SHORT(ttlnum[num]->width); +} + // // Find string width from hu_font chars // diff --git a/src/v_video.h b/src/v_video.h index f9fd475f4..50c927aef 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -177,10 +177,12 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string) // Draw tall nums, used for menu, HUD, intermission void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num); void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits); +void V_DrawLevelActNum(INT32 x, INT32 y, INT32 flags, INT32 num); // Find string width from lt_font chars INT32 V_LevelNameWidth(const char *string); INT32 V_LevelNameHeight(const char *string); +INT32 V_LevelActNumWidth(INT32 num); // act number width void V_DrawCreditString(fixed_t x, fixed_t y, INT32 option, const char *string); INT32 V_CreditStringWidth(const char *string); diff --git a/src/y_inter.c b/src/y_inter.c index 8a93b3eab..10cf27369 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -70,7 +70,7 @@ typedef union UINT32 score, total; // fake score, total UINT32 tics; // time - patch_t *ttlnum; // act number being displayed + INT32 actnum; // act number being displayed patch_t *ptotal; // TOTAL UINT8 gotlife; // Number of extra lives obtained } coop; @@ -287,8 +287,8 @@ void Y_IntermissionDrawer(void) V_DrawLevelTitle(data.coop.passedx1, 49, 0, data.coop.passed1); V_DrawLevelTitle(data.coop.passedx2, 49+V_LevelNameHeight(data.coop.passed2)+2, 0, data.coop.passed2); - if (mapheaderinfo[gamemap-1]->actnum) - V_DrawScaledPatch(244, 57, 0, data.coop.ttlnum); + if (data.coop.actnum) + V_DrawLevelActNum(244, 57, 0, data.coop.actnum); bonusy = 150; // Total @@ -1081,11 +1081,7 @@ void Y_StartIntermission(void) data.coop.ptotal = W_CachePatchName("YB_TOTAL", PU_STATIC); // get act number - if (mapheaderinfo[prevmap]->actnum) - data.coop.ttlnum = W_CachePatchName(va("TTL%.2d", mapheaderinfo[prevmap]->actnum), - PU_STATIC); - else - data.coop.ttlnum = W_CachePatchName("TTL01", PU_STATIC); + data.coop.actnum = mapheaderinfo[gamemap-1]->actnum; // get background patches widebgpatch = W_CachePatchName("INTERSCW", PU_STATIC); @@ -1888,7 +1884,6 @@ static void Y_UnloadData(void) { case int_coop: // unload the coop and single player patches - UNLOAD(data.coop.ttlnum); UNLOAD(data.coop.bonuspatches[3]); UNLOAD(data.coop.bonuspatches[2]); UNLOAD(data.coop.bonuspatches[1]); From 019d698e095b90738e031c706003582aa3ee7169 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Sun, 25 Mar 2018 18:07:15 -0400 Subject: [PATCH 02/79] NiGHTS objectplace: Place new hoop 1713 instead of 1705 * Add cv_ophoopflags consvar to manage hoop flags separately from other flags * Simplify hoop mt->options operation so we're not recalculating Z flags; just XOR cv_opflags OR cv_ophoopflags --- src/d_netcmd.c | 1 + src/m_cheat.c | 13 ++++--------- src/m_cheat.h | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 94eada152..916cd7fa5 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -807,6 +807,7 @@ void D_RegisterClientCommands(void) COM_AddCommand("writethings", Command_Writethings_f); CV_RegisterVar(&cv_speed); CV_RegisterVar(&cv_opflags); + CV_RegisterVar(&cv_ophoopflags); CV_RegisterVar(&cv_mapthingnum); // CV_RegisterVar(&cv_grid); // CV_RegisterVar(&cv_snapto); diff --git a/src/m_cheat.c b/src/m_cheat.c index a4eaede3a..a1adf71eb 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -779,10 +779,12 @@ void Command_Setcontinues_f(void) static CV_PossibleValue_t op_mapthing_t[] = {{0, "MIN"}, {4095, "MAX"}, {0, NULL}}; static CV_PossibleValue_t op_speed_t[] = {{1, "MIN"}, {128, "MAX"}, {0, NULL}}; static CV_PossibleValue_t op_flags_t[] = {{0, "MIN"}, {15, "MAX"}, {0, NULL}}; +static CV_PossibleValue_t op_hoopflags_t[] = {{0, "MIN"}, {15, "MAX"}, {0, NULL}}; consvar_t cv_mapthingnum = {"op_mapthingnum", "0", CV_NOTINNET, op_mapthing_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_speed = {"op_speed", "16", CV_NOTINNET, op_speed_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_opflags = {"op_flags", "0", CV_NOTINNET, op_flags_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_ophoopflags = {"op_hoopflags", "4", CV_NOTINNET, op_hoopflags_t, NULL, 0, NULL, NULL, 0, 0, NULL}; boolean objectplacing = false; mobjtype_t op_currentthing = 0; // For the object placement mode @@ -986,17 +988,10 @@ void OP_NightsObjectplace(player_t *player) { UINT16 angle = (UINT16)(player->anotherflyangle % 360); INT16 temp = (INT16)FixedInt(AngleFixed(player->mo->angle)); // Traditional 2D Angle - sector_t *sec = player->mo->subsector->sector; -#ifdef ESLOPE - fixed_t fheight = sec->f_slope ? P_GetZAt(sec->f_slope, player->mo->x & 0xFFFF0000, player->mo->y & 0xFFFF0000) : sec->floorheight; -#else - fixed_t fheight = sec->floorheight; -#endif - player->pflags |= PF_ATTACKDOWN; - mt = OP_CreateNewMapThing(player, 1705, false); + mt = OP_CreateNewMapThing(player, 1713, false); // Tilt mt->angle = (INT16)FixedInt(FixedDiv(angle*FRACUNIT, 360*(FRACUNIT/256))); @@ -1007,7 +1002,7 @@ void OP_NightsObjectplace(player_t *player) temp += 90; temp %= 360; - mt->options = (UINT16)((player->mo->z - fheight)>>FRACBITS); + mt->options = (mt->options ^ (UINT16)cv_opflags.value) | (UINT16)cv_ophoopflags.value; mt->angle = (INT16)(mt->angle+(INT16)((FixedInt(FixedDiv(temp*FRACUNIT, 360*(FRACUNIT/256))))<<8)); P_SpawnHoopsAndRings(mt); diff --git a/src/m_cheat.h b/src/m_cheat.h index 951c7a16a..d50ddc119 100644 --- a/src/m_cheat.h +++ b/src/m_cheat.h @@ -28,7 +28,7 @@ void cht_Init(void); void Command_ObjectPlace_f(void); void Command_Writethings_f(void); -extern consvar_t cv_opflags, cv_mapthingnum, cv_speed; +extern consvar_t cv_opflags, cv_ophoopflags, cv_mapthingnum, cv_speed; //extern consvar_t cv_snapto, cv_grid; extern boolean objectplacing; From fd6f5c7ef0409d957cf7ad1f838552cac81aa794 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Mon, 26 Mar 2018 01:40:15 -0400 Subject: [PATCH 03/79] Nightserize: Reset player another/flyangle on new mare --- src/p_user.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_user.c b/src/p_user.c index f422e9b65..bdc493a75 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -758,6 +758,8 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->marebegunat = leveltime; player->rings = 0; + player->flyangle = 0; + player->anotherflyangle = 0; } else { From 58089255ef1c7438066e10dcb1e3ecd029508ecd Mon Sep 17 00:00:00 2001 From: mazmazz Date: Mon, 26 Mar 2018 01:54:38 -0400 Subject: [PATCH 04/79] Reset player->another/flyangle on any Nightserize or DeNightserize --- src/p_user.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index bdc493a75..6a87f8ca3 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -586,6 +586,8 @@ static void P_DeNightserizePlayer(player_t *player) player->climbing = 0; player->mo->fuse = 0; player->speed = 0; + player->flyangle = 0; + player->anotherflyangle = 0; P_SetTarget(&player->mo->target, NULL); P_SetTarget(&player->axis1, P_SetTarget(&player->axis2, NULL)); @@ -659,6 +661,8 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->speed = 0; player->climbing = 0; player->secondjump = 0; + player->flyangle = 0; + player->anotherflyangle = 0; player->powers[pw_shield] = SH_NONE; player->powers[pw_super] = 0; @@ -758,8 +762,6 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->marebegunat = leveltime; player->rings = 0; - player->flyangle = 0; - player->anotherflyangle = 0; } else { From e28d30e1eec2e42898ec36fcaaeb599685dd296b Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 30 Mar 2018 14:22:59 -0400 Subject: [PATCH 05/79] Nights LE checkpoint --- src/p_map.c | 7 +++++-- src/p_spec.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ src/p_user.c | 4 ++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/p_map.c b/src/p_map.c index 6d1760596..8c58570c8 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -905,7 +905,7 @@ static boolean PIT_CheckThing(mobj_t *thing) P_SetTarget(&thing->target, tmthing); } - // Respawn rings and items + // NiGHTS lap logic if ((tmthing->type == MT_NIGHTSDRONE || thing->type == MT_NIGHTSDRONE) && (tmthing->player || thing->player)) { @@ -921,8 +921,11 @@ static boolean PIT_CheckThing(mobj_t *thing) ^ (droneobj->extravalue1 >= 90 && droneobj->extravalue1 <= 270) )) { - // Reload all the fancy ring stuff! + // Respawn rings and items P_ReloadRings(); + + // \todo store current lap in player mobj + P_RunNightsLapExecutors(pl->mo); } droneobj->extravalue1 = pl->anotherflyangle; droneobj->extravalue2 = (INT32)leveltime + TICRATE; diff --git a/src/p_spec.c b/src/p_spec.c index 0b005baff..b26fab821 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -5487,6 +5487,48 @@ static void P_RunLevelLoadExecutors(void) } } +// +// P_RunNightserizeExecutors +// +static void P_RunNightserizeExecutors(mobj_t *actor) +{ + size_t i; + + for (i = 0; i < numlines; i++) + { + if (lines[i].special == 323 || lines[i].special == 324) + P_RunTriggerLinedef(&lines[i], actor, NULL); + } +} + +// +// P_RunDeNightserizeExecutors +// +static void P_RunDeNightserizeExecutors(mobj_t *actor) +{ + size_t i; + + for (i = 0; i < numlines; i++) + { + if (lines[i].special == 325 || lines[i].special == 326) + P_RunTriggerLinedef(&lines[i], actor, NULL); + } +} + +// +// P_RunNightsLapExecutors +// +static void P_RunNightsLapExecutors(mobj_t *actor) +{ + size_t i; + + for (i = 0; i < numlines; i++) + { + if (lines[i].special == 327 || lines[i].special == 328) + P_RunTriggerLinedef(&lines[i], actor, NULL); + } +} + /** Before things are loaded, initialises certain stuff in case they're needed * by P_ResetDynamicSlopes or P_LoadThings. This was split off from * P_SpawnSpecials, in case you couldn't tell. @@ -6408,6 +6450,12 @@ void P_SpawnSpecials(INT32 fromnetsave) } break; + // NiGHTS trigger executors + case 323: + case 324: + case 325: + break; + case 399: // Linedef execute on map load // This is handled in P_RunLevelLoadExecutors. break; diff --git a/src/p_user.c b/src/p_user.c index f422e9b65..f4be0cf59 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -636,6 +636,8 @@ static void P_DeNightserizePlayer(player_t *player) // Restore from drowning music P_RestoreMusic(player); + + P_RunDeNightserizeExecutors(); } // @@ -771,6 +773,8 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) } player->powers[pw_carry] = CR_NIGHTSMODE; + + P_RunNightserizeExecutors(player->mo); } pflags_t P_GetJumpFlags(player_t *player) From 224eb980bba4151516a5fdebe2212e048124c21f Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 30 Mar 2018 14:36:49 -0400 Subject: [PATCH 06/79] Add player_t marelap and marebonuslap, with logic --- src/d_player.h | 2 ++ src/lua_playerlib.c | 8 ++++++++ src/p_map.c | 17 ++++++++++++----- src/p_saveg.c | 4 ++++ src/p_setup.c | 1 + src/p_user.c | 2 ++ 6 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index e1350fe67..f919eddb0 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -455,6 +455,8 @@ typedef struct player_s boolean bonustime; // Capsule destroyed, now it's bonus time! mobj_t *capsule; // Go inside the capsule UINT8 mare; // Current mare + UINT8 marelap; // Current mare lap + UINT8 marebonuslap; // Current mare lap starting from bonus time // Statistical purposes. tic_t marebegunat; // Leveltime when mare begun diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 12b2646d0..9b5aa6fdc 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -288,6 +288,10 @@ static int player_get(lua_State *L) LUA_PushUserdata(L, plr->capsule, META_MOBJ); else if (fastcmp(field,"mare")) lua_pushinteger(L, plr->mare); + else if (fastcmp(field,"marelap")) + lua_pushinteger(L, plr->marelap); + else if (fastcmp(field,"marebonuslap")) + lua_pushinteger(L, plr->marebonuslap); else if (fastcmp(field,"marebegunat")) lua_pushinteger(L, plr->marebegunat); else if (fastcmp(field,"startedtime")) @@ -564,6 +568,10 @@ static int player_set(lua_State *L) } else if (fastcmp(field,"mare")) plr->mare = (UINT8)luaL_checkinteger(L, 3); + else if (fastcmp(field,"marelap")) + plr->marelap = (UINT8)luaL_checkinteger(L, 3); + else if (fastcmp(field,"marebonuslap")) + plr->marebonuslap = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"marebegunat")) plr->marebegunat = (tic_t)luaL_checkinteger(L, 3); else if (fastcmp(field,"startedtime")) diff --git a/src/p_map.c b/src/p_map.c index 6d1760596..565680e22 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -905,24 +905,31 @@ static boolean PIT_CheckThing(mobj_t *thing) P_SetTarget(&thing->target, tmthing); } - // Respawn rings and items + // NiGHTS lap logic if ((tmthing->type == MT_NIGHTSDRONE || thing->type == MT_NIGHTSDRONE) && (tmthing->player || thing->player)) { mobj_t *droneobj = (tmthing->type == MT_NIGHTSDRONE) ? tmthing : thing; player_t *pl = (droneobj == thing) ? tmthing->player : thing->player; - // Must be in bonus time, and must be NiGHTS, must wait about a second + // Must be NiGHTS, must wait about a second // must be flying in the SAME DIRECTION as the last time you came through. // not (your direction) xor (stored direction) // In other words, you can't u-turn and respawn rings near the drone. - if (pl->bonustime && (pl->powers[pw_carry] == CR_NIGHTSMODE) && (INT32)leveltime > droneobj->extravalue2 && ( + if ((pl->powers[pw_carry] == CR_NIGHTSMODE) && (INT32)leveltime > droneobj->extravalue2 && ( !(pl->anotherflyangle >= 90 && pl->anotherflyangle <= 270) ^ (droneobj->extravalue1 >= 90 && droneobj->extravalue1 <= 270) )) { - // Reload all the fancy ring stuff! - P_ReloadRings(); + pl->marelap++; + + if (pl->bonustime) + { + pl->marebonuslap++; + + // Respawn rings and items + P_ReloadRings(); + } } droneobj->extravalue1 = pl->anotherflyangle; droneobj->extravalue2 = (INT32)leveltime + TICRATE; diff --git a/src/p_saveg.c b/src/p_saveg.c index 029df08f4..59d1775a8 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -197,6 +197,8 @@ static void P_NetArchivePlayers(void) WRITEUINT8(save_p, players[i].drilldelay); WRITEUINT8(save_p, players[i].bonustime); WRITEUINT8(save_p, players[i].mare); + WRITEUINT8(save_p, players[i].marelap); + WRITEUINT8(save_p, players[i].marebonuslap); WRITEUINT32(save_p, players[i].marebegunat); WRITEUINT32(save_p, players[i].startedtime); @@ -384,6 +386,8 @@ static void P_NetUnArchivePlayers(void) players[i].drilldelay = READUINT8(save_p); players[i].bonustime = (boolean)READUINT8(save_p); players[i].mare = READUINT8(save_p); + players[i].marelap = READUINT8(save_p); + players[i].marebonuslap = READUINT8(save_p); players[i].marebegunat = READUINT32(save_p); players[i].startedtime = READUINT32(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index a9fc57652..f3e92abce 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2350,6 +2350,7 @@ static void P_LevelInitStuff(void) players[i].linkcount = players[i].linktimer = 0; players[i].flyangle = players[i].anotherflyangle = 0; players[i].nightstime = players[i].mare = 0; + players[i].marelap = 0; players[i].marebonuslap = 0; P_SetTarget(&players[i].capsule, NULL); players[i].drillmeter = 40*20; diff --git a/src/p_user.c b/src/p_user.c index f422e9b65..c8f364193 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -399,6 +399,8 @@ boolean P_TransferToNextMare(player_t *player) CONS_Debug(DBG_NIGHTS, "Mare is %d\n", mare); player->mare = mare; + player->marelap = 0; + player->marebonuslap = 0; // scan the thinkers // to find the closest axis point From 02d525ae15caf8e5728ddecdf89c7d5d1722b926 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 30 Mar 2018 14:36:49 -0400 Subject: [PATCH 07/79] Add player_t marelap and marebonuslap, with logic --- src/d_player.h | 2 ++ src/g_game.c | 2 ++ src/lua_playerlib.c | 8 ++++++++ src/p_map.c | 17 ++++++++++++----- src/p_saveg.c | 4 ++++ src/p_setup.c | 1 + src/p_user.c | 4 ++++ 7 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index e1350fe67..f919eddb0 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -455,6 +455,8 @@ typedef struct player_s boolean bonustime; // Capsule destroyed, now it's bonus time! mobj_t *capsule; // Go inside the capsule UINT8 mare; // Current mare + UINT8 marelap; // Current mare lap + UINT8 marebonuslap; // Current mare lap starting from bonus time // Statistical purposes. tic_t marebegunat; // Leveltime when mare begun diff --git a/src/g_game.c b/src/g_game.c index 9db9c413b..a2e9b881a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2230,6 +2230,8 @@ void G_PlayerReborn(INT32 player) if (p->mare == 255) p->mare = 0; + p->marelap = p->marebonuslap = 0; + // Check to make sure their color didn't change somehow... if (G_GametypeHasTeams()) { diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 12b2646d0..9b5aa6fdc 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -288,6 +288,10 @@ static int player_get(lua_State *L) LUA_PushUserdata(L, plr->capsule, META_MOBJ); else if (fastcmp(field,"mare")) lua_pushinteger(L, plr->mare); + else if (fastcmp(field,"marelap")) + lua_pushinteger(L, plr->marelap); + else if (fastcmp(field,"marebonuslap")) + lua_pushinteger(L, plr->marebonuslap); else if (fastcmp(field,"marebegunat")) lua_pushinteger(L, plr->marebegunat); else if (fastcmp(field,"startedtime")) @@ -564,6 +568,10 @@ static int player_set(lua_State *L) } else if (fastcmp(field,"mare")) plr->mare = (UINT8)luaL_checkinteger(L, 3); + else if (fastcmp(field,"marelap")) + plr->marelap = (UINT8)luaL_checkinteger(L, 3); + else if (fastcmp(field,"marebonuslap")) + plr->marebonuslap = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"marebegunat")) plr->marebegunat = (tic_t)luaL_checkinteger(L, 3); else if (fastcmp(field,"startedtime")) diff --git a/src/p_map.c b/src/p_map.c index 6d1760596..565680e22 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -905,24 +905,31 @@ static boolean PIT_CheckThing(mobj_t *thing) P_SetTarget(&thing->target, tmthing); } - // Respawn rings and items + // NiGHTS lap logic if ((tmthing->type == MT_NIGHTSDRONE || thing->type == MT_NIGHTSDRONE) && (tmthing->player || thing->player)) { mobj_t *droneobj = (tmthing->type == MT_NIGHTSDRONE) ? tmthing : thing; player_t *pl = (droneobj == thing) ? tmthing->player : thing->player; - // Must be in bonus time, and must be NiGHTS, must wait about a second + // Must be NiGHTS, must wait about a second // must be flying in the SAME DIRECTION as the last time you came through. // not (your direction) xor (stored direction) // In other words, you can't u-turn and respawn rings near the drone. - if (pl->bonustime && (pl->powers[pw_carry] == CR_NIGHTSMODE) && (INT32)leveltime > droneobj->extravalue2 && ( + if ((pl->powers[pw_carry] == CR_NIGHTSMODE) && (INT32)leveltime > droneobj->extravalue2 && ( !(pl->anotherflyangle >= 90 && pl->anotherflyangle <= 270) ^ (droneobj->extravalue1 >= 90 && droneobj->extravalue1 <= 270) )) { - // Reload all the fancy ring stuff! - P_ReloadRings(); + pl->marelap++; + + if (pl->bonustime) + { + pl->marebonuslap++; + + // Respawn rings and items + P_ReloadRings(); + } } droneobj->extravalue1 = pl->anotherflyangle; droneobj->extravalue2 = (INT32)leveltime + TICRATE; diff --git a/src/p_saveg.c b/src/p_saveg.c index 029df08f4..59d1775a8 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -197,6 +197,8 @@ static void P_NetArchivePlayers(void) WRITEUINT8(save_p, players[i].drilldelay); WRITEUINT8(save_p, players[i].bonustime); WRITEUINT8(save_p, players[i].mare); + WRITEUINT8(save_p, players[i].marelap); + WRITEUINT8(save_p, players[i].marebonuslap); WRITEUINT32(save_p, players[i].marebegunat); WRITEUINT32(save_p, players[i].startedtime); @@ -384,6 +386,8 @@ static void P_NetUnArchivePlayers(void) players[i].drilldelay = READUINT8(save_p); players[i].bonustime = (boolean)READUINT8(save_p); players[i].mare = READUINT8(save_p); + players[i].marelap = READUINT8(save_p); + players[i].marebonuslap = READUINT8(save_p); players[i].marebegunat = READUINT32(save_p); players[i].startedtime = READUINT32(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index a9fc57652..f3e92abce 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2350,6 +2350,7 @@ static void P_LevelInitStuff(void) players[i].linkcount = players[i].linktimer = 0; players[i].flyangle = players[i].anotherflyangle = 0; players[i].nightstime = players[i].mare = 0; + players[i].marelap = 0; players[i].marebonuslap = 0; P_SetTarget(&players[i].capsule, NULL); players[i].drillmeter = 40*20; diff --git a/src/p_user.c b/src/p_user.c index f422e9b65..a6fe9e315 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -399,6 +399,8 @@ boolean P_TransferToNextMare(player_t *player) CONS_Debug(DBG_NIGHTS, "Mare is %d\n", mare); player->mare = mare; + player->marelap = 0; + player->marebonuslap = 0; // scan the thinkers // to find the closest axis point @@ -586,6 +588,8 @@ static void P_DeNightserizePlayer(player_t *player) player->climbing = 0; player->mo->fuse = 0; player->speed = 0; + player->marelap = 0; + player->marebonuslap = 0; P_SetTarget(&player->mo->target, NULL); P_SetTarget(&player->axis1, P_SetTarget(&player->axis2, NULL)); From 6e10b27f38fe30b0b4368f0df99792c4429b994b Mon Sep 17 00:00:00 2001 From: mazmazz Date: Sat, 31 Mar 2018 10:11:27 -0400 Subject: [PATCH 08/79] Initial checkpoint for NiGHTS LE triggers --- src/p_spec.c | 147 ++++++++++++++++++++++++++++++++++++--------------- src/p_spec.h | 3 ++ src/p_user.c | 7 +-- 3 files changed, 112 insertions(+), 45 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index b26fab821..a23374c13 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1374,6 +1374,48 @@ void P_ChangeSectorTag(UINT32 sector, INT16 newtag) } } +// +// P_RunNightserizeExecutors +// +void P_RunNightserizeExecutors(mobj_t *actor) +{ + size_t i; + + for (i = 0; i < numlines; i++) + { + if (lines[i].special == 323 || lines[i].special == 324) + P_RunTriggerLinedef(&lines[i], actor, NULL); + } +} + +// +// P_RunDeNightserizeExecutors +// +void P_RunDeNightserizeExecutors(mobj_t *actor) +{ + size_t i; + + for (i = 0; i < numlines; i++) + { + if (lines[i].special == 325 || lines[i].special == 326) + P_RunTriggerLinedef(&lines[i], actor, NULL); + } +} + +// +// P_RunNightsLapExecutors +// +void P_RunNightsLapExecutors(mobj_t *actor) +{ + size_t i; + + for (i = 0; i < numlines; i++) + { + if (lines[i].special == 327 || lines[i].special == 328) + P_RunTriggerLinedef(&lines[i], actor, NULL); + } +} + /** Hashes the sector tags across the sectors and linedefs. * * \sa P_FindSectorFromTag, P_ChangeSectorTag @@ -1667,6 +1709,69 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller return false; } break; + case 323: // each time + case 324: // once + { // nightserize + INT32 mareinput = sides[triggerline->sidenum[0]].textureoffset>>FRACBITS; + INT32 lapinput = sides[triggerline->sidenum[0]].rowoffset>>FRACBITS; + BOOL ltemare = triggerline->flags & ML_NOCLIMB; + BOOL gtemare = triggerline->flags & ML_BLOCKMONSTERS; + BOOL ltelap = triggerline->flags & ML_EFFECT1; + BOOL gtelap = triggerline->flags & ML_EFFECT2; + BOOL lapfrombonustime = triggerline->flags & ML_EFFECT3; + BOOL doglobal = triggerline->flags & ML_EFFECT4; + BOOL donomares = triggerline->flags & ML_EFFECT5; // unused for nights lap + BOOL fromnonights = triggerline->flags & ML_BOUNCY; // unused for nights lap + + CONS_Printf("Trigger Nightserize %i\n", triggerline->special); + CONS_Printf("M-I %i | M-V %i | M-GTE %i | M-LTE %i | L-I %i | L-V %i/%i | L-GTE %i | L-LTE %i\n" + , mareinput, actor->player->mare, gtemare, ltemare + , lapinput, actor->player->marebonuslap, actor->player->marelap, gtelap, ltelap); + CONS_Printf("L-Bonus %i | Global %i | No Mares %i | From No NiGHTS %i\n", lapfrombonustime, doglobal, donomares, fromnonights); + } + break; + case 325: // each time + case 326: // once + { // denightserize + INT32 mareinput = sides[triggerline->sidenum[0]].textureoffset>>FRACBITS; + INT32 lapinput = sides[triggerline->sidenum[0]].rowoffset>>FRACBITS; + BOOL ltemare = triggerline->flags & ML_NOCLIMB; + BOOL gtemare = triggerline->flags & ML_BLOCKMONSTERS; + BOOL ltelap = triggerline->flags & ML_EFFECT1; + BOOL gtelap = triggerline->flags & ML_EFFECT2; + BOOL lapfrombonustime = triggerline->flags & ML_EFFECT3; + BOOL doglobal = triggerline->flags & ML_EFFECT4; + BOOL donomares = triggerline->flags & ML_EFFECT5; // all no nights // unused for nights lap + BOOL fromnonights = triggerline->flags & ML_BOUNCY; // unused for nights lap + + CONS_Printf("Trigger DeNightserize %i\n", triggerline->special); + CONS_Printf("M-I %i | M-V %i | M-GTE %i | M-LTE %i | L-I %i | L-V %i/%i | L-GTE %i | L-LTE %i\n" + , mareinput, actor->player->mare, gtemare, ltemare + , lapinput, actor->player->marebonuslap, actor->player->marelap, gtelap, ltelap); + CONS_Printf("L-Bonus %i | Global %i | No Mares %i | From No NiGHTS %i\n", lapfrombonustime, doglobal, donomares, fromnonights); + } + break; + case 327: // each time + case 328: // once + { // nights lap + INT32 mareinput = sides[triggerline->sidenum[0]].textureoffset>>FRACBITS; + INT32 lapinput = sides[triggerline->sidenum[0]].rowoffset>>FRACBITS; + BOOL ltemare = triggerline->flags & ML_NOCLIMB; + BOOL gtemare = triggerline->flags & ML_BLOCKMONSTERS; + BOOL ltelap = triggerline->flags & ML_EFFECT1; + BOOL gtelap = triggerline->flags & ML_EFFECT2; + BOOL lapfrombonustime = triggerline->flags & ML_EFFECT3; + BOOL doglobal = triggerline->flags & ML_EFFECT4; + BOOL donomares = triggerline->flags & ML_EFFECT5; // unused for nights lap + BOOL fromnonights = triggerline->flags & ML_BOUNCY; // unused for nights lap + + CONS_Printf("Trigger NiGHTS Lap %i\n", triggerline->special); + CONS_Printf("M-I %i | M-V %i | M-GTE %i | M-LTE %i | L-I %i | L-V %i/%i | L-GTE %i | L-LTE %i\n" + , mareinput, actor->player->mare, gtemare, ltemare + , lapinput, actor->player->marebonuslap, actor->player->marelap, gtelap, ltelap); + CONS_Printf("L-Bonus %i | Global %i | No Mares %i | From No NiGHTS %i\n", lapfrombonustime, doglobal, donomares, fromnonights); + } + break; default: break; } @@ -5487,48 +5592,6 @@ static void P_RunLevelLoadExecutors(void) } } -// -// P_RunNightserizeExecutors -// -static void P_RunNightserizeExecutors(mobj_t *actor) -{ - size_t i; - - for (i = 0; i < numlines; i++) - { - if (lines[i].special == 323 || lines[i].special == 324) - P_RunTriggerLinedef(&lines[i], actor, NULL); - } -} - -// -// P_RunDeNightserizeExecutors -// -static void P_RunDeNightserizeExecutors(mobj_t *actor) -{ - size_t i; - - for (i = 0; i < numlines; i++) - { - if (lines[i].special == 325 || lines[i].special == 326) - P_RunTriggerLinedef(&lines[i], actor, NULL); - } -} - -// -// P_RunNightsLapExecutors -// -static void P_RunNightsLapExecutors(mobj_t *actor) -{ - size_t i; - - for (i = 0; i < numlines; i++) - { - if (lines[i].special == 327 || lines[i].special == 328) - P_RunTriggerLinedef(&lines[i], actor, NULL); - } -} - /** Before things are loaded, initialises certain stuff in case they're needed * by P_ResetDynamicSlopes or P_LoadThings. This was split off from * P_SpawnSpecials, in case you couldn't tell. diff --git a/src/p_spec.h b/src/p_spec.h index c4e05e072..76ddc237c 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -66,6 +66,9 @@ void P_SwitchWeather(INT32 weathernum); boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller); void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller); void P_ChangeSectorTag(UINT32 sector, INT16 newtag); +void P_RunNightserizeExecutors(mobj_t *actor); +void P_RunDeNightserizeExecutors(mobj_t *actor); +void P_RunNightsLapExecutors(mobj_t *actor); ffloor_t *P_GetFFloorByID(sector_t *sec, UINT16 id); diff --git a/src/p_user.c b/src/p_user.c index 54411b25a..befd91f63 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -639,7 +639,7 @@ static void P_DeNightserizePlayer(player_t *player) // Restore from drowning music P_RestoreMusic(player); - P_RunDeNightserizeExecutors(); + P_RunDeNightserizeExecutors(player->mo); } // @@ -774,9 +774,10 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->texttimer = (UINT8)(110 - timeinmap); } - player->powers[pw_carry] = CR_NIGHTSMODE; - + // Do this before setting CR_NIGHTSMODE so we can tell if player was non-NiGHTS P_RunNightserizeExecutors(player->mo); + + player->powers[pw_carry] = CR_NIGHTSMODE; } pflags_t P_GetJumpFlags(player_t *player) From 4d95019db583b0911eb873ecaecc66f4349fa374 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Sun, 1 Apr 2018 08:23:59 -0400 Subject: [PATCH 09/79] Implement logic branching for NiGHTS LE triggers and add Bonus Time trigger --- src/p_spec.c | 179 +++++++++++++++++++++++++++++++++++---------------- src/p_spec.h | 1 + src/p_user.c | 1 + 3 files changed, 127 insertions(+), 54 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index a23374c13..9c3f3df7d 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1416,6 +1416,20 @@ void P_RunNightsLapExecutors(mobj_t *actor) } } +// +// P_RunNightsBonusTimeExecutors +// +void P_RunNightsBonusTimeExecutors(mobj_t *actor) +{ + size_t i; + + for (i = 0; i < numlines; i++) + { + if (lines[i].special == 329 || lines[i].special == 330) + P_RunTriggerLinedef(&lines[i], actor, NULL); + } +} + /** Hashes the sector tags across the sectors and linedefs. * * \sa P_FindSectorFromTag, P_ChangeSectorTag @@ -1709,69 +1723,117 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller return false; } break; - case 323: // each time - case 324: // once + + // Let's do the NiGHTS triggers in one code block; they mostly have the same logic + case 323: // nightserize - each time + case 324: // nightserize - once + case 325: // denightserize - each time + case 326: // denightserize - once + case 327: // nights lap - each time + case 328: // nights lap - once + case 329: // nights bonus time - each time + case 330: // nights bonus time - once { // nightserize - INT32 mareinput = sides[triggerline->sidenum[0]].textureoffset>>FRACBITS; - INT32 lapinput = sides[triggerline->sidenum[0]].rowoffset>>FRACBITS; + UINT8 inputmare = max(0, min(255, sides[triggerline->sidenum[0]].textureoffset>>FRACBITS)); + UINT8 inputlap = max(0, min(255, sides[triggerline->sidenum[0]].rowoffset>>FRACBITS)); + BOOL ltemare = triggerline->flags & ML_NOCLIMB; BOOL gtemare = triggerline->flags & ML_BLOCKMONSTERS; BOOL ltelap = triggerline->flags & ML_EFFECT1; BOOL gtelap = triggerline->flags & ML_EFFECT2; - BOOL lapfrombonustime = triggerline->flags & ML_EFFECT3; - BOOL doglobal = triggerline->flags & ML_EFFECT4; - BOOL donomares = triggerline->flags & ML_EFFECT5; // unused for nights lap - BOOL fromnonights = triggerline->flags & ML_BOUNCY; // unused for nights lap - CONS_Printf("Trigger Nightserize %i\n", triggerline->special); - CONS_Printf("M-I %i | M-V %i | M-GTE %i | M-LTE %i | L-I %i | L-V %i/%i | L-GTE %i | L-LTE %i\n" - , mareinput, actor->player->mare, gtemare, ltemare - , lapinput, actor->player->marebonuslap, actor->player->marelap, gtelap, ltelap); - CONS_Printf("L-Bonus %i | Global %i | No Mares %i | From No NiGHTS %i\n", lapfrombonustime, doglobal, donomares, fromnonights); + BOOL lapfrombonustime = triggerline->flags & ML_EFFECT3; + BOOL perglobalinverse = triggerline->flags & ML_DONTPEGBOTTOM; + BOOL perglobal = !(triggerline->flags & ML_EFFECT4) && !perglobalinverse; + + BOOL donomares = triggerline->flags & ML_BOUNCY; // nightserize: run at end of level (no mares) + BOOL fromnonights = triggerline->flags & ML_TFERLINE; // nightserize: from non-nights // denightserize: all players no nights + + UINT8 currentmare = UINT8_MAX; + UINT8 currentlap = UINT8_MAX; + + // Do early returns for Nightserize + if (specialtype >= 323 && specialtype <= 324) + { + // run only when no mares are found + if (donomares && P_FindLowestMare() != UINT8_MAX) + return false; + + // run only if player is nightserizing from non-nights + if (fromnonights) + { + if (!actor->player) + return false; + else if (actor->player->powers[pw_carry] == CR_NIGHTSMODE) + return false; + } + } + + // Get current mare and lap (and check early return for DeNightserize) + if (perglobal || perglobalinverse + || (specialtype >= 325 && specialtype <= 326 && fromnonights)) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + + // denightserize: run only if all players are not nights + if (specialtype >= 325 && specialtype <= 326 && fromnonights + && players[i].powers[pw_carry] == CR_NIGHTSMODE) + return false; + + UINT8 lap = lapfrombonustime ? players[i].marebonuslap : players[i].marelap; + + // get highest mare/lap of players + if (perglobal) + { + if (players[i].mare > currentmare || currentmare == UINT8_MAX) + { + currentmare = players[i].mare; + currentlap = UINT8_MAX; + } + if (players[i].mare == currentmare + && (lap > currentlap || currentlap == UINT8_MAX)) + currentlap = lap; + } + // get lowest mare/lap of players + else if (perglobalinverse) + { + if (players[i].mare < currentmare || currentmare == UINT8_MAX) + { + currentmare = players[i].mare; + currentlap = UINT8_MAX; + } + if (players[i].mare == currentmare + && (lap < currentlap || currentlap == UINT8_MAX)) + currentlap = lap; + } + } + } + + // get current mare/lap from triggering player + if (!perglobal && !perglobalinverse) + { + if (!actor->player) + return false; + currentmare = actor->player->mare; + currentlap = lapfrombonustime ? actor->player->marebonuslap : actor->player->marelap; + } + + // Compare current mare/lap to input mare/lap based on rules + if (!(specialtype >= 323 && specialtype <= 324 && donomares) // don't return false if donomares and we got this far + && ((ltemare && currentmare > inputmare) + || (gtemare && currentmare < inputmare) + || (!ltemare && !gtemare && currentmare != inputmare) + || (ltelap && currentlap > inputlap) + || (gtelap && currentlap < inputlap) + || (!ltelap && !gtelap && currentlap != inputlap)) + ) + return false; } break; - case 325: // each time - case 326: // once - { // denightserize - INT32 mareinput = sides[triggerline->sidenum[0]].textureoffset>>FRACBITS; - INT32 lapinput = sides[triggerline->sidenum[0]].rowoffset>>FRACBITS; - BOOL ltemare = triggerline->flags & ML_NOCLIMB; - BOOL gtemare = triggerline->flags & ML_BLOCKMONSTERS; - BOOL ltelap = triggerline->flags & ML_EFFECT1; - BOOL gtelap = triggerline->flags & ML_EFFECT2; - BOOL lapfrombonustime = triggerline->flags & ML_EFFECT3; - BOOL doglobal = triggerline->flags & ML_EFFECT4; - BOOL donomares = triggerline->flags & ML_EFFECT5; // all no nights // unused for nights lap - BOOL fromnonights = triggerline->flags & ML_BOUNCY; // unused for nights lap - CONS_Printf("Trigger DeNightserize %i\n", triggerline->special); - CONS_Printf("M-I %i | M-V %i | M-GTE %i | M-LTE %i | L-I %i | L-V %i/%i | L-GTE %i | L-LTE %i\n" - , mareinput, actor->player->mare, gtemare, ltemare - , lapinput, actor->player->marebonuslap, actor->player->marelap, gtelap, ltelap); - CONS_Printf("L-Bonus %i | Global %i | No Mares %i | From No NiGHTS %i\n", lapfrombonustime, doglobal, donomares, fromnonights); - } - break; - case 327: // each time - case 328: // once - { // nights lap - INT32 mareinput = sides[triggerline->sidenum[0]].textureoffset>>FRACBITS; - INT32 lapinput = sides[triggerline->sidenum[0]].rowoffset>>FRACBITS; - BOOL ltemare = triggerline->flags & ML_NOCLIMB; - BOOL gtemare = triggerline->flags & ML_BLOCKMONSTERS; - BOOL ltelap = triggerline->flags & ML_EFFECT1; - BOOL gtelap = triggerline->flags & ML_EFFECT2; - BOOL lapfrombonustime = triggerline->flags & ML_EFFECT3; - BOOL doglobal = triggerline->flags & ML_EFFECT4; - BOOL donomares = triggerline->flags & ML_EFFECT5; // unused for nights lap - BOOL fromnonights = triggerline->flags & ML_BOUNCY; // unused for nights lap - - CONS_Printf("Trigger NiGHTS Lap %i\n", triggerline->special); - CONS_Printf("M-I %i | M-V %i | M-GTE %i | M-LTE %i | L-I %i | L-V %i/%i | L-GTE %i | L-LTE %i\n" - , mareinput, actor->player->mare, gtemare, ltemare - , lapinput, actor->player->marebonuslap, actor->player->marelap, gtelap, ltelap); - CONS_Printf("L-Bonus %i | Global %i | No Mares %i | From No NiGHTS %i\n", lapfrombonustime, doglobal, donomares, fromnonights); - } - break; default: break; } @@ -1900,6 +1962,10 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller || specialtype == 318 // Unlockable trigger - Once || specialtype == 320 // Unlockable - Once || specialtype == 321 || specialtype == 322 // Trigger on X calls - Continuous + Each Time + || specialtype == 324 // Nightserize - Once + || specialtype == 326 // DeNightserize - Once + || specialtype == 328 // Nights lap - Once + || specialtype == 330 // Nights Bonus Time - Once || specialtype == 399) // Level Load triggerline->special = 0; // Clear it out @@ -6517,6 +6583,11 @@ void P_SpawnSpecials(INT32 fromnetsave) case 323: case 324: case 325: + case 326: + case 327: + case 328: + case 329: + case 330: break; case 399: // Linedef execute on map load diff --git a/src/p_spec.h b/src/p_spec.h index 76ddc237c..5bcf25b7b 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -69,6 +69,7 @@ void P_ChangeSectorTag(UINT32 sector, INT16 newtag); void P_RunNightserizeExecutors(mobj_t *actor); void P_RunDeNightserizeExecutors(mobj_t *actor); void P_RunNightsLapExecutors(mobj_t *actor); +void P_RunNightsBonusTimeExecutors(mobj_t *actor); ffloor_t *P_GetFFloorByID(sector_t *sec, UINT16 id); diff --git a/src/p_user.c b/src/p_user.c index befd91f63..4de9759c1 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -6031,6 +6031,7 @@ static void P_DoNiGHTSCapsule(player_t *player) if (playeringame[i] && players[i].mare == player->mare) P_SetTarget(&players[i].capsule, NULL); // Remove capsule from everyone now that it is dead! S_StartScreamSound(player->mo, sfx_ngdone); + P_RunNightsBonusTimeExecutors(player->mo); } } else From 630843d76e7e9736f41dc1e447aff8c5bb1c5f39 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Mon, 2 Apr 2018 08:11:28 -0400 Subject: [PATCH 10/79] Split NiGHTS LE trigger logic into P_CheckNightsTriggerLine This makes P_RunTriggerLinedef more readable because the logic is too different from the other triggers. Also replace BOOL with boolean --- src/p_spec.c | 213 +++++++++++++++++++++++++++------------------------ 1 file changed, 114 insertions(+), 99 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index 9c3f3df7d..4f6898658 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1513,6 +1513,118 @@ static void P_AddExecutorDelay(line_t *line, mobj_t *mobj, sector_t *sector) P_AddThinker(&e->thinker); } +/** Used by P_RunTriggerLinedef to check a NiGHTS trigger linedef's conditions + * + * \param triggerline Trigger linedef to check conditions for; should NEVER be NULL. + * \param actor Object initiating the action; should not be NULL. + * \sa P_RunTriggerLinedef + */ +static boolean P_CheckNightsTriggerLine(line_t *triggerline, mobj_t *actor) +{ + INT16 specialtype = triggerline->special; + size_t i; + + UINT8 inputmare = max(0, min(255, sides[triggerline->sidenum[0]].textureoffset>>FRACBITS)); + UINT8 inputlap = max(0, min(255, sides[triggerline->sidenum[0]].rowoffset>>FRACBITS)); + + boolean ltemare = triggerline->flags & ML_NOCLIMB; + boolean gtemare = triggerline->flags & ML_BLOCKMONSTERS; + boolean ltelap = triggerline->flags & ML_EFFECT1; + boolean gtelap = triggerline->flags & ML_EFFECT2; + + boolean lapfrombonustime = triggerline->flags & ML_EFFECT3; + boolean perglobalinverse = triggerline->flags & ML_DONTPEGBOTTOM; + boolean perglobal = !(triggerline->flags & ML_EFFECT4) && !perglobalinverse; + + boolean donomares = triggerline->flags & ML_BOUNCY; // nightserize: run at end of level (no mares) + boolean fromnonights = triggerline->flags & ML_TFERLINE; // nightserize: from non-nights // denightserize: all players no nights + + UINT8 currentmare = UINT8_MAX; + UINT8 currentlap = UINT8_MAX; + + // Do early returns for Nightserize + if (specialtype >= 323 && specialtype <= 324) + { + // run only when no mares are found + if (donomares && P_FindLowestMare() != UINT8_MAX) + return false; + + // run only if player is nightserizing from non-nights + if (fromnonights) + { + if (!actor->player) + return false; + else if (actor->player->powers[pw_carry] == CR_NIGHTSMODE) + return false; + } + } + + // Get current mare and lap (and check early return for DeNightserize) + if (perglobal || perglobalinverse + || (specialtype >= 325 && specialtype <= 326 && fromnonights)) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + + // denightserize: run only if all players are not nights + if (specialtype >= 325 && specialtype <= 326 && fromnonights + && players[i].powers[pw_carry] == CR_NIGHTSMODE) + return false; + + UINT8 lap = lapfrombonustime ? players[i].marebonuslap : players[i].marelap; + + // get highest mare/lap of players + if (perglobal) + { + if (players[i].mare > currentmare || currentmare == UINT8_MAX) + { + currentmare = players[i].mare; + currentlap = UINT8_MAX; + } + if (players[i].mare == currentmare + && (lap > currentlap || currentlap == UINT8_MAX)) + currentlap = lap; + } + // get lowest mare/lap of players + else if (perglobalinverse) + { + if (players[i].mare < currentmare || currentmare == UINT8_MAX) + { + currentmare = players[i].mare; + currentlap = UINT8_MAX; + } + if (players[i].mare == currentmare + && (lap < currentlap || currentlap == UINT8_MAX)) + currentlap = lap; + } + } + } + + // get current mare/lap from triggering player + if (!perglobal && !perglobalinverse) + { + if (!actor->player) + return false; + currentmare = actor->player->mare; + currentlap = lapfrombonustime ? actor->player->marebonuslap : actor->player->marelap; + } + + // Compare current mare/lap to input mare/lap based on rules + if (!(specialtype >= 323 && specialtype <= 324 && donomares) // don't return false if donomares and we got this far + && ((ltemare && currentmare > inputmare) + || (gtemare && currentmare < inputmare) + || (!ltemare && !gtemare && currentmare != inputmare) + || (ltelap && currentlap > inputlap) + || (gtelap && currentlap < inputlap) + || (!ltelap && !gtelap && currentlap != inputlap)) + ) + return false; + + return true; +} + /** Used by P_LinedefExecute to check a trigger linedef's conditions * The linedef executor specials in the trigger linedef's sector are run if all conditions are met. * Return false cancels P_LinedefExecute, this happens if a condition is not met. @@ -1733,105 +1845,8 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller case 328: // nights lap - once case 329: // nights bonus time - each time case 330: // nights bonus time - once - { // nightserize - UINT8 inputmare = max(0, min(255, sides[triggerline->sidenum[0]].textureoffset>>FRACBITS)); - UINT8 inputlap = max(0, min(255, sides[triggerline->sidenum[0]].rowoffset>>FRACBITS)); - - BOOL ltemare = triggerline->flags & ML_NOCLIMB; - BOOL gtemare = triggerline->flags & ML_BLOCKMONSTERS; - BOOL ltelap = triggerline->flags & ML_EFFECT1; - BOOL gtelap = triggerline->flags & ML_EFFECT2; - - BOOL lapfrombonustime = triggerline->flags & ML_EFFECT3; - BOOL perglobalinverse = triggerline->flags & ML_DONTPEGBOTTOM; - BOOL perglobal = !(triggerline->flags & ML_EFFECT4) && !perglobalinverse; - - BOOL donomares = triggerline->flags & ML_BOUNCY; // nightserize: run at end of level (no mares) - BOOL fromnonights = triggerline->flags & ML_TFERLINE; // nightserize: from non-nights // denightserize: all players no nights - - UINT8 currentmare = UINT8_MAX; - UINT8 currentlap = UINT8_MAX; - - // Do early returns for Nightserize - if (specialtype >= 323 && specialtype <= 324) - { - // run only when no mares are found - if (donomares && P_FindLowestMare() != UINT8_MAX) - return false; - - // run only if player is nightserizing from non-nights - if (fromnonights) - { - if (!actor->player) - return false; - else if (actor->player->powers[pw_carry] == CR_NIGHTSMODE) - return false; - } - } - - // Get current mare and lap (and check early return for DeNightserize) - if (perglobal || perglobalinverse - || (specialtype >= 325 && specialtype <= 326 && fromnonights)) - { - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - - // denightserize: run only if all players are not nights - if (specialtype >= 325 && specialtype <= 326 && fromnonights - && players[i].powers[pw_carry] == CR_NIGHTSMODE) - return false; - - UINT8 lap = lapfrombonustime ? players[i].marebonuslap : players[i].marelap; - - // get highest mare/lap of players - if (perglobal) - { - if (players[i].mare > currentmare || currentmare == UINT8_MAX) - { - currentmare = players[i].mare; - currentlap = UINT8_MAX; - } - if (players[i].mare == currentmare - && (lap > currentlap || currentlap == UINT8_MAX)) - currentlap = lap; - } - // get lowest mare/lap of players - else if (perglobalinverse) - { - if (players[i].mare < currentmare || currentmare == UINT8_MAX) - { - currentmare = players[i].mare; - currentlap = UINT8_MAX; - } - if (players[i].mare == currentmare - && (lap < currentlap || currentlap == UINT8_MAX)) - currentlap = lap; - } - } - } - - // get current mare/lap from triggering player - if (!perglobal && !perglobalinverse) - { - if (!actor->player) - return false; - currentmare = actor->player->mare; - currentlap = lapfrombonustime ? actor->player->marebonuslap : actor->player->marelap; - } - - // Compare current mare/lap to input mare/lap based on rules - if (!(specialtype >= 323 && specialtype <= 324 && donomares) // don't return false if donomares and we got this far - && ((ltemare && currentmare > inputmare) - || (gtemare && currentmare < inputmare) - || (!ltemare && !gtemare && currentmare != inputmare) - || (ltelap && currentlap > inputlap) - || (gtelap && currentlap < inputlap) - || (!ltelap && !gtelap && currentlap != inputlap)) - ) - return false; - } + if (!P_CheckNightsTriggerLine(triggerline, actor)) + return false; break; default: From 2138706d38f4a7fc62bcccfb4d672c020c5f0841 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Mon, 2 Apr 2018 08:13:53 -0400 Subject: [PATCH 11/79] Slight fixes for NiGHTS LE Trigger logic --- src/p_spec.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index 4f6898658..fe9f8b18c 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1549,6 +1549,10 @@ static boolean P_CheckNightsTriggerLine(line_t *triggerline, mobj_t *actor) if (donomares && P_FindLowestMare() != UINT8_MAX) return false; + // run only if there is a mare present + if (!donomares && P_FindLowestMare() == UINT8_MAX) + return false; + // run only if player is nightserizing from non-nights if (fromnonights) { @@ -1601,9 +1605,8 @@ static boolean P_CheckNightsTriggerLine(line_t *triggerline, mobj_t *actor) } } } - // get current mare/lap from triggering player - if (!perglobal && !perglobalinverse) + else if (!perglobal && !perglobalinverse) { if (!actor->player) return false; From e5feb0552f90b328ac9d7230fcbe2554858d7767 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Mon, 2 Apr 2018 08:36:33 -0400 Subject: [PATCH 12/79] For NiGHTS OP Hoop, make mt->options XOR cv_opflags more SRB2-like --- src/m_cheat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_cheat.c b/src/m_cheat.c index a1adf71eb..432b9b99e 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -1002,7 +1002,7 @@ void OP_NightsObjectplace(player_t *player) temp += 90; temp %= 360; - mt->options = (mt->options ^ (UINT16)cv_opflags.value) | (UINT16)cv_ophoopflags.value; + mt->options = (mt->options & ~(UINT16)cv_opflags.value) | (UINT16)cv_ophoopflags.value; mt->angle = (INT16)(mt->angle+(INT16)((FixedInt(FixedDiv(temp*FRACUNIT, 360*(FRACUNIT/256))))<<8)); P_SpawnHoopsAndRings(mt); From 7a14d7dc181626c7f409c7939fe7d025f8509908 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Mon, 2 Apr 2018 10:02:16 -0400 Subject: [PATCH 13/79] Dumb stupid hack where NiGHTS faces you upon Nightserizing Because I can't figure out how to tell which player angle is "forward" in an axis track. --- src/p_user.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/p_user.c b/src/p_user.c index f422e9b65..24c3f1249 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -770,6 +770,11 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->texttimer = (UINT8)(110 - timeinmap); } + // make NiGHTS face you only upon Nightserizing (w/ attitude!) + // calculate player->angle_pos ourselves because it won't be set the first time + if (player->mo->target) + player->mo->angle = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y); + player->powers[pw_carry] = CR_NIGHTSMODE; } From 53d20c635e8fa3211c992da0046314cabe42f6ae Mon Sep 17 00:00:00 2001 From: mazmazz Date: Mon, 2 Apr 2018 15:21:19 -0400 Subject: [PATCH 14/79] Make NiGHTS face forward or backward upon Nightserizing --- src/p_user.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 24c3f1249..8f1d2f380 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -770,10 +770,14 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->texttimer = (UINT8)(110 - timeinmap); } - // make NiGHTS face you only upon Nightserizing (w/ attitude!) + // force NiGHTS to face forward or backward // calculate player->angle_pos ourselves because it won't be set the first time if (player->mo->target) - player->mo->angle = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y); + player->mo->angle = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y) + + ((player->mo->target->flags2 & MF2_AMBUSH) ? // if axis is invert, take the opposite right angle + (player->flyangle > 90 && player->flyangle < 270 ? ANGLE_90 : -ANGLE_90) + : (player->flyangle > 90 && player->flyangle < 270 ? -ANGLE_90 : ANGLE_90) + ); player->powers[pw_carry] = CR_NIGHTSMODE; } From 91b9e5308e93b8af78da525f3306caa2e5dae95c Mon Sep 17 00:00:00 2001 From: Digiku Date: Sat, 7 Apr 2018 06:28:31 -0400 Subject: [PATCH 15/79] Fix whitespace weirdness --- src/p_user.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 19ddde833..d59b4ec15 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -777,10 +777,9 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) // force NiGHTS to face forward or backward if (player->mo->target) player->mo->angle = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y) // player->angle_pos, won't be set on first instance - + ((player->mo->target->flags2 & MF2_AMBUSH) ? // if axis is invert, take the opposite right angle - (player->flyangle > 90 && player->flyangle < 270 ? ANGLE_90 : -ANGLE_90) - : (player->flyangle > 90 && player->flyangle < 270 ? -ANGLE_90 : ANGLE_90) - ); + + ((player->mo->target->flags2 & MF2_AMBUSH) ? // if axis is invert, take the opposite right angle + (player->flyangle > 90 && player->flyangle < 270 ? ANGLE_90 : -ANGLE_90) + : (player->flyangle > 90 && player->flyangle < 270 ? -ANGLE_90 : ANGLE_90)); player->powers[pw_carry] = CR_NIGHTSMODE; } From 963f3d167a8d813a6ac8f77ed922657478a75ea3 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Sat, 7 Apr 2018 07:33:07 -0400 Subject: [PATCH 16/79] Add ML_DONPTEGTOP flag to Nights LE * Nightserize: Run only if player is already NiGHTS * DeNightserize: Run only if >0 players are NiGHTS --- src/p_spec.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index 590d97a5d..e628b853d 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1539,6 +1539,7 @@ static boolean P_CheckNightsTriggerLine(line_t *triggerline, mobj_t *actor) boolean donomares = triggerline->flags & ML_BOUNCY; // nightserize: run at end of level (no mares) boolean fromnonights = triggerline->flags & ML_TFERLINE; // nightserize: from non-nights // denightserize: all players no nights + boolean fromnights = triggerline->flags & ML_DONTPEGTOP; // nightserize: from nights // denightserize: >0 players are nights UINT8 currentmare = UINT8_MAX; UINT8 currentlap = UINT8_MAX; @@ -1562,12 +1563,22 @@ static boolean P_CheckNightsTriggerLine(line_t *triggerline, mobj_t *actor) else if (actor->player->powers[pw_carry] == CR_NIGHTSMODE) return false; } + // run only if player is nightserizing from nights + else if (fromnights) + { + if (!actor->player) + return false; + else if (actor->player->powers[pw_carry] != CR_NIGHTSMODE) + return false; + } } // Get current mare and lap (and check early return for DeNightserize) if (perglobal || perglobalinverse - || (specialtype >= 325 && specialtype <= 326 && fromnonights)) + || (specialtype >= 325 && specialtype <= 326 && (fromnonights || fromnights))) { + UINT8 playersarenights = 0; + for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i] || players[i].spectator) @@ -1578,6 +1589,11 @@ static boolean P_CheckNightsTriggerLine(line_t *triggerline, mobj_t *actor) && players[i].powers[pw_carry] == CR_NIGHTSMODE) return false; + // count number of nights players for denightserize return + if (specialtype >= 325 && specialtype <= 326 && fromnights + && players[i].powers[pw_carry] == CR_NIGHTSMODE) + playersarenights++; + UINT8 lap = lapfrombonustime ? players[i].marebonuslap : players[i].marelap; // get highest mare/lap of players @@ -1605,6 +1621,11 @@ static boolean P_CheckNightsTriggerLine(line_t *triggerline, mobj_t *actor) currentlap = lap; } } + + // denightserize: run only if >0 players are nights + if (specialtype >= 325 && specialtype <= 326 && fromnights + && playersarenights < 1) + return false; } // get current mare/lap from triggering player else if (!perglobal && !perglobalinverse) @@ -1839,8 +1860,6 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller return false; } break; - - // Let's do the NiGHTS triggers in one code block; they mostly have the same logic case 323: // nightserize - each time case 324: // nightserize - once case 325: // denightserize - each time From 8f169e184d3f87526fe23edf685e5f6514a7e615 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Wed, 11 Apr 2018 14:40:05 -0400 Subject: [PATCH 17/79] Default sound volume is too loud --- src/android/i_cdmus.c | 2 +- src/djgppdos/i_cdmus.c | 2 +- src/s_sound.c | 2 +- src/sdl12/i_cdmus.c | 2 +- src/win32/win_cd.c | 2 +- src/win32ce/win_cd.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/android/i_cdmus.c b/src/android/i_cdmus.c index c5aac8f18..426bc5dc9 100644 --- a/src/android/i_cdmus.c +++ b/src/android/i_cdmus.c @@ -8,7 +8,7 @@ UINT8 cdaudio_started = 0; -consvar_t cd_volume = {"cd_volume","31",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cd_volume = {"cd_volume","18",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cdUpdate = {"cd_update","1",CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; diff --git a/src/djgppdos/i_cdmus.c b/src/djgppdos/i_cdmus.c index f707add5e..2a629ca17 100644 --- a/src/djgppdos/i_cdmus.c +++ b/src/djgppdos/i_cdmus.c @@ -50,7 +50,7 @@ static boolean wasPlaying; static int cdVolume=0; // current cd volume (0-31) // 0-31 like Music & Sfx, though CD hardware volume is 0-255. -consvar_t cd_volume = {"cd_volume","31",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cd_volume = {"cd_volume","18",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // allow Update for next/loop track // some crap cd drivers take up to diff --git a/src/s_sound.c b/src/s_sound.c index 47a955561..76ee4c649 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -78,7 +78,7 @@ consvar_t stereoreverse = {"stereoreverse", "Off", CV_SAVE, CV_OnOff, NULL, 0, N static consvar_t precachesound = {"precachesound", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; // actual general (maximum) sound & music volume, saved into the config -consvar_t cv_soundvolume = {"soundvolume", "31", CV_SAVE, soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_soundvolume = {"soundvolume", "18", CV_SAVE, soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_digmusicvolume = {"digmusicvolume", "18", CV_SAVE, soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_midimusicvolume = {"midimusicvolume", "18", CV_SAVE, soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // number of channels available diff --git a/src/sdl12/i_cdmus.c b/src/sdl12/i_cdmus.c index 1eeac370b..b3490592e 100644 --- a/src/sdl12/i_cdmus.c +++ b/src/sdl12/i_cdmus.c @@ -60,7 +60,7 @@ void SDL_SYS_CDQuit(void) UINT8 cdaudio_started = 0; // for system startup/shutdown -consvar_t cd_volume = {"cd_volume","31",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cd_volume = {"cd_volume","18",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cdUpdate = {"cd_update","1",CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; #ifndef NOSDLCD diff --git a/src/win32/win_cd.c b/src/win32/win_cd.c index 4ac1506e5..f6c430748 100644 --- a/src/win32/win_cd.c +++ b/src/win32/win_cd.c @@ -161,7 +161,7 @@ static BOOL wasPlaying; //static INT cdVolume = 0; // current cd volume (0-31) // 0-31 like Music & Sfx, though CD hardware volume is 0-255. -consvar_t cd_volume = {"cd_volume","31",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cd_volume = {"cd_volume","18",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // allow Update for next/loop track // some crap cd drivers take up to diff --git a/src/win32ce/win_cd.c b/src/win32ce/win_cd.c index 2b1a8be9a..940f59ff2 100644 --- a/src/win32ce/win_cd.c +++ b/src/win32ce/win_cd.c @@ -159,7 +159,7 @@ static boolean wasPlaying; //static int cdVolume = 0; // current cd volume (0-31) // 0-31 like Music & Sfx, though CD hardware volume is 0-255. -consvar_t cd_volume = {"cd_volume","31",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cd_volume = {"cd_volume","18",CV_SAVE,soundvolume_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // allow Update for next/loop track // some crap cd drivers take up to From b6835e8579deee55d4b44ae4485ff092b7167320 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Fri, 25 May 2018 20:54:50 +0100 Subject: [PATCH 18/79] change fickle's hack so that the bottoms of polyobj plane columns are set to 0 instead of 65535 when not intended to be visible --- src/r_segs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/r_segs.c b/src/r_segs.c index 5395f414a..9e5060da9 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -1443,7 +1443,8 @@ static void R_RenderSegLoop (void) #ifdef POLYOBJECTS_PLANES // Polyobject-specific hack to fix plane leaking -Red if (ffloor[i].polyobj && top_w >= bottom_w) { - ffloor[i].plane->top[rw_x] = ffloor[i].plane->bottom[rw_x] = 0xFFFF; + ffloor[i].plane->top[rw_x] = 0xFFFF; + ffloor[i].plane->bottom[rw_x] = 0x0000; // fix for sky plane drawing crashes - Monster Iestyn 25/05/18 } else #endif @@ -1467,7 +1468,8 @@ static void R_RenderSegLoop (void) #ifdef POLYOBJECTS_PLANES // Polyobject-specific hack to fix plane leaking -Red if (ffloor[i].polyobj && top_w >= bottom_w) { - ffloor[i].plane->top[rw_x] = ffloor[i].plane->bottom[rw_x] = 0xFFFF; + ffloor[i].plane->top[rw_x] = 0xFFFF; + ffloor[i].plane->bottom[rw_x] = 0x0000; // fix for sky plane drawing crashes - Monster Iestyn 25/05/18 } else #endif From dce1e1a1032d7ed2a5b3b4f3f79382de01b4fd4f Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Sun, 8 Jul 2018 22:30:37 +0100 Subject: [PATCH 19/79] Backport Kart Krew's fix for the 1px HOM with horizon lines --- src/r_segs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/r_segs.c b/src/r_segs.c index 5395f414a..f492d03f8 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -2693,6 +2693,7 @@ void R_StoreWallRange(INT32 start, INT32 stop) if (linedef->special == 41) { // HORIZON LINES topstep = bottomstep = 0; topfrac = bottomfrac = (centeryfrac>>4); + topfrac++; // Prevent 1px HOM } else { topstep = -FixedMul (rw_scalestep, worldtop); topfrac = (centeryfrac>>4) - FixedMul (worldtop, rw_scale); From 594eb92bed61e2e6d323d988557c136814674a53 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Sat, 14 Jul 2018 18:15:59 +0100 Subject: [PATCH 20/79] Use plain malloc instead of Z_Malloc to allocate sound buffers in mixer_sound.c's I_GetSfx. This should prevent I_FreeSfx making a mess of things later, hopefully. --- src/sdl/mixer_sound.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c index 718324591..0447b5210 100644 --- a/src/sdl/mixer_sound.c +++ b/src/sdl/mixer_sound.c @@ -176,7 +176,7 @@ static Mix_Chunk *ds2chunk(void *stream) return NULL; // would and/or did wrap, can't store. break; } - sound = Z_Malloc(newsamples<<2, PU_SOUND, NULL); // samples * frequency shift * bytes per sample * channels + sound = malloc(newsamples<<2); // samples * frequency shift * bytes per sample * channels s = (SINT8 *)stream; d = (INT16 *)sound; @@ -304,7 +304,7 @@ void *I_GetSfx(sfxinfo_t *sfx) gme_track_info(emu, &info, 0); len = (info->play_length * 441 / 10) << 2; - mem = Z_Malloc(len, PU_SOUND, NULL); + mem = malloc(len); gme_play(emu, len >> 1, mem); gme_delete(emu); @@ -376,7 +376,7 @@ void *I_GetSfx(sfxinfo_t *sfx) gme_track_info(emu, &info, 0); len = (info->play_length * 441 / 10) << 2; - mem = Z_Malloc(len, PU_SOUND, NULL); + mem = malloc(len); gme_play(emu, len >> 1, mem); gme_delete(emu); From 73ca0b481430236faf5dc30b44a6d8e095197fcf Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Mon, 6 Aug 2018 22:37:44 +0100 Subject: [PATCH 21/79] Trim off any extra null bytes off the end of sector floorpic/ceiling when you access them in Lua --- src/lua_maplib.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 208aebe37..4e9dbd5af 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -327,6 +327,7 @@ static int sector_get(lua_State *L) { sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR)); enum sector_e field = luaL_checkoption(L, 2, sector_opt[0], sector_opt); + INT16 i; if (!sector) { @@ -349,11 +350,23 @@ static int sector_get(lua_State *L) lua_pushfixed(L, sector->ceilingheight); return 1; case sector_floorpic: // floorpic - lua_pushlstring(L, levelflats[sector->floorpic].name, 8); + { + levelflat_t *levelflat = &levelflats[sector->floorpic]; + for (i = 0; i < 8; i++) + if (!levelflat->name[i]) + break; + lua_pushlstring(L, levelflat->name, i); return 1; + } case sector_ceilingpic: // ceilingpic - lua_pushlstring(L, levelflats[sector->ceilingpic].name, 8); + { + levelflat_t *levelflat = &levelflats[sector->ceilingpic]; + for (i = 0; i < 8; i++) + if (!levelflat->name[i]) + break; + lua_pushlstring(L, levelflat->name, i); return 1; + } case sector_lightlevel: lua_pushinteger(L, sector->lightlevel); return 1; From 9d8af5da0133c6d8e45cdbfba827ffd2d3e126fe Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 9 Aug 2018 21:00:05 -0400 Subject: [PATCH 22/79] Snappier NiGHTS pickup attraction * Old attraction is still used for non-NiGHTS players due to a momentum bug. The old way is good enough to sidestep the bug. * Thanks Inuyasha (KS) for original code :dog: --- src/p_inter.c | 4 ++++ src/p_mobj.c | 40 ++++++++++++++++++++++++++++++++-------- src/p_user.c | 1 + 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index 5737e2c2a..01343cfb1 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -944,6 +944,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Yay! The thing's in reach! Pull it in! mo2->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT; mo2->flags2 |= MF2_NIGHTSPULL; + mo2->movecount = 24*FRACUNIT; // initialize the NightsItemChase timer P_SetTarget(&mo2->tracer, toucher); } } @@ -2122,7 +2123,10 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget target->flags |= MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT; // Don't drop Tails 03-08-2000 if (target->flags2 & MF2_NIGHTSPULL) + { P_SetTarget(&target->tracer, NULL); + target->movecount = 0; // reset NightsItemChase timer + } // dead target is no more shootable target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SPECIAL); diff --git a/src/p_mobj.c b/src/p_mobj.c index be373fbf4..c0924050c 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6096,9 +6096,11 @@ void P_SetScale(mobj_t *mobj, fixed_t newscale) void P_Attract(mobj_t *source, mobj_t *dest, boolean nightsgrab) // Home in on your target { fixed_t dist, ndist, speedmul; + angle_t vangle; fixed_t tx = dest->x; fixed_t ty = dest->y; fixed_t tz = dest->z + (dest->height/2); // Aim for center + fixed_t xydist = P_AproxDistance(tx - source->x, ty - source->y); if (!dest || dest->health <= 0 || !dest->player || !source->tracer) return; @@ -6107,19 +6109,40 @@ void P_Attract(mobj_t *source, mobj_t *dest, boolean nightsgrab) // Home in on y source->angle = R_PointToAngle2(source->x, source->y, tx, ty); // change slope - dist = P_AproxDistance(P_AproxDistance(tx - source->x, ty - source->y), tz - source->z); + dist = P_AproxDistance(xydist, tz - source->z); if (dist < 1) dist = 1; - if (nightsgrab) - speedmul = P_AproxDistance(dest->momx, dest->momy) + FixedMul(8*FRACUNIT, source->scale); - else - speedmul = P_AproxDistance(dest->momx, dest->momy) + FixedMul(source->info->speed, source->scale); + if (nightsgrab && dest->player->powers[pw_carry] == CR_NIGHTSMODE) + { + source->movecount += FRACUNIT/2; - source->momx = FixedMul(FixedDiv(tx - source->x, dist), speedmul); - source->momy = FixedMul(FixedDiv(ty - source->y, dist), speedmul); - source->momz = FixedMul(FixedDiv(tz - source->z, dist), speedmul); + if (dist < source->movecount) + { + source->momx = source->momy = source->momz = 0; + P_TeleportMove(source, tx, ty, tz); + } + else + { + vangle = R_PointToAngle2(source->z, 0, tz, xydist); + + source->momx = FixedMul(FINESINE(vangle >> ANGLETOFINESHIFT), FixedMul(FINECOSINE(source->angle >> ANGLETOFINESHIFT), source->movecount)); + source->momy = FixedMul(FINESINE(vangle >> ANGLETOFINESHIFT), FixedMul(FINESINE(source->angle >> ANGLETOFINESHIFT), source->movecount)); + source->momz = FixedMul(FINECOSINE(vangle >> ANGLETOFINESHIFT), source->movecount); + } + } + else + { + if (nightsgrab) + speedmul = P_AproxDistance(dest->momx, dest->momy) + FixedMul(8*FRACUNIT, source->scale); + else + speedmul = P_AproxDistance(dest->momx, dest->momy) + FixedMul(source->info->speed, source->scale); + + source->momx = FixedMul(FixedDiv(tx - source->x, dist), speedmul); + source->momy = FixedMul(FixedDiv(ty - source->y, dist), speedmul); + source->momz = FixedMul(FixedDiv(tz - source->z, dist), speedmul); + } // Instead of just unsetting NOCLIP like an idiot, let's check the distance to our target. ndist = P_AproxDistance(P_AproxDistance(tx - (source->x+source->momx), @@ -6144,6 +6167,7 @@ static void P_NightsItemChase(mobj_t *thing) { P_SetTarget(&thing->tracer, NULL); thing->flags2 &= ~MF2_NIGHTSPULL; + //thing->movecount = 0; return; } diff --git a/src/p_user.c b/src/p_user.c index 357933f14..a601c5c2f 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9762,6 +9762,7 @@ void P_PlayerThink(player_t *player) // Yay! The thing's in reach! Pull it in! mo2->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT; mo2->flags2 |= MF2_NIGHTSPULL; + mo2->movecount = 24*FRACUNIT; // initialize NightsItemChase timer P_SetTarget(&mo2->tracer, player->mo); } } From 205678cb9ad4065e8ebf609f73c87e0e11053e36 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 9 Aug 2018 22:13:06 -0400 Subject: [PATCH 23/79] NiGHTS drill meter penalty when mashing jump button Thanks Inuyasha (KS) for code --- src/p_user.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/p_user.c b/src/p_user.c index a601c5c2f..416ca807e 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -6635,6 +6635,13 @@ static void P_NiGHTSMovement(player_t *player) S_StartSound(player->mo, sfx_drill1); player->drilltimer = 32; } + else if (player->drilltimer == 32) + { + player->drilltimer = 31; + player->drillmeter -= TICRATE/2; + if (player->drillmeter <= 0) + player->drillmeter = TICRATE/10; + } else if (--player->drilltimer <= 0) { player->drilltimer = 10; From 4d4e52d981b69fcfd562e8c7632d60e43bc9bb80 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 9 Aug 2018 23:14:14 -0400 Subject: [PATCH 24/79] Standardize NiGHTS link timer w/ NightsLinkTics MAINCFG option --- src/dehacked.c | 5 ++++- src/doomstat.h | 1 + src/g_game.c | 1 + src/p_inter.c | 10 +++++----- src/st_stuff.c | 4 ++-- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index c4d0bc104..3a18f2a45 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -2730,11 +2730,14 @@ static void readmaincfg(MYFILE *f) { extralifetics = (UINT16)get_number(word2); } + else if (fastcmp(word, "NIGHTSLINKTICS")) + { + nightslinktics = (UINT16)get_number(word2); + } else if (fastcmp(word, "GAMEOVERTICS")) { gameovertics = get_number(word2); } - else if (fastcmp(word, "INTROTOPLAY")) { introtoplay = (UINT8)get_number(word2); diff --git a/src/doomstat.h b/src/doomstat.h index 24b9e5753..97710c05d 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -413,6 +413,7 @@ extern UINT16 tailsflytics; extern UINT16 underwatertics; extern UINT16 spacetimetics; extern UINT16 extralifetics; +extern UINT16 nightslinktics; extern UINT8 introtoplay; extern UINT8 creditscutscene; diff --git a/src/g_game.c b/src/g_game.c index 52358a8b9..5af4743c4 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -201,6 +201,7 @@ UINT16 tailsflytics = 8*TICRATE; UINT16 underwatertics = 30*TICRATE; UINT16 spacetimetics = 11*TICRATE + (TICRATE/2); UINT16 extralifetics = 4*TICRATE; +UINT16 nightslinktics = 2*TICRATE; INT32 gameovertics = 15*TICRATE; diff --git a/src/p_inter.c b/src/p_inter.c index 01343cfb1..f4c1afada 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -182,14 +182,14 @@ void P_DoNightsScore(player_t *player) { if (++players[i].linkcount > players[i].maxlink) players[i].maxlink = players[i].linkcount; - players[i].linktimer = 2*TICRATE; + players[i].linktimer = nightslinktics; } } else // Individual link counts { if (++player->linkcount > player->maxlink) player->maxlink = player->linkcount; - player->linktimer = 2*TICRATE; + player->linktimer = nightslinktics; } if (player->linkcount < 10) @@ -1162,7 +1162,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!G_IsSpecialStage(gamemap)) { player->powers[pw_nights_linkfreeze] = (UINT16)special->info->speed; - player->linktimer = 2*TICRATE; + player->linktimer = nightslinktics; } else { @@ -1170,7 +1170,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (playeringame[i] && players[i].powers[pw_carry] == CR_NIGHTSMODE) { players[i].powers[pw_nights_linkfreeze] += (UINT16)special->info->speed; - players[i].linktimer = 2*TICRATE; + players[i].linktimer = nightslinktics; } if (special->info->deathsound != sfx_None) S_StartSound(NULL, special->info->deathsound); @@ -2185,7 +2185,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget // to make people want to actually dash towards/paraloop enemies if (++source->player->linkcount > source->player->maxlink) source->player->maxlink = source->player->linkcount; - source->player->linktimer = 2*TICRATE; + source->player->linktimer = nightslinktics; } } else diff --git a/src/st_stuff.c b/src/st_stuff.c index 3ecd0acdf..64d3db7f2 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1535,8 +1535,8 @@ static void ST_drawNiGHTSHUD(void) else colornum = linkColor[mag][sel]; - aflag |= ((stplyr->linktimer < 2*TICRATE/3) - ? (9 - 9*stplyr->linktimer/(2*TICRATE/3)) << V_ALPHASHIFT + aflag |= ((stplyr->linktimer < nightslinktics/3) + ? (9 - 9*stplyr->linktimer/(nightslinktics/3)) << V_ALPHASHIFT : 0); y = (160+11)< Date: Fri, 10 Aug 2018 16:17:28 -0400 Subject: [PATCH 25/79] Set player->finishedrings upon new mare (Forgot this line from the player->spheres merge, so let's straggle here) --- src/p_user.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/p_user.c b/src/p_user.c index 5f1284ef8..b7336b545 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -746,6 +746,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->texttimer = 4*TICRATE; player->textvar = 4; // Score and grades player->finishedspheres = (INT16)(player->spheres); + player->finishedrings = (INT16)(player->rings); // Add score to temp leaderboards if (!(netgame||multiplayer) && P_IsLocalPlayer(player)) From 2f5186d014c05d9454936ca3cceff33bd5c8379a Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 10 Aug 2018 16:30:49 -0400 Subject: [PATCH 26/79] Add lastmarelap and lastmarebonuslap player variables --- src/d_player.h | 2 ++ src/lua_playerlib.c | 8 ++++++++ src/p_saveg.c | 4 ++++ src/p_setup.c | 1 + src/p_user.c | 10 ++++++++-- 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index cea3d5130..498b7166d 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -468,6 +468,8 @@ typedef struct player_s UINT32 marescore; // score for this nights stage UINT32 lastmarescore; // score for the last mare UINT8 lastmare; // previous mare + UINT8 lastmarelap; // previous mare lap + UINT8 lastmarebonuslap; // previous mare bonus lap INT32 maxlink; // maximum link obtained UINT8 texttimer; // nights_texttime should not be local UINT8 textvar; // which line of NiGHTS text to show -- let's not use cheap hacks diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 345f4a4d0..7deefa7ff 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -310,6 +310,10 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->lastmarescore); else if (fastcmp(field,"lastmare")) lua_pushinteger(L, plr->lastmare); + else if (fastcmp(field,"lastmarelap")) + lua_pushinteger(L, plr->lastmarelap); + else if (fastcmp(field,"lastmarebonuslap")) + lua_pushinteger(L, plr->lastmarebonuslap); else if (fastcmp(field,"maxlink")) lua_pushinteger(L, plr->maxlink); else if (fastcmp(field,"texttimer")) @@ -594,6 +598,10 @@ static int player_set(lua_State *L) plr->lastmarescore = (UINT32)luaL_checkinteger(L, 3); else if (fastcmp(field,"lastmare")) plr->lastmare = (UINT8)luaL_checkinteger(L, 3); + else if (fastcmp(field,"lastmarelap")) + plr->lastmarelap = (UINT8)luaL_checkinteger(L, 3); + else if (fastcmp(field,"lastmarebonuslap")) + plr->lastmarebonuslap = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"maxlink")) plr->maxlink = (INT32)luaL_checkinteger(L, 3); else if (fastcmp(field,"texttimer")) diff --git a/src/p_saveg.c b/src/p_saveg.c index 729342424..2b48cc783 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -209,6 +209,8 @@ static void P_NetArchivePlayers(void) WRITEUINT32(save_p, players[i].marescore); WRITEUINT32(save_p, players[i].lastmarescore); WRITEUINT8(save_p, players[i].lastmare); + WRITEUINT8(save_p, players[i].lastmarelap); + WRITEUINT8(save_p, players[i].lastmarebonuslap); WRITEINT32(save_p, players[i].maxlink); WRITEUINT8(save_p, players[i].texttimer); WRITEUINT8(save_p, players[i].textvar); @@ -400,6 +402,8 @@ static void P_NetUnArchivePlayers(void) players[i].marescore = READUINT32(save_p); players[i].lastmarescore = READUINT32(save_p); players[i].lastmare = READUINT8(save_p); + players[i].lastmarelap = READUINT8(save_p); + players[i].lastmarebonuslap = READUINT8(save_p); players[i].maxlink = READINT32(save_p); players[i].texttimer = READUINT8(save_p); players[i].textvar = READUINT8(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index 46f77536d..e4b6034b1 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2381,6 +2381,7 @@ static void P_LevelInitStuff(void) players[i].maxlink = players[i].startedtime =\ players[i].finishedtime = players[i].finishedspheres =\ players[i].finishedrings = players[i].lastmare =\ + players[i].lastmarelap = players[i].lastmarebonuslap =\ players[i].marebegunat = players[i].textvar =\ players[i].texttimer = players[i].linkcount =\ players[i].linktimer = players[i].flyangle =\ diff --git a/src/p_user.c b/src/p_user.c index b7336b545..99ecb8154 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -637,7 +637,7 @@ static void P_DeNightserizePlayer(player_t *player) // NiGHTS Time! void P_NightserizePlayer(player_t *player, INT32 nighttime) { - INT32 oldmare; + UINT8 oldmare, oldmarelap, oldmarebonuslap; // Bots can't be NiGHTSerized, silly!1 :P if (player->bot) @@ -684,6 +684,8 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) } oldmare = player->mare; + oldmarelap = player->marelap; + oldmarebonuslap = player->marebonuslap; if (!P_TransferToNextMare(player)) { @@ -711,6 +713,8 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) players[i].texttimer = (3 * TICRATE) - 10; players[i].textvar = 4; // Score and grades players[i].lastmare = players[i].mare; + players[i].lastmarelap = players[i].marelap; + players[i].lastmarebonuslap = players[i].marebonuslap; if (G_IsSpecialStage(gamemap)) { players[i].finishedspheres = (INT16)total_spheres; @@ -742,7 +746,9 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) // Spheres bonus P_AddPlayerScore(player, (player->spheres) * 50); - player->lastmare = (UINT8)oldmare; + player->lastmare = oldmare; + player->lastmarelap = oldmarelap; + player->lastmarebonuslap = oldmarebonuslap; player->texttimer = 4*TICRATE; player->textvar = 4; // Score and grades player->finishedspheres = (INT16)(player->spheres); From 8f6d20d53cb156c4ebdb99c94f74c7b55c1548fa Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Fri, 10 Aug 2018 17:08:39 -0400 Subject: [PATCH 27/79] SDL: update IMG_xpm.c --- src/sdl/IMG_xpm.c | 1412 +++++++++++++++++++++++++++++++----------- src/sdl/SDL_icon.xpm | 2 +- src/sdl/i_video.c | 4 + 3 files changed, 1057 insertions(+), 361 deletions(-) diff --git a/src/sdl/IMG_xpm.c b/src/sdl/IMG_xpm.c index e08736d66..8adfd3434 100644 --- a/src/sdl/IMG_xpm.c +++ b/src/sdl/IMG_xpm.c @@ -1,27 +1,24 @@ /* - SDL_image: An example image loading library for use with SDL - Copyright (C) 1999-2004 Sam Lantinga + SDL_image: An example image loading library for use with SDL + Copyright (C) 1997-2018 Sam Lantinga - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library 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 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - Sam Lantinga - slouken@libsdl.org + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. */ -/* $Id: IMG_xpm.c,v 1.10 2004/01/04 22:04:38 slouken Exp $ */ - /* * XPM (X PixMap) image loader: * @@ -45,98 +42,110 @@ * requires about 13K in binary form. */ -#include -#include -#include -#include - -//#include "SDL_image.h" - +#if 0 +#include "SDL_image.h" +#else +extern SDLCALL int SDLCALL IMG_isXPM(SDL_RWops *src); +extern SDLCALL SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src); +extern SDLCALL SDL_Surface * SDLCALL IMG_ReadXPMFromArray(const char **xpm); +#define IMG_SetError SDL_SetError +#define IMG_GetError SDL_GetError +#endif #ifdef LOAD_XPM /* See if an image is contained in a data source */ -#if 0 int IMG_isXPM(SDL_RWops *src) { - char magic[9]; + Sint64 start; + int is_XPM; + char magic[9]; - return (SDL_RWread(src, magic, sizeof (magic), 1) - && memcmp(magic, "/* XPM */", 9) == 0); + if ( !src ) + return 0; + start = SDL_RWtell(src); + is_XPM = 0; + if ( SDL_RWread(src, magic, sizeof(magic), 1) ) { + if ( SDL_memcmp(magic, "/* XPM */", sizeof(magic)) == 0 ) { + is_XPM = 1; + } + } + SDL_RWseek(src, start, RW_SEEK_SET); + return(is_XPM); } -#endif /* Hash table to look up colors from pixel strings */ #define STARTING_HASH_SIZE 256 struct hash_entry { - char *key; - Uint32 color; - struct hash_entry *next; + const char *key; + Uint32 color; + struct hash_entry *next; }; struct color_hash { - struct hash_entry **table; - struct hash_entry *entries; /* array of all entries */ - struct hash_entry *next_free; - size_t size; - int maxnum; + struct hash_entry **table; + struct hash_entry *entries; /* array of all entries */ + struct hash_entry *next_free; + int size; + int maxnum; }; -static int hash_key(const char *key, int cpp, size_t size) +static int hash_key(const char *key, int cpp, int size) { - int hash; + int hash; - hash = 0; - while ( cpp-- > 0 ) { - hash = hash * 33 + *key++; - } - return (int)(hash & (size - 1)); + hash = 0; + while ( cpp-- > 0 ) { + hash = hash * 33 + *key++; + } + return hash & (size - 1); } static struct color_hash *create_colorhash(int maxnum) { - size_t bytes; - int s; - struct color_hash *hash; + int bytes, s; + struct color_hash *hash; - /* we know how many entries we need, so we can allocate - everything here */ - hash = malloc(sizeof *hash); - if (!hash) - return NULL; + /* we know how many entries we need, so we can allocate + everything here */ + hash = (struct color_hash *)SDL_malloc(sizeof *hash); + if (!hash) + return NULL; - /* use power-of-2 sized hash table for decoding speed */ - for (s = STARTING_HASH_SIZE; s < maxnum; s <<= 1) - ; - hash->size = s; - hash->maxnum = maxnum; - bytes = hash->size * sizeof (struct hash_entry **); - hash->entries = NULL; /* in case malloc fails */ - hash->table = malloc(bytes); - if (!hash->table) - return NULL; - memset(hash->table, 0, bytes); - hash->entries = malloc(maxnum * sizeof (struct hash_entry)); - if (!hash->entries) - { - free(hash->table); - return NULL; - } - hash->next_free = hash->entries; - return hash; + /* use power-of-2 sized hash table for decoding speed */ + for (s = STARTING_HASH_SIZE; s < maxnum; s <<= 1) + ; + hash->size = s; + hash->maxnum = maxnum; + bytes = hash->size * sizeof(struct hash_entry **); + hash->entries = NULL; /* in case malloc fails */ + hash->table = (struct hash_entry **)SDL_malloc(bytes); + if (!hash->table) { + SDL_free(hash); + return NULL; + } + SDL_memset(hash->table, 0, bytes); + hash->entries = (struct hash_entry *)SDL_malloc(maxnum * sizeof(struct hash_entry)); + if (!hash->entries) { + SDL_free(hash->table); + SDL_free(hash); + return NULL; + } + hash->next_free = hash->entries; + return hash; } static int add_colorhash(struct color_hash *hash, char *key, int cpp, Uint32 color) { - const int indexkey = hash_key(key, cpp, hash->size); - struct hash_entry *e = hash->next_free++; - e->color = color; - e->key = key; - e->next = hash->table[indexkey]; - hash->table[indexkey] = e; - return 1; + int index = hash_key(key, cpp, hash->size); + struct hash_entry *e = hash->next_free++; + e->color = color; + e->key = key; + e->next = hash->table[index]; + hash->table[index] = e; + return 1; } /* fast lookup that works if cpp == 1 */ @@ -144,88 +153,756 @@ static int add_colorhash(struct color_hash *hash, static Uint32 get_colorhash(struct color_hash *hash, const char *key, int cpp) { - struct hash_entry *entry = hash->table[hash_key(key, cpp, hash->size)]; - while (entry) { - if (memcmp(key, entry->key, cpp) == 0) - return entry->color; - entry = entry->next; - } - return 0; /* garbage in - garbage out */ + struct hash_entry *entry = hash->table[hash_key(key, cpp, hash->size)]; + while (entry) { + if (SDL_memcmp(key, entry->key, cpp) == 0) + return entry->color; + entry = entry->next; + } + return 0; /* garbage in - garbage out */ } static void free_colorhash(struct color_hash *hash) { - if (hash && hash->table) { - free(hash->table); - free(hash->entries); - free(hash); - } + if (hash) { + if (hash->table) + SDL_free(hash->table); + if (hash->entries) + SDL_free(hash->entries); + SDL_free(hash); + } } -/* portable case-insensitive string comparison */ -static int string_equal(const char *a, const char *b, size_t n) -{ - while (*a && *b && n) { - if (toupper((unsigned char)*a) != toupper((unsigned char)*b)) - return 0; - a++; - b++; - n--; - } - return *a == *b; -} - -#undef ARRAYSIZE -#define ARRAYSIZE(a) (int)(sizeof (a) / sizeof ((a)[0])) - /* * convert colour spec to RGB (in 0xrrggbb format). * return 1 if successful. */ -static int color_to_rgb(const char *spec, size_t speclen, Uint32 *rgb) +static int color_to_rgb(const char *spec, int speclen, Uint32 *rgb) { - /* poor man's rgb.txt */ - static struct { const char *name; Uint32 rgb; } known[] = { - {"none", 0xffffffff}, - {"black", 0x00000000}, - {"white", 0x00ffffff}, - {"red", 0x00ff0000}, - {"green", 0x0000ff00}, - {"blue", 0x000000ff} - }; + /* poor man's rgb.txt */ + static struct { const char *name; Uint32 rgb; } known[] = { + { "none", 0xFFFFFFFF }, + { "black", 0x000000 }, + { "white", 0xFFFFFF }, + { "red", 0xFF0000 }, + { "green", 0x00FF00 }, + { "blue", 0x0000FF }, +/* This table increases the size of the library by 40K, so it's disabled by default */ +#ifdef EXTENDED_XPM_COLORS + { "aliceblue", 0xf0f8ff }, + { "antiquewhite", 0xfaebd7 }, + { "antiquewhite1", 0xffefdb }, + { "antiquewhite2", 0xeedfcc }, + { "antiquewhite3", 0xcdc0b0 }, + { "antiquewhite4", 0x8b8378 }, + { "aqua", 0x00ffff }, + { "aquamarine", 0x7fffd4 }, + { "aquamarine1", 0x7fffd4 }, + { "aquamarine2", 0x76eec6 }, + { "aquamarine3", 0x66cdaa }, + { "aquamarine4", 0x458b74 }, + { "azure", 0xf0ffff }, + { "azure1", 0xf0ffff }, + { "azure2", 0xe0eeee }, + { "azure3", 0xc1cdcd }, + { "azure4", 0x838b8b }, + { "beige", 0xf5f5dc }, + { "bisque", 0xffe4c4 }, + { "bisque1", 0xffe4c4 }, + { "bisque2", 0xeed5b7 }, + { "bisque3", 0xcdb79e }, + { "bisque4", 0x8b7d6b }, + { "black", 0x000000 }, + { "blanchedalmond", 0xffebcd }, + { "blue", 0x0000ff }, + { "blue1", 0x0000ff }, + { "blue2", 0x0000ee }, + { "blue3", 0x0000cd }, + { "blue4", 0x00008B }, + { "blueviolet", 0x8a2be2 }, + { "brown", 0xA52A2A }, + { "brown1", 0xFF4040 }, + { "brown2", 0xEE3B3B }, + { "brown3", 0xCD3333 }, + { "brown4", 0x8B2323 }, + { "burlywood", 0xDEB887 }, + { "burlywood1", 0xFFD39B }, + { "burlywood2", 0xEEC591 }, + { "burlywood3", 0xCDAA7D }, + { "burlywood4", 0x8B7355 }, + { "cadetblue", 0x5F9EA0 }, + { "cadetblue", 0x5f9ea0 }, + { "cadetblue1", 0x98f5ff }, + { "cadetblue2", 0x8ee5ee }, + { "cadetblue3", 0x7ac5cd }, + { "cadetblue4", 0x53868b }, + { "chartreuse", 0x7FFF00 }, + { "chartreuse1", 0x7FFF00 }, + { "chartreuse2", 0x76EE00 }, + { "chartreuse3", 0x66CD00 }, + { "chartreuse4", 0x458B00 }, + { "chocolate", 0xD2691E }, + { "chocolate1", 0xFF7F24 }, + { "chocolate2", 0xEE7621 }, + { "chocolate3", 0xCD661D }, + { "chocolate4", 0x8B4513 }, + { "coral", 0xFF7F50 }, + { "coral1", 0xFF7256 }, + { "coral2", 0xEE6A50 }, + { "coral3", 0xCD5B45 }, + { "coral4", 0x8B3E2F }, + { "cornflowerblue", 0x6495ed }, + { "cornsilk", 0xFFF8DC }, + { "cornsilk1", 0xFFF8DC }, + { "cornsilk2", 0xEEE8CD }, + { "cornsilk3", 0xCDC8B1 }, + { "cornsilk4", 0x8B8878 }, + { "crimson", 0xDC143C }, + { "cyan", 0x00FFFF }, + { "cyan1", 0x00FFFF }, + { "cyan2", 0x00EEEE }, + { "cyan3", 0x00CDCD }, + { "cyan4", 0x008B8B }, + { "darkblue", 0x00008b }, + { "darkcyan", 0x008b8b }, + { "darkgoldenrod", 0xb8860b }, + { "darkgoldenrod1", 0xffb90f }, + { "darkgoldenrod2", 0xeead0e }, + { "darkgoldenrod3", 0xcd950c }, + { "darkgoldenrod4", 0x8b6508 }, + { "darkgray", 0xa9a9a9 }, + { "darkgreen", 0x006400 }, + { "darkgrey", 0xa9a9a9 }, + { "darkkhaki", 0xbdb76b }, + { "darkmagenta", 0x8b008b }, + { "darkolivegreen", 0x556b2f }, + { "darkolivegreen1", 0xcaff70 }, + { "darkolivegreen2", 0xbcee68 }, + { "darkolivegreen3", 0xa2cd5a }, + { "darkolivegreen4", 0x6e8b3d }, + { "darkorange", 0xff8c00 }, + { "darkorange1", 0xff7f00 }, + { "darkorange2", 0xee7600 }, + { "darkorange3", 0xcd6600 }, + { "darkorange4", 0x8b4500 }, + { "darkorchid", 0x9932cc }, + { "darkorchid1", 0xbf3eff }, + { "darkorchid2", 0xb23aee }, + { "darkorchid3", 0x9a32cd }, + { "darkorchid4", 0x68228b }, + { "darkred", 0x8b0000 }, + { "darksalmon", 0xe9967a }, + { "darkseagreen", 0x8fbc8f }, + { "darkseagreen1", 0xc1ffc1 }, + { "darkseagreen2", 0xb4eeb4 }, + { "darkseagreen3", 0x9bcd9b }, + { "darkseagreen4", 0x698b69 }, + { "darkslateblue", 0x483d8b }, + { "darkslategray", 0x2f4f4f }, + { "darkslategray1", 0x97ffff }, + { "darkslategray2", 0x8deeee }, + { "darkslategray3", 0x79cdcd }, + { "darkslategray4", 0x528b8b }, + { "darkslategrey", 0x2f4f4f }, + { "darkturquoise", 0x00ced1 }, + { "darkviolet", 0x9400D3 }, + { "darkviolet", 0x9400d3 }, + { "deeppink", 0xff1493 }, + { "deeppink1", 0xff1493 }, + { "deeppink2", 0xee1289 }, + { "deeppink3", 0xcd1076 }, + { "deeppink4", 0x8b0a50 }, + { "deepskyblue", 0x00bfff }, + { "deepskyblue1", 0x00bfff }, + { "deepskyblue2", 0x00b2ee }, + { "deepskyblue3", 0x009acd }, + { "deepskyblue4", 0x00688b }, + { "dimgray", 0x696969 }, + { "dimgrey", 0x696969 }, + { "dodgerblue", 0x1e90ff }, + { "dodgerblue1", 0x1e90ff }, + { "dodgerblue2", 0x1c86ee }, + { "dodgerblue3", 0x1874cd }, + { "dodgerblue4", 0x104e8b }, + { "firebrick", 0xB22222 }, + { "firebrick1", 0xFF3030 }, + { "firebrick2", 0xEE2C2C }, + { "firebrick3", 0xCD2626 }, + { "firebrick4", 0x8B1A1A }, + { "floralwhite", 0xfffaf0 }, + { "forestgreen", 0x228b22 }, + { "fractal", 0x808080 }, + { "fuchsia", 0xFF00FF }, + { "gainsboro", 0xDCDCDC }, + { "ghostwhite", 0xf8f8ff }, + { "gold", 0xFFD700 }, + { "gold1", 0xFFD700 }, + { "gold2", 0xEEC900 }, + { "gold3", 0xCDAD00 }, + { "gold4", 0x8B7500 }, + { "goldenrod", 0xDAA520 }, + { "goldenrod1", 0xFFC125 }, + { "goldenrod2", 0xEEB422 }, + { "goldenrod3", 0xCD9B1D }, + { "goldenrod4", 0x8B6914 }, + { "gray", 0x7E7E7E }, + { "gray", 0xBEBEBE }, + { "gray0", 0x000000 }, + { "gray1", 0x030303 }, + { "gray10", 0x1A1A1A }, + { "gray100", 0xFFFFFF }, + { "gray11", 0x1C1C1C }, + { "gray12", 0x1F1F1F }, + { "gray13", 0x212121 }, + { "gray14", 0x242424 }, + { "gray15", 0x262626 }, + { "gray16", 0x292929 }, + { "gray17", 0x2B2B2B }, + { "gray18", 0x2E2E2E }, + { "gray19", 0x303030 }, + { "gray2", 0x050505 }, + { "gray20", 0x333333 }, + { "gray21", 0x363636 }, + { "gray22", 0x383838 }, + { "gray23", 0x3B3B3B }, + { "gray24", 0x3D3D3D }, + { "gray25", 0x404040 }, + { "gray26", 0x424242 }, + { "gray27", 0x454545 }, + { "gray28", 0x474747 }, + { "gray29", 0x4A4A4A }, + { "gray3", 0x080808 }, + { "gray30", 0x4D4D4D }, + { "gray31", 0x4F4F4F }, + { "gray32", 0x525252 }, + { "gray33", 0x545454 }, + { "gray34", 0x575757 }, + { "gray35", 0x595959 }, + { "gray36", 0x5C5C5C }, + { "gray37", 0x5E5E5E }, + { "gray38", 0x616161 }, + { "gray39", 0x636363 }, + { "gray4", 0x0A0A0A }, + { "gray40", 0x666666 }, + { "gray41", 0x696969 }, + { "gray42", 0x6B6B6B }, + { "gray43", 0x6E6E6E }, + { "gray44", 0x707070 }, + { "gray45", 0x737373 }, + { "gray46", 0x757575 }, + { "gray47", 0x787878 }, + { "gray48", 0x7A7A7A }, + { "gray49", 0x7D7D7D }, + { "gray5", 0x0D0D0D }, + { "gray50", 0x7F7F7F }, + { "gray51", 0x828282 }, + { "gray52", 0x858585 }, + { "gray53", 0x878787 }, + { "gray54", 0x8A8A8A }, + { "gray55", 0x8C8C8C }, + { "gray56", 0x8F8F8F }, + { "gray57", 0x919191 }, + { "gray58", 0x949494 }, + { "gray59", 0x969696 }, + { "gray6", 0x0F0F0F }, + { "gray60", 0x999999 }, + { "gray61", 0x9C9C9C }, + { "gray62", 0x9E9E9E }, + { "gray63", 0xA1A1A1 }, + { "gray64", 0xA3A3A3 }, + { "gray65", 0xA6A6A6 }, + { "gray66", 0xA8A8A8 }, + { "gray67", 0xABABAB }, + { "gray68", 0xADADAD }, + { "gray69", 0xB0B0B0 }, + { "gray7", 0x121212 }, + { "gray70", 0xB3B3B3 }, + { "gray71", 0xB5B5B5 }, + { "gray72", 0xB8B8B8 }, + { "gray73", 0xBABABA }, + { "gray74", 0xBDBDBD }, + { "gray75", 0xBFBFBF }, + { "gray76", 0xC2C2C2 }, + { "gray77", 0xC4C4C4 }, + { "gray78", 0xC7C7C7 }, + { "gray79", 0xC9C9C9 }, + { "gray8", 0x141414 }, + { "gray80", 0xCCCCCC }, + { "gray81", 0xCFCFCF }, + { "gray82", 0xD1D1D1 }, + { "gray83", 0xD4D4D4 }, + { "gray84", 0xD6D6D6 }, + { "gray85", 0xD9D9D9 }, + { "gray86", 0xDBDBDB }, + { "gray87", 0xDEDEDE }, + { "gray88", 0xE0E0E0 }, + { "gray89", 0xE3E3E3 }, + { "gray9", 0x171717 }, + { "gray90", 0xE5E5E5 }, + { "gray91", 0xE8E8E8 }, + { "gray92", 0xEBEBEB }, + { "gray93", 0xEDEDED }, + { "gray94", 0xF0F0F0 }, + { "gray95", 0xF2F2F2 }, + { "gray96", 0xF5F5F5 }, + { "gray97", 0xF7F7F7 }, + { "gray98", 0xFAFAFA }, + { "gray99", 0xFCFCFC }, + { "green", 0x008000 }, + { "green", 0x00FF00 }, + { "green1", 0x00FF00 }, + { "green2", 0x00EE00 }, + { "green3", 0x00CD00 }, + { "green4", 0x008B00 }, + { "greenyellow", 0xadff2f }, + { "grey", 0xBEBEBE }, + { "grey0", 0x000000 }, + { "grey1", 0x030303 }, + { "grey10", 0x1A1A1A }, + { "grey100", 0xFFFFFF }, + { "grey11", 0x1C1C1C }, + { "grey12", 0x1F1F1F }, + { "grey13", 0x212121 }, + { "grey14", 0x242424 }, + { "grey15", 0x262626 }, + { "grey16", 0x292929 }, + { "grey17", 0x2B2B2B }, + { "grey18", 0x2E2E2E }, + { "grey19", 0x303030 }, + { "grey2", 0x050505 }, + { "grey20", 0x333333 }, + { "grey21", 0x363636 }, + { "grey22", 0x383838 }, + { "grey23", 0x3B3B3B }, + { "grey24", 0x3D3D3D }, + { "grey25", 0x404040 }, + { "grey26", 0x424242 }, + { "grey27", 0x454545 }, + { "grey28", 0x474747 }, + { "grey29", 0x4A4A4A }, + { "grey3", 0x080808 }, + { "grey30", 0x4D4D4D }, + { "grey31", 0x4F4F4F }, + { "grey32", 0x525252 }, + { "grey33", 0x545454 }, + { "grey34", 0x575757 }, + { "grey35", 0x595959 }, + { "grey36", 0x5C5C5C }, + { "grey37", 0x5E5E5E }, + { "grey38", 0x616161 }, + { "grey39", 0x636363 }, + { "grey4", 0x0A0A0A }, + { "grey40", 0x666666 }, + { "grey41", 0x696969 }, + { "grey42", 0x6B6B6B }, + { "grey43", 0x6E6E6E }, + { "grey44", 0x707070 }, + { "grey45", 0x737373 }, + { "grey46", 0x757575 }, + { "grey47", 0x787878 }, + { "grey48", 0x7A7A7A }, + { "grey49", 0x7D7D7D }, + { "grey5", 0x0D0D0D }, + { "grey50", 0x7F7F7F }, + { "grey51", 0x828282 }, + { "grey52", 0x858585 }, + { "grey53", 0x878787 }, + { "grey54", 0x8A8A8A }, + { "grey55", 0x8C8C8C }, + { "grey56", 0x8F8F8F }, + { "grey57", 0x919191 }, + { "grey58", 0x949494 }, + { "grey59", 0x969696 }, + { "grey6", 0x0F0F0F }, + { "grey60", 0x999999 }, + { "grey61", 0x9C9C9C }, + { "grey62", 0x9E9E9E }, + { "grey63", 0xA1A1A1 }, + { "grey64", 0xA3A3A3 }, + { "grey65", 0xA6A6A6 }, + { "grey66", 0xA8A8A8 }, + { "grey67", 0xABABAB }, + { "grey68", 0xADADAD }, + { "grey69", 0xB0B0B0 }, + { "grey7", 0x121212 }, + { "grey70", 0xB3B3B3 }, + { "grey71", 0xB5B5B5 }, + { "grey72", 0xB8B8B8 }, + { "grey73", 0xBABABA }, + { "grey74", 0xBDBDBD }, + { "grey75", 0xBFBFBF }, + { "grey76", 0xC2C2C2 }, + { "grey77", 0xC4C4C4 }, + { "grey78", 0xC7C7C7 }, + { "grey79", 0xC9C9C9 }, + { "grey8", 0x141414 }, + { "grey80", 0xCCCCCC }, + { "grey81", 0xCFCFCF }, + { "grey82", 0xD1D1D1 }, + { "grey83", 0xD4D4D4 }, + { "grey84", 0xD6D6D6 }, + { "grey85", 0xD9D9D9 }, + { "grey86", 0xDBDBDB }, + { "grey87", 0xDEDEDE }, + { "grey88", 0xE0E0E0 }, + { "grey89", 0xE3E3E3 }, + { "grey9", 0x171717 }, + { "grey90", 0xE5E5E5 }, + { "grey91", 0xE8E8E8 }, + { "grey92", 0xEBEBEB }, + { "grey93", 0xEDEDED }, + { "grey94", 0xF0F0F0 }, + { "grey95", 0xF2F2F2 }, + { "grey96", 0xF5F5F5 }, + { "grey97", 0xF7F7F7 }, + { "grey98", 0xFAFAFA }, + { "grey99", 0xFCFCFC }, + { "honeydew", 0xF0FFF0 }, + { "honeydew1", 0xF0FFF0 }, + { "honeydew2", 0xE0EEE0 }, + { "honeydew3", 0xC1CDC1 }, + { "honeydew4", 0x838B83 }, + { "hotpink", 0xff69b4 }, + { "hotpink1", 0xff6eb4 }, + { "hotpink2", 0xee6aa7 }, + { "hotpink3", 0xcd6090 }, + { "hotpink4", 0x8b3a62 }, + { "indianred", 0xcd5c5c }, + { "indianred1", 0xff6a6a }, + { "indianred2", 0xee6363 }, + { "indianred3", 0xcd5555 }, + { "indianred4", 0x8b3a3a }, + { "indigo", 0x4B0082 }, + { "ivory", 0xFFFFF0 }, + { "ivory1", 0xFFFFF0 }, + { "ivory2", 0xEEEEE0 }, + { "ivory3", 0xCDCDC1 }, + { "ivory4", 0x8B8B83 }, + { "khaki", 0xF0E68C }, + { "khaki1", 0xFFF68F }, + { "khaki2", 0xEEE685 }, + { "khaki3", 0xCDC673 }, + { "khaki4", 0x8B864E }, + { "lavender", 0xE6E6FA }, + { "lavenderblush", 0xfff0f5 }, + { "lavenderblush1", 0xfff0f5 }, + { "lavenderblush2", 0xeee0e5 }, + { "lavenderblush3", 0xcdc1c5 }, + { "lavenderblush4", 0x8b8386 }, + { "lawngreen", 0x7cfc00 }, + { "lemonchiffon", 0xfffacd }, + { "lemonchiffon1", 0xfffacd }, + { "lemonchiffon2", 0xeee9bf }, + { "lemonchiffon3", 0xcdc9a5 }, + { "lemonchiffon4", 0x8b8970 }, + { "lightblue", 0xadd8e6 }, + { "lightblue1", 0xbfefff }, + { "lightblue2", 0xb2dfee }, + { "lightblue3", 0x9ac0cd }, + { "lightblue4", 0x68838b }, + { "lightcoral", 0xf08080 }, + { "lightcyan", 0xe0ffff }, + { "lightcyan1", 0xe0ffff }, + { "lightcyan2", 0xd1eeee }, + { "lightcyan3", 0xb4cdcd }, + { "lightcyan4", 0x7a8b8b }, + { "lightgoldenrod", 0xeedd82 }, + { "lightgoldenrod1", 0xffec8b }, + { "lightgoldenrod2", 0xeedc82 }, + { "lightgoldenrod3", 0xcdbe70 }, + { "lightgoldenrod4", 0x8b814c }, + { "lightgoldenrodyellow", 0xfafad2 }, + { "lightgray", 0xd3d3d3 }, + { "lightgreen", 0x90ee90 }, + { "lightgrey", 0xd3d3d3 }, + { "lightpink", 0xffb6c1 }, + { "lightpink1", 0xffaeb9 }, + { "lightpink2", 0xeea2ad }, + { "lightpink3", 0xcd8c95 }, + { "lightpink4", 0x8b5f65 }, + { "lightsalmon", 0xffa07a }, + { "lightsalmon1", 0xffa07a }, + { "lightsalmon2", 0xee9572 }, + { "lightsalmon3", 0xcd8162 }, + { "lightsalmon4", 0x8b5742 }, + { "lightseagreen", 0x20b2aa }, + { "lightskyblue", 0x87cefa }, + { "lightskyblue1", 0xb0e2ff }, + { "lightskyblue2", 0xa4d3ee }, + { "lightskyblue3", 0x8db6cd }, + { "lightskyblue4", 0x607b8b }, + { "lightslateblue", 0x8470ff }, + { "lightslategray", 0x778899 }, + { "lightslategrey", 0x778899 }, + { "lightsteelblue", 0xb0c4de }, + { "lightsteelblue1", 0xcae1ff }, + { "lightsteelblue2", 0xbcd2ee }, + { "lightsteelblue3", 0xa2b5cd }, + { "lightsteelblue4", 0x6e7b8b }, + { "lightyellow", 0xffffe0 }, + { "lightyellow1", 0xffffe0 }, + { "lightyellow2", 0xeeeed1 }, + { "lightyellow3", 0xcdcdb4 }, + { "lightyellow4", 0x8b8b7a }, + { "lime", 0x00FF00 }, + { "limegreen", 0x32cd32 }, + { "linen", 0xFAF0E6 }, + { "magenta", 0xFF00FF }, + { "magenta1", 0xFF00FF }, + { "magenta2", 0xEE00EE }, + { "magenta3", 0xCD00CD }, + { "magenta4", 0x8B008B }, + { "maroon", 0x800000 }, + { "maroon", 0xB03060 }, + { "maroon1", 0xFF34B3 }, + { "maroon2", 0xEE30A7 }, + { "maroon3", 0xCD2990 }, + { "maroon4", 0x8B1C62 }, + { "mediumaquamarine", 0x66cdaa }, + { "mediumblue", 0x0000cd }, + { "mediumforestgreen", 0x32814b }, + { "mediumgoldenrod", 0xd1c166 }, + { "mediumorchid", 0xba55d3 }, + { "mediumorchid1", 0xe066ff }, + { "mediumorchid2", 0xd15fee }, + { "mediumorchid3", 0xb452cd }, + { "mediumorchid4", 0x7a378b }, + { "mediumpurple", 0x9370db }, + { "mediumpurple1", 0xab82ff }, + { "mediumpurple2", 0x9f79ee }, + { "mediumpurple3", 0x8968cd }, + { "mediumpurple4", 0x5d478b }, + { "mediumseagreen", 0x3cb371 }, + { "mediumslateblue", 0x7b68ee }, + { "mediumspringgreen", 0x00fa9a }, + { "mediumturquoise", 0x48d1cc }, + { "mediumvioletred", 0xc71585 }, + { "midnightblue", 0x191970 }, + { "mintcream", 0xf5fffa }, + { "mistyrose", 0xffe4e1 }, + { "mistyrose1", 0xffe4e1 }, + { "mistyrose2", 0xeed5d2 }, + { "mistyrose3", 0xcdb7b5 }, + { "mistyrose4", 0x8b7d7b }, + { "moccasin", 0xFFE4B5 }, + { "navajowhite", 0xffdead }, + { "navajowhite1", 0xffdead }, + { "navajowhite2", 0xeecfa1 }, + { "navajowhite3", 0xcdb38b }, + { "navajowhite4", 0x8b795e }, + { "navy", 0x000080 }, + { "navyblue", 0x000080 }, + { "none", 0x0000FF }, + { "oldlace", 0xfdf5e6 }, + { "olive", 0x808000 }, + { "olivedrab", 0x6b8e23 }, + { "olivedrab1", 0xc0ff3e }, + { "olivedrab2", 0xb3ee3a }, + { "olivedrab3", 0x9acd32 }, + { "olivedrab4", 0x698b22 }, + { "opaque", 0x000000 }, + { "orange", 0xFFA500 }, + { "orange1", 0xFFA500 }, + { "orange2", 0xEE9A00 }, + { "orange3", 0xCD8500 }, + { "orange4", 0x8B5A00 }, + { "orangered", 0xff4500 }, + { "orangered1", 0xff4500 }, + { "orangered2", 0xee4000 }, + { "orangered3", 0xcd3700 }, + { "orangered4", 0x8b2500 }, + { "orchid", 0xDA70D6 }, + { "orchid1", 0xFF83FA }, + { "orchid2", 0xEE7AE9 }, + { "orchid3", 0xCD69C9 }, + { "orchid4", 0x8B4789 }, + { "palegoldenrod", 0xeee8aa }, + { "palegreen", 0x98fb98 }, + { "palegreen1", 0x9aff9a }, + { "palegreen2", 0x90ee90 }, + { "palegreen3", 0x7ccd7c }, + { "palegreen4", 0x548b54 }, + { "paleturquoise", 0xafeeee }, + { "paleturquoise1", 0xbbffff }, + { "paleturquoise2", 0xaeeeee }, + { "paleturquoise3", 0x96cdcd }, + { "paleturquoise4", 0x668b8b }, + { "palevioletred", 0xdb7093 }, + { "palevioletred1", 0xff82ab }, + { "palevioletred2", 0xee799f }, + { "palevioletred3", 0xcd6889 }, + { "palevioletred4", 0x8b475d }, + { "papayawhip", 0xffefd5 }, + { "peachpuff", 0xffdab9 }, + { "peachpuff1", 0xffdab9 }, + { "peachpuff2", 0xeecbad }, + { "peachpuff3", 0xcdaf95 }, + { "peachpuff4", 0x8b7765 }, + { "peru", 0xCD853F }, + { "pink", 0xFFC0CB }, + { "pink1", 0xFFB5C5 }, + { "pink2", 0xEEA9B8 }, + { "pink3", 0xCD919E }, + { "pink4", 0x8B636C }, + { "plum", 0xDDA0DD }, + { "plum1", 0xFFBBFF }, + { "plum2", 0xEEAEEE }, + { "plum3", 0xCD96CD }, + { "plum4", 0x8B668B }, + { "powderblue", 0xb0e0e6 }, + { "purple", 0x800080 }, + { "purple", 0xA020F0 }, + { "purple1", 0x9B30FF }, + { "purple2", 0x912CEE }, + { "purple3", 0x7D26CD }, + { "purple4", 0x551A8B }, + { "red", 0xFF0000 }, + { "red1", 0xFF0000 }, + { "red2", 0xEE0000 }, + { "red3", 0xCD0000 }, + { "red4", 0x8B0000 }, + { "rosybrown", 0xbc8f8f }, + { "rosybrown1", 0xffc1c1 }, + { "rosybrown2", 0xeeb4b4 }, + { "rosybrown3", 0xcd9b9b }, + { "rosybrown4", 0x8b6969 }, + { "royalblue", 0x4169e1 }, + { "royalblue1", 0x4876ff }, + { "royalblue2", 0x436eee }, + { "royalblue3", 0x3a5fcd }, + { "royalblue4", 0x27408b }, + { "saddlebrown", 0x8b4513 }, + { "salmon", 0xFA8072 }, + { "salmon1", 0xFF8C69 }, + { "salmon2", 0xEE8262 }, + { "salmon3", 0xCD7054 }, + { "salmon4", 0x8B4C39 }, + { "sandybrown", 0xf4a460 }, + { "seagreen", 0x2e8b57 }, + { "seagreen1", 0x54ff9f }, + { "seagreen2", 0x4eee94 }, + { "seagreen3", 0x43cd80 }, + { "seagreen4", 0x2e8b57 }, + { "seashell", 0xFFF5EE }, + { "seashell1", 0xFFF5EE }, + { "seashell2", 0xEEE5DE }, + { "seashell3", 0xCDC5BF }, + { "seashell4", 0x8B8682 }, + { "sienna", 0xA0522D }, + { "sienna1", 0xFF8247 }, + { "sienna2", 0xEE7942 }, + { "sienna3", 0xCD6839 }, + { "sienna4", 0x8B4726 }, + { "silver", 0xC0C0C0 }, + { "skyblue", 0x87ceeb }, + { "skyblue1", 0x87ceff }, + { "skyblue2", 0x7ec0ee }, + { "skyblue3", 0x6ca6cd }, + { "skyblue4", 0x4a708b }, + { "slateblue", 0x6a5acd }, + { "slateblue1", 0x836fff }, + { "slateblue2", 0x7a67ee }, + { "slateblue3", 0x6959cd }, + { "slateblue4", 0x473c8b }, + { "slategray", 0x708090 }, + { "slategray1", 0xc6e2ff }, + { "slategray2", 0xb9d3ee }, + { "slategray3", 0x9fb6cd }, + { "slategray4", 0x6c7b8b }, + { "slategrey", 0x708090 }, + { "snow", 0xFFFAFA }, + { "snow1", 0xFFFAFA }, + { "snow2", 0xEEE9E9 }, + { "snow3", 0xCDC9C9 }, + { "snow4", 0x8B8989 }, + { "springgreen", 0x00ff7f }, + { "springgreen1", 0x00ff7f }, + { "springgreen2", 0x00ee76 }, + { "springgreen3", 0x00cd66 }, + { "springgreen4", 0x008b45 }, + { "steelblue", 0x4682b4 }, + { "steelblue1", 0x63b8ff }, + { "steelblue2", 0x5cacee }, + { "steelblue3", 0x4f94cd }, + { "steelblue4", 0x36648b }, + { "tan", 0xD2B48C }, + { "tan1", 0xFFA54F }, + { "tan2", 0xEE9A49 }, + { "tan3", 0xCD853F }, + { "tan4", 0x8B5A2B }, + { "teal", 0x008080 }, + { "thistle", 0xD8BFD8 }, + { "thistle1", 0xFFE1FF }, + { "thistle2", 0xEED2EE }, + { "thistle3", 0xCDB5CD }, + { "thistle4", 0x8B7B8B }, + { "tomato", 0xFF6347 }, + { "tomato1", 0xFF6347 }, + { "tomato2", 0xEE5C42 }, + { "tomato3", 0xCD4F39 }, + { "tomato4", 0x8B3626 }, + { "transparent", 0x0000FF }, + { "turquoise", 0x40E0D0 }, + { "turquoise1", 0x00F5FF }, + { "turquoise2", 0x00E5EE }, + { "turquoise3", 0x00C5CD }, + { "turquoise4", 0x00868B }, + { "violet", 0xEE82EE }, + { "violetred", 0xd02090 }, + { "violetred1", 0xff3e96 }, + { "violetred2", 0xee3a8c }, + { "violetred3", 0xcd3278 }, + { "violetred4", 0x8b2252 }, + { "wheat", 0xF5DEB3 }, + { "wheat1", 0xFFE7BA }, + { "wheat2", 0xEED8AE }, + { "wheat3", 0xCDBA96 }, + { "wheat4", 0x8B7E66 }, + { "white", 0xFFFFFF }, + { "whitesmoke", 0xf5f5f5 }, + { "yellow", 0xFFFF00 }, + { "yellow1", 0xFFFF00 }, + { "yellow2", 0xEEEE00 }, + { "yellow3", 0xCDCD00 }, + { "yellow4", 0x8B8B00 }, + { "yellowgreen", 0x9acd32 }, +#endif /* EXTENDED_XPM_COLORS */ + {"none", 0xFFFFFF} + }; - if (spec[0] == '#') { - char buf[7]; - switch (speclen) { - case 4: - buf[0] = buf[1] = spec[1]; - buf[2] = buf[3] = spec[2]; - buf[4] = buf[5] = spec[3]; - break; - case 7: - memcpy(buf, spec + 1, 6); - break; - case 13: - buf[0] = spec[1]; - buf[1] = spec[2]; - buf[2] = spec[5]; - buf[3] = spec[6]; - buf[4] = spec[9]; - buf[5] = spec[10]; - break; - } - buf[6] = '\0'; - *rgb = (Uint32)strtol(buf, NULL, 16); - return 1; - } else { - int i; - for (i = 0; i < ARRAYSIZE(known); i++) - if (string_equal(known[i].name, spec, speclen)) { - *rgb = known[i].rgb; - return 1; - } - return 0; + if (spec[0] == '#') { + char buf[7]; + switch(speclen) { + case 4: + buf[0] = buf[1] = spec[1]; + buf[2] = buf[3] = spec[2]; + buf[4] = buf[5] = spec[3]; + break; + case 7: + SDL_memcpy(buf, spec + 1, 6); + break; + case 13: + buf[0] = spec[1]; + buf[1] = spec[2]; + buf[2] = spec[5]; + buf[3] = spec[6]; + buf[4] = spec[9]; + buf[5] = spec[10]; + break; } + buf[6] = '\0'; + *rgb = (Uint32)SDL_strtol(buf, NULL, 16); + return 1; + } else { + size_t i; + for (i = 0; i < SDL_arraysize(known); i++) { + if (SDL_strncasecmp(known[i].name, spec, speclen) == 0) { + *rgb = known[i].rgb; + return 1; + } + } + return 0; + } } #ifndef MAX @@ -243,263 +920,278 @@ static const char *error; */ static const char *get_next_line(const char ***lines, SDL_RWops *src, int len) { - char *linebufnew; - if (lines) { - return *(*lines)++; - } else { - char c; - int n; - do { - if (SDL_RWread(src, &c, 1, 1) <= 0) { - error = "Premature end of data"; - return NULL; - } - } while (c != '"'); - if (len) { - len += 4; /* "\",\n\0" */ - if (len > buflen){ - buflen = len; - linebufnew = realloc(linebuf, buflen); - if(!linebufnew) { - free(linebuf); - error = "Out of memory"; - return NULL; - } - linebuf = linebufnew; - } - if (SDL_RWread(src, linebuf, len - 1, 1) <= 0) { - error = "Premature end of data"; - return NULL; - } - n = len - 2; - } else { - n = 0; - do { - if (n >= buflen - 1) { - if (buflen == 0) - buflen = 16; - buflen *= 2; - linebufnew = realloc(linebuf, buflen); - if(!linebufnew) { - free(linebuf); - error = "Out of memory"; - return NULL; - } - linebuf = linebufnew; - } - if (SDL_RWread(src, linebuf + n, 1, 1) <= 0) { - error = "Premature end of data"; - return NULL; - } - } while (linebuf[n++] != '"'); - n--; + char *linebufnew; + + if (lines) { + return *(*lines)++; + } else { + char c; + int n; + do { + if (SDL_RWread(src, &c, 1, 1) <= 0) { + error = "Premature end of data"; + return NULL; + } + } while (c != '"'); + if (len) { + len += 4; /* "\",\n\0" */ + if (len > buflen){ + buflen = len; + linebufnew = (char *)SDL_realloc(linebuf, buflen); + if (!linebufnew) { + SDL_free(linebuf); + error = "Out of memory"; + return NULL; } - linebuf[n] = '\0'; - return linebuf; + linebuf = linebufnew; + } + if (SDL_RWread(src, linebuf, len - 1, 1) <= 0) { + error = "Premature end of data"; + return NULL; + } + n = len - 2; + } else { + n = 0; + do { + if (n >= buflen - 1) { + if (buflen == 0) + buflen = 16; + buflen *= 2; + linebufnew = (char *)SDL_realloc(linebuf, buflen); + if (!linebufnew) { + SDL_free(linebuf); + error = "Out of memory"; + return NULL; + } + linebuf = linebufnew; + } + if (SDL_RWread(src, linebuf + n, 1, 1) <= 0) { + error = "Premature end of data"; + return NULL; + } + } while (linebuf[n++] != '"'); + n--; } + linebuf[n] = '\0'; + return linebuf; + } } -#define SKIPSPACE(p) \ -do { \ - while (isspace((unsigned char)*(p))) \ - ++(p); \ +#define SKIPSPACE(p) \ +do { \ + while (SDL_isspace((unsigned char)*(p))) \ + ++(p); \ } while (0) -#define SKIPNONSPACE(p) \ -do { \ - while (!isspace((unsigned char)*(p)) && *p) \ - ++(p); \ +#define SKIPNONSPACE(p) \ +do { \ + while (!SDL_isspace((unsigned char)*(p)) && *p) \ + ++(p); \ } while (0) /* read XPM from either array or RWops */ static SDL_Surface *load_xpm(const char **xpm, SDL_RWops *src) { - SDL_Surface *image = NULL; - int indexc; - int x, y; - int w, h, ncolors, cpp; - int indexed; - Uint8 *dst; - struct color_hash *colors = NULL; - SDL_Color *im_colors = NULL; - char *keystrings = NULL, *nextkey; - const char *line; - const char ***xpmlines = NULL; - int pixels_len; + Sint64 start = 0; + SDL_Surface *image = NULL; + int index; + int x, y; + int w, h, ncolors, cpp; + int indexed; + Uint8 *dst; + struct color_hash *colors = NULL; + SDL_Color *im_colors = NULL; + char *keystrings = NULL, *nextkey; + const char *line; + const char ***xpmlines = NULL; + int pixels_len; - error = NULL; - linebuf = NULL; - buflen = 0; + error = NULL; + linebuf = NULL; + buflen = 0; - if (xpm) - xpmlines = &xpm; + if (src) + start = SDL_RWtell(src); + if (xpm) + xpmlines = &xpm; + + line = get_next_line(xpmlines, src, 0); + if (!line) + goto done; + /* + * The header string of an XPMv3 image has the format + * + * [ ] + * + * where the hotspot coords are intended for mouse cursors. + * Right now we don't use the hotspots but it should be handled + * one day. + */ + if (SDL_sscanf(line, "%d %d %d %d", &w, &h, &ncolors, &cpp) != 4 + || w <= 0 || h <= 0 || ncolors <= 0 || cpp <= 0) { + error = "Invalid format description"; + goto done; + } + + keystrings = (char *)SDL_malloc(ncolors * cpp); + if (!keystrings) { + error = "Out of memory"; + goto done; + } + nextkey = keystrings; + + /* Create the new surface */ + if (ncolors <= 256) { + indexed = 1; + image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 8, + 0, 0, 0, 0); + im_colors = image->format->palette->colors; + image->format->palette->ncolors = ncolors; + } else { + indexed = 0; + image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, + 0xff0000, 0x00ff00, 0x0000ff, 0); + } + if (!image) { + /* Hmm, some SDL error (out of memory?) */ + goto done; + } + + /* Read the colors */ + colors = create_colorhash(ncolors); + if (!colors) { + error = "Out of memory"; + goto done; + } + for (index = 0; index < ncolors; ++index ) { + const char *p; line = get_next_line(xpmlines, src, 0); if (!line) - goto done; - /* - * The header string of an XPMv3 image has the format - * - * [ ] - * - * where the hotspot coords are intended for mouse cursors. - * Right now we don't use the hotspots but it should be handled - * one day. - */ - if (sscanf(line, "%d %d %d %d", &w, &h, &ncolors, &cpp) != 4 - || w <= 0 || h <= 0 || ncolors <= 0 || cpp <= 0) { - error = "Invalid format description"; - goto done; - } + goto done; - keystrings = malloc(ncolors * cpp); - if (!keystrings) { - error = "Out of memory"; - goto done; - } - nextkey = keystrings; + p = line + cpp + 1; - /* Create the new surface */ - if (ncolors <= 256) { - indexed = 1; - image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 8, - 0, 0, 0, 0); - im_colors = image->format->palette->colors; - image->format->palette->ncolors = ncolors; + /* parse a colour definition */ + for (;;) { + char nametype; + const char *colname; + Uint32 rgb, pixel; + + SKIPSPACE(p); + if (!*p) { + error = "colour parse error"; + goto done; + } + nametype = *p; + SKIPNONSPACE(p); + SKIPSPACE(p); + colname = p; + SKIPNONSPACE(p); + if (nametype == 's') + continue; /* skip symbolic colour names */ + + if (!color_to_rgb(colname, (int)(p - colname), &rgb)) + continue; + + SDL_memcpy(nextkey, line, cpp); + if (indexed) { + SDL_Color *c = im_colors + index; + c->r = (Uint8)(rgb >> 16); + c->g = (Uint8)(rgb >> 8); + c->b = (Uint8)(rgb); + pixel = index; + } else + pixel = rgb; + add_colorhash(colors, nextkey, cpp, pixel); + nextkey += cpp; + if (rgb == 0xffffffff) + SDL_SetColorKey(image, SDL_TRUE, pixel); + break; + } + } + + /* Read the pixels */ + pixels_len = w * cpp; + dst = (Uint8 *)image->pixels; + for (y = 0; y < h; y++) { + line = get_next_line(xpmlines, src, pixels_len); + if (!line) + goto done; + + if (indexed) { + /* optimization for some common cases */ + if (cpp == 1) + for (x = 0; x < w; x++) + dst[x] = (Uint8)QUICK_COLORHASH(colors, + line + x); + else + for (x = 0; x < w; x++) + dst[x] = (Uint8)get_colorhash(colors, + line + x * cpp, + cpp); } else { - indexed = 0; - image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, - 0xff0000, 0x00ff00, 0x0000ff, 0); - } - if (!image) { - /* Hmm, some SDL error (out of memory?) */ - goto done; - } - - /* Read the colors */ - colors = create_colorhash(ncolors); - if (!colors) { - error = "Out of memory"; - goto done; - } - for (indexc = 0; indexc < ncolors; ++indexc ) { - const char *p; - line = get_next_line(xpmlines, src, 0); - if (!line) - goto done; - - p = line + cpp + 1; - - /* parse a colour definition */ - for (;;) { - char nametype; - const char *colname; - Uint32 rgb, pixel; - - SKIPSPACE(p); - if (!*p) { - error = "colour parse error"; - goto done; - } - nametype = *p; - SKIPNONSPACE(p); - SKIPSPACE(p); - colname = p; - SKIPNONSPACE(p); - if (nametype == 's') - continue; /* skip symbolic colour names */ - - if (!color_to_rgb(colname, p - colname, &rgb)) - continue; - - memcpy(nextkey, line, cpp); - if (indexed) { - SDL_Color *c = im_colors + indexc; - c->r = (Uint8)(rgb >> 16); - c->g = (Uint8)(rgb >> 8); - c->b = (Uint8)(rgb); - pixel = indexc; - } else - pixel = rgb; - add_colorhash(colors, nextkey, cpp, pixel); - nextkey += cpp; - if (rgb == 0xffffffff) - SDL_SetColorKey(image, SDL_SRCCOLORKEY, pixel); - break; - } - } - - /* Read the pixels */ - pixels_len = w * cpp; - dst = image->pixels; - for (y = 0; y < h; y++) { - line = get_next_line(xpmlines, src, pixels_len); - if (indexed) { - /* optimization for some common cases */ - if (cpp == 1) - for (x = 0; x < w; x++) - dst[x] = (Uint8)QUICK_COLORHASH(colors, - line + x); - else - for (x = 0; x < w; x++) - dst[x] = (Uint8)get_colorhash(colors, - line + x * cpp, - cpp); - } else { - for (x = 0; x < w; x++) - ((Uint32*)dst)[x] = get_colorhash(colors, - line + x * cpp, - cpp); - } - dst += image->pitch; + for (x = 0; x < w; x++) + ((Uint32*)dst)[x] = get_colorhash(colors, + line + x * cpp, + cpp); } + dst += image->pitch; + } done: - if (error) { - SDL_FreeSurface(image); - image = NULL; - SDL_SetError(error); + if (error) { + if ( src ) + SDL_RWseek(src, start, RW_SEEK_SET); + if ( image ) { + SDL_FreeSurface(image); + image = NULL; } - free(keystrings); - free_colorhash(colors); - free(linebuf); - return(image); + IMG_SetError("%s", error); + } + if (keystrings) + SDL_free(keystrings); + free_colorhash(colors); + if (linebuf) + SDL_free(linebuf); + return(image); } /* Load a XPM type image from an RWops datasource */ -#if 0 SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src) { - if ( !src ) { - /* The error message has been set in SDL_RWFromFile */ - return NULL; - } - return load_xpm(NULL, src); + if ( !src ) { + /* The error message has been set in SDL_RWFromFile */ + return NULL; + } + return load_xpm(NULL, src); } -#endif -static inline SDL_Surface *IMG_ReadXPMFromArray(const char **xpm) +SDL_Surface *IMG_ReadXPMFromArray(const char **xpm) { - return load_xpm(xpm, NULL); + if (!xpm) { + IMG_SetError("array is NULL"); + return NULL; + } + return load_xpm(xpm, NULL); } #else /* not LOAD_XPM */ /* See if an image is contained in a data source */ -#if 0 int IMG_isXPM(SDL_RWops *src) { - return(0); + return(0); } + /* Load a XPM type image from an SDL datasource */ SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src) { - return(NULL); + return(NULL); } -#endif -static inline SDL_Surface *IMG_ReadXPMFromArray(const char **xpm) +SDL_Surface *IMG_ReadXPMFromArray(char **xpm) { return NULL; } diff --git a/src/sdl/SDL_icon.xpm b/src/sdl/SDL_icon.xpm index cf72960df..ba252276a 100644 --- a/src/sdl/SDL_icon.xpm +++ b/src/sdl/SDL_icon.xpm @@ -1,5 +1,5 @@ /* XPM */ -static char * C:\Repo\srb2\src\sdl\SDL_icon_xpm[] = { +static const char * SDL_icon_xpm[] = { "32 32 390 2", " c None", ". c #4F4F70", diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 4eab0ae3c..ce84e86a6 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -39,6 +39,10 @@ #ifdef HAVE_IMAGE #include "SDL_image.h" +#elif 1 +#define LOAD_XPM //I want XPM! +#include "IMG_xpm.c" //Alam: I don't want to add SDL_Image.dll/so +#define HAVE_IMAGE //I have SDL_Image, sortof #endif #ifdef HAVE_IMAGE From 70c99ed669f72eb8cb80f7428272ef9de9773255 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 10 Aug 2018 17:12:26 -0400 Subject: [PATCH 28/79] Add lapbegunat and lapstartedtime player variables There is no lapfinishedtime because [mare]finishedtime refers to when Egg Capsule is destroyed. That concept does not apply to laps. --- src/d_player.h | 2 ++ src/lua_playerlib.c | 8 ++++++++ src/p_inter.c | 2 ++ src/p_map.c | 2 ++ src/p_saveg.c | 4 ++++ src/p_setup.c | 3 ++- src/p_user.c | 3 ++- 7 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 498b7166d..648867b92 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -463,6 +463,8 @@ typedef struct player_s tic_t marebegunat; // Leveltime when mare begun tic_t startedtime; // Time which you started this mare with. tic_t finishedtime; // Time it took you to finish the mare (used for display) + tic_t lapbegunat; // Leveltime when lap begun + tic_t lapstartedtime; // Time which you started this lap with. INT16 finishedspheres; // The spheres you had left upon finishing the mare INT16 finishedrings; // The rings/stars you had left upon finishing the mare UINT32 marescore; // score for this nights stage diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 7deefa7ff..114aa27b0 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -300,6 +300,10 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->startedtime); else if (fastcmp(field,"finishedtime")) lua_pushinteger(L, plr->finishedtime); + else if (fastcmp(field,"lapbegunat")) + lua_pushinteger(L, plr->lapbegunat); + else if (fastcmp(field,"lapstartedtime")) + lua_pushinteger(L, plr->lapstartedtime); else if (fastcmp(field,"finishedspheres")) lua_pushinteger(L, plr->finishedspheres); else if (fastcmp(field,"finishedrings")) @@ -588,6 +592,10 @@ static int player_set(lua_State *L) plr->startedtime = (tic_t)luaL_checkinteger(L, 3); else if (fastcmp(field,"finishedtime")) plr->finishedtime = (tic_t)luaL_checkinteger(L, 3); + else if (fastcmp(field,"lapbegunat")) + plr->lapbegunat = (tic_t)luaL_checkinteger(L, 3); + else if (fastcmp(field,"lapstartedtime")) + plr->lapstartedtime = (tic_t)luaL_checkinteger(L, 3); else if (fastcmp(field,"finishedspheres")) plr->finishedspheres = (INT16)luaL_checkinteger(L, 3); else if (fastcmp(field,"finishedrings")) diff --git a/src/p_inter.c b/src/p_inter.c index ce8bba6b6..e7590439e 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1134,6 +1134,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) { player->nightstime += special->info->speed; player->startedtime += special->info->speed; + player->lapstartedtime += special->info->speed; P_RestoreMusic(player); } else @@ -1143,6 +1144,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) { players[i].nightstime += special->info->speed; players[i].startedtime += special->info->speed; + players[i].lapstartedtime += special->info->speed; P_RestoreMusic(&players[i]); } if (special->info->deathsound != sfx_None) diff --git a/src/p_map.c b/src/p_map.c index 415e2bac1..7d3538573 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1114,6 +1114,8 @@ static boolean PIT_CheckThing(mobj_t *thing) )) { pl->marelap++; + pl->lapbegunat = leveltime; + pl->lapstartedtime = pl->nightstime; if (pl->bonustime) { diff --git a/src/p_saveg.c b/src/p_saveg.c index 2b48cc783..e18802f57 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -204,6 +204,8 @@ static void P_NetArchivePlayers(void) WRITEUINT32(save_p, players[i].marebegunat); WRITEUINT32(save_p, players[i].startedtime); WRITEUINT32(save_p, players[i].finishedtime); + WRITEUINT32(save_p, players[i].lapbegunat); + WRITEUINT32(save_p, players[i].lapstartedtime); WRITEINT16(save_p, players[i].finishedspheres); WRITEINT16(save_p, players[i].finishedrings); WRITEUINT32(save_p, players[i].marescore); @@ -397,6 +399,8 @@ static void P_NetUnArchivePlayers(void) players[i].marebegunat = READUINT32(save_p); players[i].startedtime = READUINT32(save_p); players[i].finishedtime = READUINT32(save_p); + players[i].lapbegunat = READUINT32(save_p); + players[i].lapstartedtime = READUINT32(save_p); players[i].finishedspheres = READINT16(save_p); players[i].finishedrings = READINT16(save_p); players[i].marescore = READUINT32(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index e4b6034b1..4b10f431c 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2387,7 +2387,8 @@ static void P_LevelInitStuff(void) players[i].linktimer = players[i].flyangle =\ players[i].anotherflyangle = players[i].nightstime =\ players[i].mare = players[i].marelap =\ - players[i].marebonuslap = players[i].realtime =\ + players[i].marebonuslap = players[i].lapbegunat =\ + players[i].lapstartedtime = players[i].realtime =\ players[i].exiting = 0; // i guess this could be part of the above but i feel mildly uncomfortable implicitly casting diff --git a/src/p_user.c b/src/p_user.c index 99ecb8154..ddaa6384a 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -666,7 +666,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->followitem = skins[DEFAULTNIGHTSSKIN].followitem; } - player->nightstime = player->startedtime = nighttime*TICRATE; + player->nightstime = player->startedtime = player->lapstartedtime = nightstime*TICRATE; player->bonustime = false; P_RestoreMusic(player); @@ -762,6 +762,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->lastmarescore = player->marescore; player->marescore = 0; player->marebegunat = leveltime; + player->lapbegunat = leveltime; player->spheres = player->rings = 0; } From e04d13f647da8bd75da2684eca7637f3fddc0bea Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Fri, 10 Aug 2018 17:16:10 -0400 Subject: [PATCH 29/79] TravisCI: remove gcc-5 test, gcc-5 package is gone --- .travis.yml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4648ae567..c2f7f1f72 100644 --- a/.travis.yml +++ b/.travis.yml @@ -71,20 +71,6 @@ matrix: - gcc-4.9 compiler: gcc-4.9 #gcc-4.9 (Ubuntu 4.9.3-8ubuntu2~14.04) 4.9.3 - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - libsdl2-mixer-dev - - libpng-dev - - libgl1-mesa-dev - - libgme-dev - - p7zip-full - - gcc-5 - compiler: gcc-5 - #gcc-5 (Ubuntu 5.3.0-3ubuntu1~14.04) 5.3.0 20151204 - os: linux addons: apt: From cd790eca9104b85e5ae83dd9b5fc6516ff9bf4ce Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Fri, 10 Aug 2018 19:02:03 -0400 Subject: [PATCH 30/79] Fix white being treated as transparent. --- src/sdl/SDL_icon.xpm | 624 ++++++++++++++----------------------------- 1 file changed, 206 insertions(+), 418 deletions(-) diff --git a/src/sdl/SDL_icon.xpm b/src/sdl/SDL_icon.xpm index ba252276a..30259d55e 100644 --- a/src/sdl/SDL_icon.xpm +++ b/src/sdl/SDL_icon.xpm @@ -1,425 +1,213 @@ /* XPM */ -static const char * SDL_icon_xpm[] = { -"32 32 390 2", -" c None", -". c #4F4F70", -"+ c #4D4D87", -"@ c #4D4D84", -"# c #4E4E6C", -"$ c #6C6C95", -"% c #5E5EB2", -"& c #6B6BE7", -"* c #7373F9", -"= c #7C7CFF", -"- c #6F70E7", -"; c #494BB2", -"> c #4F4FA3", -", c #6464D4", -"' c #7979F5", -") c #5F5FCA", -"! c #5D5D93", -"~ c #3A3A9F", -"{ c #6060AC", -"] c #777793", -"^ c #5C5CB3", -"/ c #7373EA", -"( c #7A7AFF", -"_ c #7575FF", -": c #7979FF", -"< c #6264DD", -"[ c #47478C", -"} c #564567", -"| c #4647D0", -"1 c #5C5CAE", -"2 c #5E5EFF", -"3 c #2929FF", -"4 c #1D1DFF", -"5 c #1919D1", -"6 c #4F4F90", -"7 c #1E1ECE", -"8 c #5858FF", -"9 c #6767A8", -"0 c #4949A0", -"a c #7070FB", -"b c #7D7DFF", -"c c #7777FF", -"d c #7373FF", -"e c #7272FF", -"f c #7878FF", -"g c #6465D8", -"h c #363886", -"i c #9F7655", -"j c #C89B5C", -"k c #1D1CB7", -"l c #3031B1", -"m c #1919F4", -"n c #1111FF", -"o c #1818FF", -"p c #1B1BFF", -"q c #1C1CFF", -"r c #2626B3", -"s c #1E1EC8", -"t c #1A1AE8", -"u c #24249F", -"v c #2F2FD2", -"w c #7676FF", -"x c #6869E2", -"y c #414290", -"z c #8C6751", -"A c #FCBA68", -"B c #E9BD7D", -"C c #201EB8", -"D c #090AB8", -"E c #1616EB", -"F c #1818FD", -"G c #1414EE", -"H c #1010E1", -"I c #0E0EE2", -"J c #0E0EF4", -"K c #0606B2", -"L c #7A7A89", -"M c #0C0C9A", -"N c #0A0AA7", -"O c #2424E4", -"P c #6669E6", -"Q c #4F4A8F", -"R c #BF853B", -"S c #FFD98D", -"T c #CDAB76", -"U c #1717C4", -"V c #0F10BA", -"W c #0909B6", -"X c #0505C3", -"Y c #0000B6", -"Z c #0000BE", -"` c #0000AD", -" . c #1D1D83", -".. c #63638E", -"+. c #090975", -"@. c #1414F3", -"#. c #5B5BFF", -"$. c #7B7BFF", -"%. c #7070FF", -"&. c #6E6EFF", -"*. c #7172F6", -"=. c #625DAF", -"-. c #BA9E6C", -";. c #887167", -">. c #090DF2", -",. c #1313BE", -"'. c #000085", -"). c #0000AC", -"!. c #0202AA", -"~. c #242488", -"{. c #1414C7", -"]. c #1717FF", -"^. c #5959FF", -"/. c #7F7FFF", -"(. c #7474FF", -"_. c #7171FF", -":. c #8686FF", -"<. c #7574FF", -"[. c #797CFF", -"}. c #5756B8", -"|. c #1C19A4", -"1. c #1617FF", -"2. c #1212BD", -"3. c #040485", -"4. c #0707A4", -"5. c #1B1B71", -"6. c #373797", -"7. c #1616FF", -"8. c #5050FF", -"9. c #8080FF", -"0. c #AAAAFF", -"a. c #AEAEF6", -"b. c #8A8AEF", -"c. c #6969FB", -"d. c #2728FF", -"e. c #1314FF", -"f. c #1919FF", -"g. c #1313E8", -"h. c #1F1FF4", -"i. c #5454FF", -"j. c #6D6DF0", -"k. c #6868B5", -"l. c #0B0BB8", -"m. c #1212C5", -"n. c #1616FC", -"o. c #1515FF", -"p. c #1212FF", -"q. c #2323FF", -"r. c #3636FF", -"s. c #4040FF", -"t. c #4343F9", -"u. c #5D5DB8", -"v. c #7F7F92", -"w. c #878793", -"x. c #4B4B94", -"y. c #0B0CE2", -"z. c #1313FF", -"A. c #4C4CFF", -"B. c #8282FF", -"C. c #7171ED", -"D. c #636394", -"E. c #575785", -"F. c #A9A99C", -"G. c #1414BC", -"H. c #1414FF", -"I. c #0707FD", -"J. c #2525AA", -"K. c #A8A8A4", -"L. c #EBEBE2", -"M. c #F9F9F2", -"N. c #E1E1CC", -"O. c #4D4D9F", -"P. c #0B0BF7", -"Q. c #2121FF", -"R. c #3232FF", -"S. c #5555FF", -"T. c #6161B4", -"U. c #B5B5B2", -"V. c #FFFFF8", -"W. c #4F4F9A", -"X. c #0B0BF5", -"Y. c #1616C5", -"Z. c #A8A8A1", -"`. c #FFFFFC", -" + c #FFFFFF", -".+ c #C0C0C4", -"++ c #1212D4", -"@+ c #4444FF", -"#+ c #6464FF", -"$+ c #8383FF", -"%+ c #6767C3", -"&+ c #E4E4E4", -"*+ c #9494AE", -"=+ c #0808DF", -"-+ c #0D0DF2", -";+ c #61619A", -">+ c #F1F1E0", -",+ c #E8E8DD", -"'+ c #2424BB", -")+ c #1010FF", -"!+ c #3434FF", -"~+ c #6161FF", -"{+ c #6969D2", -"]+ c #EFEFF0", -"^+ c #C2C2BA", -"/+ c #1010B6", -"(+ c #0909AC", -"_+ c #A4A49A", -":+ c #EAEADE", -"<+ c #2525B8", -"[+ c #2F2FFF", -"}+ c #3C3CB5", -"|+ c #EEEEEE", -"1+ c #BBBBAD", -"2+ c #0B0B56", -"3+ c #0B0BFC", -"4+ c #1212EF", -"5+ c #0C0C3E", -"6+ c #919187", -"7+ c #DEDED6", -"8+ c #1F1FC0", -"9+ c #1A1AFF", -"0+ c #1717FA", -"a+ c #1515F8", -"b+ c #1111FC", -"c+ c #494992", -"d+ c #999998", -"e+ c #3E3E3B", -"f+ c #3C3C99", -"g+ c #535397", -"h+ c #5A5A4D", -"i+ c #6F6F70", -"j+ c #BFBFC9", -"k+ c #1111D6", -"l+ c #1515F1", -"m+ c #0F0FE2", -"n+ c #0D0DD9", -"o+ c #0909CD", -"p+ c #0808C7", -"q+ c #0505C7", -"r+ c #0303CB", -"s+ c #0101C0", -"t+ c #0202AF", -"u+ c #0606AC", -"v+ c #121283", -"w+ c #BBBBBB", -"x+ c #BEBEBE", -"y+ c #2F2F2E", -"z+ c #C7C8BB", -"A+ c #D8DAD1", -"B+ c #272828", -"C+ c #929292", -"D+ c #8688C7", -"E+ c #0506F6", -"F+ c #1616F5", -"G+ c #0B0BD3", -"H+ c #0202B6", -"I+ c #0000AF", -"J+ c #0000B4", -"K+ c #0000BD", -"L+ c #0000BB", -"M+ c #00009E", -"N+ c #2C2C7E", -"O+ c #6A6A8B", -"P+ c #959595", -"Q+ c #F0F0F1", -"R+ c #E1E1E1", -"S+ c #8C8E90", -"T+ c #BEBEBF", -"U+ c #C9C7C5", -"V+ c #939699", -"W+ c #E7EAED", -"X+ c #CBCBC7", -"Y+ c #413B9B", -"Z+ c #0607DD", -"`+ c #0C0CE2", -" @ c #0303B9", -".@ c #0000A8", -"+@ c #181888", -"@@ c #6A6A6A", -"#@ c #626263", -"$@ c #4B4B4C", -"%@ c #3E3B36", -"&@ c #9B805C", -"*@ c #D9B07D", -"=@ c #C9AE89", -"-@ c #B9AF9E", -";@ c #C7C5C4", -">@ c #CBCCCF", -",@ c #C7C6C6", -"'@ c #AEA59A", -")@ c #B69974", -"!@ c #D8B87F", -"~@ c #9B8272", -"{@ c #0E0B9B", -"]@ c #0000B7", -"^@ c #0000B8", -"/@ c #000082", -"(@ c #00007A", -"_@ c #636379", -":@ c #62533E", -"<@ c #B59B6C", -"[@ c #DEB07B", -"}@ c #FECC90", -"|@ c #FFCE92", -"1@ c #FEC98C", -"2@ c #F1BD82", -"3@ c #D1A979", -"4@ c #BC9E73", -"5@ c #CCA777", -"6@ c #EAB980", -"7@ c #FFCD90", -"8@ c #FFD595", -"9@ c #FDD782", -"0@ c #413678", -"a@ c #0000AE", -"b@ c #000077", -"c@ c #010193", -"d@ c #0C0CE4", -"e@ c #38389E", -"f@ c #EEC585", -"g@ c #FFDA9D", -"h@ c #FFC992", -"i@ c #FFC88F", -"j@ c #FFC990", -"k@ c #FFCE93", -"l@ c #FFD094", -"m@ c #FFCC92", -"n@ c #C9A174", -"o@ c #EDBD88", -"p@ c #FAD287", -"q@ c #3A2F7F", -"r@ c #0000BA", -"s@ c #0000B0", -"t@ c #0101B2", -"u@ c #1111ED", -"v@ c #1919C1", -"w@ c #95887C", -"x@ c #DCAC6E", -"y@ c #FFD393", -"z@ c #FFCD94", -"A@ c #FFCA93", -"B@ c #FFC991", -"C@ c #FFC78E", -"D@ c #FFCB91", -"E@ c #E0B581", -"F@ c #BB9A6F", -"G@ c #FFDC97", -"H@ c #C1A173", -"I@ c #0E0B9A", -"J@ c #0000B5", -"K@ c #0101B6", -"L@ c #1010E0", -"M@ c #1616EC", -"N@ c #A68156", -"O@ c #E7AC6B", -"P@ c #FFC582", -"Q@ c #FFCF8F", -"R@ c #FFD195", -"S@ c #FFD296", -"T@ c #FFD396", -"U@ c #FFD193", -"V@ c #FFD28F", -"W@ c #D2A96B", -"X@ c #2F2482", -"Y@ c #0000C1", -"Z@ c #0000C0", -"`@ c #0000BF", -" # c #0101BF", -".# c #1212F0", -"+# c #767698", -"@# c #9C866E", -"## c #A9865D", -"$# c #C0915D", -"%# c #C89760", -"&# c #C29360", -"*# c #AD8A61", -"=# c #9D8971", -"-# c #7F7A7A", -";# c #70708F", -"># c #6F6F91", -",# c #575788", -"'# c #464687", -")# c #2F2F87", -"!# c #15158F", -"~# c #0101A8", -"{# c #1313FB", -"]# c #57579F", -"^# c #343487", -"/# c #434388", +static const char *SDL_icon_xpm[] = { +/* columns rows colors chars-per-pixel */ +"32 32 175 2 ", +" c None", +". c #2E2E2E", +"X c #3C3C3C", +"o c #493939", +"O c #4E473F", +"+ c #161658", +"@ c #131369", +"# c #06067B", +"$ c #111173", +"% c #16167F", +"& c #252567", +"* c #372B7C", +"= c #3D3679", +"- c #41414A", +"; c #575655", +": c #6A5841", +"> c #5B4B72", +", c #616160", +"< c #7B7B7B", +"1 c #906E49", +"2 c #89685D", +"3 c #A67B4A", +"4 c #AA7F50", +"5 c #9B7560", +"6 c #856C78", +"7 c #997B7D", +"8 c #B48552", +"9 c #BA8A55", +"0 c #A48665", +"q c #B98F67", +"w c #B9946A", +"e c #B7937A", +"r c #C8955C", +"t c #CA9966", +"y c #DAA469", +"u c #C9A37B", +"i c #D7AB7B", +"p c #DFB07D", +"a c #EBAE6A", +"s c #E5B27A", +"d c #F1B779", +"f c #0A0A83", +"g c #05058B", +"h c #060687", +"j c #101089", +"k c #131382", +"l c #040494", +"z c #02029D", +"x c #0C0B9C", +"c c #120F9E", +"v c #19199B", +"b c #382D84", +"n c #39398D", +"m c #222296", +"M c #0101A6", +"N c #0A0AA2", +"B c #0202AC", +"V c #1919A2", +"C c #1616AD", +"Z c #0000B5", +"A c #0202BC", +"S c #0C0CB6", +"D c #1313B3", +"F c #1011BD", +"G c #1B1BBE", +"H c #2B2BAC", +"J c #3737A1", +"K c #2A26BE", +"L c #2A29B4", +"P c #3B3BB8", +"I c #48478B", +"U c #57578A", +"Y c #4A499A", +"T c #524F95", +"R c #565399", +"E c #4C4CA8", +"W c #524DA7", +"Q c #5353A4", +"! c #5555A9", +"~ c #5555B4", +"^ c #5656B7", +"/ c #6464A6", +"( c #6F67B5", +") c #0404C3", +"_ c #0707CA", +"` c #1414CB", +"' c #1A1AC6", +"] c #0A0AD3", +"[ c #0D0DDC", +"{ c #1A1AD4", +"} c #1010DF", +"| c #1E1EDE", +" . c #1817DE", +".. c #221FCA", +"X. c #2B2BCC", +"o. c #2727C9", +"O. c #3434C3", +"+. c #3434D4", +"@. c #0F0FE2", +"#. c #1313E5", +"$. c #1515ED", +"%. c #1B1BEA", +"&. c #1C1CE4", +"*. c #1515F4", +"=. c #1818F3", +"-. c #1717FD", +";. c #1818FF", +":. c #2B2BE9", +">. c #2424FF", +",. c #2A2AFF", +"<. c #2222F1", +"1. c #3737FF", +"2. c #5D5DC3", +"3. c #5F5FC9", +"4. c #5655C2", +"5. c #4747D1", +"6. c #5B5BD4", +"7. c #6565C8", +"8. c #6363DA", +"9. c #4545FF", +"0. c #4D4DFC", +"q. c #5454FF", +"w. c #5959FF", +"e. c #6969E5", +"r. c #6B6CEA", +"t. c #6666E7", +"y. c #6B6BFE", +"u. c #6767F8", +"i. c #7070F6", +"p. c #7373FF", +"a. c #7C7CFF", +"s. c #91918F", +"d. c #8F9090", +"f. c #979797", +"g. c #9C9C9C", +"h. c #8585A1", +"j. c #9C9CA7", +"k. c #9292B6", +"l. c #A4A4A4", +"z. c #BDB2A4", +"x. c #A4A4B1", +"c. c #BFBFBD", +"v. c #BABAB7", +"b. c #C8AA87", +"n. c #DAAE82", +"m. c #DBB081", +"M. c #EBBA85", +"N. c #F3BF84", +"B. c #F2BE88", +"V. c #C2B3A3", +"C. c #FBC386", +"Z. c #FCC68C", +"A. c #FFC88F", +"S. c #F4C387", +"D. c #FFC990", +"F. c #C3C1BF", +"G. c #8F8FCB", +"H. c #BDBDC2", +"J. c #BDBDD1", +"K. c #8888F9", +"L. c #A4A4FB", +"P. c #CDCDCC", +"I. c #CECAC6", +"U. c #D3CFCA", +"Y. c #D3D0CC", +"T. c #C0C0D5", +"R. c #D6D5D4", +"E. c #D7D7DD", +"W. c #E1E1DF", +"Q. c #DEDEE1", +"!. c #E4E4E4", +"~. c #E8E8E8", +"^. c #F0F0EE", +"/. c #F5F5F2", +"(. c #FFFFFF", +/* pixels */ " ", " ", " ", -" . + @ # ", -" $ % & * = - ; > , ' ) ! ", -" ~ { ] ^ / = ( _ : < [ } | 1 2 3 4 5 6 ", -" 7 8 9 0 a b c d e f g h i j k l m n o p q r ", -" s t u v _ f d d d w x y z A B C D E F G H I J K L ", -" M N O _ c e d d d _ P Q R S T U V W X Y Z ` ... ", -" +.@.#.$.d d d d %.&._ *.=.-.;.>.,.'.).!.~. ", -" {.].^./.(.d d _.$.:._ <.[.}.|.1.2.3.4.5. ", -" 6.7.7.4 8.e : w 9.0.a.b.c.2 d.e.f.g.h.i.j.k. ", -" l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.o o z.A.B./.b C.D. ", -" E.F.G.].o H.z.I.J.K.L.M.N.O.P.o o o Q.R.S._.b B.T. ", -" U.V.W.X.f.f.7.Y.Z.`. + + +.+++].o o o.n z.q.@+#+$+%+ ", -" &+ +*+=+].o -+;+>+ + + + +,+'+H.o o o o o H.)+o !+~+{+ ", -" ]+ +^+/+H.o.(+_+ + + + + +:+<+z.o o o o o o o 7.n H.[+}+ ", -" |+ +1+2+3+4+5+6+ + + + + +7+8+H.o o f.9+f.9+f.F 0+a+b+o.c+ ", -" &+ +d+e+f+g+h+i+ + + + + +j+k+].f.9+l+m+n+o+p+q+r+s+t+u+v+ ", -" w+ +x+y+z+A+B+C+ + + + + +D+E+9+F+G+H+I+J+K+L+M+N+O+ ", -" P+Q+R+S+T+U+V+W+ + + + +X+Y+Z+`+ @I+J+Z .@+@E. ", -" @@#@$@%@&@*@=@-@;@>@,@'@)@!@~@{@]@^@I+/@(@_@ ", -" :@<@[@}@|@1@2@3@4@5@6@7@8@9@0@L+a@b@c@d@e@ ", -" f@g@h@i@i@j@k@l@|@m@n@o@p@q@r@s@t@u@p v@ ", -" w@x@y@z@A@B@i@C@D@E@F@G@H@I@L+J@K@L@p M@ ", -" N@O@P@Q@R@S@T@U@V@W@X@Y@Z@Y@`@ #.#p +# ", -" @###$#%#&#*#=#-#;#>#,#'#)#!#~#{#]# ", -" ^#/# ", +" I Q T = ", +" Q 7.e.r.i.8.E E 3.r.6.J ", +" H ~ n 4.r.p.p.p.p.8.R > 5.^ w.,.-.{ v ", +" { 9.^ & P t.p.p.p.p.p.8.I 5 q K L <.;.;.;.-.' ", +" { %.H +.y.p.p.p.p.p.e.Y 2 a n.K F $.*.$.@.} ] N ", +" x D :.y.p.p.p.p.p.p.r.R 8 C.u ..F A ) A Z M h $ ", +" f =.q.p.p.p.p.p.p.p.p.i.( e 6 $.` l B M g ", +" ` ;.q.p.p.p.p.p.a.K.a.p.p.4.L -.` l N % ", +" V =.-.>.q.y.p.p.p.L.L.K.i.w.,.-.;.$.<.q.u.2. ", +" D { =.-.;.>.1.1.9.( h.h.Q &.-.-.-.;.9.p.p.p.r.! ", +" U j.o.-.;.-.;.-.P x.Q.^.R.~ *.-.;.;.>.1.q.y.p.i.2. ", +" H./.! *.;.;.;.o.x./.(.(.(.J.| -.-.;.-.-.;.,.9.u.p.7. ", +" !.(.k.#.;.-.=./ !.(.(.(.(.Q.X.-.;.;.;.;.-.-.;.;.1.w.6. ", +" ~.(.H.G ;.-.D j.(.(.(.(.(.!.O.-.-.;.;.;.-.;.-.;.-.;.,.O. ", +" ~.(.v.@ *.$.+ d.(.(.(.(.(.E.o.-.-.;.;.-.;.;.;.*.=.=.*.$.v ", +" ~.(.l.- Y T ; < (.(.(.(.(.J.&.-.;.;.$.@.[ ] _ ) ) Z B B f ", +" P.(.F.X c.I.X f.(.(.(.(.(.G.=.-.=.] A Z Z Z Z z f $ ", +" l.!.R.s.F.I.g.W.(.(.(.(.R.E .[ A Z Z Z B g $ ", +" . , ; - 0 M.b.V.U.R.Y.z.u n.7 c Z Z B g # + ", +" : w p Z.D.A.S.p u i M.A.A.S.* Z B h z ] C ", +" s D.D.A.A.A.A.A.A.A.i B.B.b A Z Z @.-.` ", +" 1 y C.D.A.A.A.A.A.M.u Z.e c A Z Z [ ;.&. ", +" 8 y d C.A.A.A.C.B.t * B Z Z Z A #.=.m ", +" 3 9 r t r 9 8 o @ $ # f j l B #.V ", +" j k ", " ", " ", " ", -" "}; +" " +}; From 5b5013c9b1ce67d30f8053b6de4bfc7a557c2509 Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Sat, 11 Aug 2018 21:44:07 -0400 Subject: [PATCH 31/79] TravisCI: gcc-4.9 is also gone --- .travis.yml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index c2f7f1f72..a2a0648c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,20 +57,6 @@ matrix: - gcc-4.8 compiler: gcc-4.8 #gcc-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5 - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - libsdl2-mixer-dev - - libpng-dev - - libgl1-mesa-dev - - libgme-dev - - p7zip-full - - gcc-4.9 - compiler: gcc-4.9 - #gcc-4.9 (Ubuntu 4.9.3-8ubuntu2~14.04) 4.9.3 - os: linux addons: apt: From ca8de769a8352bb6c16b0fc6e144b59b54f791a3 Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Sat, 11 Aug 2018 21:58:57 -0400 Subject: [PATCH 32/79] TravisCI: also remove gcc-6 --- .travis.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index a2a0648c0..a9f4ddfb4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,21 +57,6 @@ matrix: - gcc-4.8 compiler: gcc-4.8 #gcc-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5 - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - libsdl2-mixer-dev - - libpng-dev - - libgl1-mesa-dev - - libgme-dev - - p7zip-full - - gcc-6 - compiler: gcc-6 - env: WFLAGS="-Wno-tautological-compare" - #gcc-6 (Ubuntu 6.1.1-3ubuntu11~14.04.1) 6.1.1 20160511 - os: linux addons: apt: From 269c1a70bae5f706b4ea17b11c8d4d07d37a0882 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Fri, 10 Aug 2018 17:12:26 -0400 Subject: [PATCH 33/79] Add lapbegunat and lapstartedtime player variables There is no lapfinishedtime because [mare]finishedtime refers to when Egg Capsule is destroyed. That concept does not apply to laps. --- src/d_player.h | 2 ++ src/lua_playerlib.c | 8 ++++++++ src/p_inter.c | 2 ++ src/p_map.c | 2 ++ src/p_saveg.c | 4 ++++ src/p_setup.c | 3 ++- src/p_user.c | 3 ++- 7 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 498b7166d..648867b92 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -463,6 +463,8 @@ typedef struct player_s tic_t marebegunat; // Leveltime when mare begun tic_t startedtime; // Time which you started this mare with. tic_t finishedtime; // Time it took you to finish the mare (used for display) + tic_t lapbegunat; // Leveltime when lap begun + tic_t lapstartedtime; // Time which you started this lap with. INT16 finishedspheres; // The spheres you had left upon finishing the mare INT16 finishedrings; // The rings/stars you had left upon finishing the mare UINT32 marescore; // score for this nights stage diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 7deefa7ff..114aa27b0 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -300,6 +300,10 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->startedtime); else if (fastcmp(field,"finishedtime")) lua_pushinteger(L, plr->finishedtime); + else if (fastcmp(field,"lapbegunat")) + lua_pushinteger(L, plr->lapbegunat); + else if (fastcmp(field,"lapstartedtime")) + lua_pushinteger(L, plr->lapstartedtime); else if (fastcmp(field,"finishedspheres")) lua_pushinteger(L, plr->finishedspheres); else if (fastcmp(field,"finishedrings")) @@ -588,6 +592,10 @@ static int player_set(lua_State *L) plr->startedtime = (tic_t)luaL_checkinteger(L, 3); else if (fastcmp(field,"finishedtime")) plr->finishedtime = (tic_t)luaL_checkinteger(L, 3); + else if (fastcmp(field,"lapbegunat")) + plr->lapbegunat = (tic_t)luaL_checkinteger(L, 3); + else if (fastcmp(field,"lapstartedtime")) + plr->lapstartedtime = (tic_t)luaL_checkinteger(L, 3); else if (fastcmp(field,"finishedspheres")) plr->finishedspheres = (INT16)luaL_checkinteger(L, 3); else if (fastcmp(field,"finishedrings")) diff --git a/src/p_inter.c b/src/p_inter.c index ce8bba6b6..e7590439e 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1134,6 +1134,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) { player->nightstime += special->info->speed; player->startedtime += special->info->speed; + player->lapstartedtime += special->info->speed; P_RestoreMusic(player); } else @@ -1143,6 +1144,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) { players[i].nightstime += special->info->speed; players[i].startedtime += special->info->speed; + players[i].lapstartedtime += special->info->speed; P_RestoreMusic(&players[i]); } if (special->info->deathsound != sfx_None) diff --git a/src/p_map.c b/src/p_map.c index 415e2bac1..7d3538573 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1114,6 +1114,8 @@ static boolean PIT_CheckThing(mobj_t *thing) )) { pl->marelap++; + pl->lapbegunat = leveltime; + pl->lapstartedtime = pl->nightstime; if (pl->bonustime) { diff --git a/src/p_saveg.c b/src/p_saveg.c index 2b48cc783..e18802f57 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -204,6 +204,8 @@ static void P_NetArchivePlayers(void) WRITEUINT32(save_p, players[i].marebegunat); WRITEUINT32(save_p, players[i].startedtime); WRITEUINT32(save_p, players[i].finishedtime); + WRITEUINT32(save_p, players[i].lapbegunat); + WRITEUINT32(save_p, players[i].lapstartedtime); WRITEINT16(save_p, players[i].finishedspheres); WRITEINT16(save_p, players[i].finishedrings); WRITEUINT32(save_p, players[i].marescore); @@ -397,6 +399,8 @@ static void P_NetUnArchivePlayers(void) players[i].marebegunat = READUINT32(save_p); players[i].startedtime = READUINT32(save_p); players[i].finishedtime = READUINT32(save_p); + players[i].lapbegunat = READUINT32(save_p); + players[i].lapstartedtime = READUINT32(save_p); players[i].finishedspheres = READINT16(save_p); players[i].finishedrings = READINT16(save_p); players[i].marescore = READUINT32(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index e4b6034b1..4b10f431c 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2387,7 +2387,8 @@ static void P_LevelInitStuff(void) players[i].linktimer = players[i].flyangle =\ players[i].anotherflyangle = players[i].nightstime =\ players[i].mare = players[i].marelap =\ - players[i].marebonuslap = players[i].realtime =\ + players[i].marebonuslap = players[i].lapbegunat =\ + players[i].lapstartedtime = players[i].realtime =\ players[i].exiting = 0; // i guess this could be part of the above but i feel mildly uncomfortable implicitly casting diff --git a/src/p_user.c b/src/p_user.c index 99ecb8154..06f42dcad 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -666,7 +666,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->followitem = skins[DEFAULTNIGHTSSKIN].followitem; } - player->nightstime = player->startedtime = nighttime*TICRATE; + player->nightstime = player->startedtime = player->lapstartedtime = nighttime*TICRATE; player->bonustime = false; P_RestoreMusic(player); @@ -762,6 +762,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->lastmarescore = player->marescore; player->marescore = 0; player->marebegunat = leveltime; + player->lapbegunat = leveltime; player->spheres = player->rings = 0; } From 9a9a2d94cc209731c9ba4ec8a9ebe393f6bdc204 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Sun, 12 Aug 2018 19:57:33 -0400 Subject: [PATCH 34/79] totalmarelap and totalmarebonuslap player variables --- src/d_player.h | 2 ++ src/lua_playerlib.c | 8 ++++++++ src/p_map.c | 4 +++- src/p_saveg.c | 4 ++++ src/p_setup.c | 1 + 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/d_player.h b/src/d_player.h index 648867b92..e7e6e4827 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -472,6 +472,8 @@ typedef struct player_s UINT8 lastmare; // previous mare UINT8 lastmarelap; // previous mare lap UINT8 lastmarebonuslap; // previous mare bonus lap + UINT8 totalmarelap; // total mare lap + UINT8 totalmarebonuslap; // total mare bonus lap INT32 maxlink; // maximum link obtained UINT8 texttimer; // nights_texttime should not be local UINT8 textvar; // which line of NiGHTS text to show -- let's not use cheap hacks diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 114aa27b0..67efe7710 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -318,6 +318,10 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->lastmarelap); else if (fastcmp(field,"lastmarebonuslap")) lua_pushinteger(L, plr->lastmarebonuslap); + else if (fastcmp(field,"totalmarelap")) + lua_pushinteger(L, plr->totalmarelap); + else if (fastcmp(field,"totalmarebonuslap")) + lua_pushinteger(L, plr->totalmarebonuslap); else if (fastcmp(field,"maxlink")) lua_pushinteger(L, plr->maxlink); else if (fastcmp(field,"texttimer")) @@ -610,6 +614,10 @@ static int player_set(lua_State *L) plr->lastmarelap = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"lastmarebonuslap")) plr->lastmarebonuslap = (UINT8)luaL_checkinteger(L, 3); + else if (fastcmp(field,"totalmarelap")) + plr->totalmarelap = (UINT8)luaL_checkinteger(L, 3); + else if (fastcmp(field,"totalmarebonuslap")) + plr->totalmarebonuslap = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"maxlink")) plr->maxlink = (INT32)luaL_checkinteger(L, 3); else if (fastcmp(field,"texttimer")) diff --git a/src/p_map.c b/src/p_map.c index 7d3538573..6e5e03a8d 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1114,12 +1114,14 @@ static boolean PIT_CheckThing(mobj_t *thing) )) { pl->marelap++; + pl->totalmarelap++; pl->lapbegunat = leveltime; pl->lapstartedtime = pl->nightstime; - if (pl->bonustime) + if (pl->bonustime) { pl->marebonuslap++; + pl->totalmarebonuslap++; // Respawn rings and items P_ReloadRings(); diff --git a/src/p_saveg.c b/src/p_saveg.c index e18802f57..bcc582cd0 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -213,6 +213,8 @@ static void P_NetArchivePlayers(void) WRITEUINT8(save_p, players[i].lastmare); WRITEUINT8(save_p, players[i].lastmarelap); WRITEUINT8(save_p, players[i].lastmarebonuslap); + WRITEUINT8(save_p, players[i].totalmarelap); + WRITEUINT8(save_p, players[i].totalmarebonuslap); WRITEINT32(save_p, players[i].maxlink); WRITEUINT8(save_p, players[i].texttimer); WRITEUINT8(save_p, players[i].textvar); @@ -408,6 +410,8 @@ static void P_NetUnArchivePlayers(void) players[i].lastmare = READUINT8(save_p); players[i].lastmarelap = READUINT8(save_p); players[i].lastmarebonuslap = READUINT8(save_p); + players[i].totalmarelap = READUINT8(save_p); + players[i].totalmarebonuslap = READUINT8(save_p); players[i].maxlink = READINT32(save_p); players[i].texttimer = READUINT8(save_p); players[i].textvar = READUINT8(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index 4b10f431c..ee462193d 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2382,6 +2382,7 @@ static void P_LevelInitStuff(void) players[i].finishedtime = players[i].finishedspheres =\ players[i].finishedrings = players[i].lastmare =\ players[i].lastmarelap = players[i].lastmarebonuslap =\ + players[i].totalmarelap = players[i].totalmarebonuslap =\ players[i].marebegunat = players[i].textvar =\ players[i].texttimer = players[i].linkcount =\ players[i].linktimer = players[i].flyangle =\ From d24de8bc2fbfcf437b976dd9d9a32b06b6435793 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Sun, 12 Aug 2018 19:59:34 -0400 Subject: [PATCH 35/79] totalmarescore player variable for NiGHTS scoring # Conflicts: # src/p_setup.c --- src/d_player.h | 1 + src/lua_playerlib.c | 4 ++++ src/p_saveg.c | 2 ++ src/p_setup.c | 4 ++-- src/p_user.c | 2 ++ 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index e7e6e4827..fd41c7621 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -469,6 +469,7 @@ typedef struct player_s INT16 finishedrings; // The rings/stars you had left upon finishing the mare UINT32 marescore; // score for this nights stage UINT32 lastmarescore; // score for the last mare + UINT32 totalmarescore; // score for all mares UINT8 lastmare; // previous mare UINT8 lastmarelap; // previous mare lap UINT8 lastmarebonuslap; // previous mare bonus lap diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 67efe7710..f973061f1 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -312,6 +312,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->marescore); else if (fastcmp(field,"lastmarescore")) lua_pushinteger(L, plr->lastmarescore); + else if (fastcmp(field,"totalmarescore")) + lua_pushinteger(L, plr->totalmarescore); else if (fastcmp(field,"lastmare")) lua_pushinteger(L, plr->lastmare); else if (fastcmp(field,"lastmarelap")) @@ -608,6 +610,8 @@ static int player_set(lua_State *L) plr->marescore = (UINT32)luaL_checkinteger(L, 3); else if (fastcmp(field,"lastmarescore")) plr->lastmarescore = (UINT32)luaL_checkinteger(L, 3); + else if (fastcmp(field,"totalmarescore")) + plr->totalmarescore = (UINT32)luaL_checkinteger(L, 3); else if (fastcmp(field,"lastmare")) plr->lastmare = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"lastmarelap")) diff --git a/src/p_saveg.c b/src/p_saveg.c index bcc582cd0..22d43f358 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -210,6 +210,7 @@ static void P_NetArchivePlayers(void) WRITEINT16(save_p, players[i].finishedrings); WRITEUINT32(save_p, players[i].marescore); WRITEUINT32(save_p, players[i].lastmarescore); + WRITEUINT32(save_p, players[i].totalmarescore); WRITEUINT8(save_p, players[i].lastmare); WRITEUINT8(save_p, players[i].lastmarelap); WRITEUINT8(save_p, players[i].lastmarebonuslap); @@ -407,6 +408,7 @@ static void P_NetUnArchivePlayers(void) players[i].finishedrings = READINT16(save_p); players[i].marescore = READUINT32(save_p); players[i].lastmarescore = READUINT32(save_p); + players[i].totalmarescore = READUINT32(save_p); players[i].lastmare = READUINT8(save_p); players[i].lastmarelap = READUINT8(save_p); players[i].lastmarebonuslap = READUINT8(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index ee462193d..368b95dd5 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2389,8 +2389,8 @@ static void P_LevelInitStuff(void) players[i].anotherflyangle = players[i].nightstime =\ players[i].mare = players[i].marelap =\ players[i].marebonuslap = players[i].lapbegunat =\ - players[i].lapstartedtime = players[i].realtime =\ - players[i].exiting = 0; + players[i].lapstartedtime = players[i].totalmarescore =\ + players[i].realtime = players[i].exiting = 0; // i guess this could be part of the above but i feel mildly uncomfortable implicitly casting players[i].gotcontinue = false; diff --git a/src/p_user.c b/src/p_user.c index 06f42dcad..9d677b4bc 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -733,6 +733,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) G_AddTempNightsRecords(players[i].marescore, leveltime - player->marebegunat, players[i].mare + 1); // transfer scores anyway + players[i].totalmarescore += players[i].marescore; players[i].lastmarescore = players[i].marescore; players[i].marescore = 0; @@ -759,6 +760,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) G_AddTempNightsRecords(player->marescore, leveltime - player->marebegunat, (UINT8)(oldmare + 1)); // Starting a new mare, transfer scores + player->totalmarescore += players[i].marescore; player->lastmarescore = player->marescore; player->marescore = 0; player->marebegunat = leveltime; From 921b50550a130f7cef95400676e23049719aeff7 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Sun, 12 Aug 2018 19:26:18 -0400 Subject: [PATCH 36/79] Totalmarescore typo --- src/p_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index 9d677b4bc..8177338da 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -760,7 +760,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) G_AddTempNightsRecords(player->marescore, leveltime - player->marebegunat, (UINT8)(oldmare + 1)); // Starting a new mare, transfer scores - player->totalmarescore += players[i].marescore; + player->totalmarescore += player->marescore; player->lastmarescore = player->marescore; player->marescore = 0; player->marebegunat = leveltime; From c89e48e23c26c65d6e143d34a2159bfc730c166a Mon Sep 17 00:00:00 2001 From: mazmazz Date: Sun, 12 Aug 2018 23:03:12 -0400 Subject: [PATCH 37/79] Fix attraction bug on non-NiGHTS and Nightopian Helper * Switch from mo->movecount to mo->movefactor for type compat * Adjust timings for regular and paraloop attraction --- src/p_inter.c | 4 ++-- src/p_mobj.c | 14 +++++++------- src/p_user.c | 5 ++++- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index f93a190af..46c7aeb7a 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -946,7 +946,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Yay! The thing's in reach! Pull it in! mo2->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT; mo2->flags2 |= MF2_NIGHTSPULL; - mo2->movecount = 24*FRACUNIT; // initialize the NightsItemChase timer + mo2->movefactor = 32*FRACUNIT; // initialize the NightsItemChase timer P_SetTarget(&mo2->tracer, toucher); } } @@ -2127,7 +2127,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget if (target->flags2 & MF2_NIGHTSPULL) { P_SetTarget(&target->tracer, NULL); - target->movecount = 0; // reset NightsItemChase timer + target->movefactor = 0; // reset NightsItemChase timer } // dead target is no more shootable diff --git a/src/p_mobj.c b/src/p_mobj.c index d80d967a8..ac6091dc7 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6120,11 +6120,11 @@ void P_Attract(mobj_t *source, mobj_t *dest, boolean nightsgrab) // Home in on y if (dist < 1) dist = 1; - if (nightsgrab && dest->player->powers[pw_carry] == CR_NIGHTSMODE) + if (nightsgrab && source->movefactor) { - source->movecount += FRACUNIT/2; + source->movefactor += FRACUNIT/2; - if (dist < source->movecount) + if (dist < source->movefactor) { source->momx = source->momy = source->momz = 0; P_TeleportMove(source, tx, ty, tz); @@ -6133,9 +6133,9 @@ void P_Attract(mobj_t *source, mobj_t *dest, boolean nightsgrab) // Home in on y { vangle = R_PointToAngle2(source->z, 0, tz, xydist); - source->momx = FixedMul(FINESINE(vangle >> ANGLETOFINESHIFT), FixedMul(FINECOSINE(source->angle >> ANGLETOFINESHIFT), source->movecount)); - source->momy = FixedMul(FINESINE(vangle >> ANGLETOFINESHIFT), FixedMul(FINESINE(source->angle >> ANGLETOFINESHIFT), source->movecount)); - source->momz = FixedMul(FINECOSINE(vangle >> ANGLETOFINESHIFT), source->movecount); + source->momx = FixedMul(FINESINE(vangle >> ANGLETOFINESHIFT), FixedMul(FINECOSINE(source->angle >> ANGLETOFINESHIFT), source->movefactor)); + source->momy = FixedMul(FINESINE(vangle >> ANGLETOFINESHIFT), FixedMul(FINESINE(source->angle >> ANGLETOFINESHIFT), source->movefactor)); + source->momz = FixedMul(FINECOSINE(vangle >> ANGLETOFINESHIFT), source->movefactor); } } else @@ -6173,7 +6173,7 @@ static void P_NightsItemChase(mobj_t *thing) { P_SetTarget(&thing->tracer, NULL); thing->flags2 &= ~MF2_NIGHTSPULL; - //thing->movecount = 0; + thing->movefactor = 0; return; } diff --git a/src/p_user.c b/src/p_user.c index b363cb930..2962ca5ad 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9770,13 +9770,16 @@ void P_PlayerThink(player_t *player) || mo2->type == MT_NIGHTSCHIP || mo2->type == MT_NIGHTSSTAR)) continue; + if (mo2->flags2 & MF2_NIGHTSPULL) + continue; + if (P_AproxDistance(P_AproxDistance(mo2->x - x, mo2->y - y), mo2->z - z) > FixedMul(128*FRACUNIT, player->mo->scale)) continue; // Yay! The thing's in reach! Pull it in! mo2->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT; mo2->flags2 |= MF2_NIGHTSPULL; - mo2->movecount = 24*FRACUNIT; // initialize NightsItemChase timer + mo2->movefactor = 40*FRACUNIT; // initialize the NightsItemChase timer P_SetTarget(&mo2->tracer, player->mo); } } From 53303174c18b8571de99a335130562f100a99e3c Mon Sep 17 00:00:00 2001 From: mazmazz Date: Mon, 13 Aug 2018 14:16:33 -0400 Subject: [PATCH 38/79] MaxBonusLives level header option for # of lives in score tally --- src/dehacked.c | 2 ++ src/doomstat.h | 1 + src/lua_maplib.c | 2 ++ src/p_setup.c | 1 + src/y_inter.c | 8 ++++++-- 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index fb0f958c3..bae73f989 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1203,6 +1203,8 @@ static void readlevelheader(MYFILE *f, INT32 num) deh_warning("Level header %d: invalid bonus type number %d", num, i); } + else if (fastcmp(word, "MAXBONUSLIVES")) + mapheaderinfo[num-1]->maxbonuslives = (SINT8)i; else if (fastcmp(word, "LEVELFLAGS")) mapheaderinfo[num-1]->levelflags = (UINT8)i; else if (fastcmp(word, "MENUFLAGS")) diff --git a/src/doomstat.h b/src/doomstat.h index 24b9e5753..651540ecc 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -244,6 +244,7 @@ typedef struct SINT8 unlockrequired; ///< Is an unlockable required to play this level? -1 if no. UINT8 levelselect; ///< Is this map available in the level select? If so, which map list is it available in? SINT8 bonustype; ///< What type of bonus does this level have? (-1 for null.) + SINT8 maxbonuslives; ///< How many bonus lives to award at Intermission? (-1 for unlimited.) UINT8 levelflags; ///< LF_flags: merged eight booleans into one UINT8 for space, see below UINT8 menuflags; ///< LF2_flags: options that affect record attack / nights mode menus diff --git a/src/lua_maplib.c b/src/lua_maplib.c index f1bfcb8f1..a8360d9dd 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -1784,6 +1784,8 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->levelselect); else if (fastcmp(field,"bonustype")) lua_pushinteger(L, header->bonustype); + else if (fastcmp(field,"maxbonuslives")) + lua_pushinteger(L, header->maxbonuslives); else if (fastcmp(field,"levelflags")) lua_pushinteger(L, header->levelflags); else if (fastcmp(field,"menuflags")) diff --git a/src/p_setup.c b/src/p_setup.c index c62f281b3..17ca730e2 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -225,6 +225,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 i) mapheaderinfo[num]->unlockrequired = -1; mapheaderinfo[num]->levelselect = 0; mapheaderinfo[num]->bonustype = 0; + mapheaderinfo[num]->maxbonuslives = -1; mapheaderinfo[num]->levelflags = 0; mapheaderinfo[num]->menuflags = 0; #if 1 // equivalent to "FlickyList = DEMO" diff --git a/src/y_inter.c b/src/y_inter.c index 966d84477..e1fd14a79 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1874,7 +1874,9 @@ static void Y_AwardCoopBonuses(void) players[i].score = MAXSCORE; } - ptlives = (!ultimatemode && !modeattacking && players[i].lives != 0x7f) ? max((players[i].score/50000) - (oldscore/50000), 0) : 0; + ptlives = min( + ((!ultimatemode && !modeattacking && players[i].lives != 0x7f) ? max((players[i].score/50000) - (oldscore/50000), 0) : 0), + (mapheaderinfo[prevmap]->maxbonuslives < 0 ? INT32_MAX : mapheaderinfo[prevmap]->maxbonuslives)); if (ptlives) P_GivePlayerLives(&players[i], ptlives); @@ -1918,7 +1920,9 @@ static void Y_AwardSpecialStageBonus(void) players[i].score = MAXSCORE; // grant extra lives right away since tally is faked - ptlives = (!ultimatemode && !modeattacking && players[i].lives != 0x7f) ? max((players[i].score/50000) - (oldscore/50000), 0) : 0; + ptlives = min( + ((!ultimatemode && !modeattacking && players[i].lives != 0x7f) ? max((players[i].score/50000) - (oldscore/50000), 0) : 0), + (mapheaderinfo[prevmap]->maxbonuslives < 0 ? INT32_MAX : mapheaderinfo[prevmap]->maxbonuslives)); if (ptlives) P_GivePlayerLives(&players[i], ptlives); From e399d08d765b92147f52527600961a7b0593a7de Mon Sep 17 00:00:00 2001 From: mazmazz Date: Tue, 14 Aug 2018 10:48:50 -0400 Subject: [PATCH 39/79] Add ML_BOUNCY flag to Bonus Time executor to execute BEFORE the capsule is destroyed and player has enough spheres --- src/p_spec.c | 14 ++++++++------ src/p_spec.h | 2 +- src/p_user.c | 5 ++++- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index d4a2b45ce..3ea021bce 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1420,13 +1420,15 @@ void P_RunNightsLapExecutors(mobj_t *actor) // // P_RunNightsBonusTimeExecutors // -void P_RunNightsBonusTimeExecutors(mobj_t *actor) +void P_RunNightsBonusTimeExecutors(mobj_t *actor, boolean preblowup) { size_t i; for (i = 0; i < numlines; i++) { - if (lines[i].special == 329 || lines[i].special == 330) + if ((lines[i].special == 329 || lines[i].special == 330) + && ((preblowup && (lines[i].flags & ML_BOUNCY)) + || (!preblowup && !(lines[i].flags & ML_BOUNCY)))) P_RunTriggerLinedef(&lines[i], actor, NULL); } } @@ -1574,8 +1576,8 @@ static boolean P_CheckNightsTriggerLine(line_t *triggerline, mobj_t *actor) } // Get current mare and lap (and check early return for DeNightserize) - if (perglobal || perglobalinverse - || (specialtype >= 325 && specialtype <= 326 && (fromnonights || fromnights))) + if (perglobal || perglobalinverse + || (specialtype >= 325 && specialtype <= 326 && (fromnonights || fromnights))) { UINT8 playersarenights = 0; @@ -1604,7 +1606,7 @@ static boolean P_CheckNightsTriggerLine(line_t *triggerline, mobj_t *actor) currentmare = players[i].mare; currentlap = UINT8_MAX; } - if (players[i].mare == currentmare + if (players[i].mare == currentmare && (lap > currentlap || currentlap == UINT8_MAX)) currentlap = lap; } @@ -1616,7 +1618,7 @@ static boolean P_CheckNightsTriggerLine(line_t *triggerline, mobj_t *actor) currentmare = players[i].mare; currentlap = UINT8_MAX; } - if (players[i].mare == currentmare + if (players[i].mare == currentmare && (lap < currentlap || currentlap == UINT8_MAX)) currentlap = lap; } diff --git a/src/p_spec.h b/src/p_spec.h index 5bcf25b7b..0767851e1 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -69,7 +69,7 @@ void P_ChangeSectorTag(UINT32 sector, INT16 newtag); void P_RunNightserizeExecutors(mobj_t *actor); void P_RunDeNightserizeExecutors(mobj_t *actor); void P_RunNightsLapExecutors(mobj_t *actor); -void P_RunNightsBonusTimeExecutors(mobj_t *actor); +void P_RunNightsBonusTimeExecutors(mobj_t *actor, boolean preblowup); ffloor_t *P_GetFFloorByID(sector_t *sec, UINT16 id); diff --git a/src/p_user.c b/src/p_user.c index 6b8901ac7..018387284 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -6014,6 +6014,9 @@ static void P_DoNiGHTSCapsule(player_t *player) { if (player->spheres > 0) { + if (player->capsule->extravalue1 <= 0 && player->spheres >= player->capsule->health) + P_RunNightsBonusTimeExecutors(player->mo, true); + player->spheres--; player->capsule->health--; player->capsule->extravalue1++; @@ -6099,7 +6102,7 @@ static void P_DoNiGHTSCapsule(player_t *player) P_SetTarget(&players[i].capsule, NULL); // Remove capsule from everyone now that it is dead! S_StartScreamSound(player->mo, sfx_ngdone); P_SwitchSpheresBonusMode(true); - P_RunNightsBonusTimeExecutors(player->mo); + P_RunNightsBonusTimeExecutors(player->mo, false); } } else From 3a3976bd71285b6d5c72a73791e45d6661f48901 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Tue, 14 Aug 2018 11:10:10 -0400 Subject: [PATCH 40/79] Make LE Ring triggers accept spheres for NiGHTS levels --- src/p_spec.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index 103312b52..1d842a0bb 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1493,10 +1493,10 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller if (!playeringame[i] || players[i].spectator) continue; - if (!players[i].mo || players[i].rings <= 0) + if (!players[i].mo || ((maptol & TOL_NIGHTS) ? players[i].spheres : players[i].rings) <= 0) continue; - rings += players[i].rings; + rings += (maptol & TOL_NIGHTS) ? players[i].spheres : players[i].rings; } } else @@ -1504,7 +1504,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller if (!(actor && actor->player)) return false; // no player to count rings from here, sorry - rings = actor->player->rings; + rings = (maptol & TOL_NIGHTS) ? actor->player->spheres : actor->player->rings; } if (triggerline->flags & ML_NOCLIMB) From 35bcf200be64b140d5185b29778768c0f562e595 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Tue, 14 Aug 2018 11:06:09 -0400 Subject: [PATCH 41/79] Alternate take on pre-blowup: Do it on first touching tic, instead of first ring pop * Use MT_EGGCAPSULE extravalue2 for tic timer --- src/p_mobj.c | 3 ++- src/p_user.c | 16 +++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 4353e67c3..b88692d76 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8751,7 +8751,8 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) nummaprings = -1; // no perfect bonus, rings are free break; case MT_EGGCAPSULE: - mobj->extravalue1 = -1; // timer for how long a player has been at the capsule + mobj->extravalue1 = -1; // sphere timer for how long a player has been at the capsule + mobj->extravalue2 = -1; // tic timer for how long a player has been at the capsule break; case MT_REDTEAMRING: mobj->color = skincolor_redteam; diff --git a/src/p_user.c b/src/p_user.c index 018387284..e3b6f2261 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -5946,6 +5946,8 @@ static void P_DoNiGHTSCapsule(player_t *player) { INT32 i; + player->capsule->extravalue2++; // tic counter + if (abs(player->mo->x-player->capsule->x) <= 2*FRACUNIT) { P_UnsetThingPosition(player->mo); @@ -6007,6 +6009,9 @@ static void P_DoNiGHTSCapsule(player_t *player) } } + if (player->capsule->extravalue2 <= 0 && player->spheres >= player->capsule->health) + P_RunNightsBonusTimeExecutors(player->mo, true); // run pre-blowup executors + // Time to blow it up! if (player->mo->x == player->capsule->x && player->mo->y == player->capsule->y @@ -6014,9 +6019,6 @@ static void P_DoNiGHTSCapsule(player_t *player) { if (player->spheres > 0) { - if (player->capsule->extravalue1 <= 0 && player->spheres >= player->capsule->health) - P_RunNightsBonusTimeExecutors(player->mo, true); - player->spheres--; player->capsule->health--; player->capsule->extravalue1++; @@ -6033,7 +6035,7 @@ static void P_DoNiGHTSCapsule(player_t *player) player->capsule->flags &= ~MF_NOGRAVITY; player->capsule->momz = 5*FRACUNIT; player->capsule->reactiontime = 0; - player->capsule->extravalue1 = -1; + player->capsule->extravalue1 = player->capsule->extravalue2 = -1; for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && !player->exiting && players[i].mare == player->mare) @@ -6102,7 +6104,7 @@ static void P_DoNiGHTSCapsule(player_t *player) P_SetTarget(&players[i].capsule, NULL); // Remove capsule from everyone now that it is dead! S_StartScreamSound(player->mo, sfx_ngdone); P_SwitchSpheresBonusMode(true); - P_RunNightsBonusTimeExecutors(player->mo, false); + P_RunNightsBonusTimeExecutors(player->mo, false); // run post blow-up executors } } else @@ -6111,11 +6113,11 @@ static void P_DoNiGHTSCapsule(player_t *player) player->texttimer = 4*TICRATE; player->textvar = 3; // Get more rings! player->capsule->reactiontime = 0; - player->capsule->extravalue1 = -1; + player->capsule->extravalue1 = player->capsule->extravalue2 = -1; } } else - player->capsule->extravalue1 = -1; + player->capsule->extravalue1 = player->capsule->extravalue2 = -1; } // From b1368078c60fe839202760da2779ae1e7ce9c0b1 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Tue, 14 Aug 2018 12:00:37 -0400 Subject: [PATCH 42/79] Make Bonus Time Start executor into a general Egg Capsule Touch executor * Entrance/exit flags * Enough/not-enough-rings flags (with "doesn't matter" line option) --- src/p_spec.c | 11 +++++++---- src/p_spec.h | 2 +- src/p_user.c | 7 ++++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index 3ea021bce..617625fc1 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1418,17 +1418,20 @@ void P_RunNightsLapExecutors(mobj_t *actor) } // -// P_RunNightsBonusTimeExecutors +// P_RunNightsCapsuleTouchExecutors // -void P_RunNightsBonusTimeExecutors(mobj_t *actor, boolean preblowup) +void P_RunNightsCapsuleTouchExecutors(mobj_t *actor, boolean entering, boolean enoughspheres) { size_t i; for (i = 0; i < numlines; i++) { if ((lines[i].special == 329 || lines[i].special == 330) - && ((preblowup && (lines[i].flags & ML_BOUNCY)) - || (!preblowup && !(lines[i].flags & ML_BOUNCY)))) + && ((entering && (lines[i].flags & ML_TFERLINE)) + || (!entering && !(lines[i].flags & ML_TFERLINE))) + && ((lines[i].flags & ML_DONTPEGTOP) + || (enoughspheres && !(lines[i].flags & ML_BOUNCY)) + || (!enoughspheres && (lines[i].flags & ML_BOUNCY)))) P_RunTriggerLinedef(&lines[i], actor, NULL); } } diff --git a/src/p_spec.h b/src/p_spec.h index 0767851e1..e0bcc18eb 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -69,7 +69,7 @@ void P_ChangeSectorTag(UINT32 sector, INT16 newtag); void P_RunNightserizeExecutors(mobj_t *actor); void P_RunDeNightserizeExecutors(mobj_t *actor); void P_RunNightsLapExecutors(mobj_t *actor); -void P_RunNightsBonusTimeExecutors(mobj_t *actor, boolean preblowup); +void P_RunNightsCapsuleTouchExecutors(mobj_t *actor, boolean entering, boolean enoughspheres); ffloor_t *P_GetFFloorByID(sector_t *sec, UINT16 id); diff --git a/src/p_user.c b/src/p_user.c index e3b6f2261..7e678fb61 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -6009,8 +6009,8 @@ static void P_DoNiGHTSCapsule(player_t *player) } } - if (player->capsule->extravalue2 <= 0 && player->spheres >= player->capsule->health) - P_RunNightsBonusTimeExecutors(player->mo, true); // run pre-blowup executors + if (player->capsule->extravalue2 <= 0) + P_RunNightsCapsuleTouchExecutors(player->mo, true, player->spheres >= player->capsule->health); // run capsule entrance executors // Time to blow it up! if (player->mo->x == player->capsule->x @@ -6104,7 +6104,7 @@ static void P_DoNiGHTSCapsule(player_t *player) P_SetTarget(&players[i].capsule, NULL); // Remove capsule from everyone now that it is dead! S_StartScreamSound(player->mo, sfx_ngdone); P_SwitchSpheresBonusMode(true); - P_RunNightsBonusTimeExecutors(player->mo, false); // run post blow-up executors + P_RunNightsCapsuleTouchExecutors(player->mo, false, true); // run capsule exit executors, and we destroyed it } } else @@ -6114,6 +6114,7 @@ static void P_DoNiGHTSCapsule(player_t *player) player->textvar = 3; // Get more rings! player->capsule->reactiontime = 0; player->capsule->extravalue1 = player->capsule->extravalue2 = -1; + P_RunNightsCapsuleTouchExecutors(player->mo, false, false); // run capsule exit executors, and we lacked rings } } else From 9452c02ca1cb60739ffca66803edfbadf04c501d Mon Sep 17 00:00:00 2001 From: mazmazz Date: Tue, 14 Aug 2018 12:48:00 -0400 Subject: [PATCH 43/79] Fix Capsule Entrance exec firing repeatedly --- src/p_user.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 7e678fb61..f202ff97f 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -6009,7 +6009,7 @@ static void P_DoNiGHTSCapsule(player_t *player) } } - if (player->capsule->extravalue2 <= 0) + if (player->capsule->extravalue2 <= 0 && player->capsule->health > 0) P_RunNightsCapsuleTouchExecutors(player->mo, true, player->spheres >= player->capsule->health); // run capsule entrance executors // Time to blow it up! @@ -6118,7 +6118,7 @@ static void P_DoNiGHTSCapsule(player_t *player) } } else - player->capsule->extravalue1 = player->capsule->extravalue2 = -1; + player->capsule->extravalue1 = -1; } // From 6ba6454056503a6e96f9fba21b42a792f02e1d97 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Tue, 14 Aug 2018 14:57:58 -0400 Subject: [PATCH 44/79] Comments --- src/p_spec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index 617625fc1..66fedeab5 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1871,8 +1871,8 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller case 326: // denightserize - once case 327: // nights lap - each time case 328: // nights lap - once - case 329: // nights bonus time - each time - case 330: // nights bonus time - once + case 329: // nights egg capsule touch - each time + case 330: // nights egg capsule touch - once if (!P_CheckNightsTriggerLine(triggerline, actor)) return false; break; From a49aa9babe7a1bb41e57afad69dded10c607bed9 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Tue, 14 Aug 2018 16:08:21 -0400 Subject: [PATCH 45/79] Fix bonus time quirk where lines are run if player->marebonuslap is 0 * Bonus laps start at 1, so if a line is looking for bonus laps, they should only be run at >= 1. --- src/p_spec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/p_spec.c b/src/p_spec.c index 66fedeab5..e70aec650 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1641,6 +1641,9 @@ static boolean P_CheckNightsTriggerLine(line_t *triggerline, mobj_t *actor) currentlap = lapfrombonustime ? actor->player->marebonuslap : actor->player->marelap; } + if (lapfrombonustime && !currentlap) + return false; // special case: player->marebonuslap is 0 until passing through on bonus time. Don't trigger lines looking for inputlap 0. + // Compare current mare/lap to input mare/lap based on rules if (!(specialtype >= 323 && specialtype <= 324 && donomares) // don't return false if donomares and we got this far && ((ltemare && currentmare > inputmare) From 1381ffc3e688b0425f8149f59ea93b0a990a6a19 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Wed, 15 Aug 2018 18:42:20 -0400 Subject: [PATCH 46/79] Fix NiGHTS Bumper player positioning --- src/p_inter.c | 8 +------- src/p_user.c | 9 +++++++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index ce8bba6b6..21f580a99 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1005,13 +1005,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) player->flyangle = special->threshold; player->speed = FixedMul(special->info->speed, special->scale); - // Potentially causes axis transfer failures. - // Also rarely worked properly anyway. - //P_UnsetThingPosition(player->mo); - //player->mo->x = special->x; - //player->mo->y = special->y; - //P_SetThingPosition(player->mo); - toucher->z = special->z+(special->height/4); + P_SetTarget(&player->mo->hnext, special); // Reference bumper for position correction on next tic } else // More like a spring { diff --git a/src/p_user.c b/src/p_user.c index fd09b0847..595e9566e 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -6175,6 +6175,15 @@ static void P_NiGHTSMovement(player_t *player) S_ChangeMusicInternal((((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? "_ntime" : "_drown"), false); + if (player->bumpertime == TICRATE/2) + { + // Center player to bumper here because if you try to set player's position in P_TouchSpecialThing case MT_NIGHTSBUMPER, + // that position is fudged in the time between that routine in the previous tic + // and reaching here in the current tic + P_TeleportMove(player->mo, player->mo->hnext->x, player->mo->hnext->y, player->mo->hnext->z + (player->mo->hnext->height/4)); + P_SetTarget(&player->mo->hnext, NULL); + } + if (player->mo->z < player->mo->floorz) player->mo->z = player->mo->floorz; From 6010822c8449acfa24e1cc8f10b74b20680fd25d Mon Sep 17 00:00:00 2001 From: mazmazz Date: Wed, 15 Aug 2018 18:50:28 -0400 Subject: [PATCH 47/79] Use more efficient position setting. For Z, also adjust for bumper mobj scale. --- src/p_user.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index 595e9566e..551b7c5f3 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -6180,7 +6180,11 @@ static void P_NiGHTSMovement(player_t *player) // Center player to bumper here because if you try to set player's position in P_TouchSpecialThing case MT_NIGHTSBUMPER, // that position is fudged in the time between that routine in the previous tic // and reaching here in the current tic - P_TeleportMove(player->mo, player->mo->hnext->x, player->mo->hnext->y, player->mo->hnext->z + (player->mo->hnext->height/4)); + P_UnsetThingPosition(player->mo); + player->mo->x = player->mo->hnext->x; + player->mo->y = player->mo->hnext->y; + player->mo->z = player->mo->hnext->z + FixedMul(player->mo->hnext->height/4, player->mo->hnext->scale); + P_SetThingPosition(player->mo); P_SetTarget(&player->mo->hnext, NULL); } From baa4c30e992bec2f749ae8615e17e2c0998d972a Mon Sep 17 00:00:00 2001 From: mazmazz Date: Wed, 15 Aug 2018 19:09:54 -0400 Subject: [PATCH 48/79] Also center non-NiGHTS players on NiGHTS bumpers --- src/p_user.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 551b7c5f3..a647bfba0 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -6174,20 +6174,6 @@ static void P_NiGHTSMovement(player_t *player) // S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS. Dummied out, as some on the dev team thought it wasn't Sonic-y enough (Mystic, notably). Uncomment to restore. -SH S_ChangeMusicInternal((((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? "_ntime" : "_drown"), false); - - if (player->bumpertime == TICRATE/2) - { - // Center player to bumper here because if you try to set player's position in P_TouchSpecialThing case MT_NIGHTSBUMPER, - // that position is fudged in the time between that routine in the previous tic - // and reaching here in the current tic - P_UnsetThingPosition(player->mo); - player->mo->x = player->mo->hnext->x; - player->mo->y = player->mo->hnext->y; - player->mo->z = player->mo->hnext->z + FixedMul(player->mo->hnext->height/4, player->mo->hnext->scale); - P_SetThingPosition(player->mo); - P_SetTarget(&player->mo->hnext, NULL); - } - if (player->mo->z < player->mo->floorz) player->mo->z = player->mo->floorz; @@ -9821,7 +9807,19 @@ void P_PlayerThink(player_t *player) P_ResetScore(player); } else + { + if (player->bumpertime == TICRATE/2 && player->mo->hnext) + { + // Center player to NiGHTS bumper here because if you try to set player's position in + // P_TouchSpecialThing case MT_NIGHTSBUMPER, that position is fudged in the time + // between that routine in the previous tic + // and reaching here in the current tic + P_TeleportMove(player->mo, player->mo->hnext->x, player->mo->hnext->y + , player->mo->hnext->z + FixedMul(player->mo->hnext->height/4, player->mo->hnext->scale)); + P_SetTarget(&player->mo->hnext, NULL); + } P_MovePlayer(player); + } if (!player->mo) return; // P_MovePlayer removed player->mo. From 675ed7ecf07bd5ba708bbc26208e12fdf433e0ad Mon Sep 17 00:00:00 2001 From: mazmazz Date: Wed, 15 Aug 2018 23:40:42 -0400 Subject: [PATCH 49/79] Don't apply autobrake on NiGHTS bumpers --- src/p_user.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index a647bfba0..3a4906a67 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9943,7 +9943,8 @@ void P_PlayerThink(player_t *player) || player->panim == PA_PAIN || !player->mo->health || player->climbing - || player->pflags & (PF_SPINNING|PF_SLIDING)) + || player->pflags & (PF_SPINNING|PF_SLIDING) + || player->bumpertime) player->pflags &= ~PF_APPLYAUTOBRAKE; else if (currentlyonground || player->powers[pw_tailsfly]) player->pflags |= PF_APPLYAUTOBRAKE; From 4b8fd01bc02bd3a6f0fd88964b880082074e5180 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 16 Aug 2018 11:34:57 -0400 Subject: [PATCH 50/79] Revert "SETSPHERES console command for debugging/cheating" This reverts commit 9e8784a376ed0c5210e1b8ade2e74e567049c487. --- src/d_netcmd.c | 1 - src/m_cheat.c | 17 ----------------- src/m_cheat.h | 1 - src/p_inter.c | 2 +- 4 files changed, 1 insertion(+), 20 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 0cb1d4497..c901fabd5 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -822,7 +822,6 @@ void D_RegisterClientCommands(void) COM_AddCommand("getallemeralds", Command_Getallemeralds_f); COM_AddCommand("resetemeralds", Command_Resetemeralds_f); COM_AddCommand("setrings", Command_Setrings_f); - COM_AddCommand("setspheres", Command_Setspheres_f); COM_AddCommand("setlives", Command_Setlives_f); COM_AddCommand("setcontinues", Command_Setcontinues_f); COM_AddCommand("devmode", Command_Devmode_f); diff --git a/src/m_cheat.c b/src/m_cheat.c index b572b84eb..5ac742270 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -890,23 +890,6 @@ void Command_Setrings_f(void) } } -void Command_Setspheres_f(void) -{ - REQUIRE_INLEVEL; - REQUIRE_SINGLEPLAYER; - REQUIRE_NOULTIMATE; - REQUIRE_PANDORA; - - if (COM_Argc() > 1) - { - // P_GivePlayerRings does value clamping - players[consoleplayer].spheres = 0; - P_GivePlayerSpheres(&players[consoleplayer], atoi(COM_Argv(1))); - - G_SetGameModified(multiplayer); - } -} - void Command_Setlives_f(void) { REQUIRE_INLEVEL; diff --git a/src/m_cheat.h b/src/m_cheat.h index 31f650b3f..951c7a16a 100644 --- a/src/m_cheat.h +++ b/src/m_cheat.h @@ -51,7 +51,6 @@ void Command_Savecheckpoint_f(void); void Command_Getallemeralds_f(void); void Command_Resetemeralds_f(void); void Command_Setrings_f(void); -void Command_Setspheres_f(void); void Command_Setlives_f(void); void Command_Setcontinues_f(void); void Command_Devmode_f(void); diff --git a/src/p_inter.c b/src/p_inter.c index ce8bba6b6..e740b62d1 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -3409,7 +3409,7 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings) return; // If no health, don't spawn ring! - if (((maptol & TOL_NIGHTS) && player->spheres <= 0) || (!(maptol & TOL_NIGHTS) && player->rings <= 0)) + if (player->rings <= 0) num_rings = 0; if (num_rings > 32 && player->powers[pw_carry] != CR_NIGHTSFALL) From ed9e74c15a72662023857155b6bd2473b93affcf Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 16 Aug 2018 11:35:35 -0400 Subject: [PATCH 51/79] Line from revert --- src/p_inter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_inter.c b/src/p_inter.c index e740b62d1..ce8bba6b6 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -3409,7 +3409,7 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings) return; // If no health, don't spawn ring! - if (player->rings <= 0) + if (((maptol & TOL_NIGHTS) && player->spheres <= 0) || (!(maptol & TOL_NIGHTS) && player->rings <= 0)) num_rings = 0; if (num_rings > 32 && player->powers[pw_carry] != CR_NIGHTSFALL) From 32939a11f13507c0848c1a8f374bde194acf99a3 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 16 Aug 2018 12:13:57 -0400 Subject: [PATCH 52/79] Change SETRINGS to support player->spheres in NiGHTS --- src/m_cheat.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/m_cheat.c b/src/m_cheat.c index 5ac742270..6e346de91 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -880,11 +880,20 @@ void Command_Setrings_f(void) if (COM_Argc() > 1) { - // P_GivePlayerRings does value clamping - players[consoleplayer].rings = 0; - P_GivePlayerRings(&players[consoleplayer], atoi(COM_Argv(1))); - if (!G_IsSpecialStage(gamemap) || !(maptol & TOL_NIGHTS)) - players[consoleplayer].totalring -= atoi(COM_Argv(1)); //undo totalring addition done in P_GivePlayerRings + if (!(maptol & TOL_NIGHTS)) + { + // P_GivePlayerRings does value clamping + players[consoleplayer].rings = 0; + P_GivePlayerRings(&players[consoleplayer], atoi(COM_Argv(1))); + if (!G_IsSpecialStage(gamemap)) + players[consoleplayer].totalring -= atoi(COM_Argv(1)); //undo totalring addition done in P_GivePlayerRings + } + else + { + players[consoleplayer].spheres = 0; + P_GivePlayerSpheres(&player[consoleplayer], atoi(COM_Argv(1))); + // no totalsphere addition to revert + } G_SetGameModified(multiplayer); } From 4aade072efb57dfe7c13445b094da607967369af Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 16 Aug 2018 12:14:41 -0400 Subject: [PATCH 53/79] Checkpoint (unfinished): player->totalsphere --- src/g_game.c | 3 +++ src/m_cheat.c | 3 ++- src/p_user.c | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 52358a8b9..1c7abb00e 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2075,6 +2075,7 @@ void G_PlayerReborn(INT32 player) INT32 exiting; INT16 numboxes; INT16 totalring; + INT16 totalsphere; UINT8 laps; UINT8 mare; UINT8 skincolor; @@ -2103,6 +2104,7 @@ void G_PlayerReborn(INT32 player) numboxes = players[player].numboxes; laps = players[player].laps; totalring = players[player].totalring; + totalsphere = players[player].totalsphere; skincolor = players[player].skincolor; skin = players[player].skin; @@ -2187,6 +2189,7 @@ void G_PlayerReborn(INT32 player) p->numboxes = numboxes; p->laps = laps; p->totalring = totalring; + p->totalsphere = totalsphere; p->mare = mare; if (bot) diff --git a/src/m_cheat.c b/src/m_cheat.c index 6e346de91..37a55ab99 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -892,7 +892,8 @@ void Command_Setrings_f(void) { players[consoleplayer].spheres = 0; P_GivePlayerSpheres(&player[consoleplayer], atoi(COM_Argv(1))); - // no totalsphere addition to revert + if (!G_IsSpecialStage(gamemap)) + players[consoleplayer].totalsphere -= atoi(COM_Argv(1)); //undo totalsphere addition done in P_GivePlayerRings } G_SetGameModified(multiplayer); diff --git a/src/p_user.c b/src/p_user.c index fd09b0847..e7a53d0be 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -963,6 +963,8 @@ void P_GivePlayerSpheres(player_t *player, INT32 num_spheres) player->spheres += num_spheres; + player->totalsphere += num_spheres; + // Can only get up to 9999 spheres, sorry! if (player->spheres > 9999) player->spheres = 9999; From 5fdece424a7eaf6dd54ebdc536f5be9ba142501b Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 16 Aug 2018 16:49:15 -0400 Subject: [PATCH 54/79] Adjust vertical angle and forwards/backwards of NiGHTS bumper in objectplace --- src/m_cheat.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/m_cheat.c b/src/m_cheat.c index b572b84eb..aed3a3e92 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -1194,11 +1194,47 @@ void OP_NightsObjectplace(player_t *player) // This places a bumper! if (cmd->buttons & BT_TOSSFLAG) { + UINT16 vertangle = (UINT16)(player->anotherflyangle % 360); + UINT16 newflags, newz; + player->pflags |= PF_ATTACKDOWN; if (!OP_HeightOkay(player, false)) return; mt = OP_CreateNewMapThing(player, (UINT16)mobjinfo[MT_NIGHTSBUMPER].doomednum, false); + newz = min((mt->options >> ZSHIFT) - (mobjinfo[MT_NIGHTSBUMPER].height/4), 0); + // height offset: from P_TouchSpecialThing case MT_NIGHTSBUMPER + + // clockwise + if (vertangle >= 75 && vertangle < 105) // up + newflags = 3; + else if (vertangle >= 105 && vertangle < 135) // 60 upward tilt + newflags = 2; + else if (vertangle >= 135 && vertangle < 165) // 30 upward tilt + newflags = 1; + //else if (vertangle >= 165 && vertangle < 195) // forward, see else case + // newflags = 0; + else if (vertangle >= 195 && vertangle < 225) // 30 downward tilt + newflags = 11; + else if (vertangle >= 225 && vertangle < 255) // 60 downward tilt + newflags = 10; + else if (vertangle >= 255 && vertangle < 285) // down + newflags = 9; + else if (vertangle >= 285 && vertangle < 315) // 60 downward tilt backwards + newflags = 8; + else if (vertangle >= 315 && vertangle < 345) // 30 downward tilt backwards + newflags = 7; + else if (vertangle >= 345 || vertangle < 15) // backwards + newflags = 6; + else if (vertangle >= 15 && vertangle < 45) // 30 upward tilt backwards + newflags = 5; + else if (vertangle >= 45 && vertangle < 75) // 60 upward tilt backwards + newflags = 4; + else // forward + newflags = 0; + + mt->options = (newz << ZSHIFT) | newflags; + P_SpawnMapThing(mt); } From 6dd64bccbc79a481371bac3ea5a4d4c9dc431814 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 16 Aug 2018 21:21:43 -0400 Subject: [PATCH 55/79] Place backwards Bumpers with the correct Thing angle --- src/m_cheat.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/m_cheat.c b/src/m_cheat.c index aed3a3e92..53998c522 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -1235,6 +1235,11 @@ void OP_NightsObjectplace(player_t *player) mt->options = (newz << ZSHIFT) | newflags; + // if NiGHTS is facing backwards, orient the Thing angle forwards so that the sprite angle + // displays correctly. Backwards movement via the Thing flags is unaffected. + if (vertangle < 90 || vertangle > 270) + mt->angle = (mt->angle + 180) % 360; + P_SpawnMapThing(mt); } From 9b0e9a58fee4b3b012df2a8092b8a7424b96aa3e Mon Sep 17 00:00:00 2001 From: Digiku Date: Fri, 17 Aug 2018 09:09:10 -0400 Subject: [PATCH 56/79] Revert "Checkpoint (unfinished): player->totalsphere" This reverts commit 4aade072efb57dfe7c13445b094da607967369af --- src/g_game.c | 3 --- src/m_cheat.c | 3 +-- src/p_user.c | 2 -- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 1c7abb00e..52358a8b9 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2075,7 +2075,6 @@ void G_PlayerReborn(INT32 player) INT32 exiting; INT16 numboxes; INT16 totalring; - INT16 totalsphere; UINT8 laps; UINT8 mare; UINT8 skincolor; @@ -2104,7 +2103,6 @@ void G_PlayerReborn(INT32 player) numboxes = players[player].numboxes; laps = players[player].laps; totalring = players[player].totalring; - totalsphere = players[player].totalsphere; skincolor = players[player].skincolor; skin = players[player].skin; @@ -2189,7 +2187,6 @@ void G_PlayerReborn(INT32 player) p->numboxes = numboxes; p->laps = laps; p->totalring = totalring; - p->totalsphere = totalsphere; p->mare = mare; if (bot) diff --git a/src/m_cheat.c b/src/m_cheat.c index 37a55ab99..6e346de91 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -892,8 +892,7 @@ void Command_Setrings_f(void) { players[consoleplayer].spheres = 0; P_GivePlayerSpheres(&player[consoleplayer], atoi(COM_Argv(1))); - if (!G_IsSpecialStage(gamemap)) - players[consoleplayer].totalsphere -= atoi(COM_Argv(1)); //undo totalsphere addition done in P_GivePlayerRings + // no totalsphere addition to revert } G_SetGameModified(multiplayer); diff --git a/src/p_user.c b/src/p_user.c index e7a53d0be..fd09b0847 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -963,8 +963,6 @@ void P_GivePlayerSpheres(player_t *player, INT32 num_spheres) player->spheres += num_spheres; - player->totalsphere += num_spheres; - // Can only get up to 9999 spheres, sorry! if (player->spheres > 9999) player->spheres = 9999; From 20bb759ff604cd784b65e765ab100b3980aa862e Mon Sep 17 00:00:00 2001 From: Digiku Date: Fri, 17 Aug 2018 09:11:59 -0400 Subject: [PATCH 57/79] Remove unnecessary totalring deduction condition Because it would be triggered by !(maptol & TOL_NIGHTS) in the old code anyway --- src/m_cheat.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/m_cheat.c b/src/m_cheat.c index 6e346de91..8372683aa 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -885,8 +885,7 @@ void Command_Setrings_f(void) // P_GivePlayerRings does value clamping players[consoleplayer].rings = 0; P_GivePlayerRings(&players[consoleplayer], atoi(COM_Argv(1))); - if (!G_IsSpecialStage(gamemap)) - players[consoleplayer].totalring -= atoi(COM_Argv(1)); //undo totalring addition done in P_GivePlayerRings + players[consoleplayer].totalring -= atoi(COM_Argv(1)); //undo totalring addition done in P_GivePlayerRings } else { From fa637475aec78f7052669830237d7bae10c3f03a Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Wed, 22 Aug 2018 17:16:11 +0100 Subject: [PATCH 58/79] Hardcoded Brambles.lua from CEZRes.pk3, changed thing type to 1125 --- src/dehacked.c | 2 ++ src/info.c | 30 ++++++++++++++++++++++++++++++ src/info.h | 3 +++ 3 files changed, 35 insertions(+) diff --git a/src/dehacked.c b/src/dehacked.c index fb0f958c3..76a65fcc2 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -4922,6 +4922,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_SUSPICIOUSFACESTABBERSTATUE_WAIT", "S_SUSPICIOUSFACESTABBERSTATUE_BURST1", "S_SUSPICIOUSFACESTABBERSTATUE_BURST2", + "S_BRAMBLES", // Big Tumbleweed "S_BIGTUMBLEWEED", @@ -6578,6 +6579,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_CRAWLASTATUE", // Crawla statue "MT_FACESTABBERSTATUE", // Facestabber statue "MT_SUSPICIOUSFACESTABBERSTATUE", // :eggthinking: + "MT_BRAMBLES", // Brambles // Arid Canyon Scenery "MT_BIGTUMBLEWEED", diff --git a/src/info.c b/src/info.c index 782ab6381..a6d847235 100644 --- a/src/info.c +++ b/src/info.c @@ -232,6 +232,7 @@ char sprnames[NUMSPRITES + 1][5] = "CFLG", // Waving flag/segment "CSTA", // Crawla statue "CBBS", // Facestabber statue + "CABR", // Brambles // Arid Canyon Scenery "BTBL", // Big tumbleweed @@ -2158,6 +2159,8 @@ state_t states[NUMSTATES] = {SPR_CBBS, FF_ANIMATE, 23, {NULL}, 6, 1, S_SUSPICIOUSFACESTABBERSTATUE_BURST2}, // S_SUSPICIOUSFACESTABBERSTATUE_BURST1 {SPR_NULL, 0, 40, {A_StatueBurst}, MT_FACESTABBER, S_FACESTABBER_CHARGE2, S_NULL}, // S_SUSPICIOUSFACESTABBERSTATUE_BURST2 + {SPR_CABR, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BRAMBLES + // Big Tumbleweed {SPR_BTBL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BIGTUMBLEWEED {SPR_BTBL, 0, 5, {NULL}, 0, 0, S_BIGTUMBLEWEED_ROLL2}, // S_BIGTUMBLEWEED_ROLL1 @@ -10685,6 +10688,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MT_ROCKCRUMBLE3 // raisestate }, + { // MT_BRAMBLES + 1125, // doomednum + S_BRAMBLES, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 48*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SCENERY|MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + { // MT_BIGTUMBLEWEED 1200, // doomednum S_BIGTUMBLEWEED,// spawnstate diff --git a/src/info.h b/src/info.h index dfd30bc5b..89bfd3504 100644 --- a/src/info.h +++ b/src/info.h @@ -450,6 +450,7 @@ typedef enum sprite SPR_CFLG, // Waving flag/segment SPR_CSTA, // Crawla statue SPR_CBBS, // Facestabber statue + SPR_CABR, // Brambles // Arid Canyon Scenery SPR_BTBL, // Big tumbleweed @@ -2275,6 +2276,7 @@ typedef enum state S_SUSPICIOUSFACESTABBERSTATUE_WAIT, S_SUSPICIOUSFACESTABBERSTATUE_BURST1, S_SUSPICIOUSFACESTABBERSTATUE_BURST2, + S_BRAMBLES, // Big Tumbleweed S_BIGTUMBLEWEED, @@ -3951,6 +3953,7 @@ typedef enum mobj_type MT_CRAWLASTATUE, // Crawla statue MT_FACESTABBERSTATUE, // Facestabber statue MT_SUSPICIOUSFACESTABBERSTATUE, // :eggthinking: + MT_BRAMBLES, // Brambles // Arid Canyon Scenery MT_BIGTUMBLEWEED, From dc6b52a449308aecc3acd67667675b23ab0d0c25 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 23 Aug 2018 20:09:39 +0100 Subject: [PATCH 59/79] Add INFLIVES macro, to make it easier to find infinite lives-related code --- src/d_player.h | 5 ++++- src/hu_stuff.c | 4 ++-- src/m_cheat.c | 2 +- src/m_menu.c | 6 +++--- src/p_inter.c | 2 +- src/p_user.c | 8 ++++---- src/st_stuff.c | 10 +++++----- src/y_inter.c | 4 ++-- 8 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 7bee5f337..59f9ba42a 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -382,7 +382,7 @@ typedef struct player_s fixed_t height; // Bounding box changes. fixed_t spinheight; - SINT8 lives; + SINT8 lives; // number of lives - if == INFLIVES, the player has infinite lives SINT8 continues; // continues that player has acquired SINT8 xtralife; // Ring Extra Life counter @@ -491,4 +491,7 @@ typedef struct player_s #endif } player_t; +// Value for infinite lives +#define INFLIVES 0x7F + #endif diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 28438599c..6697eb09b 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -1275,7 +1275,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I } } - if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != 0x7f)) //show lives + if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //show lives V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE|(greycheck ? V_60TRANS : 0), va("%dx", players[tab[i].num].lives)); else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT) { @@ -1413,7 +1413,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline | (greycheck ? V_TRANSLUCENT : 0) | V_ALLOWLOWERCASE, name); - if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != 0x7f)) //show lives + if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //show lives V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE, va("%dx", players[tab[i].num].lives)); else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT) V_DrawSmallScaledPatch(x-28, y-4, 0, tagico); diff --git a/src/m_cheat.c b/src/m_cheat.c index b572b84eb..56bef02a3 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -918,7 +918,7 @@ void Command_Setlives_f(void) { SINT8 lives = atoi(COM_Argv(1)); if (lives == -1) - players[consoleplayer].lives = 0x7f; // infinity! + players[consoleplayer].lives = INFLIVES; // infinity! else { // P_GivePlayerLives does value clamping diff --git a/src/m_menu.c b/src/m_menu.c index f99f5d860..778f24ad9 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -5277,7 +5277,7 @@ static void M_PandorasBox(INT32 choice) CV_StealthSetValue(&cv_dummyrings, max(players[consoleplayer].spheres, 0)); else CV_StealthSetValue(&cv_dummyrings, max(players[consoleplayer].rings, 0)); - if (players[consoleplayer].lives == 0x7f) + if (players[consoleplayer].lives == INFLIVES) CV_StealthSetValue(&cv_dummylives, -1); else CV_StealthSetValue(&cv_dummylives, players[consoleplayer].lives); @@ -6334,7 +6334,7 @@ skipsign: y += 25; tempx = x + 10; - if (savegameinfo[savetodraw].lives != 0x7f + if (savegameinfo[savetodraw].lives != INFLIVES && savegameinfo[savetodraw].lives > 9) tempx -= 4; @@ -6361,7 +6361,7 @@ skiplife: V_DrawScaledPatch(tempx + 9, y + 2, 0, patch); tempx += 16; - if (savegameinfo[savetodraw].lives == 0x7f) + if (savegameinfo[savetodraw].lives == INFLIVES) V_DrawCharacter(tempx, y + 1, '\x16', false); else V_DrawString(tempx, y, 0, va("%d", savegameinfo[savetodraw].lives)); diff --git a/src/p_inter.c b/src/p_inter.c index ce8bba6b6..a4493c466 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2253,7 +2253,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget if ((target->player->lives <= 1) && (netgame || multiplayer) && (gametype == GT_COOP) && (cv_cooplives.value == 0)) ; - else if (!target->player->bot && !target->player->spectator && !G_IsSpecialStage(gamemap) && (target->player->lives != 0x7f) + else if (!target->player->bot && !target->player->spectator && !G_IsSpecialStage(gamemap) && (target->player->lives != INFLIVES) && G_GametypeUsesLives()) { target->player->lives -= 1; // Lose a life Tails 03-11-2000 diff --git a/src/p_user.c b/src/p_user.c index fd09b0847..100663d91 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -927,7 +927,7 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings) player->rings = 0; // Now extra life bonuses are handled here instead of in P_MovePlayer, since why not? - if (!ultimatemode && !modeattacking && !G_IsSpecialStage(gamemap) && G_GametypeUsesLives() && player->lives != 0x7f) + if (!ultimatemode && !modeattacking && !G_IsSpecialStage(gamemap) && G_GametypeUsesLives() && player->lives != INFLIVES) { INT32 gainlives = 0; @@ -986,7 +986,7 @@ void P_GivePlayerLives(player_t *player, INT32 numlives) if (gamestate == GS_LEVEL) { - if (player->lives == 0x7f || (gametype != GT_COOP && gametype != GT_COMPETITION)) + if (player->lives == INFLIVES || (gametype != GT_COOP && gametype != GT_COMPETITION)) { P_GivePlayerRings(player, 100*numlives); return; @@ -8377,7 +8377,7 @@ boolean P_GetLives(player_t *player) if (!(netgame || multiplayer) || (gametype != GT_COOP) || (cv_cooplives.value == 1) - || (player->lives == 0x7f)) + || (player->lives == INFLIVES)) return true; if ((cv_cooplives.value == 2 || cv_cooplives.value == 0) && player->lives > 0) @@ -8404,7 +8404,7 @@ boolean P_GetLives(player_t *player) { if (cv_cooplives.value == 2 && (P_IsLocalPlayer(player) || P_IsLocalPlayer(&players[maxlivesplayer]))) S_StartSound(NULL, sfx_jshard); // placeholder - if (players[maxlivesplayer].lives != 0x7f) + if (players[maxlivesplayer].lives != INFLIVES) players[maxlivesplayer].lives--; player->lives++; if (player->lives < 1) diff --git a/src/st_stuff.c b/src/st_stuff.c index 0079b8da9..5fbd2eafc 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -807,7 +807,7 @@ static void ST_drawLivesArea(void) // lives number if (gametype == GT_RACE) { - livescount = 0x7f; + livescount = INFLIVES; notgreyedout = true; } else if ((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 3) @@ -826,9 +826,9 @@ static void ST_drawLivesArea(void) if (players[i].lives > 1) notgreyedout = true; - if (players[i].lives == 0x7f) + if (players[i].lives == INFLIVES) { - livescount = 0x7f; + livescount = INFLIVES; break; } else if (livescount < 99) @@ -837,11 +837,11 @@ static void ST_drawLivesArea(void) } else { - livescount = (((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) ? 0x7f : stplyr->lives); + livescount = (((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) ? INFLIVES : stplyr->lives); notgreyedout = true; } - if (livescount == 0x7f) + if (livescount == INFLIVES) V_DrawCharacter(hudinfo[HUD_LIVES].x+50, hudinfo[HUD_LIVES].y+8, '\x16' | 0x80 | hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, false); else diff --git a/src/y_inter.c b/src/y_inter.c index 966d84477..fff6d2ebf 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1874,7 +1874,7 @@ static void Y_AwardCoopBonuses(void) players[i].score = MAXSCORE; } - ptlives = (!ultimatemode && !modeattacking && players[i].lives != 0x7f) ? max((players[i].score/50000) - (oldscore/50000), 0) : 0; + ptlives = (!ultimatemode && !modeattacking && players[i].lives != INFLIVES) ? max((players[i].score/50000) - (oldscore/50000), 0) : 0; if (ptlives) P_GivePlayerLives(&players[i], ptlives); @@ -1918,7 +1918,7 @@ static void Y_AwardSpecialStageBonus(void) players[i].score = MAXSCORE; // grant extra lives right away since tally is faked - ptlives = (!ultimatemode && !modeattacking && players[i].lives != 0x7f) ? max((players[i].score/50000) - (oldscore/50000), 0) : 0; + ptlives = (!ultimatemode && !modeattacking && players[i].lives != INFLIVES) ? max((players[i].score/50000) - (oldscore/50000), 0) : 0; if (ptlives) P_GivePlayerLives(&players[i], ptlives); From 2a307f4524a7d410ef64730584432733f1614213 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 23 Aug 2018 20:27:40 +0100 Subject: [PATCH 60/79] Make switch case for stplyr->textvar in ST_drawNightsRecords --- src/st_stuff.c | 100 +++++++++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/src/st_stuff.c b/src/st_stuff.c index 5fbd2eafc..9483cf449 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1361,60 +1361,64 @@ static void ST_drawNightsRecords(void) if (stplyr->texttimer < TICRATE/2) aflag |= (9 - 9*stplyr->texttimer/(TICRATE/2)) << V_ALPHASHIFT; - // A "Bonus Time Start" by any other name... - if (stplyr->textvar == 1) + switch (stplyr->textvar) { - V_DrawCenteredString(BASEVIDWIDTH/2, 52, V_GREENMAP|aflag, M_GetText("GET TO THE GOAL!")); - V_DrawCenteredString(BASEVIDWIDTH/2, 60, aflag, M_GetText("SCORE MULTIPLIER START!")); - - if (stplyr->finishedtime) + case 1: // A "Bonus Time Start" by any other name... { - V_DrawString(BASEVIDWIDTH/2 - 48, 140, aflag, "TIME:"); - V_DrawString(BASEVIDWIDTH/2 - 48, 148, aflag, "BONUS:"); - V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 140, V_ORANGEMAP|aflag, va("%d", (stplyr->startedtime - stplyr->finishedtime)/TICRATE)); - V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 148, V_ORANGEMAP|aflag, va("%d", (stplyr->finishedtime/TICRATE) * 100)); + V_DrawCenteredString(BASEVIDWIDTH/2, 52, V_GREENMAP|aflag, M_GetText("GET TO THE GOAL!")); + V_DrawCenteredString(BASEVIDWIDTH/2, 60, aflag, M_GetText("SCORE MULTIPLIER START!")); + + if (stplyr->finishedtime) + { + V_DrawString(BASEVIDWIDTH/2 - 48, 140, aflag, "TIME:"); + V_DrawString(BASEVIDWIDTH/2 - 48, 148, aflag, "BONUS:"); + V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 140, V_ORANGEMAP|aflag, va("%d", (stplyr->startedtime - stplyr->finishedtime)/TICRATE)); + V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 148, V_ORANGEMAP|aflag, va("%d", (stplyr->finishedtime/TICRATE) * 100)); + } + break; } - } - - // Get n [more] Spheres - else if (stplyr->textvar <= 3 && stplyr->textvar >= 2) - { - if (!stplyr->capsule) - return; - - // Yes, this string is an abomination. - V_DrawCenteredString(BASEVIDWIDTH/2, 60, aflag, - va(M_GetText("\x80GET\x82 %d\x80 %s%s%s!"), stplyr->capsule->health, - (stplyr->textvar == 3) ? M_GetText("MORE ") : "", - (G_IsSpecialStage(gamemap)) ? "SPHERE" : "CHIP", - (stplyr->capsule->health > 1) ? "S" : "")); - } - - // End Bonus - else if (stplyr->textvar == 4) - { - V_DrawString(BASEVIDWIDTH/2 - 56, 140, aflag, (G_IsSpecialStage(gamemap)) ? "SPHERES:" : "CHIPS:"); - V_DrawString(BASEVIDWIDTH/2 - 56, 148, aflag, "BONUS:"); - V_DrawRightAlignedString(BASEVIDWIDTH/2 + 56, 140, V_ORANGEMAP|aflag, va("%d", stplyr->finishedspheres)); - V_DrawRightAlignedString(BASEVIDWIDTH/2 + 56, 148, V_ORANGEMAP|aflag, va("%d", stplyr->finishedspheres * 50)); - ST_DrawNightsOverlayNum((BASEVIDWIDTH/2 + 56)<lastmarescore, nightsnum, SKINCOLOR_AZURE); - - // If new record, say so! - if (!(netgame || multiplayer) && G_GetBestNightsScore(gamemap, stplyr->lastmare + 1) <= stplyr->lastmarescore) + case 2: // Get n Spheres + case 3: // Get n more Spheres { - if (stplyr->texttimer & 16) - V_DrawCenteredString(BASEVIDWIDTH/2, 184, V_YELLOWMAP|aflag, "* NEW RECORD *"); - } + if (!stplyr->capsule) + return; - if (P_HasGrades(gamemap, stplyr->lastmare + 1)) - { - if (aflag) - V_DrawTranslucentPatch(BASEVIDWIDTH/2 + 60, 160, aflag, - ngradeletters[P_GetGrade(stplyr->lastmarescore, gamemap, stplyr->lastmare)]); - else - V_DrawScaledPatch(BASEVIDWIDTH/2 + 60, 160, 0, - ngradeletters[P_GetGrade(stplyr->lastmarescore, gamemap, stplyr->lastmare)]); + // Yes, this string is an abomination. + V_DrawCenteredString(BASEVIDWIDTH/2, 60, aflag, + va(M_GetText("\x80GET\x82 %d\x80 %s%s%s!"), stplyr->capsule->health, + (stplyr->textvar == 3) ? M_GetText("MORE ") : "", + (G_IsSpecialStage(gamemap)) ? "SPHERE" : "CHIP", + (stplyr->capsule->health > 1) ? "S" : "")); + break; } + case 4: // End Bonus + { + V_DrawString(BASEVIDWIDTH/2 - 56, 140, aflag, (G_IsSpecialStage(gamemap)) ? "SPHERES:" : "CHIPS:"); + V_DrawString(BASEVIDWIDTH/2 - 56, 148, aflag, "BONUS:"); + V_DrawRightAlignedString(BASEVIDWIDTH/2 + 56, 140, V_ORANGEMAP|aflag, va("%d", stplyr->finishedspheres)); + V_DrawRightAlignedString(BASEVIDWIDTH/2 + 56, 148, V_ORANGEMAP|aflag, va("%d", stplyr->finishedspheres * 50)); + ST_DrawNightsOverlayNum((BASEVIDWIDTH/2 + 56)<lastmarescore, nightsnum, SKINCOLOR_AZURE); + + // If new record, say so! + if (!(netgame || multiplayer) && G_GetBestNightsScore(gamemap, stplyr->lastmare + 1) <= stplyr->lastmarescore) + { + if (stplyr->texttimer & 16) + V_DrawCenteredString(BASEVIDWIDTH/2, 184, V_YELLOWMAP|aflag, "* NEW RECORD *"); + } + + if (P_HasGrades(gamemap, stplyr->lastmare + 1)) + { + if (aflag) + V_DrawTranslucentPatch(BASEVIDWIDTH/2 + 60, 160, aflag, + ngradeletters[P_GetGrade(stplyr->lastmarescore, gamemap, stplyr->lastmare)]); + else + V_DrawScaledPatch(BASEVIDWIDTH/2 + 60, 160, 0, + ngradeletters[P_GetGrade(stplyr->lastmarescore, gamemap, stplyr->lastmare)]); + } + break; + } + default: + break; } } From bf5d0385d82d89d2a31c7b7ad05b7455426e6cc0 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 23 Aug 2018 21:13:45 +0100 Subject: [PATCH 61/79] ST_drawNiGHTSHUD tweaks: Split NiGHTS link drawing into its own function, remove "minlink" hack and just do things properly --- src/st_stuff.c | 108 ++++++++++++++++++++++++------------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/src/st_stuff.c b/src/st_stuff.c index 9483cf449..bd0f4e1e7 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1454,20 +1454,60 @@ static skincolors_t linkColor[2][NUMLINKCOLORS] = { {SKINCOLOR_SEAFOAM, SKINCOLOR_CYAN, SKINCOLOR_WAVE, SKINCOLOR_SAPPHIRE, SKINCOLOR_VAPOR, SKINCOLOR_BUBBLEGUM, SKINCOLOR_VIOLET, SKINCOLOR_RUBY, SKINCOLOR_FLAME, SKINCOLOR_SUNSET, SKINCOLOR_SANDY, SKINCOLOR_LIME}}; +static void ST_drawNiGHTSLink(void) +{ + static INT32 prevsel[2] = {0, 0}, prevtime[2] = {0, 0}; + const UINT8 q = ((splitscreen && stplyr == &players[secondarydisplayplayer]) ? 1 : 0); + INT32 sel = ((stplyr->linkcount-1) / 5) % NUMLINKCOLORS, aflag = V_PERPLAYER, mag = ((stplyr->linkcount-1 >= 300) ? 1 : 0); + skincolors_t colornum; + fixed_t x, y, scale; + + if (sel != prevsel[q]) + { + prevsel[q] = sel; + prevtime[q] = 2 + mag; + } + + if (stplyr->powers[pw_nights_linkfreeze] && (!(stplyr->powers[pw_nights_linkfreeze] & 2) || (stplyr->powers[pw_nights_linkfreeze] > flashingtics))) + colornum = SKINCOLOR_ICY; + else + colornum = linkColor[mag][sel]; + + aflag |= ((stplyr->linktimer < 2*TICRATE/3) + ? (9 - 9*stplyr->linktimer/(2*TICRATE/3)) << V_ALPHASHIFT + : 0); + + y = (160+11)<linkcount-1), nightsnum, colornum); + V_DrawFixedPatch(x+(4*scale), y, scale, aflag, nightslink, + colornum == 0 ? colormaps : R_GetTranslationColormap(TC_DEFAULT, colornum, GTC_CACHE)); + + // Show remaining link time left in debug + if (cv_debug & DBG_NIGHTSBASIC) + V_DrawCenteredString(BASEVIDWIDTH/2, 180, V_SNAPTOBOTTOM, va("End in %d.%02d", stplyr->linktimer/TICRATE, G_TicsToCentiseconds(stplyr->linktimer))); +} + + static void ST_drawNiGHTSHUD(void) { INT32 origamount; - INT32 minlink = 1; INT32 total_spherecount; const boolean oldspecialstage = (G_IsSpecialStage(gamemap) && !(maptol & TOL_NIGHTS)); - // Cheap hack: don't display when the score is showing (it popping up for a split second when exiting a map is intentional) - if (oldspecialstage || (stplyr->texttimer && stplyr->textvar == 4)) - minlink = INT32_MAX; - // When debugging, show "0 Link". - else if (cv_debug & DBG_NIGHTSBASIC) - minlink = 0; - // Drill meter if ( #ifdef HAVE_BLUA @@ -1512,55 +1552,15 @@ static void ST_drawNiGHTSHUD(void) }*/ // Link drawing - if ( + if (!oldspecialstage + // Don't display when the score is showing (it popping up for a split second when exiting a map is intentional) + && !(stplyr->texttimer && stplyr->textvar == 4) #ifdef HAVE_BLUA - LUA_HudEnabled(hud_nightslink) && + && LUA_HudEnabled(hud_nightslink) #endif - stplyr->linkcount > minlink) + && ((cv_debug & DBG_NIGHTSBASIC) || stplyr->linkcount > 1)) // When debugging, show "0 Link". { - static INT32 prevsel[2] = {0, 0}, prevtime[2] = {0, 0}; - const UINT8 q = ((splitscreen && stplyr == &players[secondarydisplayplayer]) ? 1 : 0); - INT32 sel = ((stplyr->linkcount-1) / 5) % NUMLINKCOLORS, aflag = V_PERPLAYER, mag = ((stplyr->linkcount-1 >= 300) ? 1 : 0); - skincolors_t colornum; - fixed_t x, y, scale; - - if (sel != prevsel[q]) - { - prevsel[q] = sel; - prevtime[q] = 2 + mag; - } - - if (stplyr->powers[pw_nights_linkfreeze] && (!(stplyr->powers[pw_nights_linkfreeze] & 2) || (stplyr->powers[pw_nights_linkfreeze] > flashingtics))) - colornum = SKINCOLOR_ICY; - else - colornum = linkColor[mag][sel]; - - aflag |= ((stplyr->linktimer < 2*TICRATE/3) - ? (9 - 9*stplyr->linktimer/(2*TICRATE/3)) << V_ALPHASHIFT - : 0); - - y = (160+11)<linkcount-1), nightsnum, colornum); - V_DrawFixedPatch(x+(4*scale), y, scale, aflag, nightslink, - colornum == 0 ? colormaps : R_GetTranslationColormap(TC_DEFAULT, colornum, GTC_CACHE)); - - // Show remaining link time left in debug - if (cv_debug & DBG_NIGHTSBASIC) - V_DrawCenteredString(BASEVIDWIDTH/2, 180, V_SNAPTOBOTTOM, va("End in %d.%02d", stplyr->linktimer/TICRATE, G_TicsToCentiseconds(stplyr->linktimer))); + ST_drawNiGHTSLink(); } if (gametype == GT_RACE || gametype == GT_COMPETITION) From e37a6c767c2462683cc942a6d73fe6d83b5bbc04 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 23 Aug 2018 22:00:37 +0100 Subject: [PATCH 62/79] Add quick macro for drawing the 1st person timer icons with timer string, add comments to ST_drawPowerupHUD --- src/st_stuff.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/st_stuff.c b/src/st_stuff.c index bd0f4e1e7..d86e53f8c 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1205,6 +1205,10 @@ static void ST_drawPowerupHUD(void) if (stplyr->spectator || stplyr->playerstate != PST_LIVE) return; +// ------- +// Shields +// ------- + // Graue 06-18-2004: no V_NOSCALESTART, no SCX, no SCY, snap to right if (stplyr->powers[pw_shield] & SH_NOSTACK) { @@ -1247,6 +1251,10 @@ static void ST_drawPowerupHUD(void) offs -= shieldoffs[q]; +// --------- +// CTF flags +// --------- + // YOU have a flag. Display a monitor-like icon for it. if (stplyr->gotflag) { @@ -1264,11 +1272,20 @@ static void ST_drawPowerupHUD(void) offs -= flagoffs[q]; +// -------------------- +// Timer-based powerups +// -------------------- + +#define DRAWTIMERICON(patch, timer) \ + V_DrawSmallScaledPatch(offs, hudinfo[HUD_POWERUPS].y, V_PERPLAYER|hudinfo[HUD_POWERUPS].f|V_HUDTRANS, patch); \ + V_DrawRightAlignedThinString(offs + 16, hudinfo[HUD_POWERUPS].y + 8, V_PERPLAYER|hudinfo[HUD_POWERUPS].f, va("%d", timer/TICRATE)); + + // Invincibility, both from monitor and after being hit invulntime = stplyr->powers[pw_flashing] ? stplyr->powers[pw_flashing] : stplyr->powers[pw_invulnerability]; + // Note: pw_flashing always makes the icon flicker regardless of time, unlike pw_invulnerability if (stplyr->powers[pw_invulnerability] > 3*TICRATE || (invulntime && leveltime & 1)) { - V_DrawSmallScaledPatch(offs, hudinfo[HUD_POWERUPS].y, V_PERPLAYER|hudinfo[HUD_POWERUPS].f|V_HUDTRANS, invincibility); - V_DrawRightAlignedThinString(offs + 16, hudinfo[HUD_POWERUPS].y + 8, V_PERPLAYER|hudinfo[HUD_POWERUPS].f, va("%d", invulntime/TICRATE)); + DRAWTIMERICON(invincibility, invulntime) } if (invulntime > 7) @@ -1281,10 +1298,10 @@ static void ST_drawPowerupHUD(void) offs -= a; } + // Super Sneakers if (stplyr->powers[pw_sneakers] > 3*TICRATE || (stplyr->powers[pw_sneakers] && leveltime & 1)) { - V_DrawSmallScaledPatch(offs, hudinfo[HUD_POWERUPS].y, V_PERPLAYER|hudinfo[HUD_POWERUPS].f|V_HUDTRANS, sneakers); - V_DrawRightAlignedThinString(offs + 16, hudinfo[HUD_POWERUPS].y + 8, V_PERPLAYER|hudinfo[HUD_POWERUPS].f, va("%d", stplyr->powers[pw_sneakers]/TICRATE)); + DRAWTIMERICON(sneakers, stplyr->powers[pw_sneakers]) } if (stplyr->powers[pw_sneakers] > 7) @@ -1297,12 +1314,13 @@ static void ST_drawPowerupHUD(void) offs -= a; } + // Gravity Boots if (stplyr->powers[pw_gravityboots] > 3*TICRATE || (stplyr->powers[pw_gravityboots] && leveltime & 1)) { - V_DrawSmallScaledPatch(offs, hudinfo[HUD_POWERUPS].y, V_PERPLAYER|hudinfo[HUD_POWERUPS].f|V_HUDTRANS, gravboots); - V_DrawRightAlignedThinString(offs + 16, hudinfo[HUD_POWERUPS].y + 8, V_PERPLAYER|hudinfo[HUD_POWERUPS].f, va("%d", stplyr->powers[pw_gravityboots]/TICRATE)); + DRAWTIMERICON(gravboots, stplyr->powers[pw_gravityboots]) } +#undef DRAWTIMERICON #undef ICONSEP } From 62caadbf193e501a2911627ee8778d25f682b4ee Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 23 Aug 2018 22:09:48 +0100 Subject: [PATCH 63/79] Make switch case for cv_seenames.value in ST_Drawer --- src/st_stuff.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/st_stuff.c b/src/st_stuff.c index d86e53f8c..507cb5903 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -2470,15 +2470,22 @@ void ST_Drawer(void) #ifdef SEENAMES if (cv_seenames.value && cv_allowseenames.value && displayplayer == consoleplayer && seenplayer && seenplayer->mo) { - if (cv_seenames.value == 1) - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF, player_names[seenplayer-players]); - else if (cv_seenames.value == 2) - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF, - va("%s%s", G_GametypeHasTeams() ? ((seenplayer->ctfteam == 1) ? "\x85" : "\x84") : "", player_names[seenplayer-players])); - else //if (cv_seenames.value == 3) - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF, - va("%s%s", !G_RingSlingerGametype() || (G_GametypeHasTeams() && players[consoleplayer].ctfteam == seenplayer->ctfteam) - ? "\x83" : "\x85", player_names[seenplayer-players])); + switch (cv_seenames.value) + { + case 1: // Colorless + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF, player_names[seenplayer-players]); + break; + case 2: // Team + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF, + va("%s%s", G_GametypeHasTeams() ? ((seenplayer->ctfteam == 1) ? "\x85" : "\x84") : "", player_names[seenplayer-players])); + break; + case 3: // Ally/Foe + default: + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF, + va("%s%s", !G_RingSlingerGametype() || (G_GametypeHasTeams() && players[consoleplayer].ctfteam == seenplayer->ctfteam) + ? "\x83" : "\x85", player_names[seenplayer-players])); + break; + } } #endif From f97ea7db3909538ad6a1a9ac847b97a3769053a7 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 23 Aug 2018 22:18:52 +0100 Subject: [PATCH 64/79] Add INFLIVES as a constant that Lua/SOC can use --- src/dehacked.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dehacked.c b/src/dehacked.c index 76a65fcc2..127b367c8 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -7610,6 +7610,9 @@ struct { {"WEP_RAIL",WEP_RAIL}, {"NUM_WEAPONS",NUM_WEAPONS}, + // Value for infinite lives + {"INFLIVES", INFLIVES}, + // Got Flags, for player->gotflag! // Used to be MF_ for some stupid reason, now they're GF_ to stop them looking like mobjflags {"GF_REDFLAG",GF_REDFLAG}, From c22102b465458e2e8162da1d3927efb35d409fdf Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 23 Aug 2018 23:00:15 +0100 Subject: [PATCH 65/79] Rewrite seenames HUD code even more; use text color flags rather than the special text color chars --- src/st_stuff.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/st_stuff.c b/src/st_stuff.c index 507cb5903..c4bec135f 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -2470,22 +2470,26 @@ void ST_Drawer(void) #ifdef SEENAMES if (cv_seenames.value && cv_allowseenames.value && displayplayer == consoleplayer && seenplayer && seenplayer->mo) { + INT32 c = 0; switch (cv_seenames.value) { case 1: // Colorless - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF, player_names[seenplayer-players]); break; case 2: // Team - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF, - va("%s%s", G_GametypeHasTeams() ? ((seenplayer->ctfteam == 1) ? "\x85" : "\x84") : "", player_names[seenplayer-players])); + if (G_GametypeHasTeams()) + c = (seenplayer->ctfteam == 1) ? V_REDMAP : V_BLUEMAP; break; case 3: // Ally/Foe default: - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF, - va("%s%s", !G_RingSlingerGametype() || (G_GametypeHasTeams() && players[consoleplayer].ctfteam == seenplayer->ctfteam) - ? "\x83" : "\x85", player_names[seenplayer-players])); + // Green = Ally, Red = Foe + if (G_GametypeHasTeams()) + c = (players[consoleplayer].ctfteam == seenplayer->ctfteam) ? V_GREENMAP : V_REDMAP; + else // Everyone is an ally, or everyone is a foe! + c = (G_RingSlingerGametype()) ? V_REDMAP : V_GREENMAP; break; } + + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF|c, player_names[seenplayer-players]); } #endif From 81340985586da87c38523f22d769c94439f24121 Mon Sep 17 00:00:00 2001 From: wolfy852 Date: Sat, 25 Aug 2018 03:40:43 -0500 Subject: [PATCH 66/79] Expose NEWTICRATE and NEWTICRATERATIO to Lua This should keep 70FPS-compatible scripts from throwing nil value errors. --- src/dehacked.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dehacked.c b/src/dehacked.c index 76a65fcc2..cf42e7c1f 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -7316,6 +7316,8 @@ struct { {"CODEBASE",CODEBASE}, // or what release of SRB2 this is. {"VERSION",VERSION}, // Grab the game's version! {"SUBVERSION",SUBVERSION}, // more precise version number + {"NEWTICRATE",NEWTICRATE}, // TICRATE*NEWTICRATERATIO + {"NEWTICRATERATIO",NEWTICRATERATIO}, // Special linedef executor tag numbers! {"LE_PINCHPHASE",LE_PINCHPHASE}, // A boss entered pinch phase (and, in most cases, is preparing their pinch phase attack!) From 6bd034e3dc37dff7d479359057beb854291c23f0 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 25 Aug 2018 16:46:45 +0100 Subject: [PATCH 67/79] Continuing my recent streak of making random lighting/colormap-related fixes to long-standing bugs: * Fix that thing where ALL transparent FOF planes were continuously fullbright unless encased in a fog which disables sprite fullbrightness, which was long-hated by many people in the community! * For backwards compatibility, setting flag 1 in that fog field (which is probably the most common "in-the-wild" usage of this feature) will continue to make objects un-fullbright. * For situations where you desperately want the behaviour to be enabled, you can apply fog flag 2. * Change the fadestart and fadeend range in which colormaps are generated. * The problem HERE was that the darkest light level reached by generated colormaps was actually slightly brighter than the darkest level reached by normal colormaps. * The typo I fixed does have SOME basis in fact - standard colormap lumps are 34 (33 in 0-indexing) long rather than 32 (31), but whoever wrote this didn't realise that the code for generating them didn't do it DooM style, just bright-to-dark with no extras on the end... --- src/r_data.c | 22 +++++++++++----------- src/r_plane.c | 4 ++-- src/r_things.c | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/r_data.c b/src/r_data.c index d19882dd3..e1d4b8935 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -1087,7 +1087,7 @@ INT32 R_ColormapNumForName(char *name) extra_colormaps[num_extra_colormaps].fadecolor = 0x0; extra_colormaps[num_extra_colormaps].maskamt = 0x0; extra_colormaps[num_extra_colormaps].fadestart = 0; - extra_colormaps[num_extra_colormaps].fadeend = 33; + extra_colormaps[num_extra_colormaps].fadeend = 31; extra_colormaps[num_extra_colormaps].fog = 0; num_extra_colormaps++; @@ -1115,7 +1115,7 @@ INT32 R_CreateColormap(char *p1, char *p2, char *p3) size_t mapnum = num_extra_colormaps; size_t i; UINT32 cr, cg, cb, maskcolor, fadecolor; - UINT32 fadestart = 0, fadeend = 33, fadedist = 33; + UINT32 fadestart = 0, fadeend = 31, fadedist = 31; #define HEX2INT(x) (UINT32)(x >= '0' && x <= '9' ? x - '0' : x >= 'a' && x <= 'f' ? x - 'a' + 10 : x >= 'A' && x <= 'F' ? x - 'A' + 10 : 0) if (p1[0] == '#') @@ -1156,12 +1156,12 @@ INT32 R_CreateColormap(char *p1, char *p2, char *p3) // Get parameters like fadestart, fadeend, and the fogflag fadestart = NUMFROMCHAR(p2[3]) + (NUMFROMCHAR(p2[2]) * 10); fadeend = NUMFROMCHAR(p2[5]) + (NUMFROMCHAR(p2[4]) * 10); - if (fadestart > 32) + if (fadestart > 30) fadestart = 0; - if (fadeend > 33 || fadeend < 1) - fadeend = 33; + if (fadeend > 31 || fadeend < 1) + fadeend = 31; fadedist = fadeend - fadestart; - fog = NUMFROMCHAR(p2[1]) ? 1 : 0; + fog = NUMFROMCHAR(p2[1]); } #undef getnum @@ -1262,7 +1262,7 @@ void R_CreateColormap2(char *p1, char *p2, char *p3) size_t i; char *colormap_p; UINT32 cr, cg, cb, maskcolor, fadecolor; - UINT32 fadestart = 0, fadeend = 33, fadedist = 33; + UINT32 fadestart = 0, fadeend = 31, fadedist = 31; #define HEX2INT(x) (UINT32)(x >= '0' && x <= '9' ? x - '0' : x >= 'a' && x <= 'f' ? x - 'a' + 10 : x >= 'A' && x <= 'F' ? x - 'A' + 10 : 0) if (p1[0] == '#') @@ -1303,12 +1303,12 @@ void R_CreateColormap2(char *p1, char *p2, char *p3) // Get parameters like fadestart, fadeend, and the fogflag fadestart = NUMFROMCHAR(p2[3]) + (NUMFROMCHAR(p2[2]) * 10); fadeend = NUMFROMCHAR(p2[5]) + (NUMFROMCHAR(p2[4]) * 10); - if (fadestart > 32) + if (fadestart > 30) fadestart = 0; - if (fadeend > 33 || fadeend < 1) - fadeend = 33; + if (fadeend > 31 || fadeend < 1) + fadeend = 31; fadedist = fadeend - fadestart; - fog = NUMFROMCHAR(p2[1]) ? 1 : 0; + fog = NUMFROMCHAR(p2[1]); } #undef getnum diff --git a/src/r_plane.c b/src/r_plane.c index b7b9eaff3..734493ebb 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -768,7 +768,7 @@ void R_DrawSinglePlane(visplane_t *pl) else // Opaque, but allow transparent flat pixels spanfunc = splatfunc; - if (pl->extra_colormap && pl->extra_colormap->fog) + if (!pl->extra_colormap || !(pl->extra_colormap->fog & 2)) light = (pl->lightlevel >> LIGHTSEGSHIFT); else light = LIGHTLEVELS-1; @@ -822,7 +822,7 @@ void R_DrawSinglePlane(visplane_t *pl) else // Opaque, but allow transparent flat pixels spanfunc = splatfunc; - if (pl->extra_colormap && pl->extra_colormap->fog) + if (!pl->extra_colormap || !(pl->extra_colormap->fog & 2)) light = (pl->lightlevel >> LIGHTSEGSHIFT); else light = LIGHTLEVELS-1; diff --git a/src/r_things.c b/src/r_things.c index 331febabd..0b1764167 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1023,7 +1023,7 @@ static void R_SplitSprite(vissprite_t *sprite, mobj_t *thing) else */ if (!((thing->frame & (FF_FULLBRIGHT|FF_TRANSMASK) || thing->flags2 & MF2_SHADOW) - && (!newsprite->extra_colormap || !newsprite->extra_colormap->fog))) + && (!newsprite->extra_colormap || !(newsprite->extra_colormap->fog & 1)))) { lindex = FixedMul(sprite->xscale, FixedDiv(640, vid.width))>>(LIGHTSCALESHIFT); @@ -1324,7 +1324,7 @@ static void R_ProjectSprite(mobj_t *thing) vis->transmap = transtables + (thing->frame & FF_TRANSMASK) - 0x10000; if (((thing->frame & FF_FULLBRIGHT) || (thing->flags2 & MF2_SHADOW)) - && (!vis->extra_colormap || !vis->extra_colormap->fog)) + && (!vis->extra_colormap || !(vis->extra_colormap->fog & 1))) { // full bright: goggles vis->colormap = colormaps; From ace4c46d5f597ecb4fbdd4bbf36baca9ddba5f56 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 25 Aug 2018 17:11:49 +0100 Subject: [PATCH 68/79] Fix up them there ghosts! * Stop orphaning their memory. They ARE PU_LEVEL, so they'll disappear eventually, but, like... it's not good memory management practice to just *orphan* them when you're literally never going to do anything with them ever again. Y'know? * Make ghosts spawn properly on slopes. --- src/g_game.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index bcae69fda..4f1c49b42 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4362,6 +4362,7 @@ void G_GhostTicker(void) p->next = g->next; else ghosts = g->next; + Z_Free(g); continue; } p = g; @@ -5314,29 +5315,28 @@ void G_AddGhost(char *defdemoname) mthing = playerstarts[0]; I_Assert(mthing); { // A bit more complex than P_SpawnPlayer because ghosts aren't solid and won't just push themselves out of the ceiling. - fixed_t x,y,z; - sector_t *sector; - x = mthing->x << FRACBITS; - y = mthing->y << FRACBITS; - sector = R_PointInSubsector(x, y)->sector; + fixed_t z,f,c; + gh->mo = P_SpawnMobj(mthing->x << FRACBITS, mthing->y << FRACBITS, 0, MT_GHOST); + gh->mo->angle = FixedAngle(mthing->angle*FRACUNIT); + f = gh->mo->floorz; + c = gh->mo->ceilingz - mobjinfo[MT_PLAYER].height; if (!!(mthing->options & MTF_AMBUSH) ^ !!(mthing->options & MTF_OBJECTFLIP)) { - z = sector->ceilingheight - mobjinfo[MT_PLAYER].height; + z = c; if (mthing->options >> ZSHIFT) z -= ((mthing->options >> ZSHIFT) << FRACBITS); - if (z < sector->floorheight) - z = sector->floorheight; + if (z < f) + z = f; } else { - z = sector->floorheight; + z = f; if (mthing->options >> ZSHIFT) z += ((mthing->options >> ZSHIFT) << FRACBITS); - if (z > sector->ceilingheight - mobjinfo[MT_PLAYER].height) - z = sector->ceilingheight - mobjinfo[MT_PLAYER].height; + if (z > c) + z = c; } - gh->mo = P_SpawnMobj(x, y, z, MT_GHOST); - gh->mo->angle = FixedAngle(mthing->angle*FRACUNIT); + gh->mo->z = z; } gh->mo->state = states+S_PLAY_STND; gh->mo->sprite = gh->mo->state->sprite; @@ -5534,8 +5534,14 @@ boolean G_CheckDemoStatus(void) { boolean saved; - if(ghosts) // ... ... ... - ghosts = NULL; // :) + while (ghosts) + { + demoghost *next = ghosts->next; + Z_Free(ghosts); + ghosts = next; + } + ghosts = NULL; + // DO NOT end metal sonic demos here From 241c9c2260e3d49d5737fe4a9d05e0069c7e97d4 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 25 Aug 2018 19:52:17 +0100 Subject: [PATCH 69/79] Clean up a bunch of v_video.c functions that previously exhibited unfortunate side effects when run in non-green resolutions. * V_DrawFixedPatch and ilk: * Change the offset of V_FLIP so it's not one screen-pixel off where its non-flipped sprite would have started being drawn from. * Write to x and y as well as desttop so that anti-screen-overflow checks later in the function behave properly with non-green resolutions. * V_DrawFill: * Reduce number of operations performed upon `c`. * V_DrawString and ilk: * Offset the left and right boundary checks in non-green resolutions such that you can actually draw stuff to the left of basevid screen x coordinate 0. --- src/v_video.c | 142 +++++++++++++++++++++++++++++--------------------- 1 file changed, 84 insertions(+), 58 deletions(-) diff --git a/src/v_video.c b/src/v_video.c index 161c03d0b..7541402ac 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -418,7 +418,7 @@ void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t if (scrn & V_FLIP) { flip = true; - x -= FixedMul((SHORT(patch->width) - SHORT(patch->leftoffset))<width) - SHORT(patch->leftoffset))<leftoffset)<>= FRACBITS; y >>= FRACBITS; - desttop += (y*vid.width) + x; // Center it if necessary if (!(scrn & V_SCALEPATCHMASK)) { - if (vid.width != BASEVIDWIDTH * dupx) - { - // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx, - // so center this imaginary screen - if (scrn & V_SNAPTORIGHT) - desttop += (vid.width - (BASEVIDWIDTH * dupx)); - else if (!(scrn & V_SNAPTOLEFT)) - desttop += (vid.width - (BASEVIDWIDTH * dupx)) / 2; - } - if (vid.height != BASEVIDHEIGHT * dupy) - { - // same thing here - if ((scrn & (V_SPLITSCREEN|V_SNAPTOBOTTOM)) == (V_SPLITSCREEN|V_SNAPTOBOTTOM)) - desttop += (vid.height/2 - (BASEVIDHEIGHT/2 * dupy)) * vid.width; - else if (scrn & V_SNAPTOBOTTOM) - desttop += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width; - else if (!(scrn & V_SNAPTOTOP)) - desttop += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2; - } // if it's meant to cover the whole screen, black out the rest if (x == 0 && SHORT(patch->width) == BASEVIDWIDTH && y == 0 && SHORT(patch->height) == BASEVIDHEIGHT) { @@ -477,6 +457,27 @@ void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t source = (const UINT8 *)(column) + 3; V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0])); } + if (vid.width != BASEVIDWIDTH * dupx) + { + // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx, + // so center this imaginary screen + if (scrn & V_SNAPTORIGHT) + x += (vid.width - (BASEVIDWIDTH * dupx)); + else if (!(scrn & V_SNAPTOLEFT)) + x += (vid.width - (BASEVIDWIDTH * dupx)) / 2; + } + if (vid.height != BASEVIDHEIGHT * dupy) + { + // same thing here + if ((scrn & (V_SPLITSCREEN|V_SNAPTOBOTTOM)) == (V_SPLITSCREEN|V_SNAPTOBOTTOM)) + y += (vid.height/2 - (BASEVIDHEIGHT/2 * dupy)) * vid.width; + else if (scrn & V_SNAPTOBOTTOM) + y += (vid.height - (BASEVIDHEIGHT * dupy)); + else if (!(scrn & V_SNAPTOTOP)) + y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2; + } + + desttop += (y*vid.width) + x; } } @@ -583,28 +584,10 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ y = FixedMul(y,dupy<>= FRACBITS; y >>= FRACBITS; - desttop += (y*vid.width) + x; // Center it if necessary if (!(scrn & V_SCALEPATCHMASK)) { - if (vid.width != BASEVIDWIDTH * dupx) - { - // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx, - // so center this imaginary screen - if (scrn & V_SNAPTORIGHT) - desttop += (vid.width - (BASEVIDWIDTH * dupx)); - else if (!(scrn & V_SNAPTOLEFT)) - desttop += (vid.width - (BASEVIDWIDTH * dupx)) / 2; - } - if (vid.height != BASEVIDHEIGHT * dupy) - { - // same thing here - if (scrn & V_SNAPTOBOTTOM) - desttop += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width; - else if (!(scrn & V_SNAPTOTOP)) - desttop += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2; - } // if it's meant to cover the whole screen, black out the rest if (x == 0 && SHORT(patch->width) == BASEVIDWIDTH && y == 0 && SHORT(patch->height) == BASEVIDHEIGHT) { @@ -612,7 +595,26 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ source = (const UINT8 *)(column) + 3; V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0])); } + if (vid.width != BASEVIDWIDTH * dupx) + { + // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx, + // so center this imaginary screen + if (scrn & V_SNAPTORIGHT) + x += (vid.width - (BASEVIDWIDTH * dupx)); + else if (!(scrn & V_SNAPTOLEFT)) + x += (vid.width - (BASEVIDWIDTH * dupx)) / 2; + } + if (vid.height != BASEVIDHEIGHT * dupy) + { + // same thing here + if (scrn & V_SNAPTOBOTTOM) + y += (vid.height - (BASEVIDHEIGHT * dupy)); + else if (!(scrn & V_SNAPTOTOP)) + y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2; + } } + + desttop += (y*vid.width) + x; } for (col = sx<>FRACBITS) < SHORT(patch->width) && (col>>FRACBITS) < w; col += colfrac, ++x, desttop++) @@ -776,7 +778,7 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT) { // Clear the entire screen, from dest to deststop. Yes, this really works. - memset(screens[0], (UINT8)(c&255), vid.width * vid.height * vid.bpp); + memset(screens[0], (c&255), vid.width * vid.height * vid.bpp); return; } @@ -831,7 +833,7 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) c &= 255; for (;(--h >= 0) && dest < deststop; dest += vid.width) - memset(dest, (UINT8)(c&255), w * vid.bpp); + memset(dest, c, w * vid.bpp); } // @@ -1129,7 +1131,7 @@ char *V_WordWrap(INT32 x, INT32 w, INT32 option, const char *string) // void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string) { - INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth = BASEVIDWIDTH, center = 0; + INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, center = 0, left = 0; const char *ch = string; INT32 charflags = 0; const UINT8 *colormap = NULL; @@ -1145,7 +1147,12 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string) scrwidth = vid.width; } else + { dupx = dupy = 1; + scrwidth = vid.width/vid.dupx; + left = (scrwidth - BASEVIDWIDTH)/2; + scrwidth -= left; + } charflags = (option & V_CHARCOLORMASK); @@ -1206,9 +1213,9 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string) else w = SHORT(hu_font[c]->width) * dupx; - if (cx + w > scrwidth) + if (cx > scrwidth) break; - if (cx < 0) //left boundary check + if (cx+left + w < 0) //left boundary check { cx += w; continue; @@ -1239,7 +1246,7 @@ void V_DrawRightAlignedString(INT32 x, INT32 y, INT32 option, const char *string // void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string) { - INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth = BASEVIDWIDTH, center = 0; + INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, center = 0, left = 0; const char *ch = string; INT32 charflags = 0; const UINT8 *colormap = NULL; @@ -1255,7 +1262,12 @@ void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string) scrwidth = vid.width; } else + { dupx = dupy = 1; + scrwidth = vid.width/vid.dupx; + left = (scrwidth - BASEVIDWIDTH)/2; + scrwidth -= left; + } charflags = (option & V_CHARCOLORMASK); @@ -1314,9 +1326,9 @@ void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string) } else w = SHORT(hu_font[c]->width) * dupx / 2; - if (cx + w > scrwidth) + if (cx > scrwidth) break; - if (cx < 0) //left boundary check + if (cx+left + w < 0) //left boundary check { cx += w; continue; @@ -1341,7 +1353,7 @@ void V_DrawRightAlignedSmallString(INT32 x, INT32 y, INT32 option, const char *s // void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string) { - INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth = BASEVIDWIDTH; + INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, left = 0; const char *ch = string; INT32 charflags = 0; const UINT8 *colormap = NULL; @@ -1357,7 +1369,12 @@ void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string) scrwidth = vid.width; } else + { dupx = dupy = 1; + scrwidth = vid.width/vid.dupx; + left = (scrwidth - BASEVIDWIDTH)/2; + scrwidth -= left; + } charflags = (option & V_CHARCOLORMASK); @@ -1414,9 +1431,9 @@ void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string) else w = (SHORT(tny_font[c]->width) * dupx); - if (cx + w > scrwidth) + if (cx > scrwidth) break; - if (cx < 0) //left boundary check + if (cx+left + w < 0) //left boundary check { cx += w; continue; @@ -1439,7 +1456,7 @@ void V_DrawRightAlignedThinString(INT32 x, INT32 y, INT32 option, const char *st void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string) { fixed_t cx = x, cy = y; - INT32 w, c, dupx, dupy, scrwidth = BASEVIDWIDTH, center = 0; + INT32 w, c, dupx, dupy, scrwidth, center = 0, left = 0; const char *ch = string; INT32 spacewidth = 4, charwidth = 0; @@ -1453,7 +1470,12 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string) scrwidth = vid.width; } else + { dupx = dupy = 1; + scrwidth = vid.width/vid.dupx; + left = (scrwidth - BASEVIDWIDTH)/2; + scrwidth -= left; + } switch (option & V_SPACINGMASK) { @@ -1507,9 +1529,9 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string) else w = SHORT(hu_font[c]->width) * dupx; - if ((cx>>FRACBITS) + w > scrwidth) + if ((cx>>FRACBITS) > scrwidth) break; - if (cx < 0) //left boundary check + if ((cx>>FRACBITS)+left + w < 0) //left boundary check { cx += w<width) * dupx; - if ((cx>>FRACBITS) + w > scrwidth) + if ((cx>>FRACBITS) > scrwidth) break; V_DrawSciencePatch(cx, cy, option, cred_font[c], FRACUNIT); @@ -1645,7 +1667,7 @@ INT32 V_CreditStringWidth(const char *string) // void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string) { - INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth = BASEVIDWIDTH; + INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, left = 0; const char *ch = string; if (option & V_NOSCALESTART) @@ -1655,7 +1677,12 @@ void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string) scrwidth = vid.width; } else + { dupx = dupy = 1; + scrwidth = vid.width/vid.dupx; + left = (scrwidth - BASEVIDWIDTH)/2; + scrwidth -= left; + } for (;;) { @@ -1677,11 +1704,10 @@ void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string) } w = SHORT(lt_font[c]->width) * dupx; - if (cx + w > scrwidth) + if (cx > scrwidth) break; + if (cx+left + w < 0) //left boundary check - //left boundary check - if (cx < 0) { cx += w; continue; From 60177bba79bd2ff9b77e56537ad8c28e9977f008 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 25 Aug 2018 21:17:34 +0100 Subject: [PATCH 70/79] Introduce a temporary measure to enable the old stuff, minus one of the most obviously terrible bugbears of yesteryear. Let it be known that any downstream poirt will almost certainly toggle this ASAP. --- src/r_plane.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/r_plane.c b/src/r_plane.c index 734493ebb..2c4a13290 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -37,6 +37,9 @@ // Quincunx antialiasing of flats! //#define QUINCUNX +// good night sweet prince +#define SHITPLANESPARENCY + //SoM: 3/23/2000: Use Boom visplane hashing. #define MAXVISPLANES 512 @@ -768,7 +771,11 @@ void R_DrawSinglePlane(visplane_t *pl) else // Opaque, but allow transparent flat pixels spanfunc = splatfunc; +#ifdef SHITPLANESPARENCY + if (spanfunc == splatfunc || (pl->extra_colormap && pl->extra_colormap->fog)) +#else if (!pl->extra_colormap || !(pl->extra_colormap->fog & 2)) +#endif light = (pl->lightlevel >> LIGHTSEGSHIFT); else light = LIGHTLEVELS-1; @@ -822,7 +829,11 @@ void R_DrawSinglePlane(visplane_t *pl) else // Opaque, but allow transparent flat pixels spanfunc = splatfunc; +#ifdef SHITPLANESPARENCY + if (spanfunc == splatfunc || (pl->extra_colormap && pl->extra_colormap->fog)) +#else if (!pl->extra_colormap || !(pl->extra_colormap->fog & 2)) +#endif light = (pl->lightlevel >> LIGHTSEGSHIFT); else light = LIGHTLEVELS-1; From b26828805ed117bf30775965ca170fe18638ca63 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 26 Aug 2018 12:38:53 +0100 Subject: [PATCH 71/79] Missed one! --- src/v_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v_video.c b/src/v_video.c index 7541402ac..aa2852c01 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -470,7 +470,7 @@ void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t { // same thing here if ((scrn & (V_SPLITSCREEN|V_SNAPTOBOTTOM)) == (V_SPLITSCREEN|V_SNAPTOBOTTOM)) - y += (vid.height/2 - (BASEVIDHEIGHT/2 * dupy)) * vid.width; + y += (vid.height/2 - (BASEVIDHEIGHT/2 * dupy)); else if (scrn & V_SNAPTOBOTTOM) y += (vid.height - (BASEVIDHEIGHT * dupy)); else if (!(scrn & V_SNAPTOTOP)) From d130c48787ade08ff114d88d9d85b9ccbfb7b491 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Wed, 29 Aug 2018 20:46:08 +0100 Subject: [PATCH 72/79] Fix typo introduced by changes to the setrings command --- src/m_cheat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_cheat.c b/src/m_cheat.c index 652451749..0937e40fe 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -890,7 +890,7 @@ void Command_Setrings_f(void) else { players[consoleplayer].spheres = 0; - P_GivePlayerSpheres(&player[consoleplayer], atoi(COM_Argv(1))); + P_GivePlayerSpheres(&players[consoleplayer], atoi(COM_Argv(1))); // no totalsphere addition to revert } From f8588739b89a490fabb9e60606a375f1b1d84ef4 Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 30 Aug 2018 09:09:19 -0400 Subject: [PATCH 73/79] Drill mash penalty fix --- src/p_user.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index 76842b161..12383c423 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -6668,12 +6668,16 @@ static void P_NiGHTSMovement(player_t *player) } else if (player->drilltimer == 32) { + // drill mash penalty player->drilltimer = 31; player->drillmeter -= TICRATE/2; if (player->drillmeter <= 0) player->drillmeter = TICRATE/10; } - else if (--player->drilltimer <= 0) + else if (--player->drilltimer == 11) + // give that drill mash penalty back (after 0.6 seconds) + player->drillmeter += TICRATE/2; + else if (player->drilltimer <= 0) { player->drilltimer = 10; S_StartSound(player->mo, sfx_drill2); From dcca56bc0488f7407f3c9a6684bf48c115c0fd2e Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 30 Aug 2018 10:50:03 -0400 Subject: [PATCH 74/79] Dummy out NiGHTS item faster attract --- src/p_inter.c | 4 +++- src/p_user.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index 500018fd9..9a05bae28 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -946,7 +946,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Yay! The thing's in reach! Pull it in! mo2->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT; mo2->flags2 |= MF2_NIGHTSPULL; - mo2->movefactor = 32*FRACUNIT; // initialize the NightsItemChase timer + // New NiGHTS attract speed dummied out because the older behavior + // is exploited as a mechanic. Uncomment to enable. + mo2->movefactor = 0; // 32*FRACUNIT; // initialize the NightsItemChase timer P_SetTarget(&mo2->tracer, toucher); } } diff --git a/src/p_user.c b/src/p_user.c index 12383c423..ffb48f27c 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9807,7 +9807,9 @@ void P_PlayerThink(player_t *player) // Yay! The thing's in reach! Pull it in! mo2->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT; mo2->flags2 |= MF2_NIGHTSPULL; - mo2->movefactor = 40*FRACUNIT; // initialize the NightsItemChase timer + // New NiGHTS attract speed dummied out because the older behavior + // is exploited as a mechanic. Uncomment to enable. + mo2->movefactor = 0; // 40*FRACUNIT; // initialize the NightsItemChase timer P_SetTarget(&mo2->tracer, player->mo); } } From 277bec55c26aaa3a4bdf8fbe73f09f857036f05a Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Thu, 30 Aug 2018 18:32:26 -0400 Subject: [PATCH 75/79] Only do this if the admin player isn't the server host also. --- src/d_netcmd.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index bf26ca61a..742124000 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2697,6 +2697,12 @@ static void Command_Login_f(void) XBOXSTATIC UINT8 finalmd5[16]; const char *pw; + if (!netgame) + { + CONS_Printf(M_GetText("This only works in a netgame.\n")); + return; + } + // If the server uses login, it will effectively just remove admin privileges // from whoever has them. This is good. if (COM_Argc() != 2) @@ -2764,6 +2770,12 @@ static void Command_Verify_f(void) return; } + if (!netgame) + { + CONS_Printf(M_GetText("This only works in a netgame.\n")); + return; + } + if (COM_Argc() != 2) { CONS_Printf(M_GetText("verify : give admin privileges to a node\n")); @@ -3075,7 +3087,7 @@ static void Command_Addfile(void) WRITEMEM(buf_p, md5sum, 16); } - if (adminplayer == consoleplayer) // Request to add file + if (adminplayer == consoleplayer && (!server)) // Request to add file SendNetXCmd(XD_REQADDFILE, buf, buf_p - buf); else SendNetXCmd(XD_ADDFILE, buf, buf_p - buf); From 7fc4d864a8d1a1e99b9c265c4a2da78eb10b160a Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Fri, 31 Aug 2018 17:14:44 +0100 Subject: [PATCH 76/79] Fix HOM removal not working properly for non-green resolutions --- src/r_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/r_main.c b/src/r_main.c index e50e80013..f2a0c8894 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -1235,9 +1235,9 @@ void R_RenderPlayerView(player_t *player) if (cv_homremoval.value && player == &players[displayplayer]) // if this is display player 1 { if (cv_homremoval.value == 1) - V_DrawFill(0, 0, vid.width, vid.height, 31); // No HOM effect! + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); // No HOM effect! else //'development' HOM removal -- makes it blindingly obvious if HOM is spotted. - V_DrawFill(0, 0, vid.width, vid.height, 128+(timeinmap&15)); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 128+(timeinmap&15)); } // load previous saved value of skyVisible for the player From cd8a46b3a17745df0565f366e342e87256642bc1 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Tue, 4 Sep 2018 17:04:24 +0100 Subject: [PATCH 77/79] Fix mixed d+c in NiGHTS trigger code, fix stray "INT8" in A_ConnectToGround --- src/p_enemy.c | 2 +- src/p_spec.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 9235a1d0f..52044dfae 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -11378,7 +11378,7 @@ void A_ConnectToGround(mobj_t *actor) mobj_t *work; fixed_t workz; fixed_t workh; - INT8 dir; + SINT8 dir; angle_t ang; INT32 locvar1 = var1; INT32 locvar2 = var2; diff --git a/src/p_spec.c b/src/p_spec.c index e5c48dcaa..6c359c9cc 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1586,6 +1586,7 @@ static boolean P_CheckNightsTriggerLine(line_t *triggerline, mobj_t *actor) for (i = 0; i < MAXPLAYERS; i++) { + UINT8 lap; if (!playeringame[i] || players[i].spectator) continue; @@ -1599,7 +1600,7 @@ static boolean P_CheckNightsTriggerLine(line_t *triggerline, mobj_t *actor) && players[i].powers[pw_carry] == CR_NIGHTSMODE) playersarenights++; - UINT8 lap = lapfrombonustime ? players[i].marebonuslap : players[i].marelap; + lap = lapfrombonustime ? players[i].marebonuslap : players[i].marelap; // get highest mare/lap of players if (perglobal) From fe508088b23c6022c2bb0f2366b195da491d3fbe Mon Sep 17 00:00:00 2001 From: mazmazz Date: Thu, 6 Sep 2018 09:18:56 -0400 Subject: [PATCH 78/79] Re-organize flags for FLICKY_CENTER * Flickies can be in-place with just one flag (MF_GRENADEBOUNCE) * Flickies can now hop whether orbiting, aimless, or in-place * Removed ability to customize movedir for orbit; will always be random * Removed ability to customize gravity sink for aimless flying flickies * Misc: Use P_SetTarget to set actor->tracer for flicky center --- src/p_enemy.c | 63 +++++++++++++++++++-------------------------------- src/p_mobj.c | 4 +--- 2 files changed, 24 insertions(+), 43 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 273672987..72f68b2e5 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -10812,17 +10812,14 @@ void P_InternalFlickySetColor(mobj_t *actor, UINT8 extrainfo) // Bit 21 = Flag MF_SLIDEME (see below) // Bit 22 = Flag MF_GRENADEBOUNCE (see below) // Bit 23 = Flag MF_NOCLIPTHING (see below) -// Bit 24 = Flicky movedir, 0 or 1 // // If actor is placed from a spawnpoint (map Thing), the Thing's properties take precedence. // // var2 = maximum default distance away from spawn the flickies are allowed to travel. If angle != 0, then that's the radius. // -// If MTF_EXTRA (MF_SLIDEME) is flagged, Flickies move independently of a target. Else, move around the target. -// If MTF_EXTRA (MF_SLIDEME) and MTF_OBJECTSPECIAL (MF_GRENADEBOUNCE) are both flagged, Flying flickies sink from gravity. By default, they stay at constant Z height. -// If MTF_OBJECTSPECIAL (MF_GRENADEBOUNCE) and NOT MTF_EXTRA (MF_SLIDEME) are flagged, Angle sign determines direction of circular movement. -// If MTF_AMBUSH (MF_NOCLIPTHING) is flagged, Flickies hop in-place. -// If MTF_AMBUSH (MF_NOCLIPTHING) and MTF_OBJECTSPECIAL (MF_GRENADEBOUNCE) is flagged, Flickies stand in-place without gravity. +// If MTF_EXTRA (MF_SLIDEME): is flagged, Flickies move aimlessly. Else, orbit around the target. +// If MTF_OBJECTSPECIAL (MF_GRENADEBOUNCE): Flickies stand in-place without gravity (unless they hop, then gravity is applied.) +// If MTF_AMBUSH (MF_NOCLIPTHING): is flagged, Flickies hop. // void A_FlickyCenter(mobj_t *actor) { @@ -10838,6 +10835,10 @@ void A_FlickyCenter(mobj_t *actor) if (!actor->tracer) { + mobj_t *flicky = P_InternalFlickySpawn(actor, locvar1, 1, false); + P_SetTarget(&flicky->target, actor); + P_SetTarget(&actor->tracer, flicky); + if (actor->spawnpoint) { actor->flags &= ~(MF_SLIDEME|MF_GRENADEBOUNCE|MF_NOCLIPTHING); @@ -10860,7 +10861,7 @@ void A_FlickyCenter(mobj_t *actor) | ((flickyflags & 2) ? MF_GRENADEBOUNCE : 0) | ((flickyflags & 4) ? MF_NOCLIPTHING : 0) ); - actor->extravalue1 = locvar2; // don't abs() yet, for movedir + actor->extravalue1 = abs(locvar2); actor->extravalue2 = flickycolor; actor->friction = actor->x; actor->movefactor = actor->y; @@ -10868,45 +10869,28 @@ void A_FlickyCenter(mobj_t *actor) locvar1 = flickytype; } - if (actor->flags & MF_SLIDEME) + if (actor->flags & MF_GRENADEBOUNCE) // in-place + actor->tracer->fuse = 0; + else if (actor->flags & MF_SLIDEME) // aimless { - actor->tracer = P_InternalFlickySpawn(actor, locvar1, 1, false); - P_SetTarget(&actor->tracer->target, actor); - actor->tracer->fuse = 0; // < 2*TICRATE means move aimlessly. - - if (!(actor->flags & MF_NOCLIPTHING)) - actor->tracer->angle = P_RandomKey(180)*ANG2; + actor->tracer->fuse = 0; // less than 2*TICRATE means move aimlessly. + actor->tracer->angle = P_RandomKey(180)*ANG2; } - else + else //orbit { - actor->tracer = P_InternalFlickySpawn(actor, locvar1, 1, false); - P_SetTarget(&actor->tracer->target, actor); actor->tracer->fuse = FRACUNIT; - - if ((actor->flags & MF_GRENADEBOUNCE) && !(actor->flags & MF_SLIDEME)) - { - if (!actor->spawnpoint) - actor->tracer->movedir = (flickyflags & 8) ? 1 : -1; - else - actor->tracer->movedir = actor->extravalue1 >= 0 ? 1 : -1; - } + // Impose default home radius if flicky orbits around player + if (!actor->extravalue1) + actor->extravalue1 = 512 * FRACUNIT; } if (locvar1 == MT_FLICKY_08) P_InternalFlickySetColor(actor->tracer, actor->extravalue2); actor->extravalue2 = 0; - - // Now abs() extravalue1 (home radius) - if (!actor->spawnpoint) - actor->extravalue1 = abs(actor->extravalue1); - - // Impose default home radius if flicky orbits around player - if (!(actor->flags & MF_SLIDEME) && !actor->extravalue1) - actor->extravalue1 = 512 * FRACUNIT; } - if (!(actor->flags & MF_SLIDEME) && !(actor->flags & MF_NOCLIPTHING)) + if (!(actor->flags & MF_SLIDEME) && !(actor->flags & MF_GRENADEBOUNCE)) { fixed_t originx = actor->friction; fixed_t originy = actor->movefactor; @@ -11041,8 +11025,7 @@ void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fi if (actor->target && P_IsFlickyCenter(actor->target->type) - && !((actor->target->flags & MF_GRENADEBOUNCE) - && (actor->target->flags & MF_SLIDEME))) + && (actor->target->flags & MF_SLIDEME)) vertangle = 0; else vertangle = (R_PointToAngle2(0, actor->z, targetdist, chasez) >> ANGLETOFINESHIFT) & FINEMASK; @@ -11204,9 +11187,9 @@ void A_FlickyCheck(mobj_t *actor) #endif if (actor->target && P_IsFlickyCenter(actor->target->type) - && (actor->target->flags & MF_NOCLIPTHING)) + && (actor->target->flags & MF_GRENADEBOUNCE)) { - if (actor->target->flags & MF_GRENADEBOUNCE) + if (!(actor->target->flags & MF_NOCLIPTHING)) // no hopping { actor->momz = 0; actor->flags |= MF_NOGRAVITY; @@ -11241,9 +11224,9 @@ void A_FlickyHeightCheck(mobj_t *actor) #endif if (actor->target && P_IsFlickyCenter(actor->target->type) - && (actor->target->flags & MF_NOCLIPTHING)) + && (actor->target->flags & MF_GRENADEBOUNCE)) { - if (actor->target->flags & MF_GRENADEBOUNCE) + if (!(actor->target->flags & MF_NOCLIPTHING)) // no hopping { actor->momz = 0; actor->flags |= MF_NOGRAVITY; diff --git a/src/p_mobj.c b/src/p_mobj.c index 894304491..d55fdf9c3 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7137,9 +7137,7 @@ void P_MobjThinker(mobj_t *mobj) case MT_FLICKY_16_CENTER: case MT_SECRETFLICKY_01_CENTER: case MT_SECRETFLICKY_02_CENTER: - if (mobj->tracer - && (mobj->flags & MF_NOCLIPTHING) - && !(mobj->flags & MF_GRENADEBOUNCE)) + if (mobj->tracer && (mobj->flags & MF_NOCLIPTHING)) { if (!(mobj->tracer->flags2 & MF2_OBJECTFLIP) && mobj->tracer->z <= mobj->tracer->floorz) mobj->tracer->momz = 7*FRACUNIT; From 3f45f51a45c2bc7de7c4de617a015aee5a4e3cd5 Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Tue, 27 Nov 2018 21:29:18 -0500 Subject: [PATCH 79/79] fix EOL on src/p_enemy.c --- src/p_enemy.c | 23624 ++++++++++++++++++++++++------------------------ 1 file changed, 11812 insertions(+), 11812 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 72f68b2e5..0fde09536 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -1,11812 +1,11812 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1993-1996 by id Software, Inc. -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2016 by Sonic Team Junior. -// -// This program is free software distributed under the -// terms of the GNU General Public License, version 2. -// See the 'LICENSE' file for more details. -//----------------------------------------------------------------------------- -/// \file p_enemy.c -/// \brief Enemy thinking, AI -/// Action Pointer Functions that are associated with states/frames - -#include "doomdef.h" -#include "g_game.h" -#include "p_local.h" -#include "r_main.h" -#include "r_state.h" -#include "s_sound.h" -#include "m_random.h" -#include "m_misc.h" -#include "r_things.h" -#include "i_video.h" -#include "lua_hook.h" - -#ifdef HW3SOUND -#include "hardware/hw3sound.h" -#endif - -#ifdef HAVE_BLUA -boolean LUA_CallAction(const char *action, mobj_t *actor); -#endif - -player_t *stplyr; -INT32 var1; -INT32 var2; - -// -// P_NewChaseDir related LUT. -// -static dirtype_t opposite[] = -{ - DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, - DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR -}; - -static dirtype_t diags[] = -{ - DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST -}; - -//Real Prototypes to A_* -void A_Fall(mobj_t *actor); -void A_Look(mobj_t *actor); -void A_Chase(mobj_t *actor); -void A_FaceStabChase(mobj_t *actor); -void A_FaceStabRev(mobj_t *actor); -void A_FaceStabHurl(mobj_t *actor); -void A_FaceStabMiss(mobj_t *actor); -void A_StatueBurst(mobj_t *actor); -void A_JetJawRoam(mobj_t *actor); -void A_JetJawChomp(mobj_t *actor); -void A_PointyThink(mobj_t *actor); -void A_CheckBuddy(mobj_t *actor); -void A_HoodFire(mobj_t *actor); -void A_HoodThink(mobj_t *actor); -void A_HoodFall(mobj_t *actor); -void A_ArrowBonks(mobj_t *actor); -void A_SnailerThink(mobj_t *actor); -void A_SharpChase(mobj_t *actor); -void A_SharpSpin(mobj_t *actor); -void A_SharpDecel(mobj_t *actor); -void A_CrushstaceanWalk(mobj_t *actor); -void A_CrushstaceanPunch(mobj_t *actor); -void A_CrushclawAim(mobj_t *actor); -void A_CrushclawLaunch(mobj_t *actor); -void A_VultureVtol(mobj_t *actor); -void A_VultureCheck(mobj_t *actor); -void A_SkimChase(mobj_t *actor); -void A_FaceTarget(mobj_t *actor); -void A_FaceTracer(mobj_t *actor); -void A_LobShot(mobj_t *actor); -void A_FireShot(mobj_t *actor); -void A_SuperFireShot(mobj_t *actor); -void A_BossFireShot(mobj_t *actor); -void A_Boss7FireMissiles(mobj_t *actor); -void A_Boss1Laser(mobj_t *actor); -void A_FocusTarget(mobj_t *actor); -void A_Boss4Reverse(mobj_t *actor); -void A_Boss4SpeedUp(mobj_t *actor); -void A_Boss4Raise(mobj_t *actor); -void A_SkullAttack(mobj_t *actor); -void A_BossZoom(mobj_t *actor); -void A_BossScream(mobj_t *actor); -void A_Scream(mobj_t *actor); -void A_Pain(mobj_t *actor); -void A_1upThinker(mobj_t *actor); -void A_MonitorPop(mobj_t *actor); -void A_GoldMonitorPop(mobj_t *actor); -void A_GoldMonitorRestore(mobj_t *actor); -void A_GoldMonitorSparkle(mobj_t *actor); -void A_Explode(mobj_t *actor); -void A_BossDeath(mobj_t *actor); -void A_CustomPower(mobj_t *actor); -void A_GiveWeapon(mobj_t *actor); -void A_RingBox(mobj_t *actor); -void A_Invincibility(mobj_t *actor); -void A_SuperSneakers(mobj_t *actor); -void A_AwardScore(mobj_t *actor); -void A_ExtraLife(mobj_t *actor); -void A_GiveShield(mobj_t *actor); -void A_GravityBox(mobj_t *actor); -void A_ScoreRise(mobj_t *actor); -void A_BunnyHop(mobj_t *actor); -void A_BubbleSpawn(mobj_t *actor); -void A_FanBubbleSpawn(mobj_t *actor); -void A_BubbleRise(mobj_t *actor); -void A_BubbleCheck(mobj_t *actor); -void A_AttractChase(mobj_t *actor); -void A_DropMine(mobj_t *actor); -void A_FishJump(mobj_t *actor); -void A_ThrownRing(mobj_t *actor); -void A_SetSolidSteam(mobj_t *actor); -void A_UnsetSolidSteam(mobj_t *actor); -void A_SignPlayer(mobj_t *actor); -void A_OverlayThink(mobj_t *actor); -void A_JetChase(mobj_t *actor); -void A_JetbThink(mobj_t *actor); -void A_JetgShoot(mobj_t *actor); -void A_JetgThink(mobj_t *actor); -void A_ShootBullet(mobj_t *actor); -void A_MinusDigging(mobj_t *actor); -void A_MinusPopup(mobj_t *actor); -void A_MinusCheck(mobj_t *actor); -void A_ChickenCheck(mobj_t *actor); -void A_MouseThink(mobj_t *actor); -void A_DetonChase(mobj_t *actor); -void A_CapeChase(mobj_t *actor); -void A_RotateSpikeBall(mobj_t *actor); -void A_SlingAppear(mobj_t *actor); -void A_UnidusBall(mobj_t *actor); -void A_RockSpawn(mobj_t *actor); -void A_SetFuse(mobj_t *actor); -void A_CrawlaCommanderThink(mobj_t *actor); -void A_RingExplode(mobj_t *actor); -void A_OldRingExplode(mobj_t *actor); -void A_MixUp(mobj_t *actor); -void A_RecyclePowers(mobj_t *actor); -void A_Boss2TakeDamage(mobj_t *actor); -void A_Boss7Chase(mobj_t *actor); -void A_GoopSplat(mobj_t *actor); -void A_Boss2PogoSFX(mobj_t *actor); -void A_Boss2PogoTarget(mobj_t *actor); -void A_EggmanBox(mobj_t *actor); -void A_TurretFire(mobj_t *actor); -void A_SuperTurretFire(mobj_t *actor); -void A_TurretStop(mobj_t *actor); -void A_SparkFollow(mobj_t *actor); -void A_BuzzFly(mobj_t *actor); -void A_GuardChase(mobj_t *actor); -void A_EggShield(mobj_t *actor); -void A_SetReactionTime(mobj_t *actor); -void A_Boss1Spikeballs(mobj_t *actor); -void A_Boss3TakeDamage(mobj_t *actor); -void A_Boss3Path(mobj_t *actor); -void A_LinedefExecute(mobj_t *actor); -void A_PlaySeeSound(mobj_t *actor); -void A_PlayAttackSound(mobj_t *actor); -void A_PlayActiveSound(mobj_t *actor); -void A_SmokeTrailer(mobj_t *actor); -void A_SpawnObjectAbsolute(mobj_t *actor); -void A_SpawnObjectRelative(mobj_t *actor); -void A_ChangeAngleRelative(mobj_t *actor); -void A_ChangeAngleAbsolute(mobj_t *actor); -void A_PlaySound(mobj_t *actor); -void A_FindTarget(mobj_t *actor); -void A_FindTracer(mobj_t *actor); -void A_SetTics(mobj_t *actor); -void A_SetRandomTics(mobj_t *actor); -void A_ChangeColorRelative(mobj_t *actor); -void A_ChangeColorAbsolute(mobj_t *actor); -void A_MoveRelative(mobj_t *actor); -void A_MoveAbsolute(mobj_t *actor); -void A_Thrust(mobj_t *actor); -void A_ZThrust(mobj_t *actor); -void A_SetTargetsTarget(mobj_t *actor); -void A_SetObjectFlags(mobj_t *actor); -void A_SetObjectFlags2(mobj_t *actor); -void A_RandomState(mobj_t *actor); -void A_RandomStateRange(mobj_t *actor); -void A_DualAction(mobj_t *actor); -void A_RemoteAction(mobj_t *actor); -void A_ToggleFlameJet(mobj_t *actor); -void A_OrbitNights(mobj_t *actor); -void A_GhostMe(mobj_t *actor); -void A_SetObjectState(mobj_t *actor); -void A_SetObjectTypeState(mobj_t *actor); -void A_KnockBack(mobj_t *actor); -void A_PushAway(mobj_t *actor); -void A_RingDrain(mobj_t *actor); -void A_SplitShot(mobj_t *actor); -void A_MissileSplit(mobj_t *actor); -void A_MultiShot(mobj_t *actor); -void A_InstaLoop(mobj_t *actor); -void A_Custom3DRotate(mobj_t *actor); -void A_SearchForPlayers(mobj_t *actor); -void A_CheckRandom(mobj_t *actor); -void A_CheckTargetRings(mobj_t *actor); -void A_CheckRings(mobj_t *actor); -void A_CheckTotalRings(mobj_t *actor); -void A_CheckHealth(mobj_t *actor); -void A_CheckRange(mobj_t *actor); -void A_CheckHeight(mobj_t *actor); -void A_CheckTrueRange(mobj_t *actor); -void A_CheckThingCount(mobj_t *actor); -void A_CheckAmbush(mobj_t *actor); -void A_CheckCustomValue(mobj_t *actor); -void A_CheckCusValMemo(mobj_t *actor); -void A_SetCustomValue(mobj_t *actor); -void A_UseCusValMemo(mobj_t *actor); -void A_RelayCustomValue(mobj_t *actor); -void A_CusValAction(mobj_t *actor); -void A_ForceStop(mobj_t *actor); -void A_ForceWin(mobj_t *actor); -void A_SpikeRetract(mobj_t *actor); -void A_InfoState(mobj_t *actor); -void A_Repeat(mobj_t *actor); -void A_SetScale(mobj_t *actor); -void A_RemoteDamage(mobj_t *actor); -void A_HomingChase(mobj_t *actor); -void A_TrapShot(mobj_t *actor); -void A_Boss1Chase(mobj_t *actor); -void A_Boss2Chase(mobj_t *actor); -void A_Boss2Pogo(mobj_t *actor); -void A_BossJetFume(mobj_t *actor); -void A_VileTarget(mobj_t *actor); -void A_VileAttack(mobj_t *actor); -void A_VileFire(mobj_t *actor); -void A_BrakChase(mobj_t *actor); -void A_BrakFireShot(mobj_t *actor); -void A_BrakLobShot(mobj_t *actor); -void A_NapalmScatter(mobj_t *actor); -void A_SpawnFreshCopy(mobj_t *actor); -void A_FlickySpawn(mobj_t *actor); -void A_FlickyCenter(mobj_t *actor); -void A_FlickyAim(mobj_t *actor); -void A_FlickyFly(mobj_t *actor); -void A_FlickySoar(mobj_t *actor); -void A_FlickyCoast(mobj_t *actor); -void A_FlickyHop(mobj_t *actor); -void A_FlickyFlounder(mobj_t *actor); -void A_FlickyCheck(mobj_t *actor); -void A_FlickyHeightCheck(mobj_t *actor); -void A_FlickyFlutter(mobj_t *actor); -void A_FlameParticle(mobj_t *actor); -void A_FadeOverlay(mobj_t *actor); -void A_Boss5Jump(mobj_t *actor); -void A_LightBeamReset(mobj_t *actor); -void A_MineExplode(mobj_t *actor); -void A_MineRange(mobj_t *actor); -void A_ConnectToGround(mobj_t *actor); -void A_SpawnParticleRelative(mobj_t *actor); -void A_MultiShotDist(mobj_t *actor); -void A_WhoCaresIfYourSonIsABee(mobj_t *actor); -void A_ParentTriesToSleep(mobj_t *actor); -void A_CryingToMomma(mobj_t *actor); -void A_CheckFlags2(mobj_t *actor); -//for p_enemy.c - -// -// ENEMY THINKING -// Enemies are always spawned with targetplayer = -1, threshold = 0 -// Most monsters are spawned unaware of all players, but some can be made preaware. -// - -// -// P_CheckMeleeRange -// -boolean P_CheckMeleeRange(mobj_t *actor) -{ - mobj_t *pl; - fixed_t dist; - - if (!actor->target) - return false; - - pl = actor->target; - dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y); - - if (dist >= FixedMul(MELEERANGE - 20*FRACUNIT, actor->scale) + pl->radius) - return false; - - // check height now, so that damn crawlas cant attack - // you if you stand on a higher ledge. - if ((pl->z > actor->z + actor->height) || (actor->z > pl->z + pl->height)) - return false; - - if (!P_CheckSight(actor, actor->target)) - return false; - - return true; -} - -// P_CheckMeleeRange for Jettysyn Bomber. -boolean P_JetbCheckMeleeRange(mobj_t *actor) -{ - mobj_t *pl; - fixed_t dist; - - if (!actor->target) - return false; - - pl = actor->target; - dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y); - - if (dist >= (actor->radius + pl->radius)*2) - return false; - - if (actor->eflags & MFE_VERTICALFLIP) - { - if (pl->z < actor->z + actor->height + FixedMul(40<scale)) - return false; - } - else - { - if (pl->z + pl->height > actor->z - FixedMul(40<scale)) - return false; - } - - return true; -} - -// P_CheckMeleeRange for CastleBot FaceStabber. -boolean P_FaceStabCheckMeleeRange(mobj_t *actor) -{ - mobj_t *pl; - fixed_t dist; - - if (!actor->target) - return false; - - pl = actor->target; - dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y); - - if (dist >= (actor->radius + pl->radius)*4) - return false; - - if ((pl->z > actor->z + actor->height) || (actor->z > pl->z + pl->height)) - return false; - - if (!P_CheckSight(actor, actor->target)) - return false; - - return true; -} - -// P_CheckMeleeRange for Skim. -boolean P_SkimCheckMeleeRange(mobj_t *actor) -{ - mobj_t *pl; - fixed_t dist; - - if (!actor->target) - return false; - - pl = actor->target; - dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y); - - if (dist >= FixedMul(MELEERANGE - 20*FRACUNIT, actor->scale) + pl->radius) - return false; - - if (actor->eflags & MFE_VERTICALFLIP) - { - if (pl->z < actor->z + actor->height + FixedMul(24<scale)) - return false; - } - else - { - if (pl->z + pl->height > actor->z - FixedMul(24<scale)) - return false; - } - - return true; -} - -// -// P_CheckMissileRange -// -boolean P_CheckMissileRange(mobj_t *actor) -{ - fixed_t dist; - - if (!actor->target) - return false; - - if (actor->reactiontime) - return false; // do not attack yet - - if (!P_CheckSight(actor, actor->target)) - return false; - - // OPTIMIZE: get this from a global checksight - dist = P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) - FixedMul(64*FRACUNIT, actor->scale); - - if (!actor->info->meleestate) - dist -= FixedMul(128*FRACUNIT, actor->scale); // no melee attack, so fire more - - dist >>= FRACBITS; - - if (actor->type == MT_EGGMOBILE) - dist >>= 1; - - if (dist > 200) - dist = 200; - - if (actor->type == MT_EGGMOBILE && dist > 160) - dist = 160; - - if (P_RandomByte() < dist) - return false; - - return true; -} - -/** Checks for water in a sector. - * Used by Skim movements. - * - * \param x X coordinate on the map. - * \param y Y coordinate on the map. - * \return True if there's water at this location, false if not. - * \sa ::MT_SKIM - */ -static boolean P_WaterInSector(mobj_t *mobj, fixed_t x, fixed_t y) -{ - sector_t *sector; - - sector = R_PointInSubsector(x, y)->sector; - - if (sector->ffloors) - { - ffloor_t *rover; - - for (rover = sector->ffloors; rover; rover = rover->next) - { - if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_SWIMMABLE)) - continue; - - if (*rover->topheight >= mobj->floorz && *rover->topheight <= mobj->z) - return true; // we found water!! - } - } - - return false; -} - -static const fixed_t xspeed[NUMDIRS] = {FRACUNIT, 46341>>(16-FRACBITS), 0, -(46341>>(16-FRACBITS)), -FRACUNIT, -(46341>>(16-FRACBITS)), 0, 46341>>(16-FRACBITS)}; -static const fixed_t yspeed[NUMDIRS] = {0, 46341>>(16-FRACBITS), FRACUNIT, 46341>>(16-FRACBITS), 0, -(46341>>(16-FRACBITS)), -FRACUNIT, -(46341>>(16-FRACBITS))}; - -/** Moves an actor in its current direction. - * - * \param actor Actor object to move. - * \return False if the move is blocked, otherwise true. - */ -boolean P_Move(mobj_t *actor, fixed_t speed) -{ - fixed_t tryx, tryy; - dirtype_t movedir = actor->movedir; - - if (movedir == DI_NODIR || !actor->health) - return false; - - I_Assert(movedir < NUMDIRS); - - tryx = actor->x + FixedMul(speed*xspeed[movedir], actor->scale); - if (twodlevel || actor->flags2 & MF2_TWOD) - tryy = actor->y; - else - tryy = actor->y + FixedMul(speed*yspeed[movedir], actor->scale); - - if (actor->type == MT_SKIM && !P_WaterInSector(actor, tryx, tryy)) // bail out if sector lacks water - return false; - - if (!P_TryMove(actor, tryx, tryy, false)) - { - if (actor->flags & MF_FLOAT && floatok) - { - // must adjust height - if (actor->z < tmfloorz) - actor->z += FixedMul(FLOATSPEED, actor->scale); - else - actor->z -= FixedMul(FLOATSPEED, actor->scale); - - if (actor->type == MT_JETJAW && actor->z + actor->height > actor->watertop) - actor->z = actor->watertop - actor->height; - - actor->flags2 |= MF2_INFLOAT; - return true; - } - - return false; - } - else - actor->flags2 &= ~MF2_INFLOAT; - - return true; -} - -/** Attempts to move an actor on in its current direction. - * If the move succeeds, the actor's move count is reset - * randomly to a value from 0 to 15. - * - * \param actor Actor to move. - * \return True if the move succeeds, false if the move is blocked. - */ -static boolean P_TryWalk(mobj_t *actor) -{ - if (!P_Move(actor, actor->info->speed)) - return false; - actor->movecount = P_RandomByte() & 15; - return true; -} - -void P_NewChaseDir(mobj_t *actor) -{ - fixed_t deltax, deltay; - dirtype_t d[3]; - dirtype_t tdir = DI_NODIR, olddir, turnaround; - - I_Assert(actor->target != NULL); - I_Assert(!P_MobjWasRemoved(actor->target)); - - olddir = actor->movedir; - - if (olddir >= NUMDIRS) - olddir = DI_NODIR; - - if (olddir != DI_NODIR) - turnaround = opposite[olddir]; - else - turnaround = olddir; - - deltax = actor->target->x - actor->x; - deltay = actor->target->y - actor->y; - - if (deltax > FixedMul(10*FRACUNIT, actor->scale)) - d[1] = DI_EAST; - else if (deltax < -FixedMul(10*FRACUNIT, actor->scale)) - d[1] = DI_WEST; - else - d[1] = DI_NODIR; - - if (twodlevel || actor->flags2 & MF2_TWOD) - d[2] = DI_NODIR; - if (deltay < -FixedMul(10*FRACUNIT, actor->scale)) - d[2] = DI_SOUTH; - else if (deltay > FixedMul(10*FRACUNIT, actor->scale)) - d[2] = DI_NORTH; - else - d[2] = DI_NODIR; - - // try direct route - if (d[1] != DI_NODIR && d[2] != DI_NODIR) - { - dirtype_t newdir = diags[((deltay < 0)<<1) + (deltax > 0)]; - - actor->movedir = newdir; - if ((newdir != turnaround) && P_TryWalk(actor)) - return; - } - - // try other directions - if (P_RandomChance(25*FRACUNIT/32) || abs(deltay) > abs(deltax)) - { - tdir = d[1]; - d[1] = d[2]; - d[2] = tdir; - } - - if (d[1] == turnaround) - d[1] = DI_NODIR; - if (d[2] == turnaround) - d[2] = DI_NODIR; - - if (d[1] != DI_NODIR) - { - actor->movedir = d[1]; - - if (P_TryWalk(actor)) - return; // either moved forward or attacked - } - - if (d[2] != DI_NODIR) - { - actor->movedir = d[2]; - - if (P_TryWalk(actor)) - return; - } - - // there is no direct path to the player, so pick another direction. - if (olddir != DI_NODIR) - { - actor->movedir =olddir; - - if (P_TryWalk(actor)) - return; - } - - // randomly determine direction of search - if (P_RandomChance(FRACUNIT/2)) - { - for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++) - { - if (tdir != turnaround) - { - actor->movedir = tdir; - - if (P_TryWalk(actor)) - return; - } - } - } - else - { - for (tdir = DI_SOUTHEAST; tdir >= DI_EAST; tdir--) - { - if (tdir != turnaround) - { - actor->movedir = tdir; - - if (P_TryWalk(actor)) - return; - } - } - } - - if (turnaround != DI_NODIR) - { - actor->movedir = turnaround; - - if (P_TryWalk(actor)) - return; - } - - actor->movedir = (angle_t)DI_NODIR; // cannot move -} - -/** Looks for players to chase after, aim at, or whatever. - * - * \param actor The object looking for flesh. - * \param allaround Look all around? If false, only players in a 180-degree - * range in front will be spotted. - * \param dist If > 0, checks distance - * \return True if a player is found, otherwise false. - * \sa P_SupermanLook4Players - */ -boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed_t dist) -{ - INT32 c = 0, stop; - player_t *player; - angle_t an; - - // BP: first time init, this allow minimum lastlook changes - if (actor->lastlook < 0) - actor->lastlook = P_RandomByte(); - - actor->lastlook %= MAXPLAYERS; - - stop = (actor->lastlook - 1) & PLAYERSMASK; - - for (; ; actor->lastlook = (actor->lastlook + 1) & PLAYERSMASK) - { - // done looking - if (actor->lastlook == stop) - return false; - - if (!playeringame[actor->lastlook]) - continue; - - if (c++ == 2) - return false; - - player = &players[actor->lastlook]; - - if ((netgame || multiplayer) && player->spectator) - continue; - - if (player->pflags & PF_INVIS) - continue; // ignore notarget - - if (!player->mo || P_MobjWasRemoved(player->mo)) - continue; - - if (player->mo->health <= 0) - continue; // dead - - if (dist > 0 - && P_AproxDistance(P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y), player->mo->z - actor->z) > dist) - continue; // Too far away - - if (!allaround) - { - an = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle; - if (an > ANGLE_90 && an < ANGLE_270) - { - dist = P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y); - // if real close, react anyway - if (dist > FixedMul(MELEERANGE, actor->scale)) - continue; // behind back - } - } - - if (!P_CheckSight(actor, player->mo)) - continue; // out of sight - - if (tracer) - P_SetTarget(&actor->tracer, player->mo); - else - P_SetTarget(&actor->target, player->mo); - return true; - } - - //return false; -} - -/** Looks for a player with a ring shield. - * Used by rings. - * - * \param actor Ring looking for a shield to be attracted to. - * \return True if a player with ring shield is found, otherwise false. - * \sa A_AttractChase - */ -static boolean P_LookForShield(mobj_t *actor) -{ - INT32 c = 0, stop; - player_t *player; - - // BP: first time init, this allow minimum lastlook changes - if (actor->lastlook < 0) - actor->lastlook = P_RandomByte(); - - actor->lastlook %= MAXPLAYERS; - - stop = (actor->lastlook - 1) & PLAYERSMASK; - - for (; ; actor->lastlook = ((actor->lastlook + 1) & PLAYERSMASK)) - { - // done looking - if (actor->lastlook == stop) - return false; - - if (!playeringame[actor->lastlook]) - continue; - - if (c++ == 2) - return false; - - player = &players[actor->lastlook]; - - if (!player->mo || player->mo->health <= 0) - continue; // dead - - //When in CTF, don't pull rings that you cannot pick up. - if ((actor->type == MT_REDTEAMRING && player->ctfteam != 1) || - (actor->type == MT_BLUETEAMRING && player->ctfteam != 2)) - continue; - - if ((player->powers[pw_shield] & SH_PROTECTELECTRIC) - && (P_AproxDistance(P_AproxDistance(actor->x-player->mo->x, actor->y-player->mo->y), actor->z-player->mo->z) < FixedMul(RING_DIST, player->mo->scale))) - { - P_SetTarget(&actor->tracer, player->mo); - - if (actor->hnext) - P_SetTarget(&actor->hnext->hprev, actor->hprev); - if (actor->hprev) - P_SetTarget(&actor->hprev->hnext, actor->hnext); - - return true; - } - } - - //return false; -} - -#ifdef WEIGHTEDRECYCLER -// Compares players to see who currently has the "best" items, etc. -static int P_RecycleCompare(const void *p1, const void *p2) -{ - player_t *player1 = &players[*(const UINT8 *)p1]; - player_t *player2 = &players[*(const UINT8 *)p2]; - - // Non-shooting gametypes - if (!G_PlatformGametype()) - { - // Invincibility. - if (player1->powers[pw_invulnerability] > player2->powers[pw_invulnerability]) return -1; - else if (player2->powers[pw_invulnerability] > player1->powers[pw_invulnerability]) return 1; - - // One has a shield, the other doesn't. - if (player1->powers[pw_shield] && !player2->powers[pw_shield]) return -1; - else if (player2->powers[pw_shield] && !player1->powers[pw_shield]) return 1; - - // Sneakers. - if (player1->powers[pw_sneakers] > player2->powers[pw_sneakers]) return -1; - else if (player2->powers[pw_sneakers] > player1->powers[pw_sneakers]) return 1; - } - else // Match, Team Match, CTF, Tag, Etc. - { - UINT8 player1_em = M_CountBits((UINT32)player1->powers[pw_emeralds], 7); - UINT8 player2_em = M_CountBits((UINT32)player2->powers[pw_emeralds], 7); - - UINT8 player1_rw = M_CountBits((UINT32)player1->ringweapons, NUM_WEAPONS-1); - UINT8 player2_rw = M_CountBits((UINT32)player2->ringweapons, NUM_WEAPONS-1); - - UINT16 player1_am = player1->powers[pw_infinityring] // max 800 - + player1->powers[pw_automaticring] // max 300 - + (player1->powers[pw_bouncering] * 3) // max 100 - + (player1->powers[pw_explosionring] * 6) // max 50 - + (player1->powers[pw_scatterring] * 3) // max 100 - + (player1->powers[pw_grenadering] * 6) // max 50 - + (player1->powers[pw_railring] * 6); // max 50 - UINT16 player2_am = player2->powers[pw_infinityring] // max 800 - + player2->powers[pw_automaticring] // max 300 - + (player2->powers[pw_bouncering] * 3) // max 100 - + (player2->powers[pw_explosionring] * 6) // max 50 - + (player2->powers[pw_scatterring] * 3) // max 100 - + (player2->powers[pw_grenadering] * 6) // max 50 - + (player2->powers[pw_railring] * 6); // max 50 - - // Super trumps everything. - if (player1->powers[pw_super] && !player2->powers[pw_super]) return -1; - else if (player2->powers[pw_super] && !player1->powers[pw_super]) return 1; - - // Emerald count if neither player is Super. - if (player1_em > player2_em) return -1; - else if (player1_em < player2_em) return 1; - - // One has a shield, the other doesn't. - // (the likelihood of a shielded player being worse off than one without one is low.) - if (player1->powers[pw_shield] && !player2->powers[pw_shield]) return -1; - else if (player2->powers[pw_shield] && !player1->powers[pw_shield]) return 1; - - // Ring weapons count - if (player1_rw > player2_rw) return -1; - else if (player1_rw < player2_rw) return 1; - - // Ring ammo if they have the same number of weapons - if (player1_am > player2_am) return -1; - else if (player1_am < player2_am) return 1; - } - - // Identical for our purposes - return 0; -} -#endif - -// Handles random monitor weights via console. -static mobjtype_t P_DoRandomBoxChances(void) -{ - mobjtype_t spawnchance[256]; - INT32 numchoices = 0, i = 0; - - if (!(netgame || multiplayer)) - { - switch (P_RandomKey(10)) - { - case 0: - return MT_RING_ICON; - case 1: - return MT_SNEAKERS_ICON; - case 2: - return MT_INVULN_ICON; - case 3: - return MT_WHIRLWIND_ICON; - case 4: - return MT_ELEMENTAL_ICON; - case 5: - return MT_ATTRACT_ICON; - case 6: - return MT_FORCE_ICON; - case 7: - return MT_ARMAGEDDON_ICON; - case 8: - return MT_1UP_ICON; - case 9: - return MT_EGGMAN_ICON; - } - return MT_NULL; - } - -#define QUESTIONBOXCHANCES(type, cvar) \ -for (i = cvar.value; i; --i) spawnchance[numchoices++] = type - QUESTIONBOXCHANCES(MT_RING_ICON, cv_superring); - QUESTIONBOXCHANCES(MT_SNEAKERS_ICON, cv_supersneakers); - QUESTIONBOXCHANCES(MT_INVULN_ICON, cv_invincibility); - QUESTIONBOXCHANCES(MT_WHIRLWIND_ICON, cv_jumpshield); - QUESTIONBOXCHANCES(MT_ELEMENTAL_ICON, cv_watershield); - QUESTIONBOXCHANCES(MT_ATTRACT_ICON, cv_ringshield); - QUESTIONBOXCHANCES(MT_FORCE_ICON, cv_forceshield); - QUESTIONBOXCHANCES(MT_ARMAGEDDON_ICON, cv_bombshield); - QUESTIONBOXCHANCES(MT_1UP_ICON, cv_1up); - QUESTIONBOXCHANCES(MT_EGGMAN_ICON, cv_eggmanbox); - QUESTIONBOXCHANCES(MT_MIXUP_ICON, cv_teleporters); - QUESTIONBOXCHANCES(MT_RECYCLER_ICON, cv_recycler); -#undef QUESTIONBOXCHANCES - - if (numchoices == 0) return MT_NULL; - return spawnchance[P_RandomKey(numchoices)]; -} - -// -// ACTION ROUTINES -// - -// Function: A_Look -// -// Description: Look for a player and set your target to them. -// -// var1: -// lower 16 bits = look all around -// upper 16 bits = distance limit -// var2 = If 1, only change to seestate. If 2, only play seesound. If 0, do both. -// -void A_Look(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Look", actor)) - return; -#endif - - if (!P_LookForPlayers(actor, locvar1 & 65535, false , FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale))) - return; - - // go into chase state - if (!locvar2) - { - P_SetMobjState(actor, actor->info->seestate); - A_PlaySeeSound(actor); - } - else if (locvar2 == 1) // Only go into seestate - P_SetMobjState(actor, actor->info->seestate); - else if (locvar2 == 2) // Only play seesound - A_PlaySeeSound(actor); -} - -// Function: A_Chase -// -// Description: Chase after your target. -// -// var1: -// 1 = don't check meleestate -// 2 = don't check missilestate -// 3 = don't check meleestate and missilestate -// var2 = unused -// -void A_Chase(mobj_t *actor) -{ - INT32 delta; - INT32 locvar1 = var1; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Chase", actor)) - return; -#endif - - I_Assert(actor != NULL); - I_Assert(!P_MobjWasRemoved(actor)); - - if (actor->reactiontime) - actor->reactiontime--; - - // modify target threshold - if (actor->threshold) - { - if (!actor->target || actor->target->health <= 0) - actor->threshold = 0; - else - actor->threshold--; - } - - // turn towards movement direction if not there yet - if (actor->movedir < NUMDIRS) - { - actor->angle &= (7<<29); - delta = actor->angle - (actor->movedir << 29); - - if (delta > 0) - actor->angle -= ANGLE_45; - else if (delta < 0) - actor->angle += ANGLE_45; - } - - if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) - { - // look for a new target - if (P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - P_SetMobjStateNF(actor, actor->info->spawnstate); - return; - } - - // do not attack twice in a row - if (actor->flags2 & MF2_JUSTATTACKED) - { - actor->flags2 &= ~MF2_JUSTATTACKED; - P_NewChaseDir(actor); - return; - } - - // check for melee attack - if (!(locvar1 & 1) && actor->info->meleestate && P_CheckMeleeRange(actor)) - { - if (actor->info->attacksound) - S_StartAttackSound(actor, actor->info->attacksound); - - P_SetMobjState(actor, actor->info->meleestate); - return; - } - - // check for missile attack - if (!(locvar1 & 2) && actor->info->missilestate) - { - if (actor->movecount || !P_CheckMissileRange(actor)) - goto nomissile; - - P_SetMobjState(actor, actor->info->missilestate); - actor->flags2 |= MF2_JUSTATTACKED; - return; - } - -nomissile: - // possibly choose another target - if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)) - && P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - // chase towards player - if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed)) - P_NewChaseDir(actor); -} - -// Function: A_FaceStabChase -// -// Description: Unused variant of A_Chase for Castlebot Facestabber. -// -// var1 = unused -// var2 = unused -// -void A_FaceStabChase(mobj_t *actor) -{ - INT32 delta; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FaceStabChase", actor)) - return; -#endif - - if (actor->reactiontime) - actor->reactiontime--; - - // modify target threshold - if (actor->threshold) - { - if (!actor->target || actor->target->health <= 0) - actor->threshold = 0; - else - actor->threshold--; - } - - // turn towards movement direction if not there yet - if (actor->movedir < NUMDIRS) - { - actor->angle &= (7<<29); - delta = actor->angle - (actor->movedir << 29); - - if (delta > 0) - actor->angle -= ANGLE_45; - else if (delta < 0) - actor->angle += ANGLE_45; - } - - if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) - { - // look for a new target - if (P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - P_SetMobjStateNF(actor, actor->info->spawnstate); - return; - } - - // do not attack twice in a row - if (actor->flags2 & MF2_JUSTATTACKED) - { - actor->flags2 &= ~MF2_JUSTATTACKED; - P_NewChaseDir(actor); - return; - } - - // check for melee attack - if (actor->info->meleestate && P_FaceStabCheckMeleeRange(actor)) - { - if (actor->info->attacksound) - S_StartAttackSound(actor, actor->info->attacksound); - - P_SetMobjState(actor, actor->info->meleestate); - return; - } - - // check for missile attack - if (actor->info->missilestate) - { - if (actor->movecount || !P_CheckMissileRange(actor)) - goto nomissile; - - P_SetMobjState(actor, actor->info->missilestate); - actor->flags2 |= MF2_JUSTATTACKED; - return; - } - -nomissile: - // possibly choose another target - if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)) - && P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - // chase towards player - if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed)) - P_NewChaseDir(actor); -} - -static void P_SharpDust(mobj_t *actor, mobjtype_t type, angle_t ang) -{ - mobj_t *dust; - - if (!type || !P_IsObjectOnGround(actor)) - return; - - dust = P_SpawnMobjFromMobj(actor, - -P_ReturnThrustX(actor, ang, 16<angle, actor->radius), - -P_ReturnThrustY(actor, actor->angle, actor->radius), - actor->height/3, - MT_PARTICLE); - flume->destscale = actor->scale*3; - P_SetScale(flume, flume->destscale); - P_SetTarget(&flume->target, actor); - flume->sprite = SPR_JETF; - flume->frame = FF_FULLBRIGHT; - flume->tics = 2; -} - -// Function: A_FaceStabRev -// -// Description: Facestabber rev action -// -// var1 = effective duration -// var2 = effective nextstate -// -void A_FaceStabRev(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FaceStabRev", actor)) - return; -#endif - - if (!actor->target) - { - P_SetMobjState(actor, actor->info->spawnstate); - return; - } - - actor->extravalue1 = 0; - - if (!actor->reactiontime) - { - actor->reactiontime = locvar1; - S_StartSound(actor, actor->info->activesound); - } - else - { - if ((--actor->reactiontime) == 0) - { - S_StartSound(actor, actor->info->attacksound); - P_SetMobjState(actor, locvar2); - } - else - { - P_TryMove(actor, actor->x - P_ReturnThrustX(actor, actor->angle, 2<y - P_ReturnThrustY(actor, actor->angle, 2<target) - { - angle_t visang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); - // Calculate new direction. - angle_t dirang = actor->angle; - angle_t diffang = visang - dirang; - - if (locvar1) // Allow homing? - { - if (diffang > ANGLE_180) - { - angle_t workang = locvar1*(InvAngle(diffang)>>5); - diffang += InvAngle(workang); - } - else - diffang += (locvar1*(diffang>>5)); - } - diffang += ANGLE_45; - - // Check the sight cone. - if (diffang < ANGLE_90) - { - actor->angle = dirang; - if (++actor->extravalue2 < 4) - actor->extravalue2 = 4; - else if (actor->extravalue2 > 26) - actor->extravalue2 = 26; - - if (P_TryMove(actor, - actor->x + P_ReturnThrustX(actor, dirang, actor->extravalue2<y + P_ReturnThrustY(actor, dirang, actor->extravalue2<extravalue1); - fixed_t basesize = FRACUNIT/MAXVAL; - mobj_t *hwork = actor; - INT32 dist = 113; - fixed_t xo = P_ReturnThrustX(actor, actor->angle, dist*basesize); - fixed_t yo = P_ReturnThrustY(actor, actor->angle, dist*basesize); - - while (step > 0) - { - if (!hwork->hnext) - P_SetTarget(&hwork->hnext, P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_FACESTABBERSPEAR)); - hwork = hwork->hnext; - hwork->angle = actor->angle + ANGLE_90; - hwork->destscale = FixedSqrt(step*basesize); - P_SetScale(hwork, hwork->destscale); - hwork->fuse = 2; - P_TeleportMove(hwork, actor->x + xo*(15-step), actor->y + yo*(15-step), actor->z + (actor->height - hwork->height)/2 + (P_MobjFlip(actor)*(8<extravalue1 >= MAXVAL) - actor->extravalue1 -= NUMGRADS; - - if ((step % 5) == 0) - P_SharpDust(actor, MT_SPINDUST, actor->angle); - - P_FaceStabFlume(actor); - return; -#undef MAXVAL -#undef NUMGRADS -#undef NUMSTEPS - } - } - } - - P_SetMobjState(actor, locvar2); - actor->reactiontime = actor->info->reactiontime; -} - -// Function: A_FaceStabMiss -// -// Description: Facestabber miss action -// -// var1 = unused -// var2 = effective nextstate -// -void A_FaceStabMiss(mobj_t *actor) -{ - //INT32 locvar1 = var1; - INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FaceStabMiss", actor)) - return; -#endif - - if (++actor->extravalue1 >= 3) - { - actor->extravalue2 -= 2; - actor->extravalue1 = 0; - S_StartSound(actor, sfx_s3k47); - P_SharpDust(actor, MT_SPINDUST, actor->angle); - } - - if (actor->extravalue2 <= 0 || !P_TryMove(actor, - actor->x + P_ReturnThrustX(actor, actor->angle, actor->extravalue2<y + P_ReturnThrustY(actor, actor->angle, actor->extravalue2<extravalue2 = 0; - P_SetMobjState(actor, locvar2); - } -} - -// Function: A_StatueBurst -// -// Description: For suspicious statues only... -// -// var1 = object to create -// var2 = effective nextstate for created object -// -void A_StatueBurst(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - mobjtype_t chunktype = (mobjtype_t)actor->info->raisestate; - mobj_t *new; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_StatueBurst", actor)) - return; -#endif - - if (!locvar1 || !(new = P_SpawnMobjFromMobj(actor, 0, 0, 0, locvar1))) - return; - - new->angle = actor->angle; - new->target = actor->target; - if (locvar2) - P_SetMobjState(new, (statenum_t)locvar2); - S_StartSound(new, new->info->attacksound); - S_StopSound(actor); - S_StartSound(actor, sfx_s3k96); - - { - fixed_t a, b; - fixed_t c = (actor->height>>2) - FixedMul(actor->scale, mobjinfo[chunktype].height>>1); - fixed_t v = 4<radius>>1); - mobj_t *spawned; - UINT8 i; - for (i = 0; i < 8; i++) - { - a = ((i & 1) ? r : (-r)); - b = ((i & 2) ? r : (-r)); - if (i == 4) - { - c += (actor->height>>1); - v = 8<fuse = 3*TICRATE; - } - } -} - -// Function: A_JetJawRoam -// -// Description: Roaming routine for JetJaw -// -// var1 = unused -// var2 = unused -// -void A_JetJawRoam(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_JetJawRoam", actor)) - return; -#endif - if (actor->reactiontime) - { - actor->reactiontime--; - P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed*FRACUNIT/4, actor->scale)); - } - else - { - actor->reactiontime = actor->info->reactiontime; - actor->angle += ANGLE_180; - } - - if (P_LookForPlayers(actor, false, false, actor->radius * 16)) - P_SetMobjState(actor, actor->info->seestate); -} - -// Function: A_JetJawChomp -// -// Description: Chase and chomp at the target, as long as it is in view -// -// var1 = unused -// var2 = unused -// -void A_JetJawChomp(mobj_t *actor) -{ - INT32 delta; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_JetJawChomp", actor)) - return; -#endif - - // turn towards movement direction if not there yet - if (actor->movedir < NUMDIRS) - { - actor->angle &= (7<<29); - delta = actor->angle - (actor->movedir << 29); - - if (delta > 0) - actor->angle -= ANGLE_45; - else if (delta < 0) - actor->angle += ANGLE_45; - } - - // Stop chomping if target's dead or you can't see it - if (!actor->target || !(actor->target->flags & MF_SHOOTABLE) - || actor->target->health <= 0 || !P_CheckSight(actor, actor->target)) - { - P_SetMobjStateNF(actor, actor->info->spawnstate); - return; - } - - // chase towards player - if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed)) - P_NewChaseDir(actor); -} - -// Function: A_PointyThink -// -// Description: Thinker function for Pointy -// -// var1 = unused -// var2 = unused -// -void A_PointyThink(mobj_t *actor) -{ - INT32 i; - player_t *player = NULL; - mobj_t *ball; - TVector v; - TVector *res; - angle_t fa; - fixed_t radius = FixedMul(actor->info->radius*actor->info->reactiontime, actor->scale); - boolean firsttime = true; - INT32 sign; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_PointyThink", actor)) - return; -#endif - actor->momx = actor->momy = actor->momz = 0; - - // Find nearest player - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - - if (!players[i].mo) - continue; - - if (!players[i].mo->health) - continue; - - if (!P_CheckSight(actor, players[i].mo)) - continue; - - if (firsttime) - { - firsttime = false; - player = &players[i]; - } - else - { - if (P_AproxDistance(players[i].mo->x - actor->x, players[i].mo->y - actor->y) < - P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y)) - player = &players[i]; - } - } - - if (!player) - return; - - // Okay, we found the closest player. Let's move based on his movement. - P_SetTarget(&actor->target, player->mo); - A_FaceTarget(actor); - - if (P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y) < P_AproxDistance(player->mo->x + player->mo->momx - actor->x, player->mo->y + player->mo->momy - actor->y)) - sign = -1; // Player is moving away - else - sign = 1; // Player is moving closer - - if (player->mo->momx || player->mo->momy) - { - P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y), FixedMul(actor->info->speed*sign, actor->scale)); - - // Rotate our spike balls - actor->lastlook += actor->info->damage; - actor->lastlook %= FINEANGLES/4; - } - - if (!actor->tracer) // For some reason we do not have spike balls... - return; - - // Position spike balls relative to the value of 'lastlook'. - ball = actor->tracer; - - i = 0; - while (ball) - { - fa = actor->lastlook+i; - v[0] = FixedMul(FINECOSINE(fa),radius); - v[1] = 0; - v[2] = FixedMul(FINESINE(fa),radius); - v[3] = FRACUNIT; - - res = VectorMatrixMultiply(v, *RotateXMatrix(FixedAngle(actor->lastlook+i))); - M_Memcpy(&v, res, sizeof (v)); - res = VectorMatrixMultiply(v, *RotateZMatrix(actor->angle+ANGLE_180)); - M_Memcpy(&v, res, sizeof (v)); - - P_UnsetThingPosition(ball); - ball->x = actor->x + v[0]; - ball->y = actor->y + v[1]; - ball->z = actor->z + (actor->height>>1) + v[2]; - P_SetThingPosition(ball); - - ball = ball->tracer; - i += ANGLE_90 >> ANGLETOFINESHIFT; - } -} - -// Function: A_CheckBuddy -// -// Description: Checks if target/tracer exists/has health. If not, the object removes itself. -// -// var1: -// 0 = target -// 1 = tracer -// var2 = unused -// -void A_CheckBuddy(mobj_t *actor) -{ - INT32 locvar1 = var1; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CheckBuddy", actor)) - return; -#endif - if (locvar1 && (!actor->tracer || actor->tracer->health <= 0)) - P_RemoveMobj(actor); - else if (!locvar1 && (!actor->target || actor->target->health <= 0)) - P_RemoveMobj(actor); -} - -// Helper function for the Robo Hood. -// Don't ask me how it works. Nev3r made it with dark majyks. -static void P_ParabolicMove(mobj_t *actor, fixed_t x, fixed_t y, fixed_t z, fixed_t speed) -{ - fixed_t dh; - - x -= actor->x; - y -= actor->y; - z -= actor->z; - - dh = P_AproxDistance(x, y); - - actor->momx = FixedMul(FixedDiv(x, dh), speed); - actor->momy = FixedMul(FixedDiv(y, dh), speed); - - if (!gravity) - return; - - dh = FixedDiv(FixedMul(dh, gravity), speed); - actor->momz = (dh>>1) + FixedDiv(z, dh<<1); -} - -// Function: A_HoodFire -// -// Description: Firing Robo-Hood -// -// var1 = object type to fire -// var2 = unused -// -void A_HoodFire(mobj_t *actor) -{ - mobj_t *arrow; - INT32 locvar1 = var1; - //INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_HoodFire", actor)) - return; -#endif - - // Check target first. - if (!actor->target) - { - actor->reactiontime = actor->info->reactiontime; - P_SetMobjState(actor, actor->info->spawnstate); - return; - } - - A_FaceTarget(actor); - - if (!(arrow = P_SpawnMissile(actor, actor->target, (mobjtype_t)locvar1))) - return; - - // Set a parabolic trajectory for the arrow. - P_ParabolicMove(arrow, actor->target->x, actor->target->y, actor->target->z, arrow->info->speed); -} - -// Function: A_HoodThink -// -// Description: Thinker for Robo-Hood -// -// var1 = unused -// var2 = unused -// -void A_HoodThink(mobj_t *actor) -{ - fixed_t dx, dy, dz, dm; - boolean checksight; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_HoodThink", actor)) - return; -#endif - - // Check target first. - if (!actor->target) - { - actor->reactiontime = actor->info->reactiontime; - P_SetMobjState(actor, actor->info->spawnstate); - return; - } - - dx = (actor->target->x - actor->x), dy = (actor->target->y - actor->y), dz = (actor->target->z - actor->z); - dm = P_AproxDistance(dx, dy); - // Target dangerously close to robohood, retreat then. - if ((dm < 256<info->raisestate); - return; - } - - // If target on sight, look at it. - if ((checksight = P_CheckSight(actor, actor->target))) - { - angle_t dang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); - if (actor->angle >= ANGLE_180) - { - actor->angle = InvAngle(actor->angle)>>1; - actor->angle = InvAngle(actor->angle); - } - else - actor->angle >>= 1; - - if (dang >= ANGLE_180) - { - dang = InvAngle(dang)>>1; - dang = InvAngle(dang); - } - else - dang >>= 1; - - actor->angle += dang; - } - - // Check whether to do anything. - if ((--actor->reactiontime) <= 0) - { - actor->reactiontime = actor->info->reactiontime; - - // If way too far, don't shoot. - if ((dm < (3072<info->missilestate); - return; - } - } -} - -// Function: A_HoodFall -// -// Description: Falling Robo-Hood -// -// var1 = unused -// var2 = unused -// -void A_HoodFall(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_HoodFall", actor)) - return; -#endif - - if (!P_IsObjectOnGround(actor)) - return; - - actor->momx = actor->momy = 0; - actor->reactiontime = actor->info->reactiontime; - P_SetMobjState(actor, actor->info->seestate); -} - -// Function: A_ArrowBonks -// -// Description: Arrow momentum setting on collision -// -// var1 = unused -// var2 = unused -// -void A_ArrowBonks(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ArrowBonks", actor)) - return; -#endif - - if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz) - || (!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)) - actor->angle += ANGLE_180; - - P_SetObjectMomZ(actor, 8*actor->scale, false); - P_InstaThrust(actor, actor->angle, -6*actor->scale); - - actor->flags = (actor->flags|MF_NOCLIPHEIGHT) & ~MF_NOGRAVITY; - actor->z += P_MobjFlip(actor); -} - -// Function: A_SnailerThink -// -// Description: Thinker function for Snailer -// -// var1 = unused -// var2 = unused -// -void A_SnailerThink(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SnailerThink", actor)) - return; -#endif - - if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) - { - // look for a new target - if (!P_LookForPlayers(actor, true, false, 0)) - return; - } - - // We now have a target. Oh bliss, rapture, and contentment! - - if (actor->target->z + actor->target->height > actor->z - FixedMul(32*FRACUNIT, actor->scale) - && actor->target->z < actor->z + actor->height + FixedMul(32*FRACUNIT, actor->scale) - && !(leveltime % (TICRATE*2))) - { - angle_t an; - fixed_t z; - - // Actor shouldn't face target, so we'll do things a bit differently here - - an = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) - actor->angle; - - z = actor->z + actor->height/2; - - if (an > ANGLE_45 && an < ANGLE_315) // fire as close as you can to the target, even if too sharp an angle from your front - { - fixed_t dist; - fixed_t dx, dy; - - dist = P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y); - - if (an > ANGLE_45 && an <= ANGLE_90) // fire at 45 degrees to the left - { - dx = actor->x + P_ReturnThrustX(actor, actor->angle + ANGLE_45, dist); - dy = actor->y + P_ReturnThrustY(actor, actor->angle + ANGLE_45, dist); - } - else if (an >= ANGLE_270 && an < ANGLE_315) // fire at 45 degrees to the right - { - dx = actor->x + P_ReturnThrustX(actor, actor->angle - ANGLE_45, dist); - dy = actor->y + P_ReturnThrustY(actor, actor->angle - ANGLE_45, dist); - } - else // fire straight ahead - { - dx = actor->x + P_ReturnThrustX(actor, actor->angle, dist); - dy = actor->y + P_ReturnThrustY(actor, actor->angle, dist); - } - - P_SpawnPointMissile(actor, dx, dy, actor->target->z, MT_ROCKET, actor->x, actor->y, z); - } - else - P_SpawnXYZMissile(actor, actor->target, MT_ROCKET, actor->x, actor->y, z); - } - - if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->target->z > actor->z) - || (actor->eflags & MFE_VERTICALFLIP && (actor->target->z + actor->target->height) > (actor->z + actor->height))) - actor->momz += FixedMul(actor->info->speed, actor->scale); - else if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->target->z < actor->z) - || (actor->eflags & MFE_VERTICALFLIP && (actor->target->z + actor->target->height) < (actor->z + actor->height))) - actor->momz -= FixedMul(actor->info->speed, actor->scale); - - actor->momz /= 2; -} - -// Function: A_SharpChase -// -// Description: Thinker/Chase routine for Spincushions -// -// var1 = unused -// var2 = unused -// -void A_SharpChase(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SharpChase", actor)) - return; -#endif - - if (actor->reactiontime) - { - INT32 delta; - - actor->reactiontime--; - - // turn towards movement direction if not there yet - if (actor->movedir < NUMDIRS) - { - actor->angle &= (7<<29); - delta = actor->angle - (actor->movedir << 29); - - if (delta > 0) - actor->angle -= ANGLE_45; - else if (delta < 0) - actor->angle += ANGLE_45; - } - - if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) - { - // look for a new target - if (P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - P_SetMobjState(actor, actor->info->spawnstate); - return; - } - - // chase towards player - if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed)) - P_NewChaseDir(actor); - } - else - { - actor->threshold = actor->info->painchance; - P_SetMobjState(actor, actor->info->missilestate); - S_StartSound(actor, actor->info->attacksound); - } -} - -// Function: A_SharpSpin -// -// Description: Spin chase routine for Spincushions -// -// var1 = object # to spawn as dust (if not provided not done) -// var2 = if nonzero, do the old-style spinning using this as the angle difference -// -void A_SharpSpin(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - angle_t oldang = actor->angle; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SharpSpin", actor)) - return; -#endif - - if (actor->threshold && actor->target) - { - angle_t ang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); - P_Thrust(actor, ang, actor->info->speed*actor->scale); - if (locvar2) - actor->angle += locvar2; // ANGLE_22h; - else - actor->angle = ang; - actor->threshold--; - if (leveltime & 1) - S_StartSound(actor, actor->info->painsound); - } - else - { - actor->reactiontime = actor->info->reactiontime; - P_SetMobjState(actor, actor->info->meleestate); - } - - P_SharpDust(actor, locvar1, oldang); -} - -// Function: A_SharpDecel -// -// Description: Slow down the Spincushion -// -// var1 = unused -// var2 = unused -// -void A_SharpDecel(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SharpDecel", actor)) - return; -#endif - - if (actor->momx > 2 || actor->momy > 2) - { - actor->momx >>= 1; - actor->momy >>= 1; - } - else - P_SetMobjState(actor, actor->info->xdeathstate); -} - -// Function: A_CrushstaceanWalk -// -// Description: Crushstacean movement -// -// var1 = speed (actor info's speed if 0) -// var2 = state to switch to when blocked (spawnstate if 0) -// -void A_CrushstaceanWalk(mobj_t *actor) -{ - INT32 locvar1 = (var1 ? var1 : (INT32)actor->info->speed); - INT32 locvar2 = (var2 ? var2 : (INT32)actor->info->spawnstate); - angle_t ang = actor->angle + ((actor->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270); -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CrushstaceanWalk", actor)) - return; -#endif - - actor->reactiontime--; - - if (!P_TryMove(actor, - actor->x + P_ReturnThrustX(actor, ang, locvar1*actor->scale), - actor->y + P_ReturnThrustY(actor, ang, locvar1*actor->scale), - false) - || (actor->reactiontime-- <= 0)) - { - actor->flags2 ^= MF2_AMBUSH; - P_SetMobjState(actor, locvar2); - actor->reactiontime = actor->info->reactiontime; - } -} - -// Function: A_CrushstaceanPunch -// -// Description: Crushstacean attack -// -// var1 = unused -// var2 = state to go to if unsuccessful (spawnstate if 0) -// -void A_CrushstaceanPunch(mobj_t *actor) -{ - //INT32 locvar1 = var1; - INT32 locvar2 = (var2 ? var2 : (INT32)actor->info->spawnstate); -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CrushstaceanPunch", actor)) - return; -#endif - - if (!actor->tracer) - return; - - if (!actor->target) - { - P_SetMobjState(actor, locvar2); - return; - } - - actor->tracer->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); - P_SetMobjState(actor->tracer, actor->tracer->info->missilestate); - actor->tracer->extravalue1 = actor->tracer->extravalue2 = 0; - S_StartSound(actor, actor->info->attacksound); -} - -// Function: A_CrushclawAim -// -// Description: Crushstacean claw aiming -// -// var1 = sideways offset -// var2 = vertical offset -// -void A_CrushclawAim(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - mobj_t *crab = actor->tracer; - angle_t ang; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CrushclawAim", actor)) - return; -#endif - - if (!crab) - { - P_RemoveMobj(actor); - return; // there is only one step and it is crab - } - - if (crab->target || P_LookForPlayers(crab, true, false, 600*crab->scale)) - ang = R_PointToAngle2(crab->x, crab->y, crab->target->x, crab->target->y); - else - ang = crab->angle + ((crab->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270); - ang -= actor->angle; - -#define anglimit ANGLE_22h -#define angfactor 5 - if (ang < ANGLE_180) - { - if (ang > anglimit) - ang = anglimit; - ang /= angfactor; - } - else - { - ang = InvAngle(ang); - if (ang > anglimit) - ang = anglimit; - ang = InvAngle(ang/angfactor); - } - actor->angle += ang; -#undef anglimit -#undef angfactor - - P_TeleportMove(actor, - crab->x + P_ReturnThrustX(actor, actor->angle, locvar1*crab->scale), - crab->y + P_ReturnThrustY(actor, actor->angle, locvar1*crab->scale), - crab->z + locvar2*crab->scale); - - if (!crab->target || !crab->info->missilestate || (statenum_t)(crab->state-states) == crab->info->missilestate) - return; - - if (((ang + ANG1) < ANG2) || P_AproxDistance(crab->x - crab->target->x, crab->y - crab->target->y) < 333*crab->scale) - P_SetMobjState(crab, crab->info->missilestate); -} - -// Function: A_CrushclawLaunch -// -// Description: Crushstacean claw launching -// -// var1: -// 0 - forwards -// anything else - backwards -// var2 = state to change to when done -// -void A_CrushclawLaunch(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - mobj_t *crab = actor->tracer; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CrushclawLaunch", actor)) - return; -#endif - - if (!crab) - { - mobj_t *chainnext; - while (actor) - { - chainnext = actor->target; - P_RemoveMobj(actor); - actor = chainnext; - } - return; // there is only one step and it is crab - } - - if (!actor->extravalue1) - { - S_StartSound(actor, actor->info->activesound); - actor->extravalue1 = ((locvar1) ? -1 : 32); - } - else if (actor->extravalue1 != 1) - actor->extravalue1 -= 1; - -#define CSEGS 5 - if (!actor->target) - { - mobj_t *prevchain = actor; - UINT8 i = 0; - for (i = 0; (i < CSEGS); i++) - { - mobj_t *newchain = P_SpawnMobjFromMobj(actor, 0, 0, 0, actor->info->raisestate); - prevchain->target = newchain; - prevchain = newchain; - } - actor->target->angle = R_PointToAngle2(actor->target->x, actor->target->y, crab->target->x, crab->target->y); - } - - if ((!locvar1) && crab->target) - { -#define anglimit ANGLE_22h -#define angfactor 7 - angle_t ang = R_PointToAngle2(actor->target->x, actor->target->y, crab->target->x, crab->target->y) - actor->target->angle; - if (ang < ANGLE_180) - { - if (ang > anglimit) - ang = anglimit; - ang /= angfactor; - } - else - { - ang = InvAngle(ang); - if (ang > anglimit) - ang = anglimit; - ang /= angfactor; - ang = InvAngle(ang); - } - actor->target->angle += ang; - actor->angle = actor->target->angle; - } - - actor->extravalue2 += actor->extravalue1; - - if (!P_TryMove(actor, - actor->target->x + P_ReturnThrustX(actor, actor->target->angle, actor->extravalue2*actor->scale), - actor->target->y + P_ReturnThrustY(actor, actor->target->angle, actor->extravalue2*actor->scale), - true) - && !locvar1) - { - actor->extravalue1 = 0; - actor->extravalue2 = FixedHypot(actor->x - actor->target->x, actor->y - actor->target->y)>>FRACBITS; - P_SetMobjState(actor, locvar2); - S_StopSound(actor); - S_StartSound(actor, sfx_s3k49); - } - else - { - actor->z = actor->target->z; - if ((!locvar1 && (actor->extravalue2 > 256)) || (locvar1 && (actor->extravalue2 < 16))) - { - if (locvar1) // In case of retracting, resume crab and remove the chain. - { - mobj_t *chain = actor->target, *chainnext; - while (chain) - { - chainnext = chain->target; - P_RemoveMobj(chain); - chain = chainnext; - } - actor->extravalue2 = 0; - actor->angle = R_PointToAngle2(crab->x, crab->y, actor->x, actor->y); - P_SetTarget(&actor->target, NULL); - P_SetTarget(&crab->target, NULL); - P_SetMobjState(crab, crab->state->nextstate); - } - actor->extravalue1 = 0; - P_SetMobjState(actor, locvar2); - S_StopSound(actor); - if (!locvar1) - S_StartSound(actor, sfx_s3k64); - } - } - - if (!actor->target) - return; - - { - mobj_t *chain = actor->target->target; - fixed_t dx = (actor->x - actor->target->x)/CSEGS, dy = (actor->y - actor->target->y)/CSEGS, dz = (actor->z - actor->target->z)/CSEGS; - fixed_t idx = dx, idy = dy, idz = dz; - while (chain) - { - P_TeleportMove(chain, actor->target->x + idx, actor->target->y + idy, actor->target->z + idz); - chain->watertop = chain->z; - idx += dx; - idy += dy; - idz += dz; - chain = chain->target; - } - } -#undef CSEGS -} - -// Function: A_VultureVtol -// -// Description: Vulture rising up to match target's height -// -// var1 = unused -// var2 = unused -// -void A_VultureVtol(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_VultureVtol", actor)) - return; -#endif - - if (!actor->target) - return; - - actor->flags |= MF_NOGRAVITY; - actor->flags |= MF_FLOAT; - - A_FaceTarget(actor); - - S_StopSound(actor); - - if (actor->z < actor->target->z+(actor->target->height/4) && actor->z + actor->height < actor->ceilingz) - actor->momz = FixedMul(2*FRACUNIT, actor->scale); - else if (actor->z > (actor->target->z+(actor->target->height/4)*3) && actor->z > actor->floorz) - actor->momz = FixedMul(-2*FRACUNIT, actor->scale); - else - { - // Attack! - actor->momz = 0; - P_SetMobjState(actor, actor->info->missilestate); - S_StartSound(actor, actor->info->activesound); - } -} - -// Function: A_VultureCheck -// -// Description: If the vulture is stopped, look for a new target -// -// var1 = unused -// var2 = unused -// -void A_VultureCheck(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_VultureCheck", actor)) - return; -#endif - - if (actor->momx || actor->momy) - return; - - actor->flags &= ~MF_NOGRAVITY; // Fall down - - if (actor->z <= actor->floorz) - { - actor->angle -= ANGLE_180; // turn around - P_SetMobjState(actor, actor->info->spawnstate); - } -} - -// Function: A_SkimChase -// -// Description: Thinker/Chase routine for Skims -// -// var1 = unused -// var2 = unused -// -void A_SkimChase(mobj_t *actor) -{ - INT32 delta; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SkimChase", actor)) - return; -#endif - if (actor->reactiontime) - actor->reactiontime--; - - // modify target threshold - if (actor->threshold) - { - if (!actor->target || actor->target->health <= 0) - actor->threshold = 0; - else - actor->threshold--; - } - - // turn towards movement direction if not there yet - if (actor->movedir < NUMDIRS) - { - actor->angle &= (7<<29); - delta = actor->angle - (actor->movedir << 29); - - if (delta > 0) - actor->angle -= ANGLE_45; - else if (delta < 0) - actor->angle += ANGLE_45; - } - - if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) - { - // look for a new target - P_LookForPlayers(actor, true, false, 0); - - // the spawnstate for skims already calls this function so just return either way - // without changing state - return; - } - - // do not attack twice in a row - if (actor->flags2 & MF2_JUSTATTACKED) - { - actor->flags2 &= ~MF2_JUSTATTACKED; - P_NewChaseDir(actor); - return; - } - - // check for melee attack - if (actor->info->meleestate && P_SkimCheckMeleeRange(actor)) - { - if (actor->info->attacksound) - S_StartAttackSound(actor, actor->info->attacksound); - - P_SetMobjState(actor, actor->info->meleestate); - return; - } - - // check for missile attack - if (actor->info->missilestate) - { - if (actor->movecount || !P_CheckMissileRange(actor)) - goto nomissile; - - P_SetMobjState(actor, actor->info->missilestate); - actor->flags2 |= MF2_JUSTATTACKED; - return; - } - -nomissile: - // possibly choose another target - if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)) - && P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - // chase towards player - if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed)) - P_NewChaseDir(actor); -} - -// Function: A_FaceTarget -// -// Description: Immediately turn to face towards your target. -// -// var1 = unused -// var2 = unused -// -void A_FaceTarget(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FaceTarget", actor)) - return; -#endif - if (!actor->target) - return; - - actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); -} - -// Function: A_FaceTracer -// -// Description: Immediately turn to face towards your tracer. -// -// var1 = unused -// var2 = unused -// -void A_FaceTracer(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FaceTracer", actor)) - return; -#endif - if (!actor->tracer) - return; - - actor->angle = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); -} - -// Function: A_LobShot -// -// Description: Lob an object at your target. -// -// var1 = object # to lob -// var2: -// var2 >> 16 = height offset -// var2 & 65535 = airtime -// -void A_LobShot(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2 >> 16; - mobj_t *shot, *hitspot; - angle_t an; - fixed_t z; - fixed_t dist; - fixed_t vertical, horizontal; - fixed_t airtime = var2 & 65535; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_LobShot", actor)) - return; -#endif - if (!actor->target) - return; - - A_FaceTarget(actor); - - if (actor->eflags & MFE_VERTICALFLIP) - { - z = actor->z + actor->height - FixedMul(locvar2*FRACUNIT, actor->scale); - if (actor->type == MT_BLACKEGGMAN) - z -= FixedMul(mobjinfo[locvar1].height, actor->scale/2); - else - z -= FixedMul(mobjinfo[locvar1].height, actor->scale); - } - else - z = actor->z + FixedMul(locvar2*FRACUNIT, actor->scale); - - shot = P_SpawnMobj(actor->x, actor->y, z, locvar1); - - if (actor->type == MT_BLACKEGGMAN) - { - shot->destscale = actor->scale/2; - P_SetScale(shot, actor->scale/2); - } - else - { - shot->destscale = actor->scale; - P_SetScale(shot, actor->scale); - } - - // Keep track of where it's going to land - hitspot = P_SpawnMobj(actor->target->x&(64*FRACUNIT-1), actor->target->y&(64*FRACUNIT-1), actor->target->subsector->sector->floorheight, MT_NULL); - hitspot->tics = airtime; - P_SetTarget(&shot->tracer, hitspot); - - P_SetTarget(&shot->target, actor); // where it came from - - shot->angle = an = actor->angle; - an >>= ANGLETOFINESHIFT; - - dist = P_AproxDistance(actor->target->x - shot->x, actor->target->y - shot->y); - - horizontal = dist / airtime; - vertical = FixedMul((gravity*airtime)/2, shot->scale); - - shot->momx = FixedMul(horizontal, FINECOSINE(an)); - shot->momy = FixedMul(horizontal, FINESINE(an)); - shot->momz = vertical; - -/* Try to adjust when destination is not the same height - if (actor->z != actor->target->z) - { - fixed_t launchhyp; - fixed_t diff; - fixed_t orig; - - diff = actor->z - actor->target->z; - { - launchhyp = P_AproxDistance(horizontal, vertical); - - orig = FixedMul(FixedDiv(vertical, horizontal), diff); - - CONS_Debug(DBG_GAMELOGIC, "orig: %d\n", (orig)>>FRACBITS); - - horizontal = dist / airtime; - vertical = (gravity*airtime)/2; - } - dist -= orig; - shot->momx = FixedMul(horizontal, FINECOSINE(an)); - shot->momy = FixedMul(horizontal, FINESINE(an)); - shot->momz = vertical; -*/ - - if (shot->info->seesound) - S_StartSound(shot, shot->info->seesound); - - if (!(actor->flags & MF_BOSS)) - { - if (ultimatemode) - actor->reactiontime = actor->info->reactiontime*TICRATE; - else - actor->reactiontime = actor->info->reactiontime*TICRATE*2; - } -} - -// Function: A_FireShot -// -// Description: Shoot an object at your target. -// -// var1 = object # to shoot -// var2 = height offset -// -void A_FireShot(mobj_t *actor) -{ - fixed_t z; - INT32 locvar1 = var1; - INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FireShot", actor)) - return; -#endif - if (!actor->target) - return; - - A_FaceTarget(actor); - - if (actor->eflags & MFE_VERTICALFLIP) - z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale); - else - z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale); - - P_SpawnXYZMissile(actor, actor->target, locvar1, actor->x, actor->y, z); - - if (!(actor->flags & MF_BOSS)) - { - if (ultimatemode) - actor->reactiontime = actor->info->reactiontime*TICRATE; - else - actor->reactiontime = actor->info->reactiontime*TICRATE*2; - } -} - -// Function: A_SuperFireShot -// -// Description: Shoot an object at your target that will even stall Super Sonic. -// -// var1 = object # to shoot -// var2 = height offset -// -void A_SuperFireShot(mobj_t *actor) -{ - fixed_t z; - mobj_t *mo; - INT32 locvar1 = var1; - INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SuperFireShot", actor)) - return; -#endif - if (!actor->target) - return; - - A_FaceTarget(actor); - - if (actor->eflags & MFE_VERTICALFLIP) - z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale); - else - z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale); - - mo = P_SpawnXYZMissile(actor, actor->target, locvar1, actor->x, actor->y, z); - - if (mo) - mo->flags2 |= MF2_SUPERFIRE; - - if (!(actor->flags & MF_BOSS)) - { - if (ultimatemode) - actor->reactiontime = actor->info->reactiontime*TICRATE; - else - actor->reactiontime = actor->info->reactiontime*TICRATE*2; - } -} - -// Function: A_BossFireShot -// -// Description: Shoot an object at your target ala Bosses: -// -// var1 = object # to shoot -// var2: -// 0 - Boss 1 Left side -// 1 - Boss 1 Right side -// 2 - Boss 3 Left side upper -// 3 - Boss 3 Left side lower -// 4 - Boss 3 Right side upper -// 5 - Boss 3 Right side lower -// -void A_BossFireShot(mobj_t *actor) -{ - fixed_t x, y, z; - INT32 locvar1 = var1; - INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_BossFireShot", actor)) - return; -#endif - if (!actor->target) - return; - - A_FaceTarget(actor); - - switch (locvar2) - { - case 0: - x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); - y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); - if (actor->eflags & MFE_VERTICALFLIP) - z = actor->z + actor->height - FixedMul(48*FRACUNIT, actor->scale); - else - z = actor->z + FixedMul(48*FRACUNIT, actor->scale); - break; - case 1: - x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); - y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); - if (actor->eflags & MFE_VERTICALFLIP) - z = actor->z + actor->height - FixedMul(48*FRACUNIT, actor->scale); - else - z = actor->z + FixedMul(48*FRACUNIT, actor->scale); - break; - case 2: - x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(56*FRACUNIT, actor->scale)); - y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(56*FRACUNIT, actor->scale)); - if (actor->eflags & MFE_VERTICALFLIP) - z = actor->z + actor->height - FixedMul(42*FRACUNIT, actor->scale); - else - z = actor->z + FixedMul(42*FRACUNIT, actor->scale); - break; - case 3: - x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(58*FRACUNIT, actor->scale)); - y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(58*FRACUNIT, actor->scale)); - if (actor->eflags & MFE_VERTICALFLIP) - z = actor->z + actor->height - FixedMul(30*FRACUNIT, actor->scale); - else - z = actor->z + FixedMul(30*FRACUNIT, actor->scale); - break; - case 4: - x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(56*FRACUNIT, actor->scale)); - y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(56*FRACUNIT, actor->scale)); - if (actor->eflags & MFE_VERTICALFLIP) - z = actor->z + actor->height - FixedMul(42*FRACUNIT, actor->scale); - else - z = actor->z + FixedMul(42*FRACUNIT, actor->scale); - break; - case 5: - x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(58*FRACUNIT, actor->scale)); - y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(58*FRACUNIT, actor->scale)); - if (actor->eflags & MFE_VERTICALFLIP) - z = actor->z + actor->height - FixedMul(30*FRACUNIT, actor->scale); - else - z = actor->z + FixedMul(30*FRACUNIT, actor->scale); - break; - default: - x = actor->x; - y = actor->y; - z = actor->z + actor->height/2; - break; - } - - P_SpawnXYZMissile(actor, actor->target, locvar1, x, y, z); -} - -// Function: A_Boss7FireMissiles -// -// Description: Shoot 4 missiles of a specific object type at your target ala Black Eggman -// -// var1 = object # to shoot -// var2 = firing sound -// -void A_Boss7FireMissiles(mobj_t *actor) -{ - mobj_t dummymo; - INT32 locvar1 = var1; - INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Boss7FireMissiles", actor)) - return; -#endif - - if (!actor->target) - { - P_SetMobjState(actor, actor->info->spawnstate); - return; - } - - A_FaceTarget(actor); - - S_StartSound(NULL, locvar2); - - // set dummymo's coordinates - dummymo.x = actor->target->x; - dummymo.y = actor->target->y; - dummymo.z = actor->target->z + FixedMul(16*FRACUNIT, actor->scale); // raised height - - P_SpawnXYZMissile(actor, &dummymo, locvar1, - actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)), - actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)), - actor->z + FixedDiv(actor->height, 3*FRACUNIT/2)); - - P_SpawnXYZMissile(actor, &dummymo, locvar1, - actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)), - actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)), - actor->z + FixedDiv(actor->height, 3*FRACUNIT/2)); - - P_SpawnXYZMissile(actor, &dummymo, locvar1, - actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)), - actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)), - actor->z + actor->height/2); - - P_SpawnXYZMissile(actor, &dummymo, locvar1, - actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)), - actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)), - actor->z + actor->height/2); -} - -// Function: A_Boss1Laser -// -// Description: Shoot an object at your target ala Bosses: -// -// var1 = object # to shoot -// var2: -// 0 - Boss 1 Left side -// 1 - Boss 1 Right side -// -void A_Boss1Laser(mobj_t *actor) -{ - fixed_t x, y, z, floorz, speed; - INT32 locvar1 = var1; - INT32 locvar2 = var2; - INT32 i; - angle_t angle; - mobj_t *point; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Boss1Laser", actor)) - return; -#endif - if (!actor->target) - return; - - switch (locvar2) - { - case 0: - x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); - y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); - if (actor->eflags & MFE_VERTICALFLIP) - z = actor->z + actor->height - FixedMul(56*FRACUNIT, actor->scale) - mobjinfo[locvar1].height; - else - z = actor->z + FixedMul(56*FRACUNIT, actor->scale); - break; - case 1: - x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); - y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); - if (actor->eflags & MFE_VERTICALFLIP) - z = actor->z + actor->height - FixedMul(56*FRACUNIT, actor->scale) - mobjinfo[locvar1].height; - else - z = actor->z + FixedMul(56*FRACUNIT, actor->scale); - break; - default: - x = actor->x; - y = actor->y; - z = actor->z + actor->height/2; - break; - } - - if (!(actor->flags2 & MF2_FIRING)) - { - actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y); - if (mobjinfo[locvar1].seesound) - S_StartSound(actor, mobjinfo[locvar1].seesound); - if (!(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)) - { - point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor->angle, actor->radius), y + P_ReturnThrustY(actor, actor->angle, actor->radius), actor->z - actor->height / 2, MT_EGGMOBILE_TARGET); - point->angle = actor->angle; - point->fuse = actor->tics+1; - P_SetTarget(&point->target, actor->target); - P_SetTarget(&actor->target, point); - } - } - /* -- the following was relevant when the MT_EGGMOBILE_TARGET was allowed to move left and right from its path - else if (actor->target && !(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)) - actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);*/ - - if (actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH) - angle = FixedAngle(FixedDiv(actor->tics*160*FRACUNIT, actor->state->tics*FRACUNIT) + 10*FRACUNIT); - else - angle = R_PointToAngle2(z + (mobjinfo[locvar1].height>>1), 0, actor->target->z, R_PointToDist2(x, y, actor->target->x, actor->target->y)); - point = P_SpawnMobj(x, y, z, locvar1); - P_SetTarget(&point->target, actor); - point->angle = actor->angle; - speed = point->radius*2; - point->momz = FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT), speed); - point->momx = FixedMul(FINESINE(angle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(point->angle>>ANGLETOFINESHIFT), speed)); - point->momy = FixedMul(FINESINE(angle>>ANGLETOFINESHIFT), FixedMul(FINESINE(point->angle>>ANGLETOFINESHIFT), speed)); - - for (i = 0; i < 256; i++) - { - mobj_t *mo = P_SpawnMobj(point->x, point->y, point->z, point->type); - mo->angle = point->angle; - P_UnsetThingPosition(mo); - mo->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY; - P_SetThingPosition(mo); - - x = point->x, y = point->y, z = point->z; - if (P_RailThinker(point)) - break; - } - - floorz = P_FloorzAtPos(x, y, z, mobjinfo[MT_EGGMOBILE_FIRE].height); - if (z - floorz < mobjinfo[MT_EGGMOBILE_FIRE].height>>1) - { - point = P_SpawnMobj(x, y, floorz+1, MT_EGGMOBILE_FIRE); - point->target = actor; - point->destscale = 3*FRACUNIT; - point->scalespeed = FRACUNIT>>2; - point->fuse = TICRATE; - } - - if (actor->tics > 1) - actor->flags2 |= MF2_FIRING; - else - actor->flags2 &= ~MF2_FIRING; -} - -// Function: A_FocusTarget -// -// Description: Home in on your target. -// -// var1: -// 0 - accelerative focus with friction -// 1 - steady focus with fixed movement speed -// anything else - don't move -// var2: -// 0 - don't trace target, just move forwards -// & 1 - change horizontal angle -// & 2 - change vertical angle -// -void A_FocusTarget(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FocusTarget", actor)) - return; -#endif - - if (actor->target) - { - fixed_t speed = FixedMul(actor->info->speed, actor->scale); - fixed_t dist = (locvar2 ? R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y) : speed+1); - angle_t hangle = ((locvar2 & 1) ? R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) : actor->angle); - angle_t vangle = ((locvar2 & 2) ? R_PointToAngle2(actor->z , 0, actor->target->z + (actor->target->height>>1), dist) : ANGLE_90); - switch(locvar1) - { - case 0: - { - actor->momx -= actor->momx>>4, actor->momy -= actor->momy>>4, actor->momz -= actor->momz>>4; - actor->momz += FixedMul(FINECOSINE(vangle>>ANGLETOFINESHIFT), speed); - actor->momx += FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hangle>>ANGLETOFINESHIFT), speed)); - actor->momy += FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINESINE(hangle>>ANGLETOFINESHIFT), speed)); - } - break; - case 1: - if (dist > speed) - { - actor->momz = FixedMul(FINECOSINE(vangle>>ANGLETOFINESHIFT), speed); - actor->momx = FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hangle>>ANGLETOFINESHIFT), speed)); - actor->momy = FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINESINE(hangle>>ANGLETOFINESHIFT), speed)); - } - else - { - actor->momx = 0, actor->momy = 0, actor->momz = 0; - actor->z = actor->target->z + (actor->target->height>>1); - P_TryMove(actor, actor->target->x, actor->target->y, true); - } - break; - default: - break; - } - } -} - -// Function: A_Boss4Reverse -// -// Description: Reverse arms direction. -// -// var1 = sfx to play -// var2 = unused -// -void A_Boss4Reverse(mobj_t *actor) -{ - sfxenum_t locvar1 = (sfxenum_t)var1; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Boss4Reverse", actor)) - return; -#endif - S_StartSound(NULL, locvar1); - actor->reactiontime = 0; - if (actor->movedir == 1) - actor->movedir = 2; - else - actor->movedir = 1; -} - -// Function: A_Boss4SpeedUp -// -// Description: Speed up arms -// -// var1 = sfx to play -// var2 = unused -// -void A_Boss4SpeedUp(mobj_t *actor) -{ - sfxenum_t locvar1 = (sfxenum_t)var1; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Boss4SpeedUp", actor)) - return; -#endif - S_StartSound(NULL, locvar1); - actor->reactiontime = 2; -} - -// Function: A_Boss4Raise -// -// Description: Raise helmet -// -// var1 = sfx to play -// var2 = unused -// -void A_Boss4Raise(mobj_t *actor) -{ - sfxenum_t locvar1 = (sfxenum_t)var1; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Boss4Raise", actor)) - return; -#endif - S_StartSound(NULL, locvar1); - actor->reactiontime = 1; -} - -// Function: A_SkullAttack -// -// Description: Fly at the player like a missile. -// -// var1: -// 0 - Fly at the player -// 1 - Fly away from the player -// 2 - Strafe in relation to the player -// var2: -// 0 - Fly horizontally and vertically -// 1 - Fly horizontal-only (momz = 0) -// -#define SKULLSPEED (20*FRACUNIT) - -void A_SkullAttack(mobj_t *actor) -{ - mobj_t *dest; - angle_t an; - INT32 dist; - INT32 speed; - INT32 locvar1 = var1; - INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SkullAttack", actor)) - return; -#endif - if (!actor->target) - return; - - speed = FixedMul(SKULLSPEED, actor->scale); - - dest = actor->target; - actor->flags2 |= MF2_SKULLFLY; - if (actor->info->activesound) - S_StartSound(actor, actor->info->activesound); - A_FaceTarget(actor); - - if (locvar1 == 1) - actor->angle += ANGLE_180; - else if (locvar1 == 2) - actor->angle += (P_RandomChance(FRACUNIT/2)) ? ANGLE_90 : -ANGLE_90; - - an = actor->angle >> ANGLETOFINESHIFT; - - actor->momx = FixedMul(speed, FINECOSINE(an)); - actor->momy = FixedMul(speed, FINESINE(an)); - dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); - dist = dist / speed; - - if (dist < 1) - dist = 1; - - actor->momz = (dest->z + (dest->height>>1) - actor->z) / dist; - - if (locvar1 == 1) - actor->momz = -actor->momz; - if (locvar2 == 1) - actor->momz = 0; -} - -// Function: A_BossZoom -// -// Description: Like A_SkullAttack, but used by Boss 1. -// -// var1 = unused -// var2 = unused -// -void A_BossZoom(mobj_t *actor) -{ - mobj_t *dest; - angle_t an; - INT32 dist; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_BossZoom", actor)) - return; -#endif - if (!actor->target) - return; - - dest = actor->target; - actor->flags2 |= MF2_SKULLFLY; - if (actor->info->attacksound) - S_StartAttackSound(actor, actor->info->attacksound); - A_FaceTarget(actor); - an = actor->angle >> ANGLETOFINESHIFT; - actor->momx = FixedMul(FixedMul(actor->info->speed*5*FRACUNIT, actor->scale), FINECOSINE(an)); - actor->momy = FixedMul(FixedMul(actor->info->speed*5*FRACUNIT, actor->scale), FINESINE(an)); - dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); - dist = dist / FixedMul(actor->info->speed*5*FRACUNIT, actor->scale); - - if (dist < 1) - dist = 1; - actor->momz = (dest->z + (dest->height>>1) - actor->z) / dist; -} - -// Function: A_BossScream -// -// Description: Spawns explosions and plays appropriate sounds around the defeated boss. -// -// var1: -// 0 - Use movecount to spawn explosions evenly -// 1 - Use P_Random to spawn explosions at complete random -// var2 = Object to spawn. Default is MT_BOSSEXPLODE. -// -void A_BossScream(mobj_t *actor) -{ - mobj_t *mo; - fixed_t x, y, z; - angle_t fa; - INT32 locvar1 = var1; - INT32 locvar2 = var2; - mobjtype_t explodetype; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_BossScream", actor)) - return; -#endif - switch (locvar1) - { - default: - case 0: - actor->movecount += 4*16; - actor->movecount %= 360; - fa = (FixedAngle(actor->movecount*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK; - break; - case 1: - fa = (FixedAngle(P_RandomKey(360)*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK; - break; - } - x = actor->x + FixedMul(FINECOSINE(fa),actor->radius); - y = actor->y + FixedMul(FINESINE(fa),actor->radius); - - // Determine what mobj to spawn. If undefined or invalid, use MT_BOSSEXPLODE as default. - if (locvar2 <= 0 || locvar2 >= NUMMOBJTYPES) - explodetype = MT_BOSSEXPLODE; - else - explodetype = (mobjtype_t)locvar2; - - if (actor->eflags & MFE_VERTICALFLIP) - z = actor->z + actor->height - mobjinfo[explodetype].height - FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale); - else - z = actor->z + FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale); - - mo = P_SpawnMobj(x, y, z, explodetype); - if (actor->eflags & MFE_VERTICALFLIP) - mo->flags2 |= MF2_OBJECTFLIP; - mo->destscale = actor->scale; - P_SetScale(mo, mo->destscale); - if (actor->info->deathsound) - S_StartSound(mo, actor->info->deathsound); -} - -// Function: A_Scream -// -// Description: Starts the death sound of the object. -// -// var1 = unused -// var2 = unused -// -void A_Scream(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Scream", actor)) - return; -#endif - if (actor->tracer && (actor->tracer->type == MT_SHELL || actor->tracer->type == MT_FIREBALL)) - S_StartScreamSound(actor, sfx_mario2); - else if (actor->info->deathsound) - S_StartScreamSound(actor, actor->info->deathsound); -} - -// Function: A_Pain -// -// Description: Starts the pain sound of the object. -// -// var1 = unused -// var2 = unused -// -void A_Pain(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Pain", actor)) - return; -#endif - if (actor->info->painsound) - S_StartSound(actor, actor->info->painsound); - - actor->flags2 &= ~MF2_FIRING; - actor->flags2 &= ~MF2_SUPERFIRE; -} - -// Function: A_Fall -// -// Description: Changes a dying object's flags to reflect its having fallen to the ground. -// -// var1 = unused -// var2 = unused -// -void A_Fall(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Fall", actor)) - return; -#endif - // actor is on ground, it can be walked over - actor->flags &= ~MF_SOLID; - - // fall through the floor - actor->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; - - // So change this if corpse objects - // are meant to be obstacles. -} - -#define LIVESBOXDISPLAYPLAYER // Use displayplayer instead of closest player - -// Function: A_1upThinker -// -// Description: Used by the 1up box to show the player's face. -// -// var1 = unused -// var2 = unused -// -void A_1upThinker(mobj_t *actor) -{ - INT32 i; - fixed_t dist = INT32_MAX; - fixed_t temp; - INT32 closestplayer = -1; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_1upThinker", actor)) - return; -#endif - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].bot || players[i].spectator) - continue; - - if (!players[i].mo) - continue; - - if ((netgame || multiplayer) && players[i].playerstate != PST_LIVE) - continue; - - temp = P_AproxDistance(players[i].mo->x-actor->x, players[i].mo->y-actor->y); - - if (temp < dist) - { - closestplayer = i; - dist = temp; - } - } - - if (closestplayer == -1 || skins[players[closestplayer].skin].sprites[SPR2_LIFE].numframes == 0) - { // Closest player not found (no players in game?? may be empty dedicated server!), or does not have correct sprite. - if (actor->tracer) { - P_RemoveMobj(actor->tracer); - actor->tracer = NULL; - } - return; - } - - // We're using the overlay, so use the overlay 1up box (no text) - actor->sprite = SPR_TV1P; - - if (!actor->tracer) - { - P_SetTarget(&actor->tracer, P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY)); - P_SetTarget(&actor->tracer->target, actor); - actor->tracer->skin = &skins[players[closestplayer].skin]; // required here to prevent spr2 default showing stand for a single frame - P_SetMobjState(actor->tracer, actor->info->seestate); - - // The overlay is going to be one tic early turning off and on - // because it's going to get its thinker run the frame we spawned it. - // So make it take one tic longer if it just spawned. - ++actor->tracer->tics; - } - - actor->tracer->color = players[closestplayer].mo->color; - actor->tracer->skin = &skins[players[closestplayer].skin]; -} - -// Function: A_MonitorPop -// -// Description: Used by monitors when they explode. -// -// var1 = unused -// var2 = unused -// -void A_MonitorPop(mobj_t *actor) -{ - mobjtype_t item = 0; - mobj_t *newmobj; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_MonitorPop", actor)) - return; -#endif - - // Spawn the "pop" explosion. - if (actor->info->deathsound) - S_StartSound(actor, actor->info->deathsound); - P_SpawnMobjFromMobj(actor, 0, 0, actor->height/4, MT_EXPLODE); - - // We're dead now. De-solidify. - actor->health = 0; - P_UnsetThingPosition(actor); - actor->flags &= ~MF_SOLID; - actor->flags |= MF_NOCLIP; - P_SetThingPosition(actor); - - if (actor->info->damage == MT_UNKNOWN) - { - // MT_UNKNOWN is random. Because it's unknown to us... get it? - item = P_DoRandomBoxChances(); - - if (item == MT_NULL) - { - CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n")); - return; - } - } - else - item = actor->info->damage; - - if (item == 0) - { - CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_MonitorPop\n"); - return; - } - - newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 13*FRACUNIT, item); - P_SetTarget(&newmobj->target, actor->target); // Transfer target - - if (item == MT_1UP_ICON) - { - if (actor->tracer) // Remove the old lives icon. - P_RemoveMobj(actor->tracer); - - if (!newmobj->target - || !newmobj->target->player - || !newmobj->target->skin - || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0) - {} // No lives icon for this player, use the default. - else - { // Spawn the lives icon. - mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY); - P_SetTarget(&livesico->target, newmobj); - P_SetTarget(&newmobj->tracer, livesico); - - livesico->color = newmobj->target->player->mo->color; - livesico->skin = &skins[newmobj->target->player->skin]; - P_SetMobjState(livesico, newmobj->info->seestate); - - // We're using the overlay, so use the overlay 1up sprite (no text) - newmobj->sprite = SPR_TV1P; - } - } -} - -// Function: A_GoldMonitorPop -// -// Description: Used by repeating monitors when they turn off. They don't really pop, but, you know... -// -// var1 = unused -// var2 = unused -// -void A_GoldMonitorPop(mobj_t *actor) -{ - mobjtype_t item = 0; - mobj_t *newmobj; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_GoldMonitorPop", actor)) - return; -#endif - - // Don't spawn the "pop" explosion, because the monitor isn't broken. - if (actor->info->deathsound) - S_StartSound(actor, actor->info->deathsound); - //P_SpawnMobjFromMobj(actor, 0, 0, actor.height/4, MT_EXPLODE); - - // Remove our flags for a bit. - // Players can now stand on top of us. - P_UnsetThingPosition(actor); - actor->flags &= ~(MF_MONITOR|MF_SHOOTABLE); - P_SetThingPosition(actor); - - // Don't count this box in statistics. Sorry. - if (actor->target && actor->target->player) - --actor->target->player->numboxes; - actor->fuse = 0; // Don't let the monitor code screw us up. - - if (actor->info->damage == MT_UNKNOWN) - { - // MT_UNKNOWN is random. Because it's unknown to us... get it? - item = P_DoRandomBoxChances(); - - if (item == MT_NULL) - { - CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n")); - return; - } - } - else - item = actor->info->damage; - - if (item == 0) - { - CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_GoldMonitorPop\n"); - return; - } - - // Note: the icon spawns 1 fracunit higher - newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 14*FRACUNIT, item); - P_SetTarget(&newmobj->target, actor->target); // Transfer target - - if (item == MT_1UP_ICON) - { - if (actor->tracer) // Remove the old lives icon. - P_RemoveMobj(actor->tracer); - - if (!newmobj->target - || !newmobj->target->player - || !newmobj->target->skin - || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0) - {} // No lives icon for this player, use the default. - else - { // Spawn the lives icon. - mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY); - P_SetTarget(&livesico->target, newmobj); - P_SetTarget(&newmobj->tracer, livesico); - - livesico->color = newmobj->target->player->mo->color; - livesico->skin = &skins[newmobj->target->player->skin]; - P_SetMobjState(livesico, newmobj->info->seestate); - - // We're using the overlay, so use the overlay 1up sprite (no text) - newmobj->sprite = SPR_TV1P; - } - } -} - -// Function: A_GoldMonitorRestore -// -// Description: A repeating monitor is coming back to life. Reset monitor flags, etc. -// -// var1 = unused -// var2 = unused -// -void A_GoldMonitorRestore(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_GoldMonitorRestore", actor)) - return; -#endif - - actor->flags |= MF_MONITOR|MF_SHOOTABLE; - actor->health = 1; // Just in case. -} - -// Function: A_GoldMonitorSparkle -// -// Description: Spawns the little sparkly effect around big monitors. Looks pretty, doesn't it? -// -// var1 = unused -// var2 = unused -// -void A_GoldMonitorSparkle(mobj_t *actor) -{ - fixed_t i, ngangle, xofs, yofs; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_GoldMonitorSparkle", actor)) - return; -#endif - - ngangle = FixedAngle(((leveltime * 21) % 360) << FRACBITS); - xofs = FINESINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK) * (actor->radius>>FRACBITS); - yofs = FINECOSINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK) * (actor->radius>>FRACBITS); - - for (i = FRACUNIT*2; i <= FRACUNIT*3; i += FRACUNIT/2) - P_SetObjectMomZ(P_SpawnMobjFromMobj(actor, xofs, yofs, 0, MT_BOXSPARKLE), i, false); -} - -// Function: A_Explode -// -// Description: Explodes an object, doing damage to any objects nearby. The target is used as the cause of the explosion. Damage value is used as explosion range. -// -// var1 = damagetype -// var2 = unused -// -void A_Explode(mobj_t *actor) -{ - INT32 locvar1 = var1; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Explode", actor)) - return; -#endif - P_RadiusAttack(actor, actor->target, actor->info->damage, locvar1); -} - -// Function: A_BossDeath -// -// Description: Possibly trigger special effects when boss dies. -// -// var1 = unused -// var2 = unused -// -void A_BossDeath(mobj_t *mo) -{ - thinker_t *th; - mobj_t *mo2; - line_t junk; - INT32 i; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_BossDeath", mo)) - return; -#endif - - P_LinedefExecute(LE_BOSSDEAD, mo, NULL); - mo->health = 0; - - // Boss is dead (but not necessarily fleeing...) - // Lua may use this to ignore bosses after they start fleeing - mo->flags2 |= MF2_BOSSDEAD; - - // make sure there is a player alive for victory - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && ((players[i].mo && players[i].mo->health) - || ((netgame || multiplayer) && (players[i].lives || players[i].continues)))) - break; - - if (i == MAXPLAYERS) - return; // no one left alive, so do not end game - - // scan the remaining thinkers to see - // if all bosses are dead - for (th = thinkercap.next; th != &thinkercap; th = th->next) - { - if (th->function.acp1 != (actionf_p1)P_MobjThinker) - continue; - - mo2 = (mobj_t *)th; - if (mo2 != mo && (mo2->flags & MF_BOSS) && mo2->health > 0) - goto bossjustdie; // other boss not dead - just go straight to dying! - } - - // victory! - P_LinedefExecute(LE_ALLBOSSESDEAD, mo, NULL); - if (mo->flags2 & MF2_BOSSNOTRAP) - { - for (i = 0; i < MAXPLAYERS; i++) - P_DoPlayerExit(&players[i]); - } - else - { - // Bring the egg trap up to the surface - junk.tag = 680; - EV_DoElevator(&junk, elevateHighest, false); - junk.tag = 681; - EV_DoElevator(&junk, elevateUp, false); - junk.tag = 682; - EV_DoElevator(&junk, elevateHighest, false); - } - -bossjustdie: -#ifdef HAVE_BLUA - if (LUAh_BossDeath(mo)) - return; - else if (P_MobjWasRemoved(mo)) - return; -#endif - if (mo->type == MT_BLACKEGGMAN || mo->type == MT_CYBRAKDEMON) - { - mo->flags |= MF_NOCLIP; - mo->flags &= ~MF_SPECIAL; - - S_StartSound(NULL, sfx_befall); - } - else if (mo->type == MT_KOOPA) - { - junk.tag = 650; - EV_DoCeiling(&junk, raiseToHighest); - return; - } - else // eggmobiles - { - // Stop exploding and prepare to run. - P_SetMobjState(mo, mo->info->xdeathstate); - if (P_MobjWasRemoved(mo)) - return; - - P_SetTarget(&mo->target, NULL); - - // Flee! Flee! Find a point to escape to! If none, just shoot upward! - // scan the thinkers to find the runaway point - for (th = thinkercap.next; th != &thinkercap; th = th->next) - { - if (th->function.acp1 != (actionf_p1)P_MobjThinker) - continue; - - mo2 = (mobj_t *)th; - - if (mo2->type == MT_BOSSFLYPOINT) - { - // If this one's closer then the last one, go for it. - if (!mo->target || - P_AproxDistance(P_AproxDistance(mo->x - mo2->x, mo->y - mo2->y), mo->z - mo2->z) < - P_AproxDistance(P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y), mo->z - mo->target->z)) - P_SetTarget(&mo->target, mo2); - // Otherwise... Don't! - } - } - - mo->flags |= MF_NOGRAVITY|MF_NOCLIP; - mo->flags |= MF_NOCLIPHEIGHT; - - if (mo->target) - { - mo->angle = R_PointToAngle2(mo->x, mo->y, mo->target->x, mo->target->y); - mo->flags2 |= MF2_BOSSFLEE; - mo->momz = FixedMul(FixedDiv(mo->target->z - mo->z, P_AproxDistance(mo->x-mo->target->x,mo->y-mo->target->y)), FixedMul(2*FRACUNIT, mo->scale)); - } - else - mo->momz = FixedMul(2*FRACUNIT, mo->scale); - } - - if (mo->type == MT_EGGMOBILE2) - { - mo2 = P_SpawnMobj(mo->x + P_ReturnThrustX(mo, mo->angle - ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)), - mo->y + P_ReturnThrustY(mo, mo->angle - ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)), - mo->z + mo->height/2 + ((mo->eflags & MFE_VERTICALFLIP)? FixedMul(8*FRACUNIT, mo->scale)-mobjinfo[MT_BOSSTANK1].height : -FixedMul(8*FRACUNIT, mo->scale)), MT_BOSSTANK1); // Right tank - mo2->angle = mo->angle; - mo2->destscale = mo->scale; - P_SetScale(mo2, mo2->destscale); - if (mo->eflags & MFE_VERTICALFLIP) - { - mo2->eflags |= MFE_VERTICALFLIP; - mo2->flags2 |= MF2_OBJECTFLIP; - } - P_InstaThrust(mo2, mo2->angle - ANGLE_90, FixedMul(4*FRACUNIT, mo2->scale)); - P_SetObjectMomZ(mo2, 4*FRACUNIT, false); - - mo2 = P_SpawnMobj(mo->x + P_ReturnThrustX(mo, mo->angle + ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)), - mo->y + P_ReturnThrustY(mo, mo->angle + ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)), - mo->z + mo->height/2 + ((mo->eflags & MFE_VERTICALFLIP)? FixedMul(8*FRACUNIT, mo->scale)-mobjinfo[MT_BOSSTANK2].height : -FixedMul(8*FRACUNIT, mo->scale)), MT_BOSSTANK2); // Left tank - mo2->angle = mo->angle; - mo2->destscale = mo->scale; - P_SetScale(mo2, mo2->destscale); - if (mo->eflags & MFE_VERTICALFLIP) - { - mo2->eflags |= MFE_VERTICALFLIP; - mo2->flags2 |= MF2_OBJECTFLIP; - } - P_InstaThrust(mo2, mo2->angle + ANGLE_90, FixedMul(4*FRACUNIT, mo2->scale)); - P_SetObjectMomZ(mo2, 4*FRACUNIT, false); - - mo2 = P_SpawnMobj(mo->x, mo->y, - mo->z + ((mo->eflags & MFE_VERTICALFLIP)? mobjinfo[MT_BOSSSPIGOT].height-FixedMul(32*FRACUNIT,mo->scale): mo->height + FixedMul(32*FRACUNIT, mo->scale)), MT_BOSSSPIGOT); - mo2->angle = mo->angle; - mo2->destscale = mo->scale; - P_SetScale(mo2, mo2->destscale); - if (mo->eflags & MFE_VERTICALFLIP) - { - mo2->eflags |= MFE_VERTICALFLIP; - mo2->flags2 |= MF2_OBJECTFLIP; - } - P_SetObjectMomZ(mo2, 4*FRACUNIT, false); - return; - } -} - -// Function: A_CustomPower -// -// Description: Provides a custom powerup. Target (must be a player) is awarded the powerup. Reactiontime of the object is used as an index to the powers array. -// -// var1 = Power index # -// var2 = Power duration in tics -// -void A_CustomPower(mobj_t *actor) -{ - player_t *player; - INT32 locvar1 = var1; - INT32 locvar2 = var2; - boolean spawnshield = false; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CustomPower", actor)) - return; -#endif - if (!actor->target || !actor->target->player) - { - CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); - return; - } - - if (locvar1 >= NUMPOWERS) - { - CONS_Debug(DBG_GAMELOGIC, "Power #%d out of range!\n", locvar1); - return; - } - - player = actor->target->player; - - if (locvar1 == pw_shield && player->powers[pw_shield] != locvar2) - spawnshield = true; - - player->powers[locvar1] = (UINT16)locvar2; - if (actor->info->seesound) - S_StartSound(player->mo, actor->info->seesound); - - if (spawnshield) //workaround for a bug - P_SpawnShieldOrb(player); -} - -// Function: A_GiveWeapon -// -// Description: Gives the player the specified weapon panels. -// -// var1 = Weapon index # -// var2 = unused -// -void A_GiveWeapon(mobj_t *actor) -{ - player_t *player; - INT32 locvar1 = var1; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_GiveWeapon", actor)) - return; -#endif - if (!actor->target || !actor->target->player) - { - CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); - return; - } - - if (locvar1 >= 1<<(NUM_WEAPONS-1)) - { - CONS_Debug(DBG_GAMELOGIC, "Weapon #%d out of range!\n", locvar1); - return; - } - - player = actor->target->player; - - player->ringweapons |= locvar1; - if (actor->info->seesound) - S_StartSound(player->mo, actor->info->seesound); -} - -// Function: A_RingBox -// -// Description: Awards the player 10 rings. -// -// var1 = unused -// var2 = unused -// -void A_RingBox(mobj_t *actor) -{ - player_t *player; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_RingBox", actor)) - return; -#endif - if (!actor->target || !actor->target->player) - { - CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); - return; - } - - player = actor->target->player; - - P_GivePlayerRings(player, actor->info->reactiontime); - if (actor->info->seesound) - S_StartSound(player->mo, actor->info->seesound); -} - -// Function: A_Invincibility -// -// Description: Awards the player invincibility. -// -// var1 = unused -// var2 = unused -// -void A_Invincibility(mobj_t *actor) -{ - player_t *player; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Invincibility", actor)) - return; -#endif - if (!actor->target || !actor->target->player) - { - CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); - return; - } - - player = actor->target->player; - player->powers[pw_invulnerability] = invulntics + 1; - - if (P_IsLocalPlayer(player) && !player->powers[pw_super]) - { - S_StopMusic(); - if (mariomode) - G_GhostAddColor(GHC_INVINCIBLE); - strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14); - S_StartCaption(sfx_None, -1, player->powers[pw_invulnerability]); - S_ChangeMusicInternal((mariomode) ? "_minv" : "_inv", false); - } -} - -// Function: A_SuperSneakers -// -// Description: Awards the player super sneakers. -// -// var1 = unused -// var2 = unused -// -void A_SuperSneakers(mobj_t *actor) -{ - player_t *player; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SuperSneakers", actor)) - return; -#endif - if (!actor->target || !actor->target->player) - { - CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); - return; - } - - player = actor->target->player; - - actor->target->player->powers[pw_sneakers] = sneakertics + 1; - - if (P_IsLocalPlayer(player) && !player->powers[pw_super]) - { - if (S_SpeedMusic(0.0f) && (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC)) - S_SpeedMusic(1.4f); - else - { - S_StopMusic(); - S_ChangeMusicInternal("_shoes", false); - } - strlcpy(S_sfx[sfx_None].caption, "Speed shoes", 12); - S_StartCaption(sfx_None, -1, player->powers[pw_sneakers]); - } -} - -// Function: A_AwardScore -// -// Description: Adds a set amount of points to the player's score. -// -// var1 = unused -// var2 = unused -// -void A_AwardScore(mobj_t *actor) -{ - player_t *player; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_AwardScore", actor)) - return; -#endif - if (!actor->target || !actor->target->player) - { - CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); - return; - } - - player = actor->target->player; - - P_AddPlayerScore(player, actor->info->reactiontime); - if (actor->info->seesound) - S_StartSound(player->mo, actor->info->seesound); -} - -// Function: A_ExtraLife -// -// Description: Awards the player an extra life. -// -// var1 = unused -// var2 = unused -// -void A_ExtraLife(mobj_t *actor) -{ - player_t *player; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ExtraLife", actor)) - return; -#endif - if (!actor->target || !actor->target->player) - { - CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); - return; - } - - player = actor->target->player; - - if (actor->type == MT_1UP_ICON && actor->tracer) - { - // We're using the overlay, so use the overlay 1up sprite (no text) - actor->sprite = SPR_TV1P; - } - - if (ultimatemode) //I don't THINK so! - { - S_StartSound(player->mo, sfx_lose); - return; - } - - P_GiveCoopLives(player, 1, true); -} - -// Function: A_GiveShield -// -// Description: Awards the player a specified shield. -// -// var1 = Shield type (make with SH_ constants) -// var2 = unused -// -void A_GiveShield(mobj_t *actor) -{ - player_t *player; - UINT16 locvar1 = var1; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_GiveShield", actor)) - return; -#endif - if (!actor->target || !actor->target->player) - { - CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); - return; - } - - player = actor->target->player; - - P_SwitchShield(player, locvar1); - S_StartSound(player->mo, actor->info->seesound); -} - -// Function: A_GravityBox -// -// Description: Awards the player gravity boots. -// -// var1 = unused -// var2 = unused -// -void A_GravityBox(mobj_t *actor) -{ - player_t *player; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_GravityBox", actor)) - return; -#endif - if (!actor->target || !actor->target->player) - { - CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); - return; - } - - player = actor->target->player; - - S_StartSound(player, actor->info->activesound); - - player->powers[pw_gravityboots] = (UINT16)(actor->info->reactiontime + 1); -} - -// Function: A_ScoreRise -// -// Description: Makes the little score logos rise. Speed value sets speed. -// -// var1 = unused -// var2 = unused -// -void A_ScoreRise(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ScoreRise", actor)) - return; -#endif - // make logo rise! - P_SetObjectMomZ(actor, actor->info->speed, false); -} - -// Function: A_BunnyHop -// -// Description: Makes object hop like a bunny. -// -// var1 = jump strength -// var2 = horizontal movement -// -void A_BunnyHop(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_BunnyHop", actor)) - return; -#endif - if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz) - || (!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)) - { - P_SetObjectMomZ(actor, locvar1*FRACUNIT, false); - P_InstaThrust(actor, actor->angle, FixedMul(locvar2*FRACUNIT, actor->scale)); // Launch the hopping action! PHOOM!! - } -} - -// Function: A_BubbleSpawn -// -// Description: Spawns a randomly sized bubble from the object's location. Only works underwater. -// -// var1 = Distance to look for players. If no player is in this distance, bubbles aren't spawned. (Ambush overrides) -// var2 = unused -// -void A_BubbleSpawn(mobj_t *actor) -{ - INT32 i, locvar1 = var1; - UINT8 prandom; - mobj_t *bubble = NULL; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_BubbleSpawn", actor)) - return; -#endif - if (!(actor->eflags & MFE_UNDERWATER)) - { - // Don't draw or spawn bubbles above water - actor->flags2 |= MF2_DONTDRAW; - return; - } - actor->flags2 &= ~MF2_DONTDRAW; - - if (!(actor->flags2 & MF2_AMBUSH)) - { - // Quick! Look through players! - // Don't spawn bubbles unless a player is relatively close by (var1). - for (i = 0; i < MAXPLAYERS; ++i) - if (playeringame[i] && players[i].mo - && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (locvar1<x, actor->y, actor->z + (actor->height / 2), MT_EXTRALARGEBUBBLE); - else if (prandom > 128) - bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_SMALLBUBBLE); - else if (prandom < 128 && prandom > 96) - bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_MEDIUMBUBBLE); - - if (bubble) - { - bubble->destscale = actor->scale; - P_SetScale(bubble, actor->scale); - } -} - -// Function: A_FanBubbleSpawn -// -// Description: Spawns bubbles from fans, if they're underwater. -// -// var1 = Distance to look for players. If no player is in this distance, bubbles aren't spawned. (Ambush overrides) -// var2 = unused -// -void A_FanBubbleSpawn(mobj_t *actor) -{ - INT32 i, locvar1 = var1; - UINT8 prandom; - mobj_t *bubble = NULL; - fixed_t hz = actor->z + (4*actor->height)/5; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FanBubbleSpawn", actor)) - return; -#endif - if (!(actor->eflags & MFE_UNDERWATER)) - return; - - if (!(actor->flags2 & MF2_AMBUSH)) - { - // Quick! Look through players! - // Don't spawn bubbles unless a player is relatively close by (var2). - for (i = 0; i < MAXPLAYERS; ++i) - if (playeringame[i] && players[i].mo - && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (locvar1<x, actor->y, hz, MT_SMALLBUBBLE); - else if ((prandom & 0xF0) == 0xF0) - bubble = P_SpawnMobj(actor->x, actor->y, hz, MT_MEDIUMBUBBLE); - - if (bubble) - { - bubble->destscale = actor->scale; - P_SetScale(bubble, actor->scale); - } -} - -// Function: A_BubbleRise -// -// Description: Raises a bubble -// -// var1: -// 0 = Bend around the water abit, looking more realistic -// 1 = Rise straight up -// var2 = rising speed -// -void A_BubbleRise(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_BubbleRise", actor)) - return; -#endif - if (actor->type == MT_EXTRALARGEBUBBLE) - P_SetObjectMomZ(actor, FixedDiv(6*FRACUNIT,5*FRACUNIT), false); // make bubbles rise! - else - { - P_SetObjectMomZ(actor, locvar2, true); // make bubbles rise! - - // Move around slightly to make it look like it's bending around the water - if (!locvar1) - { - UINT8 prandom = P_RandomByte(); - if (!(prandom & 0x7)) // *****000 - { - P_InstaThrust(actor, prandom & 0x70 ? actor->angle + ANGLE_90 : actor->angle, - FixedMul(prandom & 0xF0 ? FRACUNIT/2 : -FRACUNIT/2, actor->scale)); - } - else if (!(prandom & 0x38)) // **000*** - { - P_InstaThrust(actor, prandom & 0x70 ? actor->angle - ANGLE_90 : actor->angle - ANGLE_180, - FixedMul(prandom & 0xF0 ? FRACUNIT/2 : -FRACUNIT/2, actor->scale)); - } - } - } -} - -// Function: A_BubbleCheck -// -// Description: Checks if a bubble should be drawn or not. Bubbles are not drawn above water. -// -// var1 = unused -// var2 = unused -// -void A_BubbleCheck(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_BubbleCheck", actor)) - return; -#endif - if (actor->eflags & MFE_UNDERWATER) - actor->flags2 &= ~MF2_DONTDRAW; // underwater so draw - else - actor->flags2 |= MF2_DONTDRAW; // above water so don't draw -} - -// Function: A_AttractChase -// -// Description: Makes a ring chase after a player with a ring shield and also causes spilled rings to flicker. -// -// var1 = unused -// var2 = unused -// -void A_AttractChase(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_AttractChase", actor)) - return; -#endif - if (actor->flags2 & MF2_NIGHTSPULL || !actor->health) - return; - - // spilled rings flicker before disappearing - if (leveltime & 1 && actor->type == (mobjtype_t)actor->info->reactiontime && actor->fuse && actor->fuse < 2*TICRATE) - actor->flags2 |= MF2_DONTDRAW; - else - actor->flags2 &= ~MF2_DONTDRAW; - - // Turn flingrings back into regular rings if attracted. - if (actor->tracer && actor->tracer->player - && !(actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC) && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime) - { - mobj_t *newring; - newring = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->reactiontime); - newring->momx = actor->momx; - newring->momy = actor->momy; - newring->momz = actor->momz; - P_RemoveMobj(actor); - return; - } - - P_LookForShield(actor); // Go find 'em, boy! - - if (!actor->tracer - || !actor->tracer->player - || !actor->tracer->health - || !P_CheckSight(actor, actor->tracer)) // You have to be able to SEE it...sorta - { - // Lost attracted rings don't through walls anymore. - actor->flags &= ~MF_NOCLIP; - P_SetTarget(&actor->tracer, NULL); - return; - } - - // If a FlingRing gets attracted by a shield, change it into a normal ring. - if (actor->type == (mobjtype_t)actor->info->reactiontime) - { - P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->painchance); - P_RemoveMobj(actor); - return; - } - - // Keep stuff from going down inside floors and junk - actor->flags &= ~MF_NOCLIPHEIGHT; - - // Let attracted rings move through walls and such. - actor->flags |= MF_NOCLIP; - - P_Attract(actor, actor->tracer, false); -} - -// Function: A_DropMine -// -// Description: Drops a mine. Raisestate specifies the object # to use for the mine. -// -// var1 = height offset -// var2: -// lower 16 bits = proximity check distance (0 disables) -// upper 16 bits = 0 to check proximity with target, 1 for tracer -// -void A_DropMine(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - fixed_t z; - mobj_t *mine; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_DropMine", actor)) - return; -#endif - - if (locvar2 & 65535) - { - fixed_t dist; - mobj_t *target; - - if (locvar2 >> 16) - target = actor->tracer; - else - target = actor->target; - - if (!target) - return; - - dist = P_AproxDistance(actor->x-target->x, actor->y-target->y)>>FRACBITS; - - if (dist > FixedMul((locvar2 & 65535), actor->scale)) - return; - } - - if (actor->eflags & MFE_VERTICALFLIP) - z = actor->z + actor->height - mobjinfo[actor->info->raisestate].height - FixedMul((locvar1*FRACUNIT) - 12*FRACUNIT, actor->scale); - else - z = actor->z + FixedMul((locvar1*FRACUNIT) - 12*FRACUNIT, actor->scale); - - // Use raisestate instead of MT_MINE - mine = P_SpawnMobj(actor->x, actor->y, z, (mobjtype_t)actor->info->raisestate); - if (actor->eflags & MFE_VERTICALFLIP) - mine->eflags |= MFE_VERTICALFLIP; - mine->momz = actor->momz + actor->pmomz; - - S_StartSound(actor, actor->info->attacksound); -} - -// Function: A_FishJump -// -// Description: Makes the stupid harmless fish in Greenflower Zone jump. -// -// var1 = Jump strength (in FRACBITS), if specified. Otherwise, uses the angle value. -// var2 = unused -// -void A_FishJump(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FishJump", actor)) - return; -#endif - - if (locvar2) - { - fixed_t rad = actor->radius>>FRACBITS; - P_SpawnMobjFromMobj(actor, P_RandomRange(rad, -rad)<z <= actor->floorz) || (actor->z <= actor->watertop - FixedMul((64 << FRACBITS), actor->scale))) - { - fixed_t jumpval; - - if (locvar1) - jumpval = var1; - else - jumpval = FixedMul(AngleFixed(actor->angle)/4, actor->scale); - - if (!jumpval) jumpval = FixedMul(44*(FRACUNIT/4), actor->scale); - actor->momz = jumpval; - P_SetMobjStateNF(actor, actor->info->seestate); - } - - if (actor->momz < 0 - && (actor->state < &states[actor->info->meleestate] || actor->state > &states[actor->info->xdeathstate])) - P_SetMobjStateNF(actor, actor->info->meleestate); -} - -// Function:A_ThrownRing -// -// Description: Thinker for thrown rings/sparkle trail -// -// var1 = unused -// var2 = unused -// -void A_ThrownRing(mobj_t *actor) -{ - INT32 c = 0; - INT32 stop; - player_t *player; - fixed_t dist; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ThrownRing", actor)) - return; -#endif - - if (leveltime % (TICRATE/7) == 0) - { - mobj_t *ring = NULL; - - if (actor->flags2 & MF2_EXPLOSION) - { - if (actor->momx != 0 || actor->momy != 0) - ring = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMOKE); - // Else spawn nothing because it's totally stationary and constantly smoking would be weird -SH - } - else if (actor->flags2 & MF2_AUTOMATIC) - ring = P_SpawnGhostMobj(actor); - else if (!(actor->flags2 & MF2_RAILRING)) - ring = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SPARK); - - if (ring) - { - /* - P_SetTarget(&ring->target, actor); - ring->color = actor->color; //copy color - */ - ring->destscale = actor->scale; - P_SetScale(ring, actor->scale); - } - } - - // A_GrenadeRing beeping lives once moooooore -SH - if (actor->type == MT_THROWNGRENADE && actor->fuse % TICRATE == 0) - S_StartSound(actor, actor->info->attacksound); - - // decrement bounce ring time - if (actor->flags2 & MF2_BOUNCERING) - { - if (actor->fuse) - actor->fuse--; - else { - P_RemoveMobj(actor); - return; - } - } - - // spilled rings (and thrown bounce) flicker before disappearing - if (leveltime & 1 && actor->fuse > 0 && actor->fuse < 2*TICRATE - && actor->type != MT_THROWNGRENADE) - actor->flags2 |= MF2_DONTDRAW; - else - actor->flags2 &= ~MF2_DONTDRAW; - - if (actor->tracer && actor->tracer->health <= 0) - P_SetTarget(&actor->tracer, NULL); - - // Updated homing ring special capability - // If you have a ring shield, all rings thrown - // at you become homing (except rail)! - if (actor->tracer) - { - // A non-homing ring getting attracted by a - // magnetic player. If he gets too far away, make - // sure to stop the attraction! - if ((!actor->tracer->health) || (actor->tracer->player && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC) - && P_AproxDistance(P_AproxDistance(actor->tracer->x-actor->x, - actor->tracer->y-actor->y), actor->tracer->z-actor->z) > FixedMul(RING_DIST/4, actor->tracer->scale))) - { - P_SetTarget(&actor->tracer, NULL); - } - - if (actor->tracer && (actor->tracer->health) - && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC))// Already found someone to follow. - { - const INT32 temp = actor->threshold; - actor->threshold = 32000; - P_HomingAttack(actor, actor->tracer); - actor->threshold = temp; - return; - } - } - - // first time init, this allow minimum lastlook changes - if (actor->lastlook < 0) - actor->lastlook = P_RandomByte(); - - actor->lastlook %= MAXPLAYERS; - - stop = (actor->lastlook - 1) & PLAYERSMASK; - - for (; ; actor->lastlook = (actor->lastlook + 1) & PLAYERSMASK) - { - // done looking - if (actor->lastlook == stop) - return; - - if (!playeringame[actor->lastlook]) - continue; - - if (c++ == 2) - return; - - player = &players[actor->lastlook]; - - if (!player->mo) - continue; - - if (player->mo->health <= 0) - continue; // dead - - if ((netgame || multiplayer) && player->spectator) - continue; // spectator - - if (actor->target && actor->target->player) - { - if (player->mo == actor->target) - continue; - - // Don't home in on teammates. - if (gametype == GT_CTF - && actor->target->player->ctfteam == player->ctfteam) - continue; - } - - dist = P_AproxDistance(P_AproxDistance(player->mo->x-actor->x, - player->mo->y-actor->y), player->mo->z-actor->z); - - // check distance - if (actor->flags2 & MF2_RAILRING) - { - if (dist > FixedMul(RING_DIST/2, player->mo->scale)) - continue; - } - else if (dist > FixedMul(RING_DIST, player->mo->scale)) - continue; - - // do this after distance check because it's more computationally expensive - if (!P_CheckSight(actor, player->mo)) - continue; // out of sight - - if ((player->powers[pw_shield] & SH_PROTECTELECTRIC) - && dist < FixedMul(RING_DIST/4, player->mo->scale)) - P_SetTarget(&actor->tracer, player->mo); - return; - } - - return; -} - -// Function: A_SetSolidSteam -// -// Description: Makes steam solid so it collides with the player to boost them. -// -// var1 = unused -// var2 = unused -// -void A_SetSolidSteam(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SetSolidSteam", actor)) - return; -#endif - actor->flags &= ~MF_NOCLIP; - actor->flags |= MF_SOLID; - if (!(actor->flags2 & MF2_AMBUSH)) - { - if (P_RandomChance(FRACUNIT/8)) - { - if (actor->info->deathsound) - S_StartSound(actor, actor->info->deathsound); // Hiss! - } - else - { - if (actor->info->painsound) - S_StartSound(actor, actor->info->painsound); - } - } - - P_SetObjectMomZ (actor, 1, true); -} - -// Function: A_UnsetSolidSteam -// -// Description: Makes an object non-solid and also noclip. Used by the steam. -// -// var1 = unused -// var2 = unused -// -void A_UnsetSolidSteam(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_UnsetSolidSteam", actor)) - return; -#endif - actor->flags &= ~MF_SOLID; - actor->flags |= MF_NOCLIP; -} - -// Function: A_SignPlayer -// -// Description: Changes the state of a level end sign to reflect the player that hit it. -// -// var1 = unused -// var2 = unused -// -void A_SignPlayer(mobj_t *actor) -{ - mobj_t *ov; - skin_t *skin; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SignPlayer", actor)) - return; -#endif - if (!actor->target) - return; - - if (!actor->target->player) - return; - - skin = &skins[actor->target->player->skin]; - - if ((actor->target->player->skincolor == skin->prefcolor) && (skin->prefoppositecolor)) // Set it as the skin's preferred oppositecolor? - { - actor->color = skin->prefoppositecolor; - /* - If you're here from the comment above Color_Opposite, - the following line is the one which is dependent on the - array being symmetrical. It gets the opposite of the - opposite of your desired colour just so it can get the - brightness frame for the End Sign. It's not a great - design choice, but it's constant time array access and - the idea that the colours should be OPPOSITES is kind - of in the name. If you have a better idea, feel free - to let me know. ~toast 2016/07/20 - */ - actor->frame += (15 - Color_Opposite[(Color_Opposite[(skin->prefoppositecolor - 1)*2] - 1)*2 + 1]); - } - else if (actor->target->player->skincolor) // Set the sign to be an appropriate background color for this player's skincolor. - { - actor->color = Color_Opposite[(actor->target->player->skincolor - 1)*2]; - actor->frame += (15 - Color_Opposite[(actor->target->player->skincolor - 1)*2 + 1]); - } - - if (skin->sprites[SPR2_SIGN].numframes) - { - // spawn an overlay of the player's face. - ov = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY); - P_SetTarget(&ov->target, actor); - ov->color = actor->target->player->skincolor; - ov->skin = skin; - P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN - } -} - -// Function: A_OverlayThink -// -// Description: Moves the overlay to the position of its target. -// -// var1 = unused -// var2 = invert, z offset -// -void A_OverlayThink(mobj_t *actor) -{ - fixed_t destx, desty; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_OverlayThink", actor)) - return; -#endif - if (!actor->target) - return; - - if (!splitscreen && rendermode != render_soft) - { - angle_t viewingangle; - - if (players[displayplayer].awayviewtics) - viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayer].awayviewmobj->x, players[displayplayer].awayviewmobj->y); - else if (!camera.chase && players[displayplayer].mo) - viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayer].mo->x, players[displayplayer].mo->y); - else - viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, camera.x, camera.y); - - destx = actor->target->x + P_ReturnThrustX(actor->target, viewingangle, FixedMul(FRACUNIT, actor->scale)); - desty = actor->target->y + P_ReturnThrustY(actor->target, viewingangle, FixedMul(FRACUNIT, actor->scale)); - } - else - { - destx = actor->target->x; - desty = actor->target->y; - } - P_UnsetThingPosition(actor); - actor->x = destx; - actor->y = desty; - P_SetThingPosition(actor); - if (actor->eflags & MFE_VERTICALFLIP) - actor->z = actor->target->z + actor->target->height - mobjinfo[actor->type].height - ((var2>>16) ? -1 : 1)*(var2&0xFFFF)*FRACUNIT; - else - actor->z = actor->target->z + ((var2>>16) ? -1 : 1)*(var2&0xFFFF)*FRACUNIT; - actor->angle = actor->target->angle; - actor->eflags = actor->target->eflags; - - actor->momx = actor->target->momx; - actor->momy = actor->target->momy; - actor->momz = actor->target->momz; // assume target has correct momz! Do not use P_SetObjectMomZ! -} - -// Function: A_JetChase -// -// Description: A_Chase for Jettysyns -// -// var1 = unused -// var2 = unused -// -void A_JetChase(mobj_t *actor) -{ - fixed_t thefloor; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_JetChase", actor)) - return; -#endif - - if (actor->flags2 & MF2_AMBUSH) - return; - - if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz - && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale)) - thefloor = actor->watertop; - else - thefloor = actor->floorz; - - if (actor->reactiontime) - actor->reactiontime--; - - if (P_RandomChance(FRACUNIT/32)) - { - actor->momx = actor->momx / 2; - actor->momy = actor->momy / 2; - actor->momz = actor->momz / 2; - } - - // Bounce if too close to floor or ceiling - - // ideal for Jetty-Syns above you on 3d floors - if (actor->momz && ((actor->z - FixedMul((32<scale)) < thefloor) && !((thefloor + FixedMul(32*FRACUNIT, actor->scale) + actor->height) > actor->ceilingz)) - actor->momz = -actor->momz/2; - - if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) - { - // look for a new target - if (P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - actor->momx = actor->momy = actor->momz = 0; - P_SetMobjState(actor, actor->info->spawnstate); - return; - } - - // modify target threshold - if (actor->threshold) - { - if (!actor->target || actor->target->health <= 0) - actor->threshold = 0; - else - actor->threshold--; - } - - // turn towards movement direction if not there yet - actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); - - if ((multiplayer || netgame) && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))) - if (P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - // If the player is over 3072 fracunits away, then look for another player - if (P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), - actor->target->z - actor->z) > FixedMul(3072*FRACUNIT, actor->scale) && P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale))) - { - return; // got a new target - } - - // chase towards player - if (ultimatemode) - P_Thrust(actor, actor->angle, FixedMul(actor->info->speed/2, actor->scale)); - else - P_Thrust(actor, actor->angle, FixedMul(actor->info->speed/4, actor->scale)); - - // must adjust height - if (ultimatemode) - { - if (actor->z < (actor->target->z + actor->target->height + FixedMul((64<scale))) - actor->momz += FixedMul(FRACUNIT/2, actor->scale); - else - actor->momz -= FixedMul(FRACUNIT/2, actor->scale); - } - else - { - if (actor->z < (actor->target->z + actor->target->height + FixedMul((32<scale))) - actor->momz += FixedMul(FRACUNIT/2, actor->scale); - else - actor->momz -= FixedMul(FRACUNIT/2, actor->scale); - } -} - -// Function: A_JetbThink -// -// Description: Thinker for Jetty-Syn bombers -// -// var1 = unused -// var2 = unused -// -void A_JetbThink(mobj_t *actor) -{ - sector_t *nextsector; - fixed_t thefloor; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_JetbThink", actor)) - return; -#endif - - if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz - && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale)) - thefloor = actor->watertop; - else - thefloor = actor->floorz; - - if (actor->target) - { - A_JetChase(actor); - // check for melee attack - if (actor->info->raisestate - && (actor->z > (actor->floorz + FixedMul((32<scale))) - && P_JetbCheckMeleeRange(actor) && !actor->reactiontime - && (actor->target->z >= actor->floorz)) - { - mobj_t *bomb; - if (actor->info->attacksound) - S_StartAttackSound(actor, actor->info->attacksound); - - // use raisestate instead of MT_MINE - bomb = P_SpawnMobj(actor->x, actor->y, actor->z - FixedMul((32<scale), (mobjtype_t)actor->info->raisestate); - - P_SetTarget(&bomb->target, actor); - bomb->destscale = actor->scale; - P_SetScale(bomb, actor->scale); - actor->reactiontime = TICRATE; // one second - S_StartSound(actor, actor->info->attacksound); - } - } - else if (((actor->z - FixedMul((32<scale)) < thefloor) && !((thefloor + FixedMul((32<scale) + actor->height) > actor->ceilingz)) - actor->z = thefloor+FixedMul((32<scale); - - if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) - { - // look for a new target - if (P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - P_SetMobjState(actor, actor->info->spawnstate); - return; - } - - nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector; - - // Move downwards or upwards to go through a passageway. - if (nextsector->ceilingheight < actor->z + actor->height) - actor->momz -= FixedMul(5*FRACUNIT, actor->scale); - else if (nextsector->floorheight > actor->z) - actor->momz += FixedMul(5*FRACUNIT, actor->scale); -} - -// Function: A_JetgShoot -// -// Description: Firing function for Jetty-Syn gunners. -// -// var1 = unused -// var2 = unused -// -void A_JetgShoot(mobj_t *actor) -{ - fixed_t dist; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_JetgShoot", actor)) - return; -#endif - - if (!actor->target) - return; - - if (actor->reactiontime) - return; - - dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y); - - if (dist > FixedMul(actor->info->painchance*FRACUNIT, actor->scale)) - return; - - if (dist < FixedMul(64*FRACUNIT, actor->scale)) - return; - - A_FaceTarget(actor); - P_SpawnMissile(actor, actor->target, (mobjtype_t)actor->info->raisestate); - - if (ultimatemode) - actor->reactiontime = actor->info->reactiontime*TICRATE; - else - actor->reactiontime = actor->info->reactiontime*TICRATE*2; - - if (actor->info->attacksound) - S_StartSound(actor, actor->info->attacksound); -} - -// Function: A_ShootBullet -// -// Description: Shoots a bullet. Raisestate defines object # to use as projectile. -// -// var1 = unused -// var2 = unused -// -void A_ShootBullet(mobj_t *actor) -{ - fixed_t dist; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ShootBullet", actor)) - return; -#endif - - if (!actor->target) - return; - - dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), actor->target->z - actor->z); - - if (dist > FixedMul(actor->info->painchance*FRACUNIT, actor->scale)) - return; - - A_FaceTarget(actor); - P_SpawnMissile(actor, actor->target, (mobjtype_t)actor->info->raisestate); - - if (actor->info->attacksound) - S_StartSound(actor, actor->info->attacksound); -} - -// Function: A_MinusDigging -// -// Description: Minus digging in the ground. -// -// var1 = unused -// var2 = unused -// -void A_MinusDigging(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_MinusDigging", actor)) - return; -#endif - actor->flags &= ~MF_SPECIAL; - actor->flags &= ~MF_SHOOTABLE; - - if (!actor->target) - { - A_Look(actor); - return; - } - - if (actor->reactiontime) - { - actor->reactiontime--; - return; - } - - // Dirt trail - P_SpawnGhostMobj(actor); - - actor->flags |= MF_NOCLIPTHING; - var1 = 3; - A_Chase(actor); - actor->flags &= ~MF_NOCLIPTHING; - - // Play digging sound - if (!(leveltime & 15)) - S_StartSound(actor, actor->info->activesound); - - // If we're close enough to our target, pop out of the ground - if (P_AproxDistance(actor->target->x-actor->x, actor->target->y-actor->y) < actor->radius - && abs(actor->target->z - actor->z) < 2*actor->height) - P_SetMobjState(actor, actor->info->missilestate); - - // Snap to ground - if (actor->eflags & MFE_VERTICALFLIP) - actor->z = actor->ceilingz - actor->height; - else - actor->z = actor->floorz; -} - -// Function: A_MinusPopup -// -// Description: Minus popping out of the ground. -// -// var1 = unused -// var2 = unused -// -void A_MinusPopup(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_MinusPopup", actor)) - return; -#endif - P_SetObjectMomZ(actor, 10*FRACUNIT, false); - - actor->flags |= MF_SPECIAL; - actor->flags |= MF_SHOOTABLE; - - // Sound for busting out of the ground. - S_StartSound(actor, actor->info->attacksound); -} - -// Function: A_MinusCheck -// -// Description: If the minus hits the floor, dig back into the ground. -// -// var1 = unused -// var2 = unused -// -void A_MinusCheck(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_MinusCheck", actor)) - return; -#endif - if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz) - || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)) - { - actor->flags &= ~MF_SPECIAL; - actor->flags &= ~MF_SHOOTABLE; - actor->reactiontime = TICRATE; - P_SetMobjState(actor, actor->info->seestate); - return; - } - - // 'Falling' animation - if (P_MobjFlip(actor)*actor->momz < 0 && actor->state < &states[actor->info->meleestate]) - P_SetMobjState(actor, actor->info->meleestate); -} - -// Function: A_ChickenCheck -// -// Description: Resets the chicken once it hits the floor again. -// -// var1 = unused -// var2 = unused -// -void A_ChickenCheck(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ChickenCheck", actor)) - return; -#endif - if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz) - || (actor->eflags & MFE_VERTICALFLIP && actor->z + actor->height >= actor->ceilingz)) - { - if (!(actor->momx || actor->momy || actor->momz) - && actor->state > &states[actor->info->seestate]) - { - A_Chase(actor); - P_SetMobjState(actor, actor->info->seestate); - } - - actor->momx >>= 2; - actor->momy >>= 2; - } -} - -// Function: A_JetgThink -// -// Description: Thinker for Jetty-Syn Gunners -// -// var1 = unused -// var2 = unused -// -void A_JetgThink(mobj_t *actor) -{ - sector_t *nextsector; - - fixed_t thefloor; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_JetgThink", actor)) - return; -#endif - - if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz - && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale)) - thefloor = actor->watertop; - else - thefloor = actor->floorz; - - if (actor->target) - { - if (P_RandomChance(FRACUNIT/8) && !actor->reactiontime) - P_SetMobjState(actor, actor->info->missilestate); - else - A_JetChase (actor); - } - else if (actor->z - FixedMul((32<scale) < thefloor && !(thefloor + FixedMul((32<scale) - + actor->height > actor->ceilingz)) - { - actor->z = thefloor + FixedMul((32<scale); - } - - if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) - { - // look for a new target - if (P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - P_SetMobjState(actor, actor->info->spawnstate); - return; - } - - nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector; - - // Move downwards or upwards to go through a passageway. - if (nextsector->ceilingheight < actor->z + actor->height) - actor->momz -= FixedMul(5*FRACUNIT, actor->scale); - else if (nextsector->floorheight > actor->z) - actor->momz += FixedMul(5*FRACUNIT, actor->scale); -} - -// Function: A_MouseThink -// -// Description: Thinker for scurrying mice. -// -// var1 = unused -// var2 = unused -// -void A_MouseThink(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_MouseThink", actor)) - return; -#endif - - if (actor->reactiontime) - actor->reactiontime--; - - if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z == actor->floorz) - || (actor->eflags & MFE_VERTICALFLIP && actor->z + actor->height == actor->ceilingz)) - && !actor->reactiontime) - { - if (twodlevel || actor->flags2 & MF2_TWOD) - { - if (P_RandomChance(FRACUNIT/2)) - actor->angle += ANGLE_180; - } - else if (P_RandomChance(FRACUNIT/2)) - actor->angle += ANGLE_90; - else - actor->angle -= ANGLE_90; - - P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed, actor->scale)); - actor->reactiontime = TICRATE/5; - } -} - -// Function: A_DetonChase -// -// Description: Chases a Deton after a player. -// -// var1 = unused -// var2 = unused -// -void A_DetonChase(mobj_t *actor) -{ - angle_t exact; - fixed_t xydist, dist; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_DetonChase", actor)) - return; -#endif - - // modify tracer threshold - if (!actor->tracer || actor->tracer->health <= 0) - actor->threshold = 0; - else - actor->threshold = 1; - - if (!actor->tracer || !(actor->tracer->flags & MF_SHOOTABLE)) - { - // look for a new target - if (P_LookForPlayers(actor, true, true, 0)) - return; // got a new target - - actor->momx = actor->momy = actor->momz = 0; - P_SetMobjState(actor, actor->info->spawnstate); - return; - } - - if (multiplayer && !actor->threshold && P_LookForPlayers(actor, true, true, 0)) - return; // got a new target - - // Face movement direction if not doing so - exact = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); - actor->angle = exact; - /*if (exact != actor->angle) - { - if (exact - actor->angle > ANGLE_180) - { - actor->angle -= actor->info->raisestate; - if (exact - actor->angle < ANGLE_180) - actor->angle = exact; - } - else - { - actor->angle += actor->info->raisestate; - if (exact - actor->angle > ANGLE_180) - actor->angle = exact; - } - }*/ - // movedir is up/down angle: how much it has to go up as it goes over to the player - xydist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y); - exact = R_PointToAngle2(0, 0, xydist, actor->tracer->z - actor->z); - actor->movedir = exact; - /*if (exact != actor->movedir) - { - if (exact - actor->movedir > ANGLE_180) - { - actor->movedir -= actor->info->raisestate; - if (exact - actor->movedir < ANGLE_180) - actor->movedir = exact; - } - else - { - actor->movedir += actor->info->raisestate; - if (exact - actor->movedir > ANGLE_180) - actor->movedir = exact; - } - }*/ - - // check for melee attack - if (actor->tracer) - { - if (P_AproxDistance(actor->tracer->x-actor->x, actor->tracer->y-actor->y) < actor->radius+actor->tracer->radius) - { - if (!((actor->tracer->z > actor->z + actor->height) || (actor->z > actor->tracer->z + actor->tracer->height))) - { - P_ExplodeMissile(actor); - return; - } - } - } - - // chase towards player - if ((dist = P_AproxDistance(xydist, actor->tracer->z-actor->z)) - > FixedMul((actor->info->painchance << FRACBITS), actor->scale)) - { - P_SetTarget(&actor->tracer, NULL); // Too far away - return; - } - - if (actor->reactiontime == 0) - { - actor->reactiontime = actor->info->reactiontime; - return; - } - - if (actor->reactiontime > 1) - { - actor->reactiontime--; - return; - } - - if (actor->reactiontime > 0) - { - actor->reactiontime = -42; - - if (actor->info->seesound) - S_StartScreamSound(actor, actor->info->seesound); - } - - if (actor->reactiontime == -42) - { - fixed_t xyspeed; - - actor->reactiontime = -42; - - exact = actor->movedir>>ANGLETOFINESHIFT; - xyspeed = FixedMul(FixedMul(actor->tracer->player->normalspeed,3*FRACUNIT/4), FINECOSINE(exact)); - actor->momz = FixedMul(FixedMul(actor->tracer->player->normalspeed,3*FRACUNIT/4), FINESINE(exact)); - - exact = actor->angle>>ANGLETOFINESHIFT; - actor->momx = FixedMul(xyspeed, FINECOSINE(exact)); - actor->momy = FixedMul(xyspeed, FINESINE(exact)); - - // Variable re-use - xyspeed = (P_AproxDistance(actor->tracer->x - actor->x, P_AproxDistance(actor->tracer->y - actor->y, actor->tracer->z - actor->z))>>(FRACBITS+6)); - - if (xyspeed < 1) - xyspeed = 1; - - if (leveltime % xyspeed == 0) - S_StartSound(actor, sfx_deton); - } -} - -// Function: A_CapeChase -// -// Description: Set an object's location to its target or tracer. -// -// var1: -// 0 = Use target -// 1 = Use tracer -// upper 16 bits = Z offset -// var2: -// upper 16 bits = forward/backward offset -// lower 16 bits = sideways offset -// -void A_CapeChase(mobj_t *actor) -{ - mobj_t *chaser; - fixed_t foffsetx, foffsety, boffsetx, boffsety; - INT32 locvar1 = var1; - INT32 locvar2 = var2; - angle_t angle; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CapeChase", actor)) - return; -#endif - - CONS_Debug(DBG_GAMELOGIC, "A_CapeChase called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); - - if (locvar1 & 65535) - chaser = actor->tracer; - else - chaser = actor->target; - - if (!chaser || (chaser->health <= 0)) - { - if (chaser) - CONS_Debug(DBG_GAMELOGIC, "Hmm, the guy I'm chasing (object type %d) has no health.. so I'll die too!\n", chaser->type); - - P_RemoveMobj(actor); - return; - } - - angle = (chaser->player ? chaser->player->drawangle : chaser->angle); - - foffsetx = P_ReturnThrustX(chaser, angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale)); - foffsety = P_ReturnThrustY(chaser, angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale)); - - boffsetx = P_ReturnThrustX(chaser, angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale)); - boffsety = P_ReturnThrustY(chaser, angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale)); - - P_UnsetThingPosition(actor); - actor->x = chaser->x + foffsetx + boffsetx; - actor->y = chaser->y + foffsety + boffsety; - if (chaser->eflags & MFE_VERTICALFLIP) - { - actor->eflags |= MFE_VERTICALFLIP; - actor->flags2 |= MF2_OBJECTFLIP; - actor->z = chaser->z + chaser->height - actor->height - FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale); - } - else - { - actor->eflags &= ~MFE_VERTICALFLIP; - actor->flags2 &= ~MF2_OBJECTFLIP; - actor->z = chaser->z + FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale); - } - actor->angle = angle; - P_SetThingPosition(actor); -} - -// Function: A_RotateSpikeBall -// -// Description: Rotates a spike ball around its target/tracer. -// -// var1: -// 0 = Use target -// 1 = Use tracer -// var2 = unused -// -void A_RotateSpikeBall(mobj_t *actor) -{ - INT32 locvar1 = var1; - const fixed_t radius = FixedMul(12*actor->info->speed, actor->scale); -#ifdef HAVE_BLUA - if (LUA_CallAction("A_RotateSpikeBall", actor)) - return; -#endif - - if (!((!locvar1 && (actor->target)) || (locvar1 && (actor->tracer))))// This should NEVER happen. - { - CONS_Debug(DBG_GAMELOGIC, "A_RotateSpikeBall: Spikeball has no target\n"); - P_RemoveMobj(actor); - return; - } - - if (!actor->info->speed) - { - CONS_Debug(DBG_GAMELOGIC, "A_RotateSpikeBall: Object has no speed.\n"); - return; - } - - actor->angle += FixedAngle(actor->info->speed); - P_UnsetThingPosition(actor); - { - const angle_t fa = actor->angle>>ANGLETOFINESHIFT; - if (!locvar1) - { - actor->x = actor->target->x + FixedMul(FINECOSINE(fa),radius); - actor->y = actor->target->y + FixedMul(FINESINE(fa),radius); - actor->z = actor->target->z + actor->target->height/2; - } - else - { - actor->x = actor->tracer->x + FixedMul(FINECOSINE(fa),radius); - actor->y = actor->tracer->y + FixedMul(FINESINE(fa),radius); - actor->z = actor->tracer->z + actor->tracer->height/2; - } - P_SetThingPosition(actor); - } -} - -// Function: A_UnidusBall -// -// Description: Rotates a spike ball around its target. -// -// var1: -// 0 = Don't throw -// 1 = Throw -// 2 = Throw when target leaves MF2_SKULLFLY. -// var2 = unused -// -void A_UnidusBall(mobj_t *actor) -{ - INT32 locvar1 = var1; - boolean canthrow = false; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_UnidusBall", actor)) - return; -#endif - - actor->angle += ANGLE_11hh; - - if (actor->movecount) - { - if (P_AproxDistance(actor->momx, actor->momy) < FixedMul(actor->info->damage/2, actor->scale)) - P_ExplodeMissile(actor); - return; - } - - if (!actor->target || !actor->target->health) - { - CONS_Debug(DBG_GAMELOGIC, "A_UnidusBall: Removing unthrown spikeball from nonexistant Unidus\n"); - P_RemoveMobj(actor); - return; - } - - P_UnsetThingPosition(actor); - { - const angle_t angle = actor->movedir + FixedAngle(actor->info->speed*(leveltime%360)); - const UINT16 fa = angle>>ANGLETOFINESHIFT; - - actor->x = actor->target->x + FixedMul(FINECOSINE(fa),actor->threshold); - actor->y = actor->target->y + FixedMul( FINESINE(fa),actor->threshold); - actor->z = actor->target->z + actor->target->height/2 - actor->height/2; - - if (locvar1 == 1 && actor->target->target) - { - const angle_t tang = R_PointToAngle2(actor->target->x, actor->target->y, actor->target->target->x, actor->target->target->y); - const angle_t mina = tang-ANGLE_11hh; - canthrow = (angle-mina < FixedAngle(actor->info->speed*3)); - } - } - P_SetThingPosition(actor); - - if (locvar1 == 1 && canthrow) - { - if (P_AproxDistance(actor->target->target->x - actor->target->x, actor->target->target->y - actor->target->y) > FixedMul(MISSILERANGE>>1, actor->scale) - || !P_CheckSight(actor, actor->target->target)) - return; - - actor->movecount = actor->info->damage>>FRACBITS; - actor->flags &= ~(MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING); - P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, actor->target->target->x, actor->target->target->y), FixedMul(actor->info->damage, actor->scale)); - } - else if (locvar1 == 2) - { - boolean skull = (actor->target->flags2 & MF2_SKULLFLY) == MF2_SKULLFLY; - if (actor->target->state == &states[actor->target->info->painstate]) - { - P_KillMobj(actor, NULL, NULL, 0); - return; - } - switch(actor->extravalue2) - { - case 0: // at least one frame where not dashing - if (!skull) ++actor->extravalue2; - else break; - /* FALLTHRU */ - case 1: // at least one frame where ARE dashing - if (skull) ++actor->extravalue2; - else break; - /* FALLTHRU */ - case 2: // not dashing again? - if (skull) break; - // launch. - { - mobj_t *target = actor->target; - if (actor->target->target) - target = actor->target->target; - actor->movecount = actor->info->damage>>FRACBITS; - actor->flags &= ~(MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING); - P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, target->x, target->y), FixedMul(actor->info->damage, actor->scale)); - } - default: // from our compiler appeasement program (CAP). - break; - } - } -} - -// Function: A_RockSpawn -// -// Spawns rocks at a specified interval -// -// var1 = unused -// var2 = unused -void A_RockSpawn(mobj_t *actor) -{ - mobj_t *mo; - mobjtype_t type; - INT32 i = P_FindSpecialLineFromTag(12, (INT16)actor->threshold, -1); - line_t *line; - fixed_t dist; - fixed_t randomoomph; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_RockSpawn", actor)) - return; -#endif - - if (i == -1) - { - CONS_Debug(DBG_GAMELOGIC, "A_RockSpawn: Unable to find parameter line 12 (tag %d)!\n", actor->threshold); - return; - } - - line = &lines[i]; - - if (!(sides[line->sidenum[0]].textureoffset >> FRACBITS)) - { - CONS_Debug(DBG_GAMELOGIC, "A_RockSpawn: No X-offset detected! (tag %d)!\n", actor->threshold); - return; - } - - dist = P_AproxDistance(line->dx, line->dy)/16; - - if (dist < 1) - dist = 1; - - type = MT_ROCKCRUMBLE1 + (sides[line->sidenum[0]].rowoffset >> FRACBITS); - - if (line->flags & ML_NOCLIMB) - randomoomph = P_RandomByte() * (FRACUNIT/32); - else - randomoomph = 0; - - mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FALLINGROCK); - P_SetMobjState(mo, mobjinfo[type].spawnstate); - mo->angle = R_PointToAngle2(line->v2->x, line->v2->y, line->v1->x, line->v1->y); - - P_InstaThrust(mo, mo->angle, dist + randomoomph); - mo->momz = dist + randomoomph; - - var1 = sides[line->sidenum[0]].textureoffset >> FRACBITS; - A_SetTics(actor); -} - -// -// Function: A_SlingAppear -// -// Appears a sling. -// -// var1 = unused -// var2 = unused -// -void A_SlingAppear(mobj_t *actor) -{ - UINT8 mlength = 4; - mobj_t *spawnee, *hprev; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SlingAppear", actor)) - return; -#endif - - P_UnsetThingPosition(actor); - actor->flags &= ~(MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_NOCLIPHEIGHT); - P_SetThingPosition(actor); - actor->lastlook = 128; - actor->movecount = actor->lastlook; - actor->threshold = 0; - actor->movefactor = actor->threshold; - actor->friction = 128; - - hprev = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLGRABCHAIN); - P_SetTarget(&hprev->tracer, actor); - P_SetTarget(&hprev->hprev, actor); - P_SetTarget(&actor->hnext, hprev); - hprev->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT; - hprev->movecount = mlength; - - mlength--; - - while (mlength > 0) - { - spawnee = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLMACECHAIN); - P_SetTarget(&spawnee->tracer, actor); - P_SetTarget(&spawnee->hprev, hprev); - P_SetTarget(&hprev->hnext, spawnee); - hprev = spawnee; - - spawnee->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT; - spawnee->movecount = mlength; - - mlength--; - } -} - -// Function: A_SetFuse -// -// Description: Sets the actor's fuse timer if not set already. May also change state when fuse reaches the last tic, otherwise by default the actor will die or disappear. (Replaces A_SnowBall) -// -// var1 = fuse timer duration (in tics). -// var2: -// lower 16 bits = if > 0, state to change to when fuse = 1 -// upper 16 bits: 0 = (default) don't set fuse unless 0, 1 = force change, 2 = force no change -// -void A_SetFuse(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SetFuse", actor)) - return; -#endif - - if ((!actor->fuse || (locvar2 >> 16)) && (locvar2 >> 16) != 2) // set the actor's fuse value - actor->fuse = locvar1; - - if (actor->fuse == 1 && (locvar2 & 65535)) // change state on the very last tic (fuse is handled before actions in P_MobjThinker) - { - actor->fuse = 0; // don't die/disappear the next tic! - P_SetMobjState(actor, locvar2 & 65535); - } -} - -// Function: A_CrawlaCommanderThink -// -// Description: Thinker for Crawla Commander. -// -// var1 = shoot bullets? -// var2 = "pogo mode" speed -// -void A_CrawlaCommanderThink(mobj_t *actor) -{ - fixed_t dist; - sector_t *nextsector; - fixed_t thefloor; - INT32 locvar1 = var1; - INT32 locvar2 = var2; - boolean hovermode = (actor->health > 1 || actor->fuse); -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CrawlaCommanderThink", actor)) - return; -#endif - - if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz - && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale)) - thefloor = actor->watertop; - else - thefloor = actor->floorz; - - if (!actor->fuse && actor->flags2 & MF2_FRET) - { - if (actor->info->painsound) - S_StartSound(actor, actor->info->painsound); - - actor->fuse = TICRATE/2; - actor->momz = 0; - - P_InstaThrust(actor, actor->angle-ANGLE_180, FixedMul(5*FRACUNIT, actor->scale)); - } - - if (actor->reactiontime > 0) - actor->reactiontime--; - - if (actor->fuse < 2) - { - actor->fuse = 0; - actor->flags2 &= ~MF2_FRET; - } - - // Hover mode - if (hovermode) - { - if (actor->z < thefloor + FixedMul(16*FRACUNIT, actor->scale)) - actor->momz += FixedMul(FRACUNIT, actor->scale); - else if (actor->z < thefloor + FixedMul(32*FRACUNIT, actor->scale)) - actor->momz += FixedMul(FRACUNIT/2, actor->scale); - else - actor->momz += FixedMul(16, actor->scale); - } - - if (!actor->target) - { - // look for a new target - if (P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - if (actor->state != &states[actor->info->spawnstate]) - P_SetMobjState(actor, actor->info->spawnstate); - return; - } - - dist = P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y); - - if (actor->target->player && (!hovermode || actor->reactiontime <= 2*TICRATE)) - { - if (dist < FixedMul(64<<(FRACBITS+(hovermode ? 1 : 0)), actor->scale) - && ((actor->target->player->pflags & PF_JUMPED) || (actor->target->player->pflags & PF_SPINNING))) - { - // Auugh! She's trying to kill you! Strafe! STRAAAAFFEEE!! - P_InstaThrust(actor, actor->angle - ANGLE_180, FixedMul(20*FRACUNIT, actor->scale)); - return; - } - } - - if (locvar1) - { - if (actor->health < 2 && P_RandomChance(FRACUNIT/128)) - P_SpawnMissile(actor, actor->target, locvar1); - } - - // Face the player - actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); - - if (actor->threshold && dist > FixedMul(256*FRACUNIT, actor->scale)) - actor->momx = actor->momy = 0; - - if (actor->reactiontime && actor->reactiontime <= 2*TICRATE && dist > actor->target->radius - FixedMul(FRACUNIT, actor->scale)) - { - actor->threshold = 0; - - // Roam around, somewhat in the player's direction. - actor->angle += (P_RandomByte()<<10); - actor->angle -= (P_RandomByte()<<10); - - if (hovermode) - { - fixed_t mom; - P_Thrust(actor, actor->angle, 2*actor->scale); - mom = P_AproxDistance(actor->momx, actor->momy); - if (mom > 20*actor->scale) - { - mom += 20*actor->scale; - mom >>= 1; - P_InstaThrust(actor, R_PointToAngle2(0, 0, actor->momx, actor->momy), mom); - } - } - } - else if (!actor->reactiontime) - { - if (hovermode && !(actor->flags2 & MF2_FRET)) // Hover Mode - { - if (dist < FixedMul(512*FRACUNIT, actor->scale)) - { - actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); - P_InstaThrust(actor, actor->angle, FixedMul(40*FRACUNIT, actor->scale)); - actor->threshold = 1; - if (actor->info->attacksound) - S_StartSound(actor, actor->info->attacksound); - } - } - actor->reactiontime = 3*TICRATE + (P_RandomByte()>>2); - } - - if (actor->health == 1) - P_Thrust(actor, actor->angle, 1); - - // Pogo Mode - if (!hovermode && actor->z <= actor->floorz) - { - if (actor->info->activesound) - S_StartSound(actor, actor->info->activesound); - - if (dist < FixedMul(256*FRACUNIT, actor->scale)) - { - actor->momz = FixedMul(locvar2, actor->scale); - actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); - P_InstaThrust(actor, actor->angle, FixedMul(locvar2/8, actor->scale)); - // pogo on player - } - else - { - UINT8 prandom = P_RandomByte(); - actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); - P_InstaThrust(actor, actor->angle, FixedDiv(FixedMul(locvar2, actor->scale), 3*FRACUNIT/2)); - actor->momz = FixedMul(locvar2, actor->scale); // Bounce up in air - } - } - - nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector; - - // Move downwards or upwards to go through a passageway. - if (nextsector->floorheight > actor->z && nextsector->floorheight - actor->z < FixedMul(128*FRACUNIT, actor->scale)) - actor->momz += (nextsector->floorheight - actor->z) / 4; -} - -// Function: A_RingExplode -// -// Description: An explosion ring exploding -// -// var1 = unused -// var2 = unused -// -void A_RingExplode(mobj_t *actor) -{ - mobj_t *mo2; - thinker_t *th; - angle_t d; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_RingExplode", actor)) - return; -#endif - - for (d = 0; d < 16; d++) - P_SpawnParaloop(actor->x, actor->y, actor->z + actor->height, FixedMul(actor->info->painchance, actor->scale), 16, MT_NIGHTSPARKLE, S_NULL, d*(ANGLE_22h), true); - - S_StartSound(actor, sfx_prloop); - - for (th = thinkercap.next; th != &thinkercap; th = th->next) - { - if (th->function.acp1 != (actionf_p1)P_MobjThinker) - continue; - - mo2 = (mobj_t *)th; - - if (mo2 == actor) // Don't explode yourself! Endless loop! - continue; - - if (P_AproxDistance(P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y), mo2->z - actor->z) > FixedMul(actor->info->painchance, actor->scale)) - continue; - - if (mo2->flags & MF_SHOOTABLE) - { - actor->flags2 |= MF2_DEBRIS; - P_DamageMobj(mo2, actor, actor->target, 1, 0); - continue; - } - } - return; -} - -// Function: A_OldRingExplode -// -// Description: An explosion ring exploding, 1.09.4 style -// -// var1 = object # to explode as debris -// var2 = unused -// -void A_OldRingExplode(mobj_t *actor) { - UINT8 i; - mobj_t *mo; - const fixed_t ns = FixedMul(20 * FRACUNIT, actor->scale); - INT32 locvar1 = var1; - //INT32 locvar2 = var2; - boolean changecolor = (actor->target && actor->target->player); -#ifdef HAVE_BLUA - if (LUA_CallAction("A_OldRingExplode", actor)) - return; -#endif - - for (i = 0; i < 32; i++) - { - const angle_t fa = (i*FINEANGLES/16) & FINEMASK; - - mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1); - P_SetTarget(&mo->target, actor->target); // Transfer target so player gets the points - - mo->momx = FixedMul(FINECOSINE(fa),ns); - mo->momy = FixedMul(FINESINE(fa),ns); - - if (i > 15) - { - if (i & 1) - mo->momz = ns; - else - mo->momz = -ns; - } - - mo->flags2 |= MF2_DEBRIS; - mo->fuse = TICRATE/5; - - if (changecolor) - { - if (gametype != GT_CTF) - mo->color = actor->target->color; //copy color - else if (actor->target->player->ctfteam == 2) - mo->color = skincolor_bluering; - } - } - - mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1); - - P_SetTarget(&mo->target, actor->target); - mo->momz = ns; - mo->flags2 |= MF2_DEBRIS; - mo->fuse = TICRATE/5; - - if (changecolor) - { - if (gametype != GT_CTF) - mo->color = actor->target->color; //copy color - else if (actor->target->player->ctfteam == 2) - mo->color = skincolor_bluering; - } - - mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1); - - P_SetTarget(&mo->target, actor->target); - mo->momz = -ns; - mo->flags2 |= MF2_DEBRIS; - mo->fuse = TICRATE/5; - - if (changecolor) - { - if (gametype != GT_CTF) - mo->color = actor->target->color; //copy color - else if (actor->target->player->ctfteam == 2) - mo->color = skincolor_bluering; - } -} - -// Function: A_MixUp -// -// Description: Mix up all of the player positions. -// -// var1 = unused -// var2 = unused -// -void A_MixUp(mobj_t *actor) -{ - boolean teleported[MAXPLAYERS]; - INT32 i, numplayers = 0, prandom = 0; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_MixUp", actor)) - return; -#else - (void)actor; -#endif - - if (!multiplayer) - return; - - // No mix-up monitors in hide and seek or time only race. - // The random factor is okay for other game modes, but in these, it is cripplingly unfair. - if (gametype == GT_HIDEANDSEEK || gametype == GT_RACE) - { - S_StartSound(actor, sfx_lose); - return; - } - - numplayers = 0; - memset(teleported, 0, sizeof (teleported)); - - // Count the number of players in the game - // and grab their xyz coords - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE - && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE) - { - if ((netgame || multiplayer) && players[i].spectator) // Ignore spectators - continue; - - numplayers++; - } - - if (numplayers <= 1) // Not enough players to mix up. - { - S_StartSound(actor, sfx_lose); - return; - } - else if (numplayers == 2) // Special case -- simple swap - { - fixed_t x, y, z; - angle_t angle; - INT32 one = -1, two = 0; // default value 0 to make the compiler shut up - - // Zoom tube stuff - mobj_t *tempthing = NULL; //tracer - UINT16 carry1,carry2; //carry - INT32 transspeed; //player speed - - // Starpost stuff - INT16 starpostx, starposty, starpostz; - INT32 starpostnum; - tic_t starposttime; - angle_t starpostangle; - - INT32 mflags2; - - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE - && !players[i].exiting && !players[i].powers[pw_super]) - { - if ((netgame || multiplayer) && players[i].spectator) // Ignore spectators - continue; - - if (one == -1) - one = i; - else - { - two = i; - break; - } - } - - //get this done first! - tempthing = players[one].mo->tracer; - P_SetTarget(&players[one].mo->tracer, players[two].mo->tracer); - P_SetTarget(&players[two].mo->tracer, tempthing); - - //zoom tubes use player->speed to determine direction and speed - transspeed = players[one].speed; - players[one].speed = players[two].speed; - players[two].speed = transspeed; - - //set flags variables now but DON'T set them. - carry1 = (players[one].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[one].powers[pw_carry]); - carry2 = (players[two].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[two].powers[pw_carry]); - - x = players[one].mo->x; - y = players[one].mo->y; - z = players[one].mo->z; - angle = players[one].mo->angle; - - starpostx = players[one].starpostx; - starposty = players[one].starposty; - starpostz = players[one].starpostz; - starpostangle = players[one].starpostangle; - starpostnum = players[one].starpostnum; - starposttime = players[one].starposttime; - - mflags2 = players[one].mo->flags2; - - P_MixUp(players[one].mo, players[two].mo->x, players[two].mo->y, players[two].mo->z, players[two].mo->angle, - players[two].starpostx, players[two].starposty, players[two].starpostz, - players[two].starpostnum, players[two].starposttime, players[two].starpostangle, - players[two].mo->flags2); - - P_MixUp(players[two].mo, x, y, z, angle, starpostx, starposty, starpostz, - starpostnum, starposttime, starpostangle, - mflags2); - - //carry set after mixup. Stupid P_ResetPlayer() takes away some of the stuff we look for... - //but not all of it! So we need to make sure they aren't set wrong or anything. - players[one].powers[pw_carry] = carry2; - players[two].powers[pw_carry] = carry1; - - teleported[one] = true; - teleported[two] = true; - } - else - { - fixed_t position[MAXPLAYERS][3]; - angle_t anglepos[MAXPLAYERS]; - INT32 pindex[MAXPLAYERS], counter = 0, teleportfrom = 0; - - // Zoom tube stuff - mobj_t *transtracer[MAXPLAYERS]; //tracer - //pflags_t transflag[MAXPLAYERS]; //cyan pink white pink cyan - UINT16 transcarry[MAXPLAYERS]; //player carry - INT32 transspeed[MAXPLAYERS]; //player speed - - // Star post stuff - INT16 spposition[MAXPLAYERS][3]; - INT32 starpostnum[MAXPLAYERS]; - tic_t starposttime[MAXPLAYERS]; - angle_t starpostangle[MAXPLAYERS]; - - INT32 flags2[MAXPLAYERS]; - - for (i = 0; i < MAXPLAYERS; i++) - { - position[i][0] = position[i][1] = position[i][2] = anglepos[i] = pindex[i] = -1; - teleported[i] = false; - } - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && players[i].playerstate == PST_LIVE - && players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE) - { - if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators - continue; - - position[counter][0] = players[i].mo->x; - position[counter][1] = players[i].mo->y; - position[counter][2] = players[i].mo->z; - pindex[counter] = i; - anglepos[counter] = players[i].mo->angle; - players[i].mo->momx = players[i].mo->momy = players[i].mo->momz = - players[i].rmomx = players[i].rmomy = 1; - players[i].cmomx = players[i].cmomy = 0; - - transcarry[counter] = (players[i].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[i].powers[pw_carry]); - transspeed[counter] = players[i].speed; - transtracer[counter] = players[i].mo->tracer; - - spposition[counter][0] = players[i].starpostx; - spposition[counter][1] = players[i].starposty; - spposition[counter][2] = players[i].starpostz; - starpostnum[counter] = players[i].starpostnum; - starposttime[counter] = players[i].starposttime; - starpostangle[counter] = players[i].starpostangle; - - flags2[counter] = players[i].mo->flags2; - - counter++; - } - } - - counter = 0; - - // Mix them up! - for (;;) - { - if (counter > 255) // fail-safe to avoid endless loop - break; - prandom = P_RandomByte(); - prandom %= numplayers; // I love modular arithmetic, don't you? - if (prandom) // Make sure it's not a useless mix - break; - counter++; - } - - counter = 0; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && players[i].playerstate == PST_LIVE - && players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE) - { - if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators - continue; - - teleportfrom = (counter + prandom) % numplayers; - - //speed and tracer come before... - players[i].speed = transspeed[teleportfrom]; - P_SetTarget(&players[i].mo->tracer, transtracer[teleportfrom]); - - P_MixUp(players[i].mo, position[teleportfrom][0], position[teleportfrom][1], position[teleportfrom][2], anglepos[teleportfrom], - spposition[teleportfrom][0], spposition[teleportfrom][1], spposition[teleportfrom][2], - starpostnum[teleportfrom], starposttime[teleportfrom], starpostangle[teleportfrom], - flags2[teleportfrom]); - - //...carry after. same reasoning. - players[i].powers[pw_carry] = transcarry[teleportfrom]; - - teleported[i] = true; - counter++; - } - } - } - - for (i = 0; i < MAXPLAYERS; i++) - { - if (teleported[i]) - { - if (playeringame[i] && players[i].playerstate == PST_LIVE - && players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE) - { - if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators - continue; - - P_SetThingPosition(players[i].mo); - -#ifdef ESLOPE - players[i].mo->floorz = P_GetFloorZ(players[i].mo, players[i].mo->subsector->sector, players[i].mo->x, players[i].mo->y, NULL); - players[i].mo->ceilingz = P_GetCeilingZ(players[i].mo, players[i].mo->subsector->sector, players[i].mo->x, players[i].mo->y, NULL); -#else - players[i].mo->floorz = players[i].mo->subsector->sector->floorheight; - players[i].mo->ceilingz = players[i].mo->subsector->sector->ceilingheight; -#endif - - P_CheckPosition(players[i].mo, players[i].mo->x, players[i].mo->y); - } - } - } - - // Play the 'bowrwoosh!' sound - S_StartSound(NULL, sfx_mixup); -} - -// Function: A_RecyclePowers -// -// Description: Take all player's powers, and swap 'em. -// -// var1 = unused -// var2 = unused -// -void A_RecyclePowers(mobj_t *actor) -{ - INT32 i, j, k, numplayers = 0; - -#ifdef WEIGHTEDRECYCLER - UINT8 beneficiary = 255; -#endif - UINT8 playerslist[MAXPLAYERS]; - UINT8 postscramble[MAXPLAYERS]; - - UINT16 powers[MAXPLAYERS][NUMPOWERS]; - INT32 weapons[MAXPLAYERS]; - INT32 weaponheld[MAXPLAYERS]; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_RecyclePowers", actor)) - return; -#endif - -#if !defined(WEIGHTEDRECYCLER) && !defined(HAVE_BLUA) - // actor is used in all scenarios but this one, funny enough - (void)actor; -#endif - - if (!multiplayer) - { - S_StartSound(actor, sfx_lose); - return; - } - - numplayers = 0; - - // Count the number of players in the game - for (i = 0, j = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE - && !players[i].exiting && !((netgame || multiplayer) && players[i].spectator)) - { -#ifndef WEIGHTEDRECYCLER - if (players[i].powers[pw_super]) - continue; // Ignore super players -#endif - - numplayers++; - postscramble[j] = playerslist[j] = (UINT8)i; - -#ifdef WEIGHTEDRECYCLER - // The guy who started the recycle gets the best result - if (actor && actor->target && actor->target->player && &players[i] == actor->target->player) - beneficiary = (UINT8)i; -#endif - - // Save powers - for (k = 0; k < NUMPOWERS; k++) - powers[i][k] = players[i].powers[k]; - //1.1: ring weapons too - weapons[i] = players[i].ringweapons; - weaponheld[i] = players[i].currentweapon; - - j++; - } - } - - if (numplayers <= 1) - { - S_StartSound(actor, sfx_lose); - return; //nobody to touch! - } - - //shuffle the post scramble list, whee! - // hardcoded 0-1 to 1-0 for two players - if (numplayers == 2) - { - postscramble[0] = playerslist[1]; - postscramble[1] = playerslist[0]; - } - else - for (j = 0; j < numplayers; j++) - { - UINT8 tempint; - - i = j + ((P_RandomByte() + leveltime) % (numplayers - j)); - tempint = postscramble[j]; - postscramble[j] = postscramble[i]; - postscramble[i] = tempint; - } - -#ifdef WEIGHTEDRECYCLER - //the joys of qsort... - if (beneficiary != 255) { - qsort(playerslist, numplayers, sizeof(UINT8), P_RecycleCompare); - - // now, make sure the benificiary is in the best slot - // swap out whatever poor sap was going to get the best items - for (i = 0; i < numplayers; i++) - { - if (postscramble[i] == beneficiary) - { - postscramble[i] = postscramble[0]; - postscramble[0] = beneficiary; - break; - } - } - } -#endif - - // now assign! - for (i = 0; i < numplayers; i++) - { - UINT8 send_pl = playerslist[i]; - UINT8 recv_pl = postscramble[i]; - - // debugF - CONS_Debug(DBG_GAMELOGIC, "sending player %hu's items to %hu\n", (UINT16)send_pl, (UINT16)recv_pl); - - for (j = 0; j < NUMPOWERS; j++) - { - if (j == pw_flashing || j == pw_underwater || j == pw_spacetime || j == pw_carry - || j == pw_tailsfly || j == pw_extralife || j == pw_nocontrol || j == pw_super) - continue; - players[recv_pl].powers[j] = powers[send_pl][j]; - } - - //1.1: weapon rings too - players[recv_pl].ringweapons = weapons[send_pl]; - players[recv_pl].currentweapon = weaponheld[send_pl]; - - P_SpawnShieldOrb(&players[recv_pl]); - if (P_IsLocalPlayer(&players[recv_pl])) - P_RestoreMusic(&players[recv_pl]); - P_FlashPal(&players[recv_pl], PAL_RECYCLE, 10); - } - - S_StartSound(NULL, sfx_gravch); //heh, the sound effect I used is already in -} - -// Function: A_Boss1Chase -// -// Description: Like A_Chase, but for Boss 1. -// -// var1 = unused -// var2 = unused -// -void A_Boss1Chase(mobj_t *actor) -{ - INT32 delta; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Boss1Chase", actor)) - return; -#endif - - if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) - { - // look for a new target - if (P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - P_SetMobjStateNF(actor, actor->info->spawnstate); - return; - } - - if (actor->reactiontime) - actor->reactiontime--; - - // turn towards movement direction if not there yet - if (actor->movedir < NUMDIRS) - { - actor->angle &= (7<<29); - delta = actor->angle - (actor->movedir << 29); - - if (delta > 0) - actor->angle -= ANGLE_45; - else if (delta < 0) - actor->angle += ANGLE_45; - } - - // do not attack twice in a row - if (actor->flags2 & MF2_JUSTATTACKED) - { - actor->flags2 &= ~MF2_JUSTATTACKED; - P_NewChaseDir(actor); - return; - } - - if (actor->movecount) - goto nomissile; - - if (!P_CheckMissileRange(actor)) - goto nomissile; - - if (actor->reactiontime <= 0) - { - if (actor->health > actor->info->damage) - { - if (P_RandomChance(FRACUNIT/2)) - P_SetMobjState(actor, actor->info->missilestate); - else - P_SetMobjState(actor, actor->info->meleestate); - } - else - { - P_LinedefExecute(LE_PINCHPHASE, actor, NULL); - P_SetMobjState(actor, actor->info->raisestate); - } - - actor->flags2 |= MF2_JUSTATTACKED; - actor->reactiontime = actor->info->reactiontime; - return; - } - - // ? -nomissile: - // possibly choose another target - if (multiplayer && P_RandomChance(FRACUNIT/128)) - { - if (P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - } - - if (actor->flags & MF_FLOAT && !(actor->flags2 & MF2_SKULLFLY)) - { // Float up/down to your target's position. Stay above them, but not out of jump range. - fixed_t target_min = actor->target->floorz+FixedMul(64*FRACUNIT, actor->scale); - if (target_min < actor->target->z - actor->height) - target_min = actor->target->z - actor->height; - if (target_min < actor->floorz+FixedMul(33*FRACUNIT, actor->scale)) - target_min = actor->floorz+FixedMul(33*FRACUNIT, actor->scale); - if (actor->z > target_min+FixedMul(16*FRACUNIT, actor->scale)) - actor->momz = FixedMul((-actor->info->speed<<(FRACBITS-1)), actor->scale); - else if (actor->z < target_min) - actor->momz = FixedMul(actor->info->speed<<(FRACBITS-1), actor->scale); - else - actor->momz = FixedMul(actor->momz,7*FRACUNIT/8); - } - - // chase towards player - if (P_AproxDistance(actor->target->x-actor->x, actor->target->y-actor->y) > actor->radius+actor->target->radius) - { - if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed)) - P_NewChaseDir(actor); - } - // too close, don't want to chase. - else if (--actor->movecount < 0) - { - // A mini-A_FaceTarget based on P_NewChaseDir. - // Yes, it really is this simple when you get down to it. - fixed_t deltax, deltay; - - deltax = actor->target->x - actor->x; - deltay = actor->target->y - actor->y; - - actor->movedir = diags[((deltay < 0)<<1) + (deltax > 0)]; - actor->movecount = P_RandomByte() & 15; - } -} - -// Function: A_Boss2Chase -// -// Description: Really doesn't 'chase', but rather goes in a circle. -// -// var1 = unused -// var2 = unused -// -void A_Boss2Chase(mobj_t *actor) -{ - fixed_t radius; - boolean reverse = false; - INT32 speedvar; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Boss2Chase", actor)) - return; -#endif - - if (actor->health <= 0) - return; - - // Startup randomness - if (actor->reactiontime <= -666) - actor->reactiontime = 2*TICRATE + P_RandomByte(); - - // When reactiontime hits zero, he will go the other way - if (--actor->reactiontime <= 0) - { - reverse = true; - actor->reactiontime = 2*TICRATE + P_RandomByte(); - } - - P_SetTarget(&actor->target, P_GetClosestAxis(actor)); - - if (!actor->target) // This should NEVER happen. - { - CONS_Debug(DBG_GAMELOGIC, "Boss2 has no target!\n"); - A_BossDeath(actor); - return; - } - - radius = actor->target->radius; - - if (reverse) - { - actor->watertop = -actor->watertop; - actor->extravalue1 = 18; - if (actor->flags2 & MF2_AMBUSH) - actor->extravalue1 -= (actor->info->spawnhealth - actor->health)*2; - actor->extravalue2 = actor->extravalue1; - } - - // Turnaround - if (actor->extravalue1 > 0) - { - --actor->extravalue1; - - // Set base angle - { - const angle_t fa = (actor->target->angle + FixedAngle(actor->watertop))>>ANGLETOFINESHIFT; - const fixed_t fc = FixedMul(FINECOSINE(fa),radius); - const fixed_t fs = FixedMul(FINESINE(fa),radius); - actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x + fc, actor->target->y + fs); - } - - // Now turn you around! - // Note that the start position is the final position, we move it back around - // to intermediary positions... - actor->angle -= FixedAngle(FixedMul(FixedDiv(180<extravalue2<extravalue1<flags2 & MF2_AMBUSH) - speedvar = actor->health; - else - speedvar = actor->info->spawnhealth; - - actor->target->angle += // Don't use FixedAngleC! - FixedAngle(FixedDiv(FixedMul(actor->watertop, (actor->info->spawnhealth*(FRACUNIT/4)*3)), speedvar*FRACUNIT)); - - P_UnsetThingPosition(actor); - { - const angle_t fa = actor->target->angle>>ANGLETOFINESHIFT; - const fixed_t fc = FixedMul(FINECOSINE(fa),radius); - const fixed_t fs = FixedMul(FINESINE(fa),radius); - actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x + fc, actor->target->y + fs); - actor->x = actor->target->x + fc; - actor->y = actor->target->y + fs; - } - P_SetThingPosition(actor); - - // Spray goo once every second - if (leveltime % (speedvar*15/10)-1 == 0) - { - const fixed_t ns = FixedMul(3 * FRACUNIT, actor->scale); - mobj_t *goop; - fixed_t fz = actor->z+actor->height+FixedMul(24*FRACUNIT, actor->scale); - angle_t fa; - // actor->movedir is used to determine the last - // direction goo was sprayed in. There are 8 possible - // directions to spray. (45-degree increments) - - actor->movedir++; - actor->movedir %= NUMDIRS; - fa = (actor->movedir*FINEANGLES/8) & FINEMASK; - - goop = P_SpawnMobj(actor->x, actor->y, fz, actor->info->painchance); - goop->momx = FixedMul(FINECOSINE(fa),ns); - goop->momy = FixedMul(FINESINE(fa),ns); - goop->momz = FixedMul(4*FRACUNIT, actor->scale); - goop->fuse = 10*TICRATE; - - if (actor->info->attacksound) - S_StartAttackSound(actor, actor->info->attacksound); - - if (P_RandomChance(FRACUNIT/2)) - { - goop->momx *= 2; - goop->momy *= 2; - } - else if (P_RandomChance(129*FRACUNIT/256)) - { - goop->momx *= 3; - goop->momy *= 3; - } - - actor->flags2 |= MF2_JUSTATTACKED; - } - } -} - -// Function: A_Boss2Pogo -// -// Description: Pogo part of Boss 2 AI. -// -// var1 = unused -// var2 = unused -// -void A_Boss2Pogo(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Boss2Pogo", actor)) - return; -#endif - if (actor->z <= actor->floorz + FixedMul(8*FRACUNIT, actor->scale) && actor->momz <= 0) - { - if (actor->state != &states[actor->info->raisestate]) - P_SetMobjState(actor, actor->info->raisestate); - // Pogo Mode - } - else if (actor->momz < 0 && actor->reactiontime) - { - const fixed_t ns = FixedMul(3 * FRACUNIT, actor->scale); - mobj_t *goop; - fixed_t fz = actor->z+actor->height+FixedMul(24*FRACUNIT, actor->scale); - angle_t fa; - INT32 i; - // spray in all 8 directions! - for (i = 0; i < 8; i++) - { - actor->movedir++; - actor->movedir %= NUMDIRS; - fa = (actor->movedir*FINEANGLES/8) & FINEMASK; - - goop = P_SpawnMobj(actor->x, actor->y, fz, actor->info->painchance); - goop->momx = FixedMul(FINECOSINE(fa),ns); - goop->momy = FixedMul(FINESINE(fa),ns); - goop->momz = FixedMul(4*FRACUNIT, actor->scale); - - goop->fuse = 10*TICRATE; - } - actor->reactiontime = 0; // we already shot goop, so don't do it again! - if (actor->info->attacksound) - S_StartAttackSound(actor, actor->info->attacksound); - actor->flags2 |= MF2_JUSTATTACKED; - } -} - -// Function: A_Boss2TakeDamage -// -// Description: Special function for Boss 2 so you can't just sit and destroy him. -// -// var1 = Invincibility duration -// var2 = unused -// -void A_Boss2TakeDamage(mobj_t *actor) -{ - INT32 locvar1 = var1; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Boss2TakeDamage", actor)) - return; -#endif - A_Pain(actor); - actor->reactiontime = 1; // turn around - if (locvar1 == 0) // old A_Invincibilerize behavior - actor->movecount = TICRATE; - else - actor->movecount = locvar1; // become flashing invulnerable for this long. -} - -// Function: A_Boss7Chase -// -// Description: Like A_Chase, but for Black Eggman -// -// var1 = unused -// var2 = unused -// -void A_Boss7Chase(mobj_t *actor) -{ - INT32 delta; - INT32 i; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Boss7Chase", actor)) - return; -#endif - - if (actor->z != actor->floorz) - return; - - // Self-adjust if stuck on the edge - if (actor->tracer) - { - if (P_AproxDistance(actor->x - actor->tracer->x, actor->y - actor->tracer->y) > 128*FRACUNIT - actor->radius) - P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y), FRACUNIT); - } - - if (actor->flags2 & MF2_FRET) - { - P_SetMobjState(actor, S_BLACKEGG_DESTROYPLAT1); - S_StartSound(0, sfx_s3k53); - actor->flags2 &= ~MF2_FRET; - return; - } - - // turn towards movement direction if not there yet - if (actor->movedir < NUMDIRS) - { - actor->angle &= (7<<29); - delta = actor->angle - (actor->movedir << 29); - - if (delta > 0) - actor->angle -= ANGLE_45; - else if (delta < 0) - actor->angle += ANGLE_45; - } - - // Is a player on top of us? - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - - if (!players[i].mo) - continue; - - if (players[i].mo->health <= 0) - continue; - - if (P_AproxDistance(players[i].mo->x - actor->x, players[i].mo->y - actor->y) > actor->radius) - continue; - - if (players[i].mo->z > actor->z + actor->height - 2*FRACUNIT - && players[i].mo->z < actor->z + actor->height + 32*FRACUNIT) - { - // Punch him! - P_SetMobjState(actor, actor->info->meleestate); - S_StartSound(0, sfx_begrnd); // warning sound - return; - } - } - - if (actor->health <= actor->info->damage - && actor->target - && actor->target->player - && (actor->target->player->powers[pw_carry] == CR_GENERIC)) - { - A_FaceTarget(actor); - P_SetMobjState(actor, S_BLACKEGG_SHOOT1); - actor->movecount = TICRATE + P_RandomByte()/2; - return; - } - - if (actor->reactiontime) - actor->reactiontime--; - - if (actor->reactiontime <= 0 && actor->z == actor->floorz) - { - // Here, we'll call P_RandomByte() and decide what kind of attack to do - switch(actor->threshold) - { - case 0: // Lob cannon balls - if (actor->z < 1056*FRACUNIT) - { - A_FaceTarget(actor); - P_SetMobjState(actor, actor->info->xdeathstate); - actor->movecount = 7*TICRATE + P_RandomByte(); - break; - } - actor->threshold++; - /* FALLTHRU */ - case 1: // Chaingun Goop - A_FaceTarget(actor); - P_SetMobjState(actor, S_BLACKEGG_SHOOT1); - - if (actor->health > actor->info->damage) - actor->movecount = TICRATE + P_RandomByte()/3; - else - actor->movecount = TICRATE + P_RandomByte()/2; - break; - case 2: // Homing Missile - A_FaceTarget(actor); - P_SetMobjState(actor, actor->info->missilestate); - S_StartSound(0, sfx_beflap); - break; - } - - actor->threshold++; - actor->threshold %= 3; - return; - } - - // possibly choose another target - if (multiplayer && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)) - && P_BossTargetPlayer(actor, false)) - return; // got a new target - - if (leveltime & 1) - { - // chase towards player - if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed)) - P_NewChaseDir(actor); - } -} - -// Function: A_GoopSplat -// -// Description: Black Eggman goop hits a target and sticks around for awhile. -// -// var1 = unused -// var2 = unused -// -void A_GoopSplat(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_GoopSplat", actor)) - return; -#endif - P_UnsetThingPosition(actor); - if (sector_list) - { - P_DelSeclist(sector_list); - sector_list = NULL; - } - actor->flags = MF_SPECIAL; // Not a typo - P_SetThingPosition(actor); -} - -// Function: A_Boss2PogoSFX -// -// Description: Pogoing for Boss 2 -// -// var1 = pogo jump strength -// var2 = idle pogo speed -// -void A_Boss2PogoSFX(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Boss2PogoSFX", actor)) - return; -#endif - - if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) - { - // look for a new target - if (P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - return; - } - - // Boing! - if (P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) < FixedMul(256*FRACUNIT, actor->scale)) - { - actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); - P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed, actor->scale)); - // pogo on player - } - else - { - UINT8 prandom = P_RandomByte(); - actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); - P_InstaThrust(actor, actor->angle, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale)); - } - if (actor->info->activesound) S_StartSound(actor, actor->info->activesound); - actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air - actor->reactiontime = 1; -} - -// Function: A_Boss2PogoTarget -// -// Description: Pogoing for Boss 2, tries to actually land on the player directly. -// -// var1 = pogo jump strength -// var2 = idle pogo speed -// -void A_Boss2PogoTarget(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Boss2PogoTarget", actor)) - return; -#endif - - if (!actor->target || !(actor->target->flags & MF_SHOOTABLE) || (actor->target->player && actor->target->player->powers[pw_flashing]) - || P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) >= FixedMul(512*FRACUNIT, actor->scale)) - { - // look for a new target - if (P_LookForPlayers(actor, true, false, 512*FRACUNIT)) - ; // got a new target - else if (P_LookForPlayers(actor, true, false, 0)) - ; // got a new target - else - return; - } - - // Target hit, retreat! - if (actor->target->player->powers[pw_flashing] > TICRATE || actor->flags2 & MF2_FRET) - { - UINT8 prandom = P_RandomByte(); - actor->z++; // unstick from the floor - actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air - actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); // Pick a direction, and randomize it. - P_InstaThrust(actor, actor->angle+ANGLE_180, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale)); // Move at wandering speed - } - // Try to land on top of the player. - else if (P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) < FixedMul(512*FRACUNIT, actor->scale)) - { - fixed_t airtime, gravityadd, zoffs; - - // check gravity in the sector (for later math) - P_CheckGravity(actor, true); - gravityadd = actor->momz; - - actor->z++; // unstick from the floor - actor->momz = FixedMul(locvar1 + (locvar1>>2), actor->scale); // Bounce up in air - - /*badmath = 0; - airtime = 0; - do { - badmath += momz; - momz += gravityadd; - airtime++; - } while(badmath > 0); - airtime = 2*airtime<momz<<1, gravityadd)<<1; // going from 0 to 0 is much simpler - zoffs = (P_GetPlayerHeight(actor->target->player)>>1) + (actor->target->floorz - actor->floorz); // offset by the difference in floor height plus half the player height, - airtime = FixedDiv((-actor->momz - FixedSqrt(FixedMul(actor->momz,actor->momz)+zoffs)), gravityadd)<<1; // to try and land on their head rather than on their feet - - actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); - P_InstaThrust(actor, actor->angle, FixedDiv(P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y), airtime)); - } - // Wander semi-randomly towards the player to get closer. - else - { - UINT8 prandom = P_RandomByte(); - actor->z++; // unstick from the floor - actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air - actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); // Pick a direction, and randomize it. - P_InstaThrust(actor, actor->angle, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale)); // Move at wandering speed - } - // Boing! - if (actor->info->activesound) S_StartSound(actor, actor->info->activesound); - - if (actor->info->missilestate) // spawn the pogo stick collision box - { - mobj_t *pogo = P_SpawnMobj(actor->x, actor->y, actor->z - mobjinfo[actor->info->missilestate].height, (mobjtype_t)actor->info->missilestate); - pogo->target = actor; - } - - actor->reactiontime = 1; -} - -// Function: A_EggmanBox -// -// Description: Harms the player -// -// var1 = unused -// var2 = unused -// -void A_EggmanBox(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_EggmanBox", actor)) - return; -#endif - if (!actor->target || !actor->target->player) - { - CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); - return; - } - - P_DamageMobj(actor->target, actor, actor, 1, 0); // Ow! -} - -// Function: A_TurretFire -// -// Description: Initiates turret fire. -// -// var1 = object # to repeatedly fire -// var2 = distance threshold -// -void A_TurretFire(mobj_t *actor) -{ - INT32 count = 0; - fixed_t dist; - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_TurretFire", actor)) - return; -#endif - - if (locvar2) - dist = FixedMul(locvar2*FRACUNIT, actor->scale); - else - dist = FixedMul(2048*FRACUNIT, actor->scale); - - if (!locvar1) - locvar1 = MT_TURRETLASER; - - while (P_SupermanLook4Players(actor) && count < MAXPLAYERS) - { - if (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) < dist) - { - actor->flags2 |= MF2_FIRING; - actor->extravalue1 = locvar1; - break; - } - - count++; - } -} - -// Function: A_SuperTurretFire -// -// Description: Initiates turret fire that even stops Super Sonic. -// -// var1 = object # to repeatedly fire -// var2 = distance threshold -// -void A_SuperTurretFire(mobj_t *actor) -{ - INT32 count = 0; - fixed_t dist; - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SuperTurretFire", actor)) - return; -#endif - - if (locvar2) - dist = FixedMul(locvar2*FRACUNIT, actor->scale); - else - dist = FixedMul(2048*FRACUNIT, actor->scale); - - if (!locvar1) - locvar1 = MT_TURRETLASER; - - while (P_SupermanLook4Players(actor) && count < MAXPLAYERS) - { - if (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) < dist) - { - actor->flags2 |= MF2_FIRING; - actor->flags2 |= MF2_SUPERFIRE; - actor->extravalue1 = locvar1; - break; - } - - count++; - } -} - -// Function: A_TurretStop -// -// Description: Stops the turret fire. -// -// var1 = Don't play activesound? -// var2 = unused -// -void A_TurretStop(mobj_t *actor) -{ - INT32 locvar1 = var1; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_TurretStop", actor)) - return; -#endif - - actor->flags2 &= ~MF2_FIRING; - actor->flags2 &= ~MF2_SUPERFIRE; - - if (actor->target && actor->info->activesound && !locvar1) - S_StartSound(actor, actor->info->activesound); -} - -// Function: A_SparkFollow -// -// Description: Used by the hyper sparks to rotate around their target. -// -// var1 = unused -// var2 = unused -// -void A_SparkFollow(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SparkFollow", actor)) - return; -#endif - - if ((!actor->target || (actor->target->health <= 0)) - || (actor->target->player && !actor->target->player->powers[pw_super])) - { - P_RemoveMobj(actor); - return; - } - - actor->angle += FixedAngle(actor->info->damage*FRACUNIT); - P_UnsetThingPosition(actor); - { - const angle_t fa = actor->angle>>ANGLETOFINESHIFT; - actor->x = actor->target->x + FixedMul(FINECOSINE(fa),FixedMul(actor->info->speed, actor->scale)); - actor->y = actor->target->y + FixedMul(FINESINE(fa),FixedMul(actor->info->speed, actor->scale)); - if (actor->target->eflags & MFE_VERTICALFLIP) - actor->z = actor->target->z + actor->target->height - FixedDiv(actor->target->height,3*FRACUNIT); - else - actor->z = actor->target->z + FixedDiv(actor->target->height,3*FRACUNIT) - actor->height; - } - P_SetThingPosition(actor); -} - -// Function: A_BuzzFly -// -// Description: Makes an object slowly fly after a player, in the manner of a Buzz. -// -// var1 = sfx to play -// var2 = length of sfx, set to threshold if played -// -void A_BuzzFly(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_BuzzFly", actor)) - return; -#endif - if (actor->flags2 & MF2_AMBUSH) - return; - - if (actor->reactiontime) - actor->reactiontime--; - - // modify target threshold - if (actor->threshold) - { - if (!actor->target || actor->target->health <= 0) - actor->threshold = 0; - else - actor->threshold--; - } - - if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) - { - // look for a new target - if (P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - actor->momz = actor->momy = actor->momx = 0; - P_SetMobjState(actor, actor->info->spawnstate); - return; - } - - // turn towards movement direction if not there yet - actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); - - if (actor->target->health <= 0 || (!actor->threshold && !P_CheckSight(actor, actor->target))) - { - if ((multiplayer || netgame) && P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale))) - return; // got a new target - - actor->momx = actor->momy = actor->momz = 0; - P_SetMobjState(actor, actor->info->spawnstate); // Go back to looking around - return; - } - - // If the player is over 3072 fracunits away, then look for another player - if (P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), - actor->target->z - actor->z) > FixedMul(3072*FRACUNIT, actor->scale)) - { - if (multiplayer || netgame) - P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)); // maybe get a new target - - return; - } - - // chase towards player - { - INT32 dist, realspeed; - const fixed_t mf = 5*(FRACUNIT/4); - - if (ultimatemode) - realspeed = FixedMul(FixedMul(actor->info->speed,mf), actor->scale); - else - realspeed = FixedMul(actor->info->speed, actor->scale); - - dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, - actor->target->y - actor->y), actor->target->z - actor->z); - - if (dist < 1) - dist = 1; - - actor->momx = FixedMul(FixedDiv(actor->target->x - actor->x, dist), realspeed); - actor->momy = FixedMul(FixedDiv(actor->target->y - actor->y, dist), realspeed); - actor->momz = FixedMul(FixedDiv(actor->target->z - actor->z, dist), realspeed); - - if (actor->z+actor->momz >= actor->waterbottom && actor->watertop > actor->floorz - && actor->z+actor->momz > actor->watertop - FixedMul(256*FRACUNIT, actor->scale) - && actor->z+actor->momz <= actor->watertop) - { - actor->momz = 0; - actor->z = actor->watertop; - } - } - - if (locvar1 != sfx_None && !actor->threshold) - { - S_StartSound(actor, locvar1); - actor->threshold = locvar2; - } -} - -// Function: A_GuardChase -// -// Description: Modified A_Chase for Egg Guard -// -// var1 = unused -// var2 = unused -// -void A_GuardChase(mobj_t *actor) -{ - INT32 delta; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_GuardChase", actor)) - return; -#endif - - if (actor->reactiontime) - actor->reactiontime--; - - if (actor->threshold != 42) // In formation... - { - fixed_t speed; - - if (!actor->tracer || !actor->tracer->health) - { - P_SetTarget(&actor->tracer, NULL); - actor->threshold = 42; - P_SetMobjState(actor, actor->info->painstate); - actor->flags |= MF_SPECIAL|MF_SHOOTABLE; - return; - } - - speed = actor->extravalue1*actor->scale; - - if (actor->flags2 & MF2_AMBUSH) - speed <<= 1; - - if (speed - && !P_TryMove(actor, - actor->x + P_ReturnThrustX(actor, actor->angle, speed), - actor->y + P_ReturnThrustY(actor, actor->angle, speed), - false) - && speed > 0) // can't be the same check as previous so that P_TryMove gets to happen. - { - if (actor->spawnpoint && ((actor->spawnpoint->options & (MTF_EXTRA|MTF_OBJECTSPECIAL)) == MTF_OBJECTSPECIAL)) - actor->angle += ANGLE_90; - else if (actor->spawnpoint && ((actor->spawnpoint->options & (MTF_EXTRA|MTF_OBJECTSPECIAL)) == MTF_EXTRA)) - actor->angle -= ANGLE_90; - else - actor->angle += ANGLE_180; - } - - if (actor->extravalue1 < actor->info->speed) - actor->extravalue1++; - } - else // Break ranks! - { - // turn towards movement direction if not there yet - if (actor->movedir < NUMDIRS) - { - actor->angle &= (7<<29); - delta = actor->angle - (actor->movedir << 29); - - if (delta > 0) - actor->angle -= ANGLE_45; - else if (delta < 0) - actor->angle += ANGLE_45; - } - - if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) - { - // look for a new target - if (P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - P_SetMobjStateNF(actor, actor->info->spawnstate); - return; - } - - // possibly choose another target - if (multiplayer && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)) - && P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - // chase towards player - if (--actor->movecount < 0 || !P_Move(actor, (actor->flags2 & MF2_AMBUSH) ? actor->info->speed * 2 : actor->info->speed)) - { - P_NewChaseDir(actor); - actor->movecount += 5; // Increase tics before change in direction allowed. - } - } - - // Now that we've moved, its time for our shield to move! - // Otherwise it'll never act as a proper overlay. - if (actor->tracer && actor->tracer->state - && actor->tracer->state->action.acp1) - { - var1 = actor->tracer->state->var1, var2 = actor->tracer->state->var2; - actor->tracer->state->action.acp1(actor->tracer); - } -} - -// Function: A_EggShield -// -// Description: Modified A_Chase for Egg Guard's shield -// -// var1 = unused -// var2 = unused -// -void A_EggShield(mobj_t *actor) -{ - INT32 i; - player_t *player; - fixed_t blockdist; - fixed_t newx, newy; - fixed_t movex, movey; - angle_t angle; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_EggShield", actor)) - return; -#endif - - if (!actor->target || !actor->target->health) - { - P_RemoveMobj(actor); - return; - } - - newx = actor->target->x + P_ReturnThrustX(actor, actor->target->angle, FixedMul(FRACUNIT, actor->scale)); - newy = actor->target->y + P_ReturnThrustY(actor, actor->target->angle, FixedMul(FRACUNIT, actor->scale)); - - movex = newx - actor->x; - movey = newy - actor->y; - - actor->angle = actor->target->angle; - if (actor->target->eflags & MFE_VERTICALFLIP) - { - actor->eflags |= MFE_VERTICALFLIP; - actor->z = actor->target->z + actor->target->height - actor->height; - } - else - actor->z = actor->target->z; - - actor->destscale = actor->target->destscale; - P_SetScale(actor, actor->target->scale); - - actor->floorz = actor->target->floorz; - actor->ceilingz = actor->target->ceilingz; - - if (!movex && !movey) - return; - - P_UnsetThingPosition(actor); - actor->x = newx; - actor->y = newy; - P_SetThingPosition(actor); - - // Search for players to push - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - - player = &players[i]; - - if (!player->mo) - continue; - - if (player->mo->z > actor->z + actor->height) - continue; - - if (player->mo->z + player->mo->height < actor->z) - continue; - - blockdist = actor->radius + player->mo->radius; - - if (abs(actor->x - player->mo->x) >= blockdist || abs(actor->y - player->mo->y) >= blockdist) - continue; // didn't hit it - - angle = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle; - - if (angle > ANGLE_90 && angle < ANGLE_270) - continue; - - // Blocked by the shield - player->mo->momx += movex; - player->mo->momy += movey; - return; - } -} - - -// Function: A_SetReactionTime -// -// Description: Sets the object's reaction time. -// -// var1 = 1 (use value in var2); 0 (use info table value) -// var2 = if var1 = 1, then value to set -// -void A_SetReactionTime(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SetReactionTime", actor)) - return; -#endif - if (var1) - actor->reactiontime = var2; - else - actor->reactiontime = actor->info->reactiontime; -} - -// Function: A_Boss1Spikeballs -// -// Description: Boss 1 spikeball spawning loop. -// -// var1 = ball number -// var2 = total balls -// -void A_Boss1Spikeballs(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - mobj_t *ball; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Boss1Spikeballs", actor)) - return; -#endif - - ball = P_SpawnMobj(actor->x, actor->y, actor->z, MT_EGGMOBILE_BALL); - P_SetTarget(&ball->target, actor); - ball->movedir = FixedAngle(FixedMul(FixedDiv(locvar1<threshold = ball->radius + actor->radius + ball->info->painchance; - - S_StartSound(ball, ball->info->seesound); - var1 = ball->state->var1, var2 = ball->state->var2; - ball->state->action.acp1(ball); -} - -// Function: A_Boss3TakeDamage -// -// Description: Called when Boss 3 takes damage. -// -// var1 = movecount value -// var2 = unused -// -void A_Boss3TakeDamage(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Boss3TakeDamage", actor)) - return; -#endif - actor->movecount = var1; - - if (actor->target && actor->target->spawnpoint) - actor->threshold = actor->target->spawnpoint->extrainfo; -} - -// Function: A_Boss3Path -// -// Description: Does pathfinding along Boss 3's nodes. -// -// var1 = unused -// var2 = unused -// -void A_Boss3Path(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Boss3Path", actor)) - return; -#endif - - if (actor->tracer && actor->tracer->health && actor->tracer->movecount) - actor->movecount |= 1; - else if (actor->movecount & 1) - actor->movecount = 0; - - if (actor->movecount & 2) // We've reached a firing point? - { - // Wait here and pretend to be angry or something. - actor->momx = 0; - actor->momy = 0; - actor->momz = 0; - P_SetTarget(&actor->target, actor->tracer->target); - var1 = 0, var2 = 0; - A_FaceTarget(actor); - if (actor->tracer->state == &states[actor->tracer->info->missilestate]) - P_SetMobjState(actor, actor->info->missilestate); - return; - } - else if (actor->threshold >= 0) // Traveling mode - { - thinker_t *th; - mobj_t *mo2; - fixed_t dist, dist2; - fixed_t speed; - - P_SetTarget(&actor->target, NULL); - - // scan the thinkers - // to find a point that matches - // the number - for (th = thinkercap.next; th != &thinkercap; th = th->next) - { - if (th->function.acp1 != (actionf_p1)P_MobjThinker) - continue; - - mo2 = (mobj_t *)th; - if (mo2->type == MT_BOSS3WAYPOINT && mo2->spawnpoint && mo2->spawnpoint->angle == actor->threshold) - { - P_SetTarget(&actor->target, mo2); - break; - } - } - - if (!actor->target) // Should NEVER happen - { - CONS_Debug(DBG_GAMELOGIC, "Error: Boss 3 Dummy was unable to find specified waypoint: %d\n", actor->threshold); - return; - } - - dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), actor->target->z - actor->z); - - if (dist < 1) - dist = 1; - - if (actor->tracer && ((actor->tracer->movedir) - || (actor->tracer->health <= actor->tracer->info->damage))) - speed = actor->info->speed * 2; - else - speed = actor->info->speed; - - actor->momx = FixedMul(FixedDiv(actor->target->x - actor->x, dist), speed); - actor->momy = FixedMul(FixedDiv(actor->target->y - actor->y, dist), speed); - actor->momz = FixedMul(FixedDiv(actor->target->z - actor->z, dist), speed); - - if (actor->momx != 0 || actor->momy != 0) - actor->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy); - - dist2 = P_AproxDistance(P_AproxDistance(actor->target->x - (actor->x + actor->momx), actor->target->y - (actor->y + actor->momy)), actor->target->z - (actor->z + actor->momz)); - - if (dist2 < 1) - dist2 = 1; - - if ((dist >> FRACBITS) <= (dist2 >> FRACBITS)) - { - // If further away, set XYZ of mobj to waypoint location - P_UnsetThingPosition(actor); - actor->x = actor->target->x; - actor->y = actor->target->y; - actor->z = actor->target->z; - actor->momx = actor->momy = actor->momz = 0; - P_SetThingPosition(actor); - - if (actor->threshold == 0) - { - P_RemoveMobj(actor); // Cycle completed. Dummy removed. - return; - } - - // Set to next waypoint in sequence - if (actor->target->spawnpoint) - { - // From the center point, choose one of the five paths - if (actor->target->spawnpoint->angle == 0) - { - P_RemoveMobj(actor); // Cycle completed. Dummy removed. - return; - } - else - actor->threshold = actor->target->spawnpoint->extrainfo; - - // If the deaf flag is set, go into firing mode - if (actor->target->spawnpoint->options & MTF_AMBUSH) - actor->movecount |= 2; - } - else // This should never happen, as well - CONS_Debug(DBG_GAMELOGIC, "Error: Boss 3 Dummy waypoint has no spawnpoint associated with it.\n"); - } - } -} - -// Function: A_LinedefExecute -// -// Description: Object's location is used to set the calling sector. The tag used is var1. Optionally, if var2 is set, the actor's angle (multiplied by var2) is added to the tag number as well. -// -// var1 = tag -// var2 = add angle to tag (optional) -// -void A_LinedefExecute(mobj_t *actor) -{ - INT32 tagnum; - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_LinedefExecute", actor)) - return; -#endif - - tagnum = locvar1; - // state numbers option is no more, custom states cannot be guaranteed to stay the same number anymore, now that they can be defined by names instead - - if (locvar2) - tagnum += locvar2*(AngleFixed(actor->angle)>>FRACBITS); - - CONS_Debug(DBG_GAMELOGIC, "A_LinedefExecute: Running mobjtype %d's sector with tag %d\n", actor->type, tagnum); - - // tag 32768 displayed in map editors is actually tag -32768, tag 32769 is -32767, 65535 is -1 etc. - P_LinedefExecute((INT16)tagnum, actor, actor->subsector->sector); -} - -// Function: A_PlaySeeSound -// -// Description: Plays the object's seesound. -// -// var1 = unused -// var2 = unused -// -void A_PlaySeeSound(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_PlaySeeSound", actor)) - return; -#endif - if (actor->info->seesound) - S_StartScreamSound(actor, actor->info->seesound); -} - -// Function: A_PlayAttackSound -// -// Description: Plays the object's attacksound. -// -// var1 = unused -// var2 = unused -// -void A_PlayAttackSound(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_PlayAttackSound", actor)) - return; -#endif - if (actor->info->attacksound) - S_StartAttackSound(actor, actor->info->attacksound); -} - -// Function: A_PlayActiveSound -// -// Description: Plays the object's activesound. -// -// var1 = unused -// var2 = unused -// -void A_PlayActiveSound(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_PlayActiveSound", actor)) - return; -#endif - if (actor->info->activesound) - S_StartSound(actor, actor->info->activesound); -} - -// Function: A_SmokeTrailer -// -// Description: Adds smoke trails to an object. -// -// var1 = object # to spawn as smoke -// var2 = unused -// -void A_SmokeTrailer(mobj_t *actor) -{ - mobj_t *th; - INT32 locvar1 = var1; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SmokeTrailer", actor)) - return; -#endif - - if (leveltime % 4) - return; - - // add the smoke behind the rocket - if (actor->eflags & MFE_VERTICALFLIP) - { - th = P_SpawnMobj(actor->x-actor->momx, actor->y-actor->momy, actor->z + actor->height - FixedMul(mobjinfo[locvar1].height, actor->scale), locvar1); - th->flags2 |= MF2_OBJECTFLIP; - } - else - th = P_SpawnMobj(actor->x-actor->momx, actor->y-actor->momy, actor->z, locvar1); - P_SetObjectMomZ(th, FRACUNIT, false); - th->destscale = actor->scale; - P_SetScale(th, actor->scale); - th->tics -= P_RandomByte() & 3; - if (th->tics < 1) - th->tics = 1; -} - -// Function: A_SpawnObjectAbsolute -// -// Description: Spawns an object at an absolute position -// -// var1: -// var1 >> 16 = x -// var1 & 65535 = y -// var2: -// var2 >> 16 = z -// var2 & 65535 = type -// -void A_SpawnObjectAbsolute(mobj_t *actor) -{ - INT16 x, y, z; // Want to be sure we can use negative values - mobjtype_t type; - mobj_t *mo; - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SpawnObjectAbsolute", actor)) - return; -#endif - - x = (INT16)(locvar1>>16); - y = (INT16)(locvar1&65535); - z = (INT16)(locvar2>>16); - type = (mobjtype_t)(locvar2&65535); - - mo = P_SpawnMobj(x<angle = actor->angle; - - if (actor->eflags & MFE_VERTICALFLIP) - mo->flags2 |= MF2_OBJECTFLIP; -} - -// Function: A_SpawnObjectRelative -// -// Description: Spawns an object relative to the location of the actor -// -// var1: -// var1 >> 16 = x -// var1 & 65535 = y -// var2: -// var2 >> 16 = z -// var2 & 65535 = type -// -void A_SpawnObjectRelative(mobj_t *actor) -{ - INT16 x, y, z; // Want to be sure we can use negative values - mobjtype_t type; - mobj_t *mo; - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SpawnObjectRelative", actor)) - return; -#endif - - CONS_Debug(DBG_GAMELOGIC, "A_SpawnObjectRelative called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); - - x = (INT16)(locvar1>>16); - y = (INT16)(locvar1&65535); - z = (INT16)(locvar2>>16); - type = (mobjtype_t)(locvar2&65535); - - // Spawn objects correctly in reverse gravity. - // NOTE: Doing actor->z + actor->height is the bottom of the object while the object has reverse gravity. - Flame - mo = P_SpawnMobj(actor->x + FixedMul(x<scale), - actor->y + FixedMul(y<scale), - (actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[type].height) - FixedMul(z<scale)) : (actor->z + FixedMul(z<scale)), type); - - // Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn - mo->angle = actor->angle; - - if (actor->eflags & MFE_VERTICALFLIP) - mo->flags2 |= MF2_OBJECTFLIP; - -} - -// Function: A_ChangeAngleRelative -// -// Description: Changes the angle to a random relative value between the min and max. Set min and max to the same value to eliminate randomness -// -// var1 = min -// var2 = max -// -void A_ChangeAngleRelative(mobj_t *actor) -{ - // Oh god, the old code /sucked/. Changed this and the absolute version to get a random range using amin and amax instead of - // getting a random angle from the _entire_ spectrum and then clipping. While we're at it, do the angle conversion to the result - // rather than the ranges, so <0 and >360 work as possible values. -Red - INT32 locvar1 = var1; - INT32 locvar2 = var2; - //angle_t angle = (P_RandomByte()+1)<<24; - const fixed_t amin = locvar1*FRACUNIT; - const fixed_t amax = locvar2*FRACUNIT; - //const angle_t amin = FixedAngle(locvar1*FRACUNIT); - //const angle_t amax = FixedAngle(locvar2*FRACUNIT); -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ChangeAngleRelative", actor)) - return; -#endif - -#ifdef PARANOIA - if (amin > amax) - I_Error("A_ChangeAngleRelative: var1 is greater then var2"); -#endif -/* - if (angle < amin) - angle = amin; - if (angle > amax) - angle = amax;*/ - - actor->angle += FixedAngle(P_RandomRange(amin, amax)); -} - -// Function: A_ChangeAngleAbsolute -// -// Description: Changes the angle to a random absolute value between the min and max. Set min and max to the same value to eliminate randomness -// -// var1 = min -// var2 = max -// -void A_ChangeAngleAbsolute(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - //angle_t angle = (P_RandomByte()+1)<<24; - const fixed_t amin = locvar1*FRACUNIT; - const fixed_t amax = locvar2*FRACUNIT; - //const angle_t amin = FixedAngle(locvar1*FRACUNIT); - //const angle_t amax = FixedAngle(locvar2*FRACUNIT); -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ChangeAngleAbsolute", actor)) - return; -#endif - -#ifdef PARANOIA - if (amin > amax) - I_Error("A_ChangeAngleAbsolute: var1 is greater then var2"); -#endif -/* - if (angle < amin) - angle = amin; - if (angle > amax) - angle = amax;*/ - - actor->angle = FixedAngle(P_RandomRange(amin, amax)); -} - -// Function: A_PlaySound -// -// Description: Plays a sound -// -// var1 = sound # to play -// var2: -// 0 = Play sound without an origin -// 1 = Play sound using calling object as origin -// -void A_PlaySound(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_PlaySound", actor)) - return; -#endif - - S_StartSound(locvar2 ? actor : NULL, locvar1); -} - -// Function: A_FindTarget -// -// Description: Finds the nearest/furthest mobj of the specified type and sets actor->target to it. -// -// var1 = mobj type -// var2 = if (0) nearest; else furthest; -// -void A_FindTarget(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - mobj_t *targetedmobj = NULL; - thinker_t *th; - mobj_t *mo2; - fixed_t dist1 = 0, dist2 = 0; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FindTarget", actor)) - return; -#endif - - CONS_Debug(DBG_GAMELOGIC, "A_FindTarget called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); - - // scan the thinkers - for (th = thinkercap.next; th != &thinkercap; th = th->next) - { - if (th->function.acp1 != (actionf_p1)P_MobjThinker) - continue; - - mo2 = (mobj_t *)th; - - if (mo2->type == (mobjtype_t)locvar1) - { - if (mo2->player && (mo2->player->spectator || mo2->player->pflags & PF_INVIS)) - continue; // Ignore spectators - if ((mo2->player || mo2->flags & MF_ENEMY) && mo2->health <= 0) - continue; // Ignore dead things - if (targetedmobj == NULL) - { - targetedmobj = mo2; - dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y); - } - else - { - dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y); - - if ((!locvar2 && dist1 < dist2) || (locvar2 && dist1 > dist2)) - { - targetedmobj = mo2; - dist2 = dist1; - } - } - } - } - - if (!targetedmobj) - { - CONS_Debug(DBG_GAMELOGIC, "A_FindTarget: Unable to find the specified object to target.\n"); - return; // Oops, nothing found.. - } - - CONS_Debug(DBG_GAMELOGIC, "A_FindTarget: Found a target.\n"); - - P_SetTarget(&actor->target, targetedmobj); -} - -// Function: A_FindTracer -// -// Description: Finds the nearest/furthest mobj of the specified type and sets actor->tracer to it. -// -// var1 = mobj type -// var2 = if (0) nearest; else furthest; -// -void A_FindTracer(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - mobj_t *targetedmobj = NULL; - thinker_t *th; - mobj_t *mo2; - fixed_t dist1 = 0, dist2 = 0; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FindTracer", actor)) - return; -#endif - - CONS_Debug(DBG_GAMELOGIC, "A_FindTracer called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); - - // scan the thinkers - for (th = thinkercap.next; th != &thinkercap; th = th->next) - { - if (th->function.acp1 != (actionf_p1)P_MobjThinker) - continue; - - mo2 = (mobj_t *)th; - - if (mo2->type == (mobjtype_t)locvar1) - { - if (mo2->player && (mo2->player->spectator || mo2->player->pflags & PF_INVIS)) - continue; // Ignore spectators - if ((mo2->player || mo2->flags & MF_ENEMY) && mo2->health <= 0) - continue; // Ignore dead things - if (targetedmobj == NULL) - { - targetedmobj = mo2; - dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y); - } - else - { - dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y); - - if ((!locvar2 && dist1 < dist2) || (locvar2 && dist1 > dist2)) - { - targetedmobj = mo2; - dist2 = dist1; - } - } - } - } - - if (!targetedmobj) - { - CONS_Debug(DBG_GAMELOGIC, "A_FindTracer: Unable to find the specified object to target.\n"); - return; // Oops, nothing found.. - } - - CONS_Debug(DBG_GAMELOGIC, "A_FindTracer: Found a target.\n"); - - P_SetTarget(&actor->tracer, targetedmobj); -} - -// Function: A_SetTics -// -// Description: Sets the animation tics of an object -// -// var1 = tics to set to -// var2 = if this is set, and no var1 is supplied, the mobj's threshold value will be used. -// -void A_SetTics(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SetTics", actor)) - return; -#endif - - if (locvar1) - actor->tics = locvar1; - else if (locvar2) - actor->tics = actor->threshold; -} - -// Function: A_SetRandomTics -// -// Description: Sets the animation tics of an object to a random value -// -// var1 = lower bound -// var2 = upper bound -// -void A_SetRandomTics(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SetRandomTics", actor)) - return; -#endif - - actor->tics = P_RandomRange(locvar1, locvar2); -} - -// Function: A_ChangeColorRelative -// -// Description: Changes the color of an object -// -// var1 = if (var1 > 0), find target and add its color value to yours -// var2 = if (var1 = 0), color value to add -// -void A_ChangeColorRelative(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ChangeColorRelative", actor)) - return; -#endif - - if (locvar1) - { - // Have you ever seen anything so hideous? - if (actor->target) - actor->color = (UINT8)(actor->color + actor->target->color); - } - else - actor->color = (UINT8)(actor->color + locvar2); -} - -// Function: A_ChangeColorAbsolute -// -// Description: Changes the color of an object by an absolute value. Note: 0 is default colormap. -// -// var1 = if (var1 > 0), set your color to your target's color -// var2 = if (var1 = 0), color value to set to -// -void A_ChangeColorAbsolute(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ChangeColorAbsolute", actor)) - return; -#endif - - if (locvar1) - { - if (actor->target) - actor->color = actor->target->color; - } - else - actor->color = (UINT8)locvar2; -} - -// Function: A_MoveRelative -// -// Description: Moves an object (wrapper for P_Thrust) -// -// var1 = angle -// var2 = force -// -void A_MoveRelative(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_MoveRelative", actor)) - return; -#endif - - P_Thrust(actor, actor->angle+FixedAngle(locvar1*FRACUNIT), FixedMul(locvar2*FRACUNIT, actor->scale)); -} - -// Function: A_MoveAbsolute -// -// Description: Moves an object (wrapper for P_InstaThrust) -// -// var1 = angle -// var2 = force -// -void A_MoveAbsolute(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_MoveAbsolute", actor)) - return; -#endif - - P_InstaThrust(actor, FixedAngle(locvar1*FRACUNIT), FixedMul(locvar2*FRACUNIT, actor->scale)); -} - -// Function: A_Thrust -// -// Description: Pushes the object horizontally at its current angle. -// -// var1 = amount of force -// var2 = If 1, xy momentum is lost. If 0, xy momentum is kept -// -void A_Thrust(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Thrust", actor)) - return; -#endif - - if (!locvar1) - CONS_Debug(DBG_GAMELOGIC, "A_Thrust: Var1 not specified!\n"); - - if (locvar2) - P_InstaThrust(actor, actor->angle, FixedMul(locvar1*FRACUNIT, actor->scale)); - else - P_Thrust(actor, actor->angle, FixedMul(locvar1*FRACUNIT, actor->scale)); -} - -// Function: A_ZThrust -// -// Description: Pushes the object up or down. -// -// var1 = amount of force -// var2: -// lower 16 bits = If 1, xy momentum is lost. If 0, xy momentum is kept -// upper 16 bits = If 1, z momentum is lost. If 0, z momentum is kept -// -void A_ZThrust(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ZThrust", actor)) - return; -#endif - - if (!locvar1) - CONS_Debug(DBG_GAMELOGIC, "A_ZThrust: Var1 not specified!\n"); - - if (locvar2 & 65535) - actor->momx = actor->momy = 0; - - if (actor->eflags & MFE_VERTICALFLIP) - actor->z--; - else - actor->z++; - - P_SetObjectMomZ(actor, locvar1*FRACUNIT, !(locvar2 >> 16)); -} - -// Function: A_SetTargetsTarget -// -// Description: Sets your target to the object who your target is targeting. Yikes! If it happens to be NULL, you're just out of luck. -// -// var1: (Your target) -// 0 = target -// 1 = tracer -// var2: (Your target's target) -// 0 = target/tracer's target -// 1 = target/tracer's tracer -// -void A_SetTargetsTarget(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - mobj_t *oldtarg = NULL, *newtarg = NULL; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SetTargetsTarget", actor)) - return; -#endif - - // actor's target - if (locvar1) // or tracer - oldtarg = actor->tracer; - else - oldtarg = actor->target; - - if (P_MobjWasRemoved(oldtarg)) - return; - - // actor's target's target! - if (locvar2) // or tracer - newtarg = oldtarg->tracer; - else - newtarg = oldtarg->target; - - if (P_MobjWasRemoved(newtarg)) - return; - - // set actor's new target - if (locvar1) // or tracer - P_SetTarget(&actor->tracer, newtarg); - else - P_SetTarget(&actor->target, newtarg); -} - -// Function: A_SetObjectFlags -// -// Description: Sets the flags of an object -// -// var1 = flag value to set -// var2: -// if var2 == 2, add the flag to the current flags -// else if var2 == 1, remove the flag from the current flags -// else if var2 == 0, set the flags to the exact value -// -void A_SetObjectFlags(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - boolean unlinkthings = false; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SetObjectFlags", actor)) - return; -#endif - - if (locvar2 == 2) - locvar1 = actor->flags | locvar1; - else if (locvar2 == 1) - locvar1 = actor->flags & ~locvar1; - - if ((UINT32)(locvar1 & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (actor->flags & (MF_NOBLOCKMAP|MF_NOSECTOR))) // Blockmap/sector status has changed, so reset the links - unlinkthings = true; - - if (unlinkthings) { - P_UnsetThingPosition(actor); - if (sector_list) - { - P_DelSeclist(sector_list); - sector_list = NULL; - } - } - - actor->flags = locvar1; - - if (unlinkthings) - P_SetThingPosition(actor); -} - -// Function: A_SetObjectFlags2 -// -// Description: Sets the flags2 of an object -// -// var1 = flag value to set -// var2: -// if var2 == 2, add the flag to the current flags -// else if var2 == 1, remove the flag from the current flags -// else if var2 == 0, set the flags to the exact value -// -void A_SetObjectFlags2(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SetObjectFlags2", actor)) - return; -#endif - - if (locvar2 == 2) - actor->flags2 |= locvar1; - else if (locvar2 == 1) - actor->flags2 &= ~locvar1; - else - actor->flags2 = locvar1; -} - -// Function: A_BossJetFume -// -// Description: Spawns jet fumes/other attachment miscellany for the boss. To only be used when he is spawned. -// -// var1: -// 0 - Triple jet fume pattern -// 1 - Boss 3's propeller -// 2 - Metal Sonic jet fume -// 3 - Boss 4 jet flame -// var2 = unused -// -void A_BossJetFume(mobj_t *actor) -{ - mobj_t *filler; - INT32 locvar1 = var1; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_BossJetFume", actor)) - return; -#endif - - if (locvar1 == 0) // Boss1 jet fumes - { - fixed_t jetx, jety, jetz; - - jetx = actor->x + P_ReturnThrustX(actor, actor->angle, -FixedMul(64*FRACUNIT, actor->scale)); - jety = actor->y + P_ReturnThrustY(actor, actor->angle, -FixedMul(64*FRACUNIT, actor->scale)); - if (actor->eflags & MFE_VERTICALFLIP) - jetz = actor->z + actor->height - FixedMul(38*FRACUNIT + mobjinfo[MT_JETFUME1].height, actor->scale); - else - jetz = actor->z + FixedMul(38*FRACUNIT, actor->scale); - - filler = P_SpawnMobj(jetx, jety, jetz, MT_JETFUME1); - P_SetTarget(&filler->target, actor); - filler->destscale = actor->scale; - P_SetScale(filler, filler->destscale); - if (actor->eflags & MFE_VERTICALFLIP) - filler->flags2 |= MF2_OBJECTFLIP; - filler->fuse = 56; - - if (actor->eflags & MFE_VERTICALFLIP) - jetz = actor->z + actor->height - FixedMul(12*FRACUNIT + mobjinfo[MT_JETFUME1].height, actor->scale); - else - jetz = actor->z + FixedMul(12*FRACUNIT, actor->scale); - - filler = P_SpawnMobj(jetx + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)), - jety + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)), - jetz, MT_JETFUME1); - P_SetTarget(&filler->target, actor); - filler->destscale = actor->scale; - P_SetScale(filler, filler->destscale); - if (actor->eflags & MFE_VERTICALFLIP) - filler->flags2 |= MF2_OBJECTFLIP; - filler->fuse = 57; - - filler = P_SpawnMobj(jetx + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)), - jety + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)), - jetz, MT_JETFUME1); - P_SetTarget(&filler->target, actor); - filler->destscale = actor->scale; - P_SetScale(filler, filler->destscale); - if (actor->eflags & MFE_VERTICALFLIP) - filler->flags2 |= MF2_OBJECTFLIP; - filler->fuse = 58; - - P_SetTarget(&actor->tracer, filler); - } - else if (locvar1 == 1) // Boss 3 propeller - { - fixed_t jetx, jety, jetz; - - jetx = actor->x + P_ReturnThrustX(actor, actor->angle, -FixedMul(60*FRACUNIT, actor->scale)); - jety = actor->y + P_ReturnThrustY(actor, actor->angle, -FixedMul(60*FRACUNIT, actor->scale)); - if (actor->eflags & MFE_VERTICALFLIP) - jetz = actor->z + actor->height - FixedMul(17*FRACUNIT + mobjinfo[MT_PROPELLER].height, actor->scale); - else - jetz = actor->z + FixedMul(17*FRACUNIT, actor->scale); - - filler = P_SpawnMobj(jetx, jety, jetz, MT_PROPELLER); - P_SetTarget(&filler->target, actor); - filler->destscale = actor->scale; - P_SetScale(filler, filler->destscale); - if (actor->eflags & MFE_VERTICALFLIP) - filler->flags2 |= MF2_OBJECTFLIP; - filler->angle = actor->angle - ANGLE_180; - - P_SetTarget(&actor->tracer, filler); - } - else if (locvar1 == 2) // Metal Sonic jet fumes - { - filler = P_SpawnMobj(actor->x, actor->y, actor->z, MT_JETFUME1); - P_SetTarget(&filler->target, actor); - filler->fuse = 59; - P_SetTarget(&actor->tracer, filler); - filler->destscale = actor->scale/2; - P_SetScale(filler, filler->destscale); - if (actor->eflags & MFE_VERTICALFLIP) - filler->flags2 |= MF2_OBJECTFLIP; - } - else if (locvar1 == 3) // Boss 4 jet flame - { - fixed_t jetz; - if (actor->eflags & MFE_VERTICALFLIP) - jetz = actor->z + actor->height + FixedMul(50*FRACUNIT - mobjinfo[MT_JETFLAME].height, actor->scale); - else - jetz = actor->z - FixedMul(50*FRACUNIT, actor->scale); - filler = P_SpawnMobj(actor->x, actor->y, jetz, MT_JETFLAME); - P_SetTarget(&filler->target, actor); - // Boss 4 already uses its tracer for other things - filler->destscale = actor->scale; - P_SetScale(filler, filler->destscale); - if (actor->eflags & MFE_VERTICALFLIP) - filler->flags2 |= MF2_OBJECTFLIP; - } -} - -// Function: A_RandomState -// -// Description: Chooses one of the two state numbers supplied randomly. -// -// var1 = state number 1 -// var2 = state number 2 -// -void A_RandomState(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_RandomState", actor)) - return; -#endif - - P_SetMobjState(actor, P_RandomChance(FRACUNIT/2) ? locvar1 : locvar2); -} - -// Function: A_RandomStateRange -// -// Description: Chooses a random state within the range supplied. -// -// var1 = Minimum state number to choose. -// var2 = Maximum state number to use. -// -void A_RandomStateRange(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_RandomStateRange", actor)) - return; -#endif - - P_SetMobjState(actor, P_RandomRange(locvar1, locvar2)); -} - -// Function: A_DualAction -// -// Description: Calls two actions. Be careful, if you reference the same state this action is called from, you can create an infinite loop. -// -// var1 = state # to use 1st action from -// var2 = state # to use 2nd action from -// -void A_DualAction(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_DualAction", actor)) - return; -#endif - - CONS_Debug(DBG_GAMELOGIC, "A_DualAction called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); - - var1 = states[locvar1].var1; - var2 = states[locvar1].var2; -#ifdef HAVE_BLUA - astate = &states[locvar1]; -#endif - - CONS_Debug(DBG_GAMELOGIC, "A_DualAction: Calling First Action (state %d)...\n", locvar1); - states[locvar1].action.acp1(actor); - - var1 = states[locvar2].var1; - var2 = states[locvar2].var2; -#ifdef HAVE_BLUA - astate = &states[locvar2]; -#endif - - CONS_Debug(DBG_GAMELOGIC, "A_DualAction: Calling Second Action (state %d)...\n", locvar2); - states[locvar2].action.acp1(actor); -} - -// Function: A_RemoteAction -// -// Description: var1 is the remote object. var2 is the state reference for calling the action called on var1. var1 becomes the actor's target, the action (var2) is called on var1. actor's target is restored -// -// var1 = remote object (-2 uses tracer, -1 uses target) -// var2 = state reference for calling an action -// -void A_RemoteAction(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - mobj_t *originaltarget = actor->target; // Hold on to the target for later. -#ifdef HAVE_BLUA - if (LUA_CallAction("A_RemoteAction", actor)) - return; -#endif - - // If >=0, find the closest target. - if (locvar1 >= 0) - { - ///* DO A_FINDTARGET STUFF */// - mobj_t *targetedmobj = NULL; - thinker_t *th; - mobj_t *mo2; - fixed_t dist1 = 0, dist2 = 0; - - // scan the thinkers - for (th = thinkercap.next; th != &thinkercap; th = th->next) - { - if (th->function.acp1 != (actionf_p1)P_MobjThinker) - continue; - - mo2 = (mobj_t *)th; - - if (mo2->type == (mobjtype_t)locvar1) - { - if (targetedmobj == NULL) - { - targetedmobj = mo2; - dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y); - } - else - { - dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y); - - if ((locvar2 && dist1 < dist2) || (!locvar2 && dist1 > dist2)) - { - targetedmobj = mo2; - dist2 = dist1; - } - } - } - } - - if (!targetedmobj) - { - CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Unable to find the specified object to target.\n"); - return; // Oops, nothing found.. - } - - CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Found a target.\n"); - - P_SetTarget(&actor->target, targetedmobj); - - ///* END A_FINDTARGET STUFF */// - } - - // If -2, use the tracer as the target - else if (locvar1 == -2) - P_SetTarget(&actor->target, actor->tracer); - // if -1 or anything else, just use the target. - - if (actor->target) - { - // Steal the var1 and var2 from "locvar2" - var1 = states[locvar2].var1; - var2 = states[locvar2].var2; -#ifdef HAVE_BLUA - astate = &states[locvar2]; -#endif - - CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Calling action on %p\n" - "var1 is %d\nvar2 is %d\n", actor->target, var1, var2); - states[locvar2].action.acp1(actor->target); - } - - P_SetTarget(&actor->target, originaltarget); // Restore the original target. -} - -// Function: A_ToggleFlameJet -// -// Description: Turns a flame jet on and off. -// -// var1 = unused -// var2 = unused -// -void A_ToggleFlameJet(mobj_t* actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ToggleFlameJet", actor)) - return; -#endif - // threshold - off delay - // movecount - on timer - - if (actor->flags2 & MF2_FIRING) - { - actor->flags2 &= ~MF2_FIRING; - - if (actor->threshold) - actor->tics = actor->threshold; - } - else - { - actor->flags2 |= MF2_FIRING; - - if (actor->movecount) - actor->tics = actor->movecount; - } -} - -// Function: A_OrbitNights -// -// Description: Used by Chaos Emeralds to orbit around Nights (aka Super Sonic.) -// -// var1 = Angle adjustment (aka orbit speed) -// var2 = Lower four bits: height offset, Upper 4 bits = set if object is Nightopian Helper -// -void A_OrbitNights(mobj_t* actor) -{ - INT32 ofs = (var2 & 0xFFFF); - boolean ishelper = (var2 & 0xFFFF0000); -#ifdef HAVE_BLUA - if (LUA_CallAction("A_OrbitNights", actor)) - return; -#endif - - if (!actor->target - || (actor->target->player && - // if NiGHTS special stage and not NiGHTSmode. - (((maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap) && !(actor->target->player->powers[pw_carry] == CR_NIGHTSMODE)) - // Also remove this object if they no longer have a NiGHTS helper - || (ishelper && !actor->target->player->powers[pw_nights_helper])))) - { - P_RemoveMobj(actor); - return; - } - else - { - actor->extravalue1 += var1; - P_UnsetThingPosition(actor); - { - const angle_t fa = (angle_t)actor->extravalue1 >> ANGLETOFINESHIFT; - const angle_t ofa = ((angle_t)actor->extravalue1 + (ofs*ANG1)) >> ANGLETOFINESHIFT; - - const fixed_t fc = FixedMul(FINECOSINE(fa),FixedMul(32*FRACUNIT, actor->scale)); - const fixed_t fh = FixedMul(FINECOSINE(ofa),FixedMul(20*FRACUNIT, actor->scale)); - const fixed_t fs = FixedMul(FINESINE(fa),FixedMul(32*FRACUNIT, actor->scale)); - - actor->x = actor->target->x + fc; - actor->y = actor->target->y + fs; - actor->z = actor->target->z + fh + FixedMul(16*FRACUNIT, actor->scale); - - // Semi-lazy hack - actor->angle = (angle_t)actor->extravalue1 + ANGLE_90; - } - P_SetThingPosition(actor); - - if (ishelper && actor->target->player) // Flash a helper that's about to be removed. - { - if ((actor->target->player->powers[pw_nights_helper] < TICRATE) - && (actor->target->player->powers[pw_nights_helper] & 1)) - actor->flags2 |= MF2_DONTDRAW; - else - actor->flags2 &= ~MF2_DONTDRAW; - } - } -} - -// Function: A_GhostMe -// -// Description: Spawns a "ghost" mobj of this actor, ala spindash trails and the minus's digging "trails" -// -// var1 = duration in tics -// var2 = unused -// -void A_GhostMe(mobj_t *actor) -{ - INT32 locvar1 = var1; - mobj_t *ghost; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_GhostMe", actor)) - return; -#endif - ghost = P_SpawnGhostMobj(actor); - if (ghost && locvar1 > 0) - ghost->fuse = locvar1; -} - -// Function: A_SetObjectState -// -// Description: Changes the state of an object's target/tracer. -// -// var1 = state number -// var2: -// 0 = target -// 1 = tracer -// -void A_SetObjectState(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - mobj_t *target; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SetObjectState", actor)) - return; -#endif - - if ((!locvar2 && !actor->target) || (locvar2 && !actor->tracer)) - { - if (cv_debug) - CONS_Printf("A_SetObjectState: No target to change state!\n"); - return; - } - - if (!locvar2) // target - target = actor->target; - else // tracer - target = actor->tracer; - - if (target->health > 0) - { - if (!target->player) - P_SetMobjState(target, locvar1); - else - P_SetPlayerMobjState(target, locvar1); - } -} - -// Function: A_SetObjectTypeState -// -// Description: Changes the state of all active objects of a certain type in a certain range of the actor. -// -// var1 = state number -// var2: -// lower 16 bits = type -// upper 16 bits = range (if == 0, across whole map) -// -void A_SetObjectTypeState(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - const UINT16 loc2lw = (UINT16)(locvar2 & 65535); - const UINT16 loc2up = (UINT16)(locvar2 >> 16); - - thinker_t *th; - mobj_t *mo2; - fixed_t dist = 0; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SetObjectTypeState", actor)) - return; -#endif - - for (th = thinkercap.next; th != &thinkercap; th = th->next) - { - if (th->function.acp1 != (actionf_p1)P_MobjThinker) - continue; - - mo2 = (mobj_t *)th; - - if (mo2->type == (mobjtype_t)loc2lw) - { - dist = P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y); - - if (mo2->health > 0) - { - if (loc2up == 0) - P_SetMobjState(mo2, locvar1); - else - { - if (dist <= FixedMul(loc2up*FRACUNIT, actor->scale)) - P_SetMobjState(mo2, locvar1); - } - } - } - } -} - -// Function: A_KnockBack -// -// Description: Knocks back the object's target at its current speed. -// -// var1: -// 0 = target -// 1 = tracer -// var2 = unused -// -void A_KnockBack(mobj_t *actor) -{ - INT32 locvar1 = var1; - mobj_t *target; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_KnockBack", actor)) - return; -#endif - - if (!locvar1) - target = actor->target; - else - target = actor->tracer; - - if (!target) - { - if(cv_debug) - CONS_Printf("A_KnockBack: No target!\n"); - return; - } - - target->momx *= -1; - target->momy *= -1; -} - -// Function: A_PushAway -// -// Description: Pushes an object's target away from the calling object. -// -// var1 = amount of force -// var2: -// lower 16 bits = If 1, xy momentum is lost. If 0, xy momentum is kept -// upper 16 bits = 0 - target, 1 - tracer -// -void A_PushAway(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - mobj_t *target; // target - angle_t an; // actor to target angle -#ifdef HAVE_BLUA - if (LUA_CallAction("A_PushAway", actor)) - return; -#endif - - if ((!(locvar2 >> 16) && !actor->target) || ((locvar2 >> 16) && !actor->tracer)) - return; - - if (!locvar1) - CONS_Printf("A_Thrust: Var1 not specified!\n"); - - if (!(locvar2 >> 16)) // target - target = actor->target; - else // tracer - target = actor->tracer; - - an = R_PointToAngle2(actor->x, actor->y, target->x, target->y); - - if (locvar2 & 65535) - P_InstaThrust(target, an, FixedMul(locvar1*FRACUNIT, actor->scale)); - else - P_Thrust(target, an, FixedMul(locvar1*FRACUNIT, actor->scale)); -} - -// Function: A_RingDrain -// -// Description: Drain targeted player's rings. -// -// var1 = ammount of drained rings -// var2 = unused -// -void A_RingDrain(mobj_t *actor) -{ - INT32 locvar1 = var1; - player_t *player; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_RingDrain", actor)) - return; -#endif - - if (!actor->target || !actor->target->player) - { - if(cv_debug) - CONS_Printf("A_RingDrain: No player targeted!\n"); - return; - } - - player = actor->target->player; - P_GivePlayerRings(player, -min(locvar1, player->rings)); -} - -// Function: A_SplitShot -// -// Description: Shoots 2 missiles that hit next to the player. -// -// var1 = target x-y-offset -// var2: -// lower 16 bits = missile type -// upper 16 bits = height offset -// -void A_SplitShot(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - const UINT16 loc2lw = (UINT16)(locvar2 & 65535); - const UINT16 loc2up = (UINT16)(locvar2 >> 16); - const fixed_t offs = (fixed_t)(locvar1*FRACUNIT); - const fixed_t hoffs = (fixed_t)(loc2up*FRACUNIT); -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SplitShot", actor)) - return; -#endif - - A_FaceTarget(actor); - { - const angle_t an = (actor->angle + ANGLE_90) >> ANGLETOFINESHIFT; - const fixed_t fasin = FINESINE(an); - const fixed_t facos = FINECOSINE(an); - fixed_t xs = FixedMul(facos,FixedMul(offs, actor->scale)); - fixed_t ys = FixedMul(fasin,FixedMul(offs, actor->scale)); - fixed_t z; - - if (actor->eflags & MFE_VERTICALFLIP) - z = actor->z + actor->height - FixedMul(hoffs, actor->scale); - else - z = actor->z + FixedMul(hoffs, actor->scale); - - P_SpawnPointMissile(actor, actor->target->x+xs, actor->target->y+ys, actor->target->z, loc2lw, actor->x, actor->y, z); - P_SpawnPointMissile(actor, actor->target->x-xs, actor->target->y-ys, actor->target->z, loc2lw, actor->x, actor->y, z); - } -} - -// Function: A_MissileSplit -// -// Description: If the object is a missile it will create a new missile with an alternate flight path owned by the one who shot the former missile. -// -// var1 = splitting missile type -// var2 = splitting angle -// -void A_MissileSplit(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_MissileSplit", actor)) - return; -#endif - if (actor->eflags & MFE_VERTICALFLIP) - P_SpawnAlteredDirectionMissile(actor, locvar1, actor->x, actor->y, actor->z+actor->height, locvar2); - else - P_SpawnAlteredDirectionMissile(actor, locvar1, actor->x, actor->y, actor->z, locvar2); -} - -// Function: A_MultiShot -// -// Description: Shoots objects horizontally that spread evenly in all directions. -// -// var1: -// lower 16 bits = number of missiles -// upper 16 bits = missile type # -// var2 = height offset -// -void A_MultiShot(mobj_t *actor) -{ - fixed_t z, xr, yr; - INT32 locvar1 = var1; - INT32 locvar2 = var2; - const UINT16 loc1lw = (UINT16)(locvar1 & 65535); - const UINT16 loc1up = (UINT16)(locvar1 >> 16); - INT32 count = 0; - fixed_t ad; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_MultiShot", actor)) - return; -#endif - - if (actor->target) - A_FaceTarget(actor); - - if(loc1lw > 90) - ad = FixedMul(90*FRACUNIT, actor->scale); - else - ad = FixedMul(loc1lw*FRACUNIT, actor->scale); - - if (actor->eflags & MFE_VERTICALFLIP) - z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale); - else - z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale); - xr = FixedMul((P_SignedRandom()/3)<scale); // please note p_mobj.c's P_Rand() abuse - yr = FixedMul((P_SignedRandom()/3)<scale); // of rand(), RAND_MAX and signness mess - - while(count <= loc1lw && loc1lw >= 1) - { - const angle_t fa = FixedAngleC(count*FRACUNIT*360, ad)>>ANGLETOFINESHIFT; - const fixed_t rc = FINECOSINE(fa); - const fixed_t rs = FINESINE(fa); - const fixed_t xrc = FixedMul(xr, rc); - const fixed_t yrs = FixedMul(yr, rs); - const fixed_t xrs = FixedMul(xr, rs); - const fixed_t yrc = FixedMul(yr, rc); - - P_SpawnPointMissile(actor, xrc-yrs+actor->x, xrs+yrc+actor->y, z, loc1up, actor->x, actor->y, z); - count++; - } - - if (!(actor->flags & MF_BOSS)) - { - if (ultimatemode) - actor->reactiontime = actor->info->reactiontime*TICRATE; - else - actor->reactiontime = actor->info->reactiontime*TICRATE*2; - } -} - -// Function: A_InstaLoop -// -// Description: Makes the object move along a 2d (view angle, z) polygon. -// -// var1: -// lower 16 bits = current step -// upper 16 bits = maximum step # -// var2 = force -// -void A_InstaLoop(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - fixed_t force = max(locvar2, 1)*FRACUNIT; // defaults to 1 if var2 < 1 - const UINT16 loc1lw = (UINT16)(locvar1 & 65535); - const UINT16 loc1up = (UINT16)(locvar1 >> 16); - const angle_t fa = FixedAngleC(loc1lw*FRACUNIT*360, loc1up*FRACUNIT)>>ANGLETOFINESHIFT; - const fixed_t ac = FINECOSINE(fa); - const fixed_t as = FINESINE(fa); -#ifdef HAVE_BLUA - if (LUA_CallAction("A_InstaLoop", actor)) - return; -#endif - - P_InstaThrust(actor, actor->angle, FixedMul(ac, FixedMul(force, actor->scale))); - P_SetObjectMomZ(actor, FixedMul(as, force), false); -} - -// Function: A_Custom3DRotate -// -// Description: Rotates the actor around its target in 3 dimensions. -// -// var1: -// lower 16 bits = radius in fracunits -// upper 16 bits = vertical offset -// var2: -// lower 16 bits = vertical rotation speed in 1/10 fracunits per tic -// upper 16 bits = horizontal rotation speed in 1/10 fracunits per tic -// -void A_Custom3DRotate(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - - const UINT16 loc1lw = (UINT16)(locvar1 & 65535); - const UINT16 loc1up = (UINT16)(locvar1 >> 16); - const UINT16 loc2lw = (UINT16)(locvar2 & 65535); - const UINT16 loc2up = (UINT16)(locvar2 >> 16); - - const fixed_t radius = FixedMul(loc1lw*FRACUNIT, actor->scale); - const fixed_t hOff = FixedMul(loc1up*FRACUNIT, actor->scale); - const fixed_t hspeed = FixedMul(loc2up*FRACUNIT/10, actor->scale); - const fixed_t vspeed = FixedMul(loc2lw*FRACUNIT/10, actor->scale); -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Custom3DRotate", actor)) - return; -#endif - - if (actor->target->health == 0) - { - P_RemoveMobj(actor); - return; - } - - if (!actor->target) // This should NEVER happen. - { - if (cv_debug) - CONS_Printf("Error: Object has no target\n"); - P_RemoveMobj(actor); - return; - } - if (hspeed==0 && vspeed==0) - { - CONS_Printf("Error: A_Custom3DRotate: Object has no speed.\n"); - return; - } - - actor->angle += FixedAngle(hspeed); - actor->movedir += FixedAngle(vspeed); - P_UnsetThingPosition(actor); - { - const angle_t fa = actor->angle>>ANGLETOFINESHIFT; - - if (vspeed == 0 && hspeed != 0) - { - actor->x = actor->target->x + FixedMul(FINECOSINE(fa),radius); - actor->y = actor->target->y + FixedMul(FINESINE(fa),radius); - actor->z = actor->target->z + actor->target->height/2 - actor->height/2 + hOff; - } - else - { - const angle_t md = actor->movedir>>ANGLETOFINESHIFT; - actor->x = actor->target->x + FixedMul(FixedMul(FINESINE(md),FINECOSINE(fa)),radius); - actor->y = actor->target->y + FixedMul(FixedMul(FINESINE(md),FINESINE(fa)),radius); - actor->z = actor->target->z + FixedMul(FINECOSINE(md),radius) + actor->target->height/2 - actor->height/2 + hOff; - } - } - P_SetThingPosition(actor); -} - -// Function: A_SearchForPlayers -// -// Description: Checks if the actor has targeted a vulnerable player. If not a new player will be searched all around. If no players are available the object can call a specific state. (Useful for not moving enemies) -// -// var1: -// if var1 == 0, if necessary call state with same state number as var2 -// else, do not call a specific state if no players are available -// var2 = state number -// -void A_SearchForPlayers(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SearchForPlayers", actor)) - return; -#endif - - if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) - { - // look for a new target - if (P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - if(locvar1==0) - { - P_SetMobjStateNF(actor, locvar2); - return; - } - } -} - -// Function: A_CheckRandom -// -// Description: Calls a state by chance. -// -// var1: -// lower 16 bits = denominator -// upper 16 bits = numerator (defaults to 1 if zero) -// var2 = state number -// -void A_CheckRandom(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - fixed_t chance = FRACUNIT; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CheckRandom", actor)) - return; -#endif - if ((locvar1 & 0xFFFF) == 0) - return; - - // The PRNG doesn't suck anymore, OK? - if (locvar1 >> 16) - chance *= (locvar1 >> 16); - chance /= (locvar1 & 0xFFFF); - - if (P_RandomChance(chance)) - P_SetMobjState(actor, locvar2); -} - -// Function: A_CheckTargetRings -// -// Description: Calls a state depending on the ammount of rings currently owned by targeted players. -// -// var1 = if player rings >= var1 call state -// var2 = state number -// -void A_CheckTargetRings(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CheckTargetRings", actor)) - return; -#endif - - if (!(actor->target) || !(actor->target->player)) - return; - - if (actor->target->player->rings >= locvar1) - P_SetMobjState(actor, locvar2); -} - -// Function: A_CheckRings -// -// Description: Calls a state depending on the ammount of rings currently owned by all players. -// -// var1 = if player rings >= var1 call state -// var2 = state number -// -void A_CheckRings(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - INT32 i, cntr = 0; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CheckRings", actor)) - return; -#endif - - for (i = 0; i < MAXPLAYERS; i++) - cntr += players[i].rings; - - if (cntr >= locvar1) - P_SetMobjState(actor, locvar2); -} - -// Function: A_CheckTotalRings -// -// Description: Calls a state depending on the maximum ammount of rings owned by all players during this try. -// -// var1 = if total player rings >= var1 call state -// var2 = state number -// -void A_CheckTotalRings(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - - INT32 i, cntr = 0; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CheckTotalRings", actor)) - return; -#endif - - for (i = 0; i < MAXPLAYERS; i++) - cntr += players[i].totalring; - - if (cntr >= locvar1) - P_SetMobjState(actor, locvar2); -} - -// Function: A_CheckHealth -// -// Description: Calls a state depending on the object's current health. -// -// var1 = if health <= var1 call state -// var2 = state number -// -void A_CheckHealth(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CheckHealth", actor)) - return; -#endif - - if (actor->health <= locvar1) - P_SetMobjState(actor, locvar2); -} - -// Function: A_CheckRange -// -// Description: Calls a state if the object's target is in range. -// -// var1: -// lower 16 bits = range -// upper 16 bits = 0 - target, 1 - tracer -// var2 = state number -// -void A_CheckRange(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - fixed_t dist; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CheckRange", actor)) - return; -#endif - - if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer)) - return; - - if (!(locvar1 >> 16)) //target - dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y); - else //tracer - dist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y); - - if (dist <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale)) - P_SetMobjState(actor, locvar2); -} - -// Function: A_CheckHeight -// -// Description: Calls a state if the object and it's target have a height offset <= var1 compared to each other. -// -// var1: -// lower 16 bits = height offset -// upper 16 bits = 0 - target, 1 - tracer -// var2 = state number -// -void A_CheckHeight(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - fixed_t height; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CheckHeight", actor)) - return; -#endif - - if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer)) - return; - - if (!(locvar1 >> 16)) // target - height = abs(actor->target->z - actor->z); - else // tracer - height = abs(actor->tracer->z - actor->z); - - if (height <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale)) - P_SetMobjState(actor, locvar2); -} - -// Function: A_CheckTrueRange -// -// Description: Calls a state if the object's target is in true range. (Checks height, too.) -// -// var1: -// lower 16 bits = range -// upper 16 bits = 0 - target, 1 - tracer -// var2 = state number -// -void A_CheckTrueRange(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - fixed_t height; // vertical range - fixed_t dist; // horizontal range - fixed_t l; // true range -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CheckTrueRange", actor)) - return; -#endif - - if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer)) - return; - - if (!(locvar1 >> 16)) // target - { - height = actor->target->z - actor->z; - dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y); - - } - else // tracer - { - height = actor->tracer->z - actor->z; - dist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y); - } - - l = P_AproxDistance(dist, height); - - if (l <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale)) - P_SetMobjState(actor, locvar2); - -} - -// Function: A_CheckThingCount -// -// Description: Calls a state depending on the number of active things in range. -// -// var1: -// lower 16 bits = number of things -// upper 16 bits = thing type -// var2: -// lower 16 bits = state to call -// upper 16 bits = range (if == 0, check whole map) -// -void A_CheckThingCount(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - - const UINT16 loc1lw = (UINT16)(locvar1 & 65535); - const UINT16 loc1up = (UINT16)(locvar1 >> 16); - const UINT16 loc2lw = (UINT16)(locvar2 & 65535); - const UINT16 loc2up = (UINT16)(locvar2 >> 16); - - INT32 count = 0; - thinker_t *th; - mobj_t *mo2; - fixed_t dist = 0; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CheckThingCount", actor)) - return; -#endif - - for (th = thinkercap.next; th != &thinkercap; th = th->next) - { - if (th->function.acp1 != (actionf_p1)P_MobjThinker) - continue; - - mo2 = (mobj_t *)th; - - if (mo2->type == (mobjtype_t)loc1up) - { - dist = P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y); - - if (loc2up == 0) - count++; - else - { - if (dist <= FixedMul(loc2up*FRACUNIT, actor->scale)) - count++; - } - } - } - - if(loc1lw <= count) - P_SetMobjState(actor, loc2lw); -} - -// Function: A_CheckAmbush -// -// Description: Calls a state if the actor is behind its targeted player. -// -// var1: -// 0 = target -// 1 = tracer -// var2 = state number -// -void A_CheckAmbush(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - angle_t at; // angle target is currently facing - angle_t atp; // actor to target angle - angle_t an; // angle between at and atp - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CheckAmbush", actor)) - return; -#endif - - if ((!locvar1 && !actor->target) || (locvar1 && !actor->tracer)) - return; - - if (!locvar1) // target - { - at = actor->target->angle; - atp = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); - } - else // tracer - { - at = actor->tracer->angle; - atp = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); - } - - an = atp - at; - - if (an > ANGLE_180) // flip angle if bigger than 180 - an = InvAngle(an); - - if (an < ANGLE_90+ANGLE_22h) // within an angle of 112.5 from each other? - P_SetMobjState(actor, locvar2); -} - -// Function: A_CheckCustomValue -// -// Description: Calls a state depending on the object's custom value. -// -// var1 = if custom value >= var1, call state -// var2 = state number -// -void A_CheckCustomValue(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CheckCustomValue", actor)) - return; -#endif - - if (actor->cusval >= locvar1) - P_SetMobjState(actor, locvar2); -} - -// Function: A_CheckCusValMemo -// -// Description: Calls a state depending on the object's custom memory value. -// -// var1 = if memory value >= var1, call state -// var2 = state number -// -void A_CheckCusValMemo(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CheckCusValMemo", actor)) - return; -#endif - - if (actor->cvmem >= locvar1) - P_SetMobjState(actor, locvar2); -} - -// Function: A_SetCustomValue -// -// Description: Changes the custom value of an object. -// -// var1 = manipulating value -// var2: -// if var2 == 5, multiply the custom value by var1 -// else if var2 == 4, divide the custom value by var1 -// else if var2 == 3, apply modulo var1 to the custom value -// else if var2 == 2, add var1 to the custom value -// else if var2 == 1, substract var1 from the custom value -// else if var2 == 0, replace the custom value with var1 -// -void A_SetCustomValue(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SetCustomValue", actor)) - return; -#endif - - if (cv_debug) - CONS_Printf("Init custom value is %d\n", actor->cusval); - - if (locvar1 == 0 && locvar2 == 4) - return; // DON'T DIVIDE BY ZERO - - // no need for a "temp" value here, just modify the cusval directly - if (locvar2 == 5) // multiply - actor->cusval *= locvar1; - else if (locvar2 == 4) // divide - actor->cusval /= locvar1; - else if (locvar2 == 3) // modulo - actor->cusval %= locvar1; - else if (locvar2 == 2) // add - actor->cusval += locvar1; - else if (locvar2 == 1) // subtract - actor->cusval -= locvar1; - else // replace - actor->cusval = locvar1; - - if(cv_debug) - CONS_Printf("New custom value is %d\n", actor->cusval); -} - -// Function: A_UseCusValMemo -// -// Description: Memorizes or recalls a current custom value. -// -// var1: -// if var1 == 1, manipulate memory value -// else, recall memory value replacing the custom value -// var2: -// if var2 == 5, mem = mem*cv || cv = cv*mem -// else if var2 == 4, mem = mem/cv || cv = cv/mem -// else if var2 == 3, mem = mem%cv || cv = cv%mem -// else if var2 == 2, mem += cv || cv += mem -// else if var2 == 1, mem -= cv || cv -= mem -// else mem = cv || cv = mem -// -void A_UseCusValMemo(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - - INT32 temp = actor->cusval; // value being manipulated - INT32 tempM = actor->cvmem; // value used to manipulate temp with -#ifdef HAVE_BLUA - if (LUA_CallAction("A_UseCusValMemo", actor)) - return; -#endif - - if (locvar1 == 1) // cvmem being changed using cusval - { - temp = actor->cvmem; - tempM = actor->cusval; - } - else // cusval being changed with cvmem - { - temp = actor->cusval; - tempM = actor->cvmem; - } - - if (tempM == 0 && locvar2 == 4) - return; // DON'T DIVIDE BY ZERO - - // now get new value for cusval/cvmem using the other - if (locvar2 == 5) // multiply - temp *= tempM; - else if (locvar2 == 4) // divide - temp /= tempM; - else if (locvar2 == 3) // modulo - temp %= tempM; - else if (locvar2 == 2) // add - temp += tempM; - else if (locvar2 == 1) // subtract - temp -= tempM; - else // replace - temp = tempM; - - // finally, give cusval/cvmem the new value! - if (locvar1 == 1) - actor->cvmem = temp; - else - actor->cusval = temp; -} - -// Function: A_RelayCustomValue -// -// Description: Manipulates the custom value of the object's target/tracer. -// -// var1: -// lower 16 bits: -// if var1 == 0, use own custom value -// else, use var1 value -// upper 16 bits = 0 - target, 1 - tracer -// var2: -// if var2 == 5, multiply the target's custom value by var1 -// else if var2 == 4, divide the target's custom value by var1 -// else if var2 == 3, apply modulo var1 to the target's custom value -// else if var2 == 2, add var1 to the target's custom value -// else if var2 == 1, substract var1 from the target's custom value -// else if var2 == 0, replace the target's custom value with var1 -// -void A_RelayCustomValue(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - - INT32 temp; // reference value - var1 lower 16 bits changes this - INT32 tempT; // target's value - changed to tracer if var1 upper 16 bits set, then modified to become final value -#ifdef HAVE_BLUA - if (LUA_CallAction("A_RelayCustomValue", actor)) - return; -#endif - - if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer)) - return; - - // reference custom value - if ((locvar1 & 65535) == 0) - temp = actor->cusval; // your own custom value - else - temp = (locvar1 & 65535); // var1 value - - if (!(locvar1 >> 16)) // target's custom value - tempT = actor->target->cusval; - else // tracer's custom value - tempT = actor->tracer->cusval; - - if (temp == 0 && locvar2 == 4) - return; // DON'T DIVIDE BY ZERO - - // now get new cusval using target's and the reference - if (locvar2 == 5) // multiply - tempT *= temp; - else if (locvar2 == 4) // divide - tempT /= temp; - else if (locvar2 == 3) // modulo - tempT %= temp; - else if (locvar2 == 2) // add - tempT += temp; - else if (locvar2 == 1) // subtract - tempT -= temp; - else // replace - tempT = temp; - - // finally, give target/tracer the new cusval! - if (!(locvar1 >> 16)) // target - actor->target->cusval = tempT; - else // tracer - actor->tracer->cusval = tempT; -} - -// Function: A_CusValAction -// -// Description: Calls an action from a reference state applying custom value parameters. -// -// var1 = state # to use action from -// var2: -// if var2 == 5, only replace new action's var2 with memory value -// else if var2 == 4, only replace new action's var1 with memory value -// else if var2 == 3, replace new action's var2 with custom value and var1 with memory value -// else if var2 == 2, replace new action's var1 with custom value and var2 with memory value -// else if var2 == 1, only replace new action's var2 with custom value -// else if var2 == 0, only replace new action's var1 with custom value -// -void A_CusValAction(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CusValAction", actor)) - return; -#endif - - if (locvar2 == 5) - { - var1 = states[locvar1].var1; - var2 = (INT32)actor->cvmem; - } - else if (locvar2 == 4) - { - var1 = (INT32)actor->cvmem; - var2 = states[locvar1].var2; - } - else if (locvar2 == 3) - { - var1 = (INT32)actor->cvmem; - var2 = (INT32)actor->cusval; - } - else if (locvar2 == 2) - { - var1 = (INT32)actor->cusval; - var2 = (INT32)actor->cvmem; - } - else if (locvar2 == 1) - { - var1 = states[locvar1].var1; - var2 = (INT32)actor->cusval; - } - else - { - var1 = (INT32)actor->cusval; - var2 = states[locvar1].var2; - } - -#ifdef HAVE_BLUA - astate = &states[locvar1]; -#endif - states[locvar1].action.acp1(actor); -} - -// Function: A_ForceStop -// -// Description: Actor immediately stops its current movement. -// -// var1: -// if var1 == 0, stop x-y-z-movement -// else, stop x-y-movement only -// var2 = unused -// -void A_ForceStop(mobj_t *actor) -{ - INT32 locvar1 = var1; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ForceStop", actor)) - return; -#endif - - actor->momx = actor->momy = 0; - if (locvar1 == 0) - actor->momz = 0; -} - -// Function: A_ForceWin -// -// Description: Makes all players win the level. -// -// var1 = unused -// var2 = unused -// -void A_ForceWin(mobj_t *actor) -{ - INT32 i; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ForceWin", actor)) - return; -#else - (void)actor; -#endif - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && ((players[i].mo && players[i].mo->health) - || ((netgame || multiplayer) && (players[i].lives || players[i].continues)))) - break; - } - - if (i == MAXPLAYERS) - return; - - for (i = 0; i < MAXPLAYERS; i++) - P_DoPlayerExit(&players[i]); -} - -// Function: A_SpikeRetract -// -// Description: Toggles actor solid flag. -// -// var1: -// if var1 == 0, actor no collide -// else, actor solid -// var2 = unused -// -void A_SpikeRetract(mobj_t *actor) -{ - INT32 locvar1 = var1; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SpikeRetract", actor)) - return; -#endif - - if (actor->flags & MF_NOBLOCKMAP) - return; - - if (locvar1 == 0) - { - actor->flags &= ~MF_SOLID; - actor->flags |= MF_NOCLIPTHING; - } - else - { - actor->flags |= MF_SOLID; - actor->flags &= ~MF_NOCLIPTHING; - } - if (actor->flags & MF_SOLID) - P_CheckPosition(actor, actor->x, actor->y); -} - -// Function: A_InfoState -// -// Description: Set mobj state to one predefined in mobjinfo. -// -// var1: -// if var1 == 0, set actor to spawnstate -// else if var1 == 1, set actor to seestate -// else if var1 == 2, set actor to meleestate -// else if var1 == 3, set actor to missilestate -// else if var1 == 4, set actor to deathstate -// else if var1 == 5, set actor to xdeathstate -// else if var1 == 6, set actor to raisestate -// var2 = unused -// -void A_InfoState(mobj_t *actor) -{ - INT32 locvar1 = var1; - switch (locvar1) - { - case 0: - if (actor->state != &states[actor->info->spawnstate]) - P_SetMobjState(actor, actor->info->spawnstate); - break; - case 1: - if (actor->state != &states[actor->info->seestate]) - P_SetMobjState(actor, actor->info->seestate); - break; - case 2: - if (actor->state != &states[actor->info->meleestate]) - P_SetMobjState(actor, actor->info->meleestate); - break; - case 3: - if (actor->state != &states[actor->info->missilestate]) - P_SetMobjState(actor, actor->info->missilestate); - break; - case 4: - if (actor->state != &states[actor->info->deathstate]) - P_SetMobjState(actor, actor->info->deathstate); - break; - case 5: - if (actor->state != &states[actor->info->xdeathstate]) - P_SetMobjState(actor, actor->info->xdeathstate); - break; - case 6: - if (actor->state != &states[actor->info->raisestate]) - P_SetMobjState(actor, actor->info->raisestate); - break; - default: - break; - } -} - -// Function: A_Repeat -// -// Description: Returns to state var2 until animation has been used var1 times, then continues to nextstate. -// -// var1 = repeat count -// var2 = state to return to if extravalue2 > 0 -// -void A_Repeat(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Repeat", actor)) - return; -#endif - - if (locvar1 && (!actor->extravalue2 || actor->extravalue2 > locvar1)) - actor->extravalue2 = locvar1; - - if (--actor->extravalue2 > 0) - P_SetMobjState(actor, locvar2); -} - -// Function: A_SetScale -// -// Description: Changes the scale of the actor or its target/tracer -// -// var1 = new scale (1*FRACUNIT = 100%) -// var2: -// upper 16 bits: 0 = actor, 1 = target, 2 = tracer -// lower 16 bits: 0 = instant change, 1 = smooth change -// -void A_SetScale(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - mobj_t *target; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SetScale", actor)) - return; -#endif - - if (locvar1 <= 0) - { - if(cv_debug) - CONS_Printf("A_SetScale: Valid scale not specified!\n"); - return; - } - - if ((locvar2>>16) == 1) - target = actor->target; - else if ((locvar2>>16) == 2) - target = actor->tracer; - else // default to yourself! - target = actor; - - if (!target) - { - if(cv_debug) - CONS_Printf("A_SetScale: No target!\n"); - return; - } - - target->destscale = locvar1; // destination scale - if (!(locvar2 & 65535)) - P_SetScale(target, locvar1); // this instantly changes current scale to var1 if used, if not destscale will alter scale to var1 anyway -} - -// Function: A_RemoteDamage -// -// Description: Damages, kills or even removes either the actor or its target/tracer. Actor acts as the inflictor/source unless harming itself -// -// var1 = Mobj affected: 0 - actor, 1 - target, 2 - tracer -// var2 = Action: 0 - Damage, 1 - Kill, 2 - Remove -// -void A_RemoteDamage(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - mobj_t *target; // we MUST have a target - mobj_t *source = NULL; // on the other hand we don't necessarily need a source -#ifdef HAVE_BLUA - if (LUA_CallAction("A_RemoteDamage", actor)) - return; -#endif - if (locvar1 == 1) - target = actor->target; - else if (locvar1 == 2) - target = actor->tracer; - else // default to yourself! - target = actor; - - if (locvar1 == 1 || locvar1 == 2) - source = actor; - - if (!target) - { - if(cv_debug) - CONS_Printf("A_RemoteDamage: No target!\n"); - return; - } - - if (locvar2 == 1) // Kill mobj! - { - if (target->player) // players die using P_DamageMobj instead for some reason - P_DamageMobj(target, source, source, 1, DMG_INSTAKILL); - else - P_KillMobj(target, source, source, 0); - } - else if (locvar2 == 2) // Remove mobj! - { - if (target->player) //don't remove players! - return; - - P_RemoveMobj(target); - } - else // default: Damage mobj! - P_DamageMobj(target, source, source, 1, 0); -} - -// Function: A_HomingChase -// -// Description: Actor chases directly towards its destination object -// -// var1 = speed multiple -// var2 = destination: 0 = target, 1 = tracer -// -void A_HomingChase(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - mobj_t *dest; - fixed_t dist; - fixed_t speedmul; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_HomingChase", actor)) - return; -#endif - - if (locvar2 == 1) - dest = actor->tracer; - else //default - dest = actor->target; - - if (!dest || !dest->health) - return; - - actor->angle = R_PointToAngle2(actor->x, actor->y, dest->x, dest->y); - - dist = P_AproxDistance(P_AproxDistance(dest->x - actor->x, dest->y - actor->y), dest->z - actor->z); - - if (dist < 1) - dist = 1; - - speedmul = FixedMul(locvar1, actor->scale); - - actor->momx = FixedMul(FixedDiv(dest->x - actor->x, dist), speedmul); - actor->momy = FixedMul(FixedDiv(dest->y - actor->y, dist), speedmul); - actor->momz = FixedMul(FixedDiv(dest->z - actor->z, dist), speedmul); -} - -// Function: A_TrapShot -// -// Description: Fires a missile in a particular direction and angle rather than AT something, Trapgoyle-style! -// -// var1: -// lower 16 bits = object # to fire -// upper 16 bits = front offset -// var2: -// lower 15 bits = vertical angle variable -// 16th bit: -// - 0: use vertical angle variable as vertical angle in degrees -// - 1: mimic P_SpawnXYZMissile -// use z of actor minus z of missile as vertical distance to cover during momz calculation -// use vertical angle variable as horizontal distance to cover during momz calculation -// upper 16 bits = height offset -// -void A_TrapShot(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - boolean oldstyle = (locvar2 & 32768) ? true : false; - mobjtype_t type = (mobjtype_t)(locvar1 & 65535); - mobj_t *missile; - INT16 frontoff = (INT16)(locvar1 >> 16); - INT16 vertoff = (INT16)(locvar2 >> 16); - fixed_t x, y, z; - fixed_t speed; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_TrapShot", actor)) - return; -#endif - - x = actor->x + P_ReturnThrustX(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale)); - y = actor->y + P_ReturnThrustY(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale)); - - if (actor->eflags & MFE_VERTICALFLIP) - z = actor->z + actor->height - FixedMul(vertoff*FRACUNIT, actor->scale) - FixedMul(mobjinfo[type].height, actor->scale); - else - z = actor->z + FixedMul(vertoff*FRACUNIT, actor->scale); - - CONS_Debug(DBG_GAMELOGIC, "A_TrapShot: missile no. = %d, front offset = %d, vertical angle = %d, z offset = %d\n", - type, frontoff, (INT16)(locvar2 & 65535), vertoff); - - missile = P_SpawnMobj(x, y, z, type); - - if (actor->eflags & MFE_VERTICALFLIP) - missile->flags2 |= MF2_OBJECTFLIP; - - missile->destscale = actor->scale; - P_SetScale(missile, actor->scale); - - if (missile->info->seesound) - S_StartSound(missile, missile->info->seesound); - - P_SetTarget(&missile->target, actor); - missile->angle = actor->angle; - - speed = FixedMul(missile->info->speed, missile->scale); - - if (oldstyle) - { - missile->momx = FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed); - missile->momy = FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed); - // The below line basically mimics P_SpawnXYZMissile's momz calculation. - missile->momz = (actor->z + ((actor->eflags & MFE_VERTICALFLIP) ? actor->height : 0) - z) / ((fixed_t)(locvar2 & 32767)*FRACUNIT / speed); - P_CheckMissileSpawn(missile); - } - else - { - angle_t vertang = FixedAngle(((INT16)(locvar2 & 32767))*FRACUNIT); - if (actor->eflags & MFE_VERTICALFLIP) - vertang = InvAngle(vertang); // flip firing angle - missile->momx = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed)); - missile->momy = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed)); - missile->momz = FixedMul(FINESINE(vertang>>ANGLETOFINESHIFT), speed); - } -} - -// Function: A_VileTarget -// -// Description: Spawns an object directly on the target, and sets this object as the actor's tracer. -// Originally used by Archviles to summon a pillar of hellfire, hence the name. -// -// var1 = mobj to spawn -// var2 = If 0, target only the actor's target. Else, target every player, period. -// -void A_VileTarget(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - mobj_t *fog; - mobjtype_t fogtype; - INT32 i; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_VileTarget", actor)) - return; -#endif - - if (!actor->target) - return; - - A_FaceTarget(actor); - - // Determine object to spawn - if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES) - fogtype = MT_CYBRAKDEMON_TARGET_RETICULE; - else - fogtype = (mobjtype_t)locvar1; - - if (!locvar2) - { - fog = P_SpawnMobj(actor->target->x, - actor->target->y, - actor->target->z + ((actor->target->eflags & MFE_VERTICALFLIP) ? actor->target->height - mobjinfo[fogtype].height : 0), - fogtype); - if (actor->target->eflags & MFE_VERTICALFLIP) - { - fog->eflags |= MFE_VERTICALFLIP; - fog->flags2 |= MF2_OBJECTFLIP; - } - fog->destscale = actor->target->scale; - P_SetScale(fog, fog->destscale); - - P_SetTarget(&actor->tracer, fog); - P_SetTarget(&fog->target, actor); - P_SetTarget(&fog->tracer, actor->target); - A_VileFire(fog); - } - else - { - // Our "Archvile" here is actually Oprah. "YOU GET A TARGET! YOU GET A TARGET! YOU ALL GET A TARGET!" - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - - if (!players[i].mo) - continue; - - if (!players[i].mo->health) - continue; - - fog = P_SpawnMobj(players[i].mo->x, - players[i].mo->y, - players[i].mo->z + ((players[i].mo->eflags & MFE_VERTICALFLIP) ? players[i].mo->height - mobjinfo[fogtype].height : 0), - fogtype); - if (players[i].mo->eflags & MFE_VERTICALFLIP) - { - fog->eflags |= MFE_VERTICALFLIP; - fog->flags2 |= MF2_OBJECTFLIP; - } - fog->destscale = players[i].mo->scale; - P_SetScale(fog, fog->destscale); - - if (players[i].mo == actor->target) // We only care to track the fog targeting who we REALLY hate right now - P_SetTarget(&actor->tracer, fog); - P_SetTarget(&fog->target, actor); - P_SetTarget(&fog->tracer, players[i].mo); - A_VileFire(fog); - } - } -} - -// Function: A_VileAttack -// -// Description: Instantly hurts the actor's target, if it's in the actor's line of sight. -// Originally used by Archviles to cause explosions where their hellfire pillars were, hence the name. -// -// var1 = sound to play -// var2: -// Lower 16 bits = optional explosion object -// Upper 16 bits = If 0, attack only the actor's target. Else, attack all the players. All of them. -// -void A_VileAttack(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - sfxenum_t soundtoplay; - mobjtype_t explosionType = MT_NULL; - mobj_t *fire; - INT32 i; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_VileAttack", actor)) - return; -#endif - - if (!actor->target) - return; - - A_FaceTarget(actor); - - if (locvar1 <= 0 || locvar1 >= NUMSFX) - soundtoplay = sfx_brakrx; - else - soundtoplay = (sfxenum_t)locvar1; - - if ((locvar2 & 0xFFFF) > 0 && (locvar2 & 0xFFFF) <= NUMMOBJTYPES) - { - explosionType = (mobjtype_t)(locvar2 & 0xFFFF); - } - - if (!(locvar2 & 0xFFFF0000)) { - if (!P_CheckSight(actor, actor->target)) - return; - - S_StartSound(actor, soundtoplay); - P_DamageMobj(actor->target, actor, actor, 1, 0); - //actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; // How id did it - actor->target->momz += FixedMul(10*FRACUNIT, actor->scale)*P_MobjFlip(actor->target); // How we're doing it - if (explosionType != MT_NULL) - { - P_SpawnMobj(actor->target->x, actor->target->y, actor->target->z, explosionType); - } - - // Extra attack. This was for additional damage in Doom. Doesn't really belong in SRB2, but the heck with it, it's here anyway. - fire = actor->tracer; - - if (!fire) - return; - - // move the fire between the vile and the player - //fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]); - //fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]); - P_TeleportMove(fire, - actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)), - actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)), - fire->z); - P_RadiusAttack(fire, actor, 70*FRACUNIT, 0); - } - else - { - // Oprahvile strikes again, but this time, she brings HOT PAIN - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - - if (!players[i].mo) - continue; - - if (!players[i].mo->health) - continue; - - if (!P_CheckSight(actor, players[i].mo)) - continue; - - S_StartSound(actor, soundtoplay); - P_DamageMobj(players[i].mo, actor, actor, 1, 0); - //actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; // How id did it - players[i].mo->momz += FixedMul(10*FRACUNIT, actor->scale)*P_MobjFlip(players[i].mo); // How we're doing it - if (explosionType != MT_NULL) - { - P_SpawnMobj(players[i].mo->x, players[i].mo->y, players[i].mo->z, explosionType); - } - - // Extra attack. This was for additional damage in Doom. Doesn't really belong in SRB2, but the heck with it, it's here anyway. - // However, it ONLY applies to the actor's target. Nobody else matters! - if (actor->target != players[i].mo) - continue; - - fire = actor->tracer; - - if (!fire) - continue; - - // move the fire between the vile and the player - //fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]); - //fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]); - P_TeleportMove(fire, - actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)), - actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)), - fire->z); - P_RadiusAttack(fire, actor, 70*FRACUNIT, 0); - } - } - -} - -// Function: A_VileFire -// -// Description: Kind of like A_CapeChase; keeps this object in front of its tracer, unless its target can't see it. -// Originally used by Archviles to keep their hellfire pillars on top of the player, hence the name (although it was just "A_Fire" there; added "Vile" to make it more specific). -// Added some functionality to optionally draw a line directly to the enemy doing the targetting. Y'know, to hammer things in a bit. -// -// var1 = sound to play -// var2: -// Lower 16 bits = mobj to spawn (0 doesn't spawn a line at all) -// Upper 16 bits = # to spawn (default is 8) -// -void A_VileFire(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - mobj_t *dest; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_VileFire", actor)) - return; -#endif - - dest = actor->tracer; - if (!dest) - return; - - // don't move it if the vile lost sight - if (!P_CheckSight(actor->target, dest)) - return; - - // keep to same scale and gravity as tracer ALWAYS - actor->destscale = dest->scale; - P_SetScale(actor, actor->destscale); - if (dest->eflags & MFE_VERTICALFLIP) - { - actor->eflags |= MFE_VERTICALFLIP; - actor->flags2 |= MF2_OBJECTFLIP; - } - else - { - actor->eflags &= ~MFE_VERTICALFLIP; - actor->flags2 &= ~MF2_OBJECTFLIP; - } - - P_UnsetThingPosition(actor); - actor->x = dest->x + P_ReturnThrustX(actor, dest->angle, FixedMul(24*FRACUNIT, actor->scale)); - actor->y = dest->y + P_ReturnThrustY(actor, dest->angle, FixedMul(24*FRACUNIT, actor->scale)); - actor->z = dest->z + ((actor->eflags & MFE_VERTICALFLIP) ? dest->height-actor->height : 0); - P_SetThingPosition(actor); - - // Play sound, if one's specified - if (locvar1 > 0 && locvar1 < NUMSFX) - S_StartSound(actor, (sfxenum_t)locvar1); - - // Now draw the line to the actor's target - if (locvar2 & 0xFFFF) - { - mobjtype_t lineMobj; - UINT16 numLineMobjs; - fixed_t distX; - fixed_t distY; - fixed_t distZ; - UINT16 i; - - lineMobj = (mobjtype_t)(locvar2 & 0xFFFF); - numLineMobjs = (UINT16)(locvar2 >> 16); - if (numLineMobjs == 0) { - numLineMobjs = 8; - } - - // Get distance for each step - distX = (actor->target->x - actor->x) / numLineMobjs; - distY = (actor->target->y - actor->y) / numLineMobjs; - distZ = ((actor->target->z + FixedMul(actor->target->height/2, actor->target->scale)) - (actor->z + FixedMul(actor->height/2, actor->scale))) / numLineMobjs; - - for (i = 1; i <= numLineMobjs; i++) - { - P_SpawnMobj(actor->x + (distX * i), actor->y + (distY * i), actor->z + (distZ * i) + FixedMul(actor->height/2, actor->scale), lineMobj); - } - } -} - -// Function: A_BrakChase -// -// Description: Chase after your target, but speed and attack are tied to health. -// -// Every time this is called, generate a random number from a 1/4 to 3/4 of mobj's spawn health. -// If health is above that value, use missilestate to attack. -// If health is at or below that value, use meleestate to attack (default to missile state if not available). -// -// Likewise, state will linearly speed up as health goes down. -// Upper bound will be the frame's normal length. -// Lower bound defaults to 1 tic (technically 0, but we round up), unless a lower bound is specified in var1. -// -// var1 = lower-bound of frame length, in tics -// var2 = optional sound to play -// -void A_BrakChase(mobj_t *actor) -{ - INT32 delta; - INT32 lowerbound; - INT32 newtics; - INT32 locvar1 = var1; - INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_BrakChase", actor)) - return; -#endif - - // Set new tics NOW, in case the state changes while we're doing this and we try applying this to the painstate or something silly - if (actor->tics > 1 && locvar1 < actor->tics) // Not much point, otherwise - { - if (locvar1 < 0) - lowerbound = 0; - else - lowerbound = locvar1; - - newtics = (((actor->tics - lowerbound) * actor->health) / actor->info->spawnhealth) + lowerbound; - if (newtics < 1) - newtics = 1; - - actor->tics = newtics; - } - - if (actor->reactiontime) - { - actor->reactiontime--; - if (actor->reactiontime == 0 && actor->type == MT_CYBRAKDEMON) - S_StartSound(0, sfx_bewar1 + P_RandomKey(4)); - } - - // modify target threshold - if (actor->threshold) - { - if (!actor->target || actor->target->health <= 0) - actor->threshold = 0; - else - actor->threshold--; - } - - // turn towards movement direction if not there yet - if (actor->movedir < NUMDIRS) - { - actor->angle &= (7<<29); - delta = actor->angle - (actor->movedir << 29); - - if (delta > 0) - actor->angle -= ANGLE_45; - else if (delta < 0) - actor->angle += ANGLE_45; - } - - if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) - { - // look for a new target - if (P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - P_SetMobjStateNF(actor, actor->info->spawnstate); - return; - } - - // do not attack twice in a row - if (actor->flags2 & MF2_JUSTATTACKED) - { - actor->flags2 &= ~MF2_JUSTATTACKED; - P_NewChaseDir(actor); - return; - } - - // Check if we can attack - if (P_CheckMissileRange(actor) && !actor->movecount) - { - // Check if we should use "melee" attack first. (Yes, this still runs outside of melee range. Quiet, you.) - if (actor->info->meleestate - && actor->health <= P_RandomRange(actor->info->spawnhealth/4, (actor->info->spawnhealth * 3)/4)) // Guaranteed true if <= 1/4 health, guaranteed false if > 3/4 health - { - if (actor->info->attacksound) - S_StartAttackSound(actor, actor->info->attacksound); - - P_SetMobjState(actor, actor->info->meleestate); - actor->flags2 |= MF2_JUSTATTACKED; - return; - } - // Else, check for missile attack. - else if (actor->info->missilestate) - { - P_SetMobjState(actor, actor->info->missilestate); - actor->flags2 |= MF2_JUSTATTACKED; - return; - } - } - - // possibly choose another target - if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)) - && P_LookForPlayers(actor, true, false, 0)) - return; // got a new target - - // chase towards player - if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed)) - P_NewChaseDir(actor); - - // Optionally play a sound effect - if (locvar2 > 0 && locvar2 < NUMSFX) - S_StartSound(actor, (sfxenum_t)locvar2); - - // make active sound - if (actor->type != MT_CYBRAKDEMON && actor->info->activesound && P_RandomChance(3*FRACUNIT/256)) - { - S_StartSound(actor, actor->info->activesound); - } -} - -// Function: A_BrakFireShot -// -// Description: Shoot an object at your target, offset to match where Brak's gun is. -// Also, sets Brak's reaction time; behaves normally otherwise. -// -// var1 = object # to shoot -// var2 = unused -// -void A_BrakFireShot(mobj_t *actor) -{ - fixed_t x, y, z; - INT32 locvar1 = var1; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_BrakFireShot", actor)) - return; -#endif - if (!actor->target) - return; - - A_FaceTarget(actor); - - x = actor->x - + P_ReturnThrustX(actor, actor->angle, FixedMul(64*FRACUNIT, actor->scale)) - + P_ReturnThrustX(actor, actor->angle+ANGLE_270, FixedMul(32*FRACUNIT, actor->scale)); - y = actor->y - + P_ReturnThrustY(actor, actor->angle, FixedMul(64*FRACUNIT, actor->scale)) - + P_ReturnThrustY(actor, actor->angle+ANGLE_270, FixedMul(32*FRACUNIT, actor->scale)); - if (actor->eflags & MFE_VERTICALFLIP) - z = actor->z + actor->height - FixedMul(144*FRACUNIT, actor->scale); - else - z = actor->z + FixedMul(144*FRACUNIT, actor->scale); - - P_SpawnXYZMissile(actor, actor->target, locvar1, x, y, z); - - if (!(actor->flags & MF_BOSS)) - { - if (ultimatemode) - actor->reactiontime = actor->info->reactiontime*TICRATE; - else - actor->reactiontime = actor->info->reactiontime*TICRATE*2; - } -} - -// Function: A_BrakLobShot -// -// Description: Lobs an object at the floor about a third of the way toward your target. -// Implication is it'll bounce the rest of the way. -// (You can also just aim straight at the target, but whatever) -// Formula grabbed from http://en.wikipedia.org/wiki/Trajectory_of_a_projectile#Angle_required_to_hit_coordinate_.28x.2Cy.29 -// -// var1 = object # to lob -// var2: -// Lower 16 bits: height offset to shoot from, from the actor's bottom (none that "airtime" malarky) -// Upper 16 bits: if 0, aim 1/3 of the way. Else, aim directly at target. -// - -void A_BrakLobShot(mobj_t *actor) -{ - fixed_t v; // Velocity to shoot object - fixed_t a1, a2, aToUse; // Velocity squared - fixed_t g; // Gravity - fixed_t x; // Horizontal difference - INT32 x_int; // x! But in integer form! - fixed_t y; // Vertical difference (yes that's normally z in SRB2 shut up) - INT32 y_int; // y! But in integer form! - INT32 intHypotenuse; // x^2 + y^2. Frequently overflows fixed point, hence why we need integers proper. - fixed_t fixedHypotenuse; // However, we can work around that and still get a fixed-point number. - angle_t theta; // Angle of attack - mobjtype_t typeOfShot; - mobj_t *shot; // Object to shoot - fixed_t newTargetX; // If not aiming directly - fixed_t newTargetY; // If not aiming directly - INT32 locvar1 = var1; - INT32 locvar2 = var2 & 0x0000FFFF; - INT32 aimDirect = var2 & 0xFFFF0000; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_BrakLobShot", actor)) - return; -#endif - - if (!actor->target) - return; // Don't even bother if we've got nothing to aim at. - - // Look up actor's current gravity situation - if (actor->subsector->sector->gravity) - g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000))); - else - g = gravity; - - // Look up distance between actor and its target - x = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y); - if (!aimDirect) - { - // Distance should actually be a third of the way over - x = FixedDiv(x, 3<x + P_ReturnThrustX(actor, actor->angle, x); - newTargetY = actor->y + P_ReturnThrustY(actor, actor->angle, x); - x = P_AproxDistance(newTargetX - actor->x, newTargetY - actor->y); - // Look up height difference between actor and the ground 1/3 of the way to its target - y = P_FloorzAtPos(newTargetX, newTargetY, actor->target->z, actor->target->height) - (actor->z + FixedMul(locvar2*FRACUNIT, actor->scale)); - } - else - { - // Look up height difference between actor and its target - y = actor->target->z - (actor->z + FixedMul(locvar2*FRACUNIT, actor->scale)); - } - - // Get x^2 + y^2. Have to do it in a roundabout manner, because this overflows fixed_t way too easily otherwise. - x_int = x>>FRACBITS; - y_int = y>>FRACBITS; - intHypotenuse = (x_int*x_int) + (y_int*y_int); - fixedHypotenuse = FixedSqrt(intHypotenuse) *256; - - // a = g(y+/-sqrt(x^2+y^2)). a1 can be +, a2 can be -. - a1 = FixedMul(g,y+fixedHypotenuse); - a2 = FixedMul(g,y-fixedHypotenuse); - - // Determine which one isn't actually an imaginary number (or the smaller of the two, if both are real), and use that for v. - if (a1 < 0 || a2 < 0) - { - if (a1 < 0 && a2 < 0) - { - //Somehow, v^2 is negative in both cases. v is therefore imaginary and something is horribly wrong. Abort! - return; - } - // Just find which one's NOT negative, and use that - aToUse = max(a1,a2); - } - else - { - // Both are positive; use whichever's smaller so it can decay faster - aToUse = min(a1,a2); - } - v = FixedSqrt(aToUse); - // Okay, so we know the velocity. Let's actually find theta. - // We can cut the "+/- sqrt" part out entirely, since v was calculated specifically for it to equal zero. So: - //theta = tantoangle[FixedDiv(aToUse,FixedMul(g,x)) >> DBITS]; - theta = tantoangle[SlopeDiv(aToUse,FixedMul(g,x))]; - - // Okay, complicated math done. Let's fire our object already, sheesh. - A_FaceTarget(actor); - if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES) - typeOfShot = MT_CANNONBALL; - else typeOfShot = (mobjtype_t)locvar1; - shot = P_SpawnMobj(actor->x, actor->y, actor->z + FixedMul(locvar2*FRACUNIT, actor->scale), typeOfShot); - if (shot->info->seesound) - S_StartSound(shot, shot->info->seesound); - P_SetTarget(&shot->target, actor); // where it came from - - shot->angle = actor->angle; - - // Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle. - shot->momx = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINECOSINE(shot->angle >> ANGLETOFINESHIFT)); - shot->momy = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINESINE(shot->angle >> ANGLETOFINESHIFT)); - // Then the vertical axis. No angle-correction needed here. - shot->momz = FixedMul(v, FINESINE(theta >> ANGLETOFINESHIFT)); - // I hope that's all that's needed, ugh -} - -// Function: A_NapalmScatter -// -// Description: Scatters a specific number of projectiles around in a circle. -// Intended for use with objects that are affected by gravity; would be kind of silly otherwise. -// -// var1: -// Lower 16 bits: object # to lob (TODO: come up with a default) -// Upper 16 bits: Number to lob (default 8) -// var2: -// Lower 16 bits: distance to toss them (No default - 0 does just that - but negatives will revert to 128) -// Upper 16 bits: airtime in tics (default 16) -// -void A_NapalmScatter(mobj_t *actor) -{ - mobjtype_t typeOfShot = var1 & 0x0000FFFF; // Type - INT32 numToShoot = (var1 & 0xFFFF0000) >> 16; // How many - fixed_t distance = (var2 & 0x0000FFFF) << FRACBITS; // How far - fixed_t airtime = var2 & 0xFFFF0000; // How long until impact (assuming no obstacles) - fixed_t vx; // Horizontal momentum - fixed_t vy; // Vertical momentum - fixed_t g; // Gravity - INT32 i; // for-loop cursor - mobj_t *mo; // each and every spawned napalm burst - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_NapalmScatter", actor)) - return; -#endif - - // Some quick sanity-checking - if (typeOfShot >= NUMMOBJTYPES) // I'd add a <0 check, too, but 0x0000FFFF isn't negative in this case - typeOfShot = MT_NULL; - if (numToShoot <= 0) // Presumably you forgot to set var1 up; else, why are you calling this to shoot nothing? - numToShoot = 8; - else if (numToShoot > 8192) // If you seriously need this many objects spawned, stop and ask yourself "Why am I doing this?" - numToShoot = 8192; - if (distance < 0) // Presumably you thought this was an unsigned integer, you naive fool - distance = 32767<subsector->sector->gravity) - g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000))); - else - g = gravity; - - // vy = (g*(airtime-1))/2 - vy = FixedMul(g,(airtime-(1<>1; - // vx = distance/airtime - vx = FixedDiv(distance, airtime); - - for (i = 0; ix, actor->y, actor->z, typeOfShot); - P_SetTarget(&mo->target, actor->target); // Transfer target so Brak doesn't hit himself like an idiot - - mo->angle = fa << ANGLETOFINESHIFT; - mo->momx = FixedMul(FINECOSINE(fa),vx); - mo->momy = FixedMul(FINESINE(fa),vx); - mo->momz = vy; - } -} - -// Function: A_SpawnFreshCopy -// -// Description: Spawns a copy of the mobj. x, y, z, angle, scale, target and tracer carry over; everything else starts anew. -// Mostly writing this because I want to do multiple actions to pass these along in a single frame instead of several. -// -// var1 = unused -// var2 = unused -// -void A_SpawnFreshCopy(mobj_t *actor) -{ - mobj_t *newObject; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SpawnFreshCopy", actor)) - return; -#endif - - newObject = P_SpawnMobjFromMobj(actor, 0, 0, 0, actor->type); - newObject->flags2 = actor->flags2 & MF2_AMBUSH; - newObject->angle = actor->angle; - newObject->color = actor->color; - P_SetTarget(&newObject->target, actor->target); - P_SetTarget(&newObject->tracer, actor->tracer); - - if (newObject->info->seesound) - S_StartSound(newObject, newObject->info->seesound); -} - -// Internal Flicky spawning function. -mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers) -{ - mobj_t *flicky; - - if (!flickytype) - { - if (!mapheaderinfo[gamemap-1] || !mapheaderinfo[gamemap-1]->numFlickies) // No mapheader, no shoes, no service. - return NULL; - else - { - INT32 prandom = P_RandomKey(mapheaderinfo[gamemap-1]->numFlickies); - flickytype = mapheaderinfo[gamemap-1]->flickies[prandom]; - } - } - - flicky = P_SpawnMobjFromMobj(actor, 0, 0, 0, flickytype); - flicky->angle = actor->angle; - - if (flickytype == MT_SEED) - flicky->z += P_MobjFlip(actor)*(actor->height - flicky->height)/2; - - if (actor->eflags & MFE_UNDERWATER) - momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT)); - - P_SetObjectMomZ(flicky, momz, false); - flicky->movedir = (P_RandomChance(FRACUNIT/2) ? -1 : 1); - flicky->fuse = P_RandomRange(595, 700); // originally 300, 350 - flicky->threshold = 0; - - if (lookforplayers) - P_LookForPlayers(flicky, true, false, 0); - - return flicky; -} - -// Function: A_FlickySpawn -// -// Description: Flicky spawning function. -// -// var1: -// lower 16 bits: if 0, spawns random flicky based on level header. Else, spawns the designated thing type. -// upper 16 bits: if 0, no sound is played. Else, A_Scream is called. -// var2 = upwards thrust for spawned flicky. If zero, default value is provided. -// -void A_FlickySpawn(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FlickySpawn", actor)) - return; -#endif - - if (locvar1 >> 16) { - A_Scream(actor); // A shortcut for the truly lazy. - locvar1 &= 65535; - } - - P_InternalFlickySpawn(actor, locvar1, ((locvar2) ? locvar2 : 8*FRACUNIT), true); -} - -// Internal Flicky color setting -void P_InternalFlickySetColor(mobj_t *actor, UINT8 extrainfo) -{ - UINT8 flickycolors[] = { - SKINCOLOR_RED, - SKINCOLOR_CYAN, - SKINCOLOR_BLUE, - SKINCOLOR_VAPOR, - SKINCOLOR_PURPLE, - SKINCOLOR_BUBBLEGUM, - SKINCOLOR_NEON, - SKINCOLOR_BLACK, - SKINCOLOR_BEIGE, - SKINCOLOR_LAVENDER, - SKINCOLOR_RUBY, - SKINCOLOR_SALMON, - SKINCOLOR_SUNSET, - SKINCOLOR_ORANGE, - SKINCOLOR_YELLOW, - }; - - if (extrainfo == 0) - actor->color = flickycolors[P_RandomKey(sizeof(flickycolors))]; - else if (extrainfo-1 < sizeof(flickycolors)) - actor->color = flickycolors[extrainfo-1]; -} - -// Function: A_FlickyCenter -// -// Description: Place flickies in-level. -// -// var1: -// Lower 16 bits = if 0, spawns random flicky based on level header. Else, spawns the designated thing type. -// Bits 17-20 = Flicky color, up to 15. Applies to fish. -// Bit 21 = Flag MF_SLIDEME (see below) -// Bit 22 = Flag MF_GRENADEBOUNCE (see below) -// Bit 23 = Flag MF_NOCLIPTHING (see below) -// -// If actor is placed from a spawnpoint (map Thing), the Thing's properties take precedence. -// -// var2 = maximum default distance away from spawn the flickies are allowed to travel. If angle != 0, then that's the radius. -// -// If MTF_EXTRA (MF_SLIDEME): is flagged, Flickies move aimlessly. Else, orbit around the target. -// If MTF_OBJECTSPECIAL (MF_GRENADEBOUNCE): Flickies stand in-place without gravity (unless they hop, then gravity is applied.) -// If MTF_AMBUSH (MF_NOCLIPTHING): is flagged, Flickies hop. -// -void A_FlickyCenter(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - UINT16 flickytype = (locvar1 & 0xFFFF); - UINT8 flickycolor = ((locvar1 >> 16) & 0xFF); - UINT8 flickyflags = ((locvar1 >> 20) & 0xF); -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FlickyCenter", actor)) - return; -#endif - - if (!actor->tracer) - { - mobj_t *flicky = P_InternalFlickySpawn(actor, locvar1, 1, false); - P_SetTarget(&flicky->target, actor); - P_SetTarget(&actor->tracer, flicky); - - if (actor->spawnpoint) - { - actor->flags &= ~(MF_SLIDEME|MF_GRENADEBOUNCE|MF_NOCLIPTHING); - actor->flags |= ( - ((actor->spawnpoint->options & MTF_EXTRA) ? MF_SLIDEME : 0) - | ((actor->spawnpoint->options & MTF_OBJECTSPECIAL) ? MF_GRENADEBOUNCE : 0) - | ((actor->spawnpoint->options & MTF_AMBUSH) ? MF_NOCLIPTHING : 0) - ); - actor->extravalue1 = abs(actor->spawnpoint->angle) * FRACUNIT; - actor->extravalue2 = actor->spawnpoint->extrainfo; - actor->friction = actor->spawnpoint->x*FRACUNIT; - actor->movefactor = actor->spawnpoint->y*FRACUNIT; - actor->watertop = actor->spawnpoint->z*FRACUNIT; - } - else - { - actor->flags &= ~(MF_SLIDEME|MF_GRENADEBOUNCE|MF_NOCLIPTHING); - actor->flags |= ( - ((flickyflags & 1) ? MF_SLIDEME : 0) - | ((flickyflags & 2) ? MF_GRENADEBOUNCE : 0) - | ((flickyflags & 4) ? MF_NOCLIPTHING : 0) - ); - actor->extravalue1 = abs(locvar2); - actor->extravalue2 = flickycolor; - actor->friction = actor->x; - actor->movefactor = actor->y; - actor->watertop = actor->z; - locvar1 = flickytype; - } - - if (actor->flags & MF_GRENADEBOUNCE) // in-place - actor->tracer->fuse = 0; - else if (actor->flags & MF_SLIDEME) // aimless - { - actor->tracer->fuse = 0; // less than 2*TICRATE means move aimlessly. - actor->tracer->angle = P_RandomKey(180)*ANG2; - } - else //orbit - { - actor->tracer->fuse = FRACUNIT; - // Impose default home radius if flicky orbits around player - if (!actor->extravalue1) - actor->extravalue1 = 512 * FRACUNIT; - } - - if (locvar1 == MT_FLICKY_08) - P_InternalFlickySetColor(actor->tracer, actor->extravalue2); - - actor->extravalue2 = 0; - } - - if (!(actor->flags & MF_SLIDEME) && !(actor->flags & MF_GRENADEBOUNCE)) - { - fixed_t originx = actor->friction; - fixed_t originy = actor->movefactor; - fixed_t originz = actor->watertop; - - actor->tracer->fuse = FRACUNIT; - - P_LookForPlayers(actor, true, false, locvar2); - - if (actor->target && P_AproxDistance(actor->target->x - originx, actor->target->y - originy) < actor->extravalue1) - { - actor->extravalue2 = 1; - P_TeleportMove(actor, actor->target->x, actor->target->y, actor->target->z); - } - else if(actor->extravalue2) - { - actor->extravalue2 = 0; - P_TeleportMove(actor, originx, originy, originz); - } - } -} - -// Internal Flicky bubbling function. -void P_InternalFlickyBubble(mobj_t *actor) -{ - if (actor->eflags & MFE_UNDERWATER) - { - mobj_t *overlay; - - if (!((actor->z + 3*actor->height/2) < actor->watertop) || !mobjinfo[actor->type].raisestate || actor->tracer) - return; - - overlay = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY); - P_SetMobjStateNF(overlay, mobjinfo[actor->type].raisestate); - P_SetTarget(&actor->tracer, overlay); - P_SetTarget(&overlay->target, actor); - return; - } - - if (!actor->tracer || P_MobjWasRemoved(actor->tracer)) - return; - - P_RemoveMobj(actor->tracer); - P_SetTarget(&actor->tracer, NULL); -} - -// Function: A_FlickyAim -// -// Description: Flicky aiming function. -// -// var1 = how far around the target (in angle constants) the flicky should look -// var2 = distance from target to aim for -// -void A_FlickyAim(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - boolean flickyhitwall = false; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FlickyAim", actor)) - return; -#endif - - if (actor->momx == actor->momy && actor->momy == 0) - flickyhitwall = true; - - P_InternalFlickyBubble(actor); - P_InstaThrust(actor, 0, 0); - - if (!actor->target) - { - P_LookForPlayers(actor, true, false, 0); - actor->angle = P_RandomKey(36)*ANG10; - return; - } - - if (actor->fuse > 2*TICRATE) - { - angle_t posvar; - fixed_t chasevar, chasex, chasey; - - if (flickyhitwall) - actor->movedir *= -1; - - posvar = ((R_PointToAngle2(actor->target->x, actor->target->y, actor->x, actor->y) + actor->movedir*locvar1) >> ANGLETOFINESHIFT) & FINEMASK; - chasevar = FixedSqrt(max(FRACUNIT, P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y) - locvar2)) + locvar2; - - chasex = actor->target->x + FixedMul(FINECOSINE(posvar), chasevar); - chasey = actor->target->y + FixedMul(FINESINE(posvar), chasevar); - - if (P_AproxDistance(chasex - actor->x, chasey - actor->y)) - actor->angle = R_PointToAngle2(actor->x, actor->y, chasex, chasey); - } - else if (flickyhitwall) - { - actor->angle += ANGLE_180; - actor->threshold = 0; - } -} - -//Internal Flicky flying function. Also usuable as an underwater swim thrust. -void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez) -{ - angle_t vertangle; - - flyspeed = FixedMul(flyspeed, actor->scale); - actor->flags |= MF_NOGRAVITY; - - var1 = ANG30; - var2 = 32*FRACUNIT; - A_FlickyAim(actor); - - chasez *= 8; - if (!actor->target || !(actor->fuse > 2*TICRATE)) - chasez += ((actor->eflags & MFE_VERTICALFLIP) ? actor->ceilingz - 24*FRACUNIT : actor->floorz + 24*FRACUNIT); - else - { - fixed_t add = actor->target->z + (actor->target->height - actor->height)/2; - if (add > (actor->ceilingz - 24*actor->scale - actor->height)) - add = actor->ceilingz - 24*actor->scale - actor->height; - else if (add < (actor->floorz + 24*actor->scale)) - add = actor->floorz + 24*actor->scale; - chasez += add; - } - - if (!targetdist) - targetdist = 16*FRACUNIT; //Default! - - if (actor->target && abs(chasez - actor->z) > targetdist) - targetdist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y); - - if (actor->target - && P_IsFlickyCenter(actor->target->type) - && (actor->target->flags & MF_SLIDEME)) - vertangle = 0; - else - vertangle = (R_PointToAngle2(0, actor->z, targetdist, chasez) >> ANGLETOFINESHIFT) & FINEMASK; - - P_InstaThrust(actor, actor->angle, FixedMul(FINECOSINE(vertangle), flyspeed)); - actor->momz = FixedMul(FINESINE(vertangle), flyspeed); -} - -// Function: A_FlickyFly -// -// Description: Flicky flying function. -// -// var1 = how fast to fly -// var2 = how far ahead the target should be considered -// -// If MTF_EXTRA and MTF_OBJECTSPECIAL are flagged, Flicky will always fly at same Z height. -// -void A_FlickyFly(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FlickyFly", actor)) - return; -#endif - P_InternalFlickyFly(actor, locvar1, locvar2, - FINECOSINE((((actor->fuse % 36) * ANG10) >> ANGLETOFINESHIFT) & FINEMASK) - ); -} - -// Function: A_FlickySoar -// -// Description: Flicky soaring function - specific to puffin. -// -// var1 = how fast to fly -// var2 = how far ahead the target should be considered -// -// If MTF_EXTRA and MTF_OBJECTSPECIAL are flagged, Flicky will always fly at same Z height. -// -void A_FlickySoar(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FlickySoar", actor)) - return; -#endif - P_InternalFlickyFly(actor, locvar1, locvar2, - 2*(FRACUNIT/2 - abs(FINECOSINE((((actor->fuse % 144) * 5*ANG1/2) >> ANGLETOFINESHIFT) & FINEMASK))) - ); - - if (P_MobjFlip(actor)*actor->momz > 0 && actor->frame == 1 && actor->sprite == SPR_FL10) - actor->frame = 3; -} - -//Function: A_FlickyCoast -// -// Description: Flicky swim-coasting function. -// -// var1 = speed to change state upon reaching -// var2 = state to change to upon slowing down -// the spawnstate of the mobj = state to change to when above water -// -void A_FlickyCoast(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FlickyCoast", actor)) - return; -#endif - if (actor->eflags & MFE_UNDERWATER) - { - actor->momx = (11*actor->momx)/12; - actor->momy = (11*actor->momy)/12; - actor->momz = (11*actor->momz)/12; - - if (P_AproxDistance(P_AproxDistance(actor->momx, actor->momy), actor->momz) < locvar1) - P_SetMobjState(actor, locvar2); - - return; - } - - actor->flags &= ~MF_NOGRAVITY; - P_SetMobjState(actor, mobjinfo[actor->type].spawnstate); -} - -// Internal Flicky hopping function. -void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle) -{ - if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz) - || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz))) - { - if (momz) - { - if (actor->eflags & MFE_UNDERWATER) - momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT)); - P_SetObjectMomZ(actor, momz, false); - } - P_InstaThrust(actor, angle, FixedMul(momh, actor->scale)); - } -} - -// Function: A_FlickyHop -// -// Description: Flicky hopping function. -// -// var1 = vertical thrust -// var2 = horizontal thrust -// -void A_FlickyHop(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FlickyHop", actor)) - return; -#endif - P_InternalFlickyHop(actor, locvar1, locvar2, actor->angle); -} - -// Function: A_FlickyFlounder -// -// Description: Flicky floundering function. -// -// var1 = intended vertical thrust -// var2 = intended horizontal thrust -// -void A_FlickyFlounder(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - angle_t hopangle; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FlickyFlounder", actor)) - return; -#endif - locvar1 *= (P_RandomKey(2) + 1); - locvar2 *= (P_RandomKey(2) + 1); - hopangle = (actor->angle + (P_RandomKey(9) - 4)*ANG2); - P_InternalFlickyHop(actor, locvar1, locvar2, hopangle); -} - -// Function: A_FlickyCheck -// -// Description: Flicky airtime check function. -// -// var1 = state to change to upon touching the floor -// var2 = state to change to upon falling -// the meleestate of the mobj = state to change to when underwater -// -void A_FlickyCheck(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FlickyCheck", actor)) - return; -#endif - if (actor->target - && P_IsFlickyCenter(actor->target->type) - && (actor->target->flags & MF_GRENADEBOUNCE)) - { - if (!(actor->target->flags & MF_NOCLIPTHING)) // no hopping - { - actor->momz = 0; - actor->flags |= MF_NOGRAVITY; - } - actor->flags |= MF_NOCLIP | MF_NOBLOCKMAP | MF_SCENERY; - P_SetMobjState(actor, mobjinfo[actor->type].seestate); - } - else if (locvar2 && P_MobjFlip(actor)*actor->momz < 1) - P_SetMobjState(actor, locvar2); - else if (locvar1 && ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz) - || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz))) - P_SetMobjState(actor, locvar1); - else if (mobjinfo[actor->type].meleestate && (actor->eflags & MFE_UNDERWATER)) - P_SetMobjState(actor, mobjinfo[actor->type].meleestate); - P_InternalFlickyBubble(actor); -} - -// Function: A_FlickyHeightCheck -// -// Description: Flicky height check function. -// -// var1 = state to change to when falling below height relative to target -// var2 = height relative to target to change state at -// -void A_FlickyHeightCheck(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FlickyHeightCheck", actor)) - return; -#endif - if (actor->target - && P_IsFlickyCenter(actor->target->type) - && (actor->target->flags & MF_GRENADEBOUNCE)) - { - if (!(actor->target->flags & MF_NOCLIPTHING)) // no hopping - { - actor->momz = 0; - actor->flags |= MF_NOGRAVITY; - } - actor->flags |= MF_NOCLIP | MF_NOBLOCKMAP | MF_SCENERY; - P_SetMobjState(actor, mobjinfo[actor->type].seestate); - } - else if (locvar1 && actor->target && P_MobjFlip(actor)*actor->momz < 1 - && ((P_MobjFlip(actor)*((actor->z + actor->height/2) - (actor->target->z + actor->target->height/2)) < locvar2) - || (actor->z - actor->height < actor->floorz) || (actor->z + 2*actor->height > actor->ceilingz))) - P_SetMobjState(actor, locvar1); - P_InternalFlickyBubble(actor); -} - -// Function: A_FlickyFlutter -// -// Description: Flicky fluttering function - specific to chicken. -// -// var1 = state to change to upon touching the floor -// var2 = state to change to upon falling -// the meleestate of the mobj = state to change to when underwater -// -void A_FlickyFlutter(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FlickyFlutter", actor)) - return; -#endif - var1 = locvar1; - var2 = locvar2; - A_FlickyCheck(actor); - - var1 = ANG30; - var2 = 32*FRACUNIT; - A_FlickyAim(actor); - - P_InstaThrust(actor, actor->angle, 2*actor->scale); - if (P_MobjFlip(actor)*actor->momz < -FRACUNIT/2) - actor->momz = -P_MobjFlip(actor)*actor->scale/2; -} - -#undef FLICKYHITWALL - -// Function: A_FlameParticle -// -// Description: Creates the mobj's painchance at a random position around the object's radius. -// -// var1 = unused -// var2 = unused -// -void A_FlameParticle(mobj_t *actor) -{ - mobjtype_t type = (mobjtype_t)(mobjinfo[actor->type].painchance); - fixed_t rad, hei; - mobj_t *particle; - //INT32 locvar1 = var1; - //INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FlameParticle", actor)) - return; -#endif - - if (!type) - return; - - rad = actor->radius>>FRACBITS; - hei = actor->height>>FRACBITS; - particle = P_SpawnMobjFromMobj(actor, - P_RandomRange(rad, -rad)<frame = actor->frame; - - if (!(locvar1 & 1)) - { - fade->fuse = 15; - fade->flags2 |= MF2_BOSSNOTRAP; - } - else - fade->fuse = 20; - - if (!(locvar1 & 2)) - P_SetTarget(&actor->tracer, fade); -} - -// Function: A_Boss5Jump -// -// Description: Makes an object jump in an arc to land on their tracer precicely. -// Adapted from A_BrakLobShot, see there for explanation. -// -// var1 = unused -// var2 = unused -// -void A_Boss5Jump(mobj_t *actor) -{ - fixed_t v; // Velocity to jump at - fixed_t a1, a2, aToUse; // Velocity squared - fixed_t g; // Gravity - fixed_t x; // Horizontal difference - INT32 x_int; // x! But in integer form! - fixed_t y; // Vertical difference (yes that's normally z in SRB2 shut up) - INT32 y_int; // y! But in integer form! - INT32 intHypotenuse; // x^2 + y^2. Frequently overflows fixed point, hence why we need integers proper. - fixed_t fixedHypotenuse; // However, we can work around that and still get a fixed-point number. - angle_t theta; // Angle of attack - // INT32 locvar1 = var1; - // INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Boss5Jump", actor)) - return; -#endif - - if (!actor->tracer) - return; // Don't even bother if we've got nothing to aim at. - - // Look up actor's current gravity situation - if (actor->subsector->sector->gravity) - g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000))); - else - g = gravity; - - // Look up distance between actor and its tracer - x = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y); - // Look up height difference between actor and its tracer - y = actor->tracer->z - actor->z; - - // Get x^2 + y^2. Have to do it in a roundabout manner, because this overflows fixed_t way too easily otherwise. - x_int = x>>FRACBITS; - y_int = y>>FRACBITS; - intHypotenuse = (x_int*x_int) + (y_int*y_int); - fixedHypotenuse = FixedSqrt(intHypotenuse) *256; - - // a = g(y+/-sqrt(x^2+y^2)). a1 can be +, a2 can be -. - a1 = FixedMul(g,y+fixedHypotenuse); - a2 = FixedMul(g,y-fixedHypotenuse); - - // Determine which one isn't actually an imaginary number (or the smaller of the two, if both are real), and use that for v. - if (a1 < 0 || a2 < 0) - { - if (a1 < 0 && a2 < 0) - { - //Somehow, v^2 is negative in both cases. v is therefore imaginary and something is horribly wrong. Abort! - return; - } - // Just find which one's NOT negative, and use that - aToUse = max(a1,a2); - } - else - { - // Both are positive; use whichever's smaller so it can decay faster - aToUse = min(a1,a2); - } - v = FixedSqrt(aToUse); - // Okay, so we know the velocity. Let's actually find theta. - // We can cut the "+/- sqrt" part out entirely, since v was calculated specifically for it to equal zero. So: - //theta = tantoangle[FixedDiv(aToUse,FixedMul(g,x)) >> DBITS]; - theta = tantoangle[SlopeDiv(aToUse,FixedMul(g,x))]; - - // Okay, complicated math done. Let's make this object jump already. - A_FaceTracer(actor); - - if (actor->eflags & MFE_VERTICALFLIP) - actor->z--; - else - actor->z++; - - // Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle. - fixedHypotenuse = FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)); // variable reuse - actor->momx = FixedMul(fixedHypotenuse, FINECOSINE(actor->angle >> ANGLETOFINESHIFT)); - actor->momy = FixedMul(fixedHypotenuse, FINESINE(actor->angle >> ANGLETOFINESHIFT)); - // Then the vertical axis. No angle-correction needed here. - actor->momz = FixedMul(v, FINESINE(theta >> ANGLETOFINESHIFT)); - // I hope that's all that's needed, ugh -} - -// Function: A_LightBeamReset -// Description: Resets momentum and position for DSZ's projecting light beams -// -// var1 = unused -// var2 = unused -// -void A_LightBeamReset(mobj_t *actor) -{ - // INT32 locvar1 = var1; - // INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_LightBeamReset", actor)) - return; -#endif - - actor->destscale = FRACUNIT + P_SignedRandom()*FRACUNIT/256; - P_SetScale(actor, actor->destscale); - - if (!actor->spawnpoint) - return; // this can't work properly welp - - actor->momx = -(P_SignedRandom()*FINESINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/128; - actor->momy = (P_SignedRandom()*FINECOSINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/128; - actor->momz = (P_SignedRandom()*FRACUNIT)/128; - - P_TeleportMove(actor, - actor->spawnpoint->x*FRACUNIT - (P_SignedRandom()*FINESINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/2, - actor->spawnpoint->y*FRACUNIT + (P_SignedRandom()*FINECOSINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/2, - actor->spawnpoint->z*FRACUNIT + (P_SignedRandom()*FRACUNIT)/2); -} - -// Function: A_MineExplode -// Description: Handles the explosion of a DSZ mine. -// -// var1 = unused -// var2 = unused -// -void A_MineExplode(mobj_t *actor) -{ - // INT32 locvar1 = var1; - // INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_MineExplode", actor)) - return; -#endif - - A_Scream(actor); - actor->flags = MF_NOGRAVITY|MF_NOCLIP; - - quake.epicenter = NULL; - quake.radius = 512*FRACUNIT; - quake.intensity = 8*FRACUNIT; - quake.time = TICRATE/3; - - P_RadiusAttack(actor, actor->tracer, 192*FRACUNIT, DMG_CANHURTSELF); - P_MobjCheckWater(actor); - - { -#define dist 64 - UINT8 i; - mobjtype_t type = ((actor->eflags & MFE_UNDERWATER) ? MT_UWEXPLODE : MT_BOSSEXPLODE); - S_StartSound(actor, ((actor->eflags & MFE_UNDERWATER) ? sfx_s3k57 : sfx_s3k4e)); - P_SpawnMobj(actor->x, actor->y, actor->z, type); - for (i = 0; i < 16; i++) - { - mobj_t *b = P_SpawnMobj(actor->x+P_RandomRange(-dist, dist)*FRACUNIT, - actor->y+P_RandomRange(-dist, dist)*FRACUNIT, - actor->z+P_RandomRange(((actor->eflags & MFE_UNDERWATER) ? -dist : 0), dist)*FRACUNIT, - type); - fixed_t dx = b->x - actor->x, dy = b->y - actor->y, dz = b->z - actor->z; - fixed_t dm = P_AproxDistance(dz, P_AproxDistance(dy, dx)); - b->momx = FixedDiv(dx, dm)*3; - b->momy = FixedDiv(dy, dm)*3; - b->momz = FixedDiv(dz, dm)*3; - if ((actor->watertop == INT32_MAX) || (b->z + b->height > actor->watertop)) - b->flags &= ~MF_NOGRAVITY; - } -#undef dist - - if (actor->watertop != INT32_MAX) - P_SpawnMobj(actor->x, actor->y, actor->watertop, MT_SPLISH); - } -} - -// Function: A_MineRange -// Description: If the target gets too close, change the state to meleestate. -// -// var1 = Distance to alert at -// var2 = unused -// -void A_MineRange(mobj_t *actor) -{ - fixed_t dm; - INT32 locvar1 = var1; - // INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_MineRange", actor)) - return; -#endif - - if (!actor->target) - return; - - dm = P_AproxDistance(actor->z - actor->target->z, P_AproxDistance(actor->y - actor->target->y, actor->x - actor->target->x)); - if ((dm>>FRACBITS) < locvar1) - P_SetMobjState(actor, actor->info->meleestate); -} - -// Function: A_ConnectToGround -// Description: Create a palm tree trunk/mine chain. -// -// var1 = Object type to connect to ground -// var2 = Object type to place on ground -// -void A_ConnectToGround(mobj_t *actor) -{ - mobj_t *work; - fixed_t workz; - fixed_t workh; - SINT8 dir; - angle_t ang; - INT32 locvar1 = var1; - INT32 locvar2 = var2; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ConnectToGround", actor)) - return; -#endif - - if (actor->subsector->sector->ffloors) - P_AdjustMobjFloorZ_FFloors(actor, actor->subsector->sector, 2); - - if (actor->flags2 & MF2_OBJECTFLIP) - { - workz = actor->ceilingz - (actor->z + actor->height); - dir = -1; - } - else - { - workz = actor->floorz - actor->z; - dir = 1; - } - - if (locvar2) - { - workh = FixedMul(mobjinfo[locvar2].height, actor->scale); - if (actor->flags2 & MF2_OBJECTFLIP) - workz -= workh; - work = P_SpawnMobjFromMobj(actor, 0, 0, workz, locvar2); - workz += dir*workh; - } - - if (!locvar1) - return; - - if (!(workh = FixedMul(mobjinfo[locvar1].height, actor->scale))) - return; - - if (actor->flags2 & MF2_OBJECTFLIP) - workz -= workh; - - ang = actor->angle + ANGLE_45; - while (dir*workz < 0) - { - work = P_SpawnMobjFromMobj(actor, 0, 0, workz, locvar1); - if (work) - work->angle = ang; - ang += ANGLE_90; - workz += dir*workh; - } - - if (workz != 0) - actor->z += workz; -} - -// Function: A_SpawnParticleRelative -// -// Description: Spawns a particle effect relative to the location of the actor -// -// var1: -// var1 >> 16 = x -// var1 & 65535 = y -// var2: -// var2 >> 16 = z -// var2 & 65535 = state -// -void A_SpawnParticleRelative(mobj_t *actor) -{ - INT16 x, y, z; // Want to be sure we can use negative values - statenum_t state; - mobj_t *mo; - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SpawnParticleRelative", actor)) - return; -#endif - - CONS_Debug(DBG_GAMELOGIC, "A_SpawnParticleRelative called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); - - x = (INT16)(locvar1>>16); - y = (INT16)(locvar1&65535); - z = (INT16)(locvar2>>16); - state = (statenum_t)(locvar2&65535); - - // Spawn objects correctly in reverse gravity. - // NOTE: Doing actor->z + actor->height is the bottom of the object while the object has reverse gravity. - Flame - mo = P_SpawnMobj(actor->x + FixedMul(x<scale), - actor->y + FixedMul(y<scale), - (actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[MT_PARTICLE].height) - FixedMul(z<scale)) : (actor->z + FixedMul(z<scale)), MT_PARTICLE); - - // Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn - mo->angle = actor->angle; - - if (actor->eflags & MFE_VERTICALFLIP) - mo->flags2 |= MF2_OBJECTFLIP; - - P_SetMobjState(mo, state); -} - -// Function: A_MultiShotDist -// -// Description: Spawns multiple shots based on player proximity -// -// var1 = same as A_MultiShot -// var2 = same as A_MultiShot -// -void A_MultiShotDist(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_MultiShotDist", actor)) - return; -#endif - - { - UINT8 i; - // Quick! Look through players! - // Don't spawn dust unless a player is relatively close by (var1). - for (i = 0; i < MAXPLAYERS; ++i) - if (playeringame[i] && players[i].mo - && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (1600<> 16 = mobjtype of child -// var2 & 65535 = vertical momentum -// var2: -// var2 >> 16 = forward offset -// var2 & 65535 = vertical offset -// -void A_WhoCaresIfYourSonIsABee(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; - fixed_t foffsetx; - fixed_t foffsety; - mobj_t *son; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_WhoCaresIfYourSonIsABee", actor)) - return; -#endif - - A_FaceTarget(actor); - - if (actor->extravalue1) - actor->extravalue1--; - - if (actor->info->attacksound) - S_StartSound(actor, actor->info->attacksound); - - foffsetx = P_ReturnThrustX(actor, actor->angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale)); - foffsety = P_ReturnThrustY(actor, actor->angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale)); - - if (!(son = P_SpawnMobjFromMobj(actor, foffsetx, foffsety, (locvar2&65535)*FRACUNIT, (mobjtype_t)(locvar1 >> 16)))) - return; - - P_SetObjectMomZ(son, (locvar1 & 65535)<tracer, actor); - P_SetTarget(&son->target, actor->target); -} - -// Function: A_ParentTriesToSleep -// -// Description: If extravalue1 is less than or equal to var1, go to var2. -// -// var1 = state to go to when extravalue1 -// var2 = unused -// -void A_ParentTriesToSleep(mobj_t *actor) -{ - INT32 locvar1 = var1; - //INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ParentTriesToSleep", actor)) - return; -#endif - - if (actor->extravalue1) - { - if (actor->info->seesound) - S_StartSound(actor, actor->info->seesound); - actor->reactiontime = 0; - P_SetMobjState(actor, locvar1); - } - else if (!actor->reactiontime) - { - actor->reactiontime = 1; - if (actor->info->activesound) // more like INactivesound doy hoy hoy - S_StartSound(actor, actor->info->activesound); - } -} - - -// Function: A_CryingToMomma -// -// Description: If you're a child, let your parent know something's happened to you through extravalue1. Also, prepare to die. -// -// var1 = unused -// var2 = unused -// -void A_CryingToMomma(mobj_t *actor) -{ - //INT32 locvar1 = var1; - //INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CryingToMomma", actor)) - return; -#endif - - if (actor->tracer) - actor->tracer->extravalue1++; - - actor->momx = actor->momy = actor->momz = 0; - - P_UnsetThingPosition(actor); - if (sector_list) - { - P_DelSeclist(sector_list); - sector_list = NULL; - } - actor->flags = MF_NOBLOCKMAP|MF_NOCLIPTHING; - P_SetThingPosition(actor); -} - -// Function: A_CheckFlags2 -// -// Description: If actor->flags2 & var1, goto var2. -// -// var1 = mask -// var2 = state to go -// -void A_CheckFlags2(mobj_t *actor) -{ - INT32 locvar1 = var1; - INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_CheckFlags2", actor)) - return; -#endif - - if (actor->flags2 & locvar1) - P_SetMobjState(actor, (statenum_t)locvar2); -} +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2016 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file p_enemy.c +/// \brief Enemy thinking, AI +/// Action Pointer Functions that are associated with states/frames + +#include "doomdef.h" +#include "g_game.h" +#include "p_local.h" +#include "r_main.h" +#include "r_state.h" +#include "s_sound.h" +#include "m_random.h" +#include "m_misc.h" +#include "r_things.h" +#include "i_video.h" +#include "lua_hook.h" + +#ifdef HW3SOUND +#include "hardware/hw3sound.h" +#endif + +#ifdef HAVE_BLUA +boolean LUA_CallAction(const char *action, mobj_t *actor); +#endif + +player_t *stplyr; +INT32 var1; +INT32 var2; + +// +// P_NewChaseDir related LUT. +// +static dirtype_t opposite[] = +{ + DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, + DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR +}; + +static dirtype_t diags[] = +{ + DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST +}; + +//Real Prototypes to A_* +void A_Fall(mobj_t *actor); +void A_Look(mobj_t *actor); +void A_Chase(mobj_t *actor); +void A_FaceStabChase(mobj_t *actor); +void A_FaceStabRev(mobj_t *actor); +void A_FaceStabHurl(mobj_t *actor); +void A_FaceStabMiss(mobj_t *actor); +void A_StatueBurst(mobj_t *actor); +void A_JetJawRoam(mobj_t *actor); +void A_JetJawChomp(mobj_t *actor); +void A_PointyThink(mobj_t *actor); +void A_CheckBuddy(mobj_t *actor); +void A_HoodFire(mobj_t *actor); +void A_HoodThink(mobj_t *actor); +void A_HoodFall(mobj_t *actor); +void A_ArrowBonks(mobj_t *actor); +void A_SnailerThink(mobj_t *actor); +void A_SharpChase(mobj_t *actor); +void A_SharpSpin(mobj_t *actor); +void A_SharpDecel(mobj_t *actor); +void A_CrushstaceanWalk(mobj_t *actor); +void A_CrushstaceanPunch(mobj_t *actor); +void A_CrushclawAim(mobj_t *actor); +void A_CrushclawLaunch(mobj_t *actor); +void A_VultureVtol(mobj_t *actor); +void A_VultureCheck(mobj_t *actor); +void A_SkimChase(mobj_t *actor); +void A_FaceTarget(mobj_t *actor); +void A_FaceTracer(mobj_t *actor); +void A_LobShot(mobj_t *actor); +void A_FireShot(mobj_t *actor); +void A_SuperFireShot(mobj_t *actor); +void A_BossFireShot(mobj_t *actor); +void A_Boss7FireMissiles(mobj_t *actor); +void A_Boss1Laser(mobj_t *actor); +void A_FocusTarget(mobj_t *actor); +void A_Boss4Reverse(mobj_t *actor); +void A_Boss4SpeedUp(mobj_t *actor); +void A_Boss4Raise(mobj_t *actor); +void A_SkullAttack(mobj_t *actor); +void A_BossZoom(mobj_t *actor); +void A_BossScream(mobj_t *actor); +void A_Scream(mobj_t *actor); +void A_Pain(mobj_t *actor); +void A_1upThinker(mobj_t *actor); +void A_MonitorPop(mobj_t *actor); +void A_GoldMonitorPop(mobj_t *actor); +void A_GoldMonitorRestore(mobj_t *actor); +void A_GoldMonitorSparkle(mobj_t *actor); +void A_Explode(mobj_t *actor); +void A_BossDeath(mobj_t *actor); +void A_CustomPower(mobj_t *actor); +void A_GiveWeapon(mobj_t *actor); +void A_RingBox(mobj_t *actor); +void A_Invincibility(mobj_t *actor); +void A_SuperSneakers(mobj_t *actor); +void A_AwardScore(mobj_t *actor); +void A_ExtraLife(mobj_t *actor); +void A_GiveShield(mobj_t *actor); +void A_GravityBox(mobj_t *actor); +void A_ScoreRise(mobj_t *actor); +void A_BunnyHop(mobj_t *actor); +void A_BubbleSpawn(mobj_t *actor); +void A_FanBubbleSpawn(mobj_t *actor); +void A_BubbleRise(mobj_t *actor); +void A_BubbleCheck(mobj_t *actor); +void A_AttractChase(mobj_t *actor); +void A_DropMine(mobj_t *actor); +void A_FishJump(mobj_t *actor); +void A_ThrownRing(mobj_t *actor); +void A_SetSolidSteam(mobj_t *actor); +void A_UnsetSolidSteam(mobj_t *actor); +void A_SignPlayer(mobj_t *actor); +void A_OverlayThink(mobj_t *actor); +void A_JetChase(mobj_t *actor); +void A_JetbThink(mobj_t *actor); +void A_JetgShoot(mobj_t *actor); +void A_JetgThink(mobj_t *actor); +void A_ShootBullet(mobj_t *actor); +void A_MinusDigging(mobj_t *actor); +void A_MinusPopup(mobj_t *actor); +void A_MinusCheck(mobj_t *actor); +void A_ChickenCheck(mobj_t *actor); +void A_MouseThink(mobj_t *actor); +void A_DetonChase(mobj_t *actor); +void A_CapeChase(mobj_t *actor); +void A_RotateSpikeBall(mobj_t *actor); +void A_SlingAppear(mobj_t *actor); +void A_UnidusBall(mobj_t *actor); +void A_RockSpawn(mobj_t *actor); +void A_SetFuse(mobj_t *actor); +void A_CrawlaCommanderThink(mobj_t *actor); +void A_RingExplode(mobj_t *actor); +void A_OldRingExplode(mobj_t *actor); +void A_MixUp(mobj_t *actor); +void A_RecyclePowers(mobj_t *actor); +void A_Boss2TakeDamage(mobj_t *actor); +void A_Boss7Chase(mobj_t *actor); +void A_GoopSplat(mobj_t *actor); +void A_Boss2PogoSFX(mobj_t *actor); +void A_Boss2PogoTarget(mobj_t *actor); +void A_EggmanBox(mobj_t *actor); +void A_TurretFire(mobj_t *actor); +void A_SuperTurretFire(mobj_t *actor); +void A_TurretStop(mobj_t *actor); +void A_SparkFollow(mobj_t *actor); +void A_BuzzFly(mobj_t *actor); +void A_GuardChase(mobj_t *actor); +void A_EggShield(mobj_t *actor); +void A_SetReactionTime(mobj_t *actor); +void A_Boss1Spikeballs(mobj_t *actor); +void A_Boss3TakeDamage(mobj_t *actor); +void A_Boss3Path(mobj_t *actor); +void A_LinedefExecute(mobj_t *actor); +void A_PlaySeeSound(mobj_t *actor); +void A_PlayAttackSound(mobj_t *actor); +void A_PlayActiveSound(mobj_t *actor); +void A_SmokeTrailer(mobj_t *actor); +void A_SpawnObjectAbsolute(mobj_t *actor); +void A_SpawnObjectRelative(mobj_t *actor); +void A_ChangeAngleRelative(mobj_t *actor); +void A_ChangeAngleAbsolute(mobj_t *actor); +void A_PlaySound(mobj_t *actor); +void A_FindTarget(mobj_t *actor); +void A_FindTracer(mobj_t *actor); +void A_SetTics(mobj_t *actor); +void A_SetRandomTics(mobj_t *actor); +void A_ChangeColorRelative(mobj_t *actor); +void A_ChangeColorAbsolute(mobj_t *actor); +void A_MoveRelative(mobj_t *actor); +void A_MoveAbsolute(mobj_t *actor); +void A_Thrust(mobj_t *actor); +void A_ZThrust(mobj_t *actor); +void A_SetTargetsTarget(mobj_t *actor); +void A_SetObjectFlags(mobj_t *actor); +void A_SetObjectFlags2(mobj_t *actor); +void A_RandomState(mobj_t *actor); +void A_RandomStateRange(mobj_t *actor); +void A_DualAction(mobj_t *actor); +void A_RemoteAction(mobj_t *actor); +void A_ToggleFlameJet(mobj_t *actor); +void A_OrbitNights(mobj_t *actor); +void A_GhostMe(mobj_t *actor); +void A_SetObjectState(mobj_t *actor); +void A_SetObjectTypeState(mobj_t *actor); +void A_KnockBack(mobj_t *actor); +void A_PushAway(mobj_t *actor); +void A_RingDrain(mobj_t *actor); +void A_SplitShot(mobj_t *actor); +void A_MissileSplit(mobj_t *actor); +void A_MultiShot(mobj_t *actor); +void A_InstaLoop(mobj_t *actor); +void A_Custom3DRotate(mobj_t *actor); +void A_SearchForPlayers(mobj_t *actor); +void A_CheckRandom(mobj_t *actor); +void A_CheckTargetRings(mobj_t *actor); +void A_CheckRings(mobj_t *actor); +void A_CheckTotalRings(mobj_t *actor); +void A_CheckHealth(mobj_t *actor); +void A_CheckRange(mobj_t *actor); +void A_CheckHeight(mobj_t *actor); +void A_CheckTrueRange(mobj_t *actor); +void A_CheckThingCount(mobj_t *actor); +void A_CheckAmbush(mobj_t *actor); +void A_CheckCustomValue(mobj_t *actor); +void A_CheckCusValMemo(mobj_t *actor); +void A_SetCustomValue(mobj_t *actor); +void A_UseCusValMemo(mobj_t *actor); +void A_RelayCustomValue(mobj_t *actor); +void A_CusValAction(mobj_t *actor); +void A_ForceStop(mobj_t *actor); +void A_ForceWin(mobj_t *actor); +void A_SpikeRetract(mobj_t *actor); +void A_InfoState(mobj_t *actor); +void A_Repeat(mobj_t *actor); +void A_SetScale(mobj_t *actor); +void A_RemoteDamage(mobj_t *actor); +void A_HomingChase(mobj_t *actor); +void A_TrapShot(mobj_t *actor); +void A_Boss1Chase(mobj_t *actor); +void A_Boss2Chase(mobj_t *actor); +void A_Boss2Pogo(mobj_t *actor); +void A_BossJetFume(mobj_t *actor); +void A_VileTarget(mobj_t *actor); +void A_VileAttack(mobj_t *actor); +void A_VileFire(mobj_t *actor); +void A_BrakChase(mobj_t *actor); +void A_BrakFireShot(mobj_t *actor); +void A_BrakLobShot(mobj_t *actor); +void A_NapalmScatter(mobj_t *actor); +void A_SpawnFreshCopy(mobj_t *actor); +void A_FlickySpawn(mobj_t *actor); +void A_FlickyCenter(mobj_t *actor); +void A_FlickyAim(mobj_t *actor); +void A_FlickyFly(mobj_t *actor); +void A_FlickySoar(mobj_t *actor); +void A_FlickyCoast(mobj_t *actor); +void A_FlickyHop(mobj_t *actor); +void A_FlickyFlounder(mobj_t *actor); +void A_FlickyCheck(mobj_t *actor); +void A_FlickyHeightCheck(mobj_t *actor); +void A_FlickyFlutter(mobj_t *actor); +void A_FlameParticle(mobj_t *actor); +void A_FadeOverlay(mobj_t *actor); +void A_Boss5Jump(mobj_t *actor); +void A_LightBeamReset(mobj_t *actor); +void A_MineExplode(mobj_t *actor); +void A_MineRange(mobj_t *actor); +void A_ConnectToGround(mobj_t *actor); +void A_SpawnParticleRelative(mobj_t *actor); +void A_MultiShotDist(mobj_t *actor); +void A_WhoCaresIfYourSonIsABee(mobj_t *actor); +void A_ParentTriesToSleep(mobj_t *actor); +void A_CryingToMomma(mobj_t *actor); +void A_CheckFlags2(mobj_t *actor); +//for p_enemy.c + +// +// ENEMY THINKING +// Enemies are always spawned with targetplayer = -1, threshold = 0 +// Most monsters are spawned unaware of all players, but some can be made preaware. +// + +// +// P_CheckMeleeRange +// +boolean P_CheckMeleeRange(mobj_t *actor) +{ + mobj_t *pl; + fixed_t dist; + + if (!actor->target) + return false; + + pl = actor->target; + dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y); + + if (dist >= FixedMul(MELEERANGE - 20*FRACUNIT, actor->scale) + pl->radius) + return false; + + // check height now, so that damn crawlas cant attack + // you if you stand on a higher ledge. + if ((pl->z > actor->z + actor->height) || (actor->z > pl->z + pl->height)) + return false; + + if (!P_CheckSight(actor, actor->target)) + return false; + + return true; +} + +// P_CheckMeleeRange for Jettysyn Bomber. +boolean P_JetbCheckMeleeRange(mobj_t *actor) +{ + mobj_t *pl; + fixed_t dist; + + if (!actor->target) + return false; + + pl = actor->target; + dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y); + + if (dist >= (actor->radius + pl->radius)*2) + return false; + + if (actor->eflags & MFE_VERTICALFLIP) + { + if (pl->z < actor->z + actor->height + FixedMul(40<scale)) + return false; + } + else + { + if (pl->z + pl->height > actor->z - FixedMul(40<scale)) + return false; + } + + return true; +} + +// P_CheckMeleeRange for CastleBot FaceStabber. +boolean P_FaceStabCheckMeleeRange(mobj_t *actor) +{ + mobj_t *pl; + fixed_t dist; + + if (!actor->target) + return false; + + pl = actor->target; + dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y); + + if (dist >= (actor->radius + pl->radius)*4) + return false; + + if ((pl->z > actor->z + actor->height) || (actor->z > pl->z + pl->height)) + return false; + + if (!P_CheckSight(actor, actor->target)) + return false; + + return true; +} + +// P_CheckMeleeRange for Skim. +boolean P_SkimCheckMeleeRange(mobj_t *actor) +{ + mobj_t *pl; + fixed_t dist; + + if (!actor->target) + return false; + + pl = actor->target; + dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y); + + if (dist >= FixedMul(MELEERANGE - 20*FRACUNIT, actor->scale) + pl->radius) + return false; + + if (actor->eflags & MFE_VERTICALFLIP) + { + if (pl->z < actor->z + actor->height + FixedMul(24<scale)) + return false; + } + else + { + if (pl->z + pl->height > actor->z - FixedMul(24<scale)) + return false; + } + + return true; +} + +// +// P_CheckMissileRange +// +boolean P_CheckMissileRange(mobj_t *actor) +{ + fixed_t dist; + + if (!actor->target) + return false; + + if (actor->reactiontime) + return false; // do not attack yet + + if (!P_CheckSight(actor, actor->target)) + return false; + + // OPTIMIZE: get this from a global checksight + dist = P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) - FixedMul(64*FRACUNIT, actor->scale); + + if (!actor->info->meleestate) + dist -= FixedMul(128*FRACUNIT, actor->scale); // no melee attack, so fire more + + dist >>= FRACBITS; + + if (actor->type == MT_EGGMOBILE) + dist >>= 1; + + if (dist > 200) + dist = 200; + + if (actor->type == MT_EGGMOBILE && dist > 160) + dist = 160; + + if (P_RandomByte() < dist) + return false; + + return true; +} + +/** Checks for water in a sector. + * Used by Skim movements. + * + * \param x X coordinate on the map. + * \param y Y coordinate on the map. + * \return True if there's water at this location, false if not. + * \sa ::MT_SKIM + */ +static boolean P_WaterInSector(mobj_t *mobj, fixed_t x, fixed_t y) +{ + sector_t *sector; + + sector = R_PointInSubsector(x, y)->sector; + + if (sector->ffloors) + { + ffloor_t *rover; + + for (rover = sector->ffloors; rover; rover = rover->next) + { + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_SWIMMABLE)) + continue; + + if (*rover->topheight >= mobj->floorz && *rover->topheight <= mobj->z) + return true; // we found water!! + } + } + + return false; +} + +static const fixed_t xspeed[NUMDIRS] = {FRACUNIT, 46341>>(16-FRACBITS), 0, -(46341>>(16-FRACBITS)), -FRACUNIT, -(46341>>(16-FRACBITS)), 0, 46341>>(16-FRACBITS)}; +static const fixed_t yspeed[NUMDIRS] = {0, 46341>>(16-FRACBITS), FRACUNIT, 46341>>(16-FRACBITS), 0, -(46341>>(16-FRACBITS)), -FRACUNIT, -(46341>>(16-FRACBITS))}; + +/** Moves an actor in its current direction. + * + * \param actor Actor object to move. + * \return False if the move is blocked, otherwise true. + */ +boolean P_Move(mobj_t *actor, fixed_t speed) +{ + fixed_t tryx, tryy; + dirtype_t movedir = actor->movedir; + + if (movedir == DI_NODIR || !actor->health) + return false; + + I_Assert(movedir < NUMDIRS); + + tryx = actor->x + FixedMul(speed*xspeed[movedir], actor->scale); + if (twodlevel || actor->flags2 & MF2_TWOD) + tryy = actor->y; + else + tryy = actor->y + FixedMul(speed*yspeed[movedir], actor->scale); + + if (actor->type == MT_SKIM && !P_WaterInSector(actor, tryx, tryy)) // bail out if sector lacks water + return false; + + if (!P_TryMove(actor, tryx, tryy, false)) + { + if (actor->flags & MF_FLOAT && floatok) + { + // must adjust height + if (actor->z < tmfloorz) + actor->z += FixedMul(FLOATSPEED, actor->scale); + else + actor->z -= FixedMul(FLOATSPEED, actor->scale); + + if (actor->type == MT_JETJAW && actor->z + actor->height > actor->watertop) + actor->z = actor->watertop - actor->height; + + actor->flags2 |= MF2_INFLOAT; + return true; + } + + return false; + } + else + actor->flags2 &= ~MF2_INFLOAT; + + return true; +} + +/** Attempts to move an actor on in its current direction. + * If the move succeeds, the actor's move count is reset + * randomly to a value from 0 to 15. + * + * \param actor Actor to move. + * \return True if the move succeeds, false if the move is blocked. + */ +static boolean P_TryWalk(mobj_t *actor) +{ + if (!P_Move(actor, actor->info->speed)) + return false; + actor->movecount = P_RandomByte() & 15; + return true; +} + +void P_NewChaseDir(mobj_t *actor) +{ + fixed_t deltax, deltay; + dirtype_t d[3]; + dirtype_t tdir = DI_NODIR, olddir, turnaround; + + I_Assert(actor->target != NULL); + I_Assert(!P_MobjWasRemoved(actor->target)); + + olddir = actor->movedir; + + if (olddir >= NUMDIRS) + olddir = DI_NODIR; + + if (olddir != DI_NODIR) + turnaround = opposite[olddir]; + else + turnaround = olddir; + + deltax = actor->target->x - actor->x; + deltay = actor->target->y - actor->y; + + if (deltax > FixedMul(10*FRACUNIT, actor->scale)) + d[1] = DI_EAST; + else if (deltax < -FixedMul(10*FRACUNIT, actor->scale)) + d[1] = DI_WEST; + else + d[1] = DI_NODIR; + + if (twodlevel || actor->flags2 & MF2_TWOD) + d[2] = DI_NODIR; + if (deltay < -FixedMul(10*FRACUNIT, actor->scale)) + d[2] = DI_SOUTH; + else if (deltay > FixedMul(10*FRACUNIT, actor->scale)) + d[2] = DI_NORTH; + else + d[2] = DI_NODIR; + + // try direct route + if (d[1] != DI_NODIR && d[2] != DI_NODIR) + { + dirtype_t newdir = diags[((deltay < 0)<<1) + (deltax > 0)]; + + actor->movedir = newdir; + if ((newdir != turnaround) && P_TryWalk(actor)) + return; + } + + // try other directions + if (P_RandomChance(25*FRACUNIT/32) || abs(deltay) > abs(deltax)) + { + tdir = d[1]; + d[1] = d[2]; + d[2] = tdir; + } + + if (d[1] == turnaround) + d[1] = DI_NODIR; + if (d[2] == turnaround) + d[2] = DI_NODIR; + + if (d[1] != DI_NODIR) + { + actor->movedir = d[1]; + + if (P_TryWalk(actor)) + return; // either moved forward or attacked + } + + if (d[2] != DI_NODIR) + { + actor->movedir = d[2]; + + if (P_TryWalk(actor)) + return; + } + + // there is no direct path to the player, so pick another direction. + if (olddir != DI_NODIR) + { + actor->movedir =olddir; + + if (P_TryWalk(actor)) + return; + } + + // randomly determine direction of search + if (P_RandomChance(FRACUNIT/2)) + { + for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++) + { + if (tdir != turnaround) + { + actor->movedir = tdir; + + if (P_TryWalk(actor)) + return; + } + } + } + else + { + for (tdir = DI_SOUTHEAST; tdir >= DI_EAST; tdir--) + { + if (tdir != turnaround) + { + actor->movedir = tdir; + + if (P_TryWalk(actor)) + return; + } + } + } + + if (turnaround != DI_NODIR) + { + actor->movedir = turnaround; + + if (P_TryWalk(actor)) + return; + } + + actor->movedir = (angle_t)DI_NODIR; // cannot move +} + +/** Looks for players to chase after, aim at, or whatever. + * + * \param actor The object looking for flesh. + * \param allaround Look all around? If false, only players in a 180-degree + * range in front will be spotted. + * \param dist If > 0, checks distance + * \return True if a player is found, otherwise false. + * \sa P_SupermanLook4Players + */ +boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed_t dist) +{ + INT32 c = 0, stop; + player_t *player; + angle_t an; + + // BP: first time init, this allow minimum lastlook changes + if (actor->lastlook < 0) + actor->lastlook = P_RandomByte(); + + actor->lastlook %= MAXPLAYERS; + + stop = (actor->lastlook - 1) & PLAYERSMASK; + + for (; ; actor->lastlook = (actor->lastlook + 1) & PLAYERSMASK) + { + // done looking + if (actor->lastlook == stop) + return false; + + if (!playeringame[actor->lastlook]) + continue; + + if (c++ == 2) + return false; + + player = &players[actor->lastlook]; + + if ((netgame || multiplayer) && player->spectator) + continue; + + if (player->pflags & PF_INVIS) + continue; // ignore notarget + + if (!player->mo || P_MobjWasRemoved(player->mo)) + continue; + + if (player->mo->health <= 0) + continue; // dead + + if (dist > 0 + && P_AproxDistance(P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y), player->mo->z - actor->z) > dist) + continue; // Too far away + + if (!allaround) + { + an = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle; + if (an > ANGLE_90 && an < ANGLE_270) + { + dist = P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y); + // if real close, react anyway + if (dist > FixedMul(MELEERANGE, actor->scale)) + continue; // behind back + } + } + + if (!P_CheckSight(actor, player->mo)) + continue; // out of sight + + if (tracer) + P_SetTarget(&actor->tracer, player->mo); + else + P_SetTarget(&actor->target, player->mo); + return true; + } + + //return false; +} + +/** Looks for a player with a ring shield. + * Used by rings. + * + * \param actor Ring looking for a shield to be attracted to. + * \return True if a player with ring shield is found, otherwise false. + * \sa A_AttractChase + */ +static boolean P_LookForShield(mobj_t *actor) +{ + INT32 c = 0, stop; + player_t *player; + + // BP: first time init, this allow minimum lastlook changes + if (actor->lastlook < 0) + actor->lastlook = P_RandomByte(); + + actor->lastlook %= MAXPLAYERS; + + stop = (actor->lastlook - 1) & PLAYERSMASK; + + for (; ; actor->lastlook = ((actor->lastlook + 1) & PLAYERSMASK)) + { + // done looking + if (actor->lastlook == stop) + return false; + + if (!playeringame[actor->lastlook]) + continue; + + if (c++ == 2) + return false; + + player = &players[actor->lastlook]; + + if (!player->mo || player->mo->health <= 0) + continue; // dead + + //When in CTF, don't pull rings that you cannot pick up. + if ((actor->type == MT_REDTEAMRING && player->ctfteam != 1) || + (actor->type == MT_BLUETEAMRING && player->ctfteam != 2)) + continue; + + if ((player->powers[pw_shield] & SH_PROTECTELECTRIC) + && (P_AproxDistance(P_AproxDistance(actor->x-player->mo->x, actor->y-player->mo->y), actor->z-player->mo->z) < FixedMul(RING_DIST, player->mo->scale))) + { + P_SetTarget(&actor->tracer, player->mo); + + if (actor->hnext) + P_SetTarget(&actor->hnext->hprev, actor->hprev); + if (actor->hprev) + P_SetTarget(&actor->hprev->hnext, actor->hnext); + + return true; + } + } + + //return false; +} + +#ifdef WEIGHTEDRECYCLER +// Compares players to see who currently has the "best" items, etc. +static int P_RecycleCompare(const void *p1, const void *p2) +{ + player_t *player1 = &players[*(const UINT8 *)p1]; + player_t *player2 = &players[*(const UINT8 *)p2]; + + // Non-shooting gametypes + if (!G_PlatformGametype()) + { + // Invincibility. + if (player1->powers[pw_invulnerability] > player2->powers[pw_invulnerability]) return -1; + else if (player2->powers[pw_invulnerability] > player1->powers[pw_invulnerability]) return 1; + + // One has a shield, the other doesn't. + if (player1->powers[pw_shield] && !player2->powers[pw_shield]) return -1; + else if (player2->powers[pw_shield] && !player1->powers[pw_shield]) return 1; + + // Sneakers. + if (player1->powers[pw_sneakers] > player2->powers[pw_sneakers]) return -1; + else if (player2->powers[pw_sneakers] > player1->powers[pw_sneakers]) return 1; + } + else // Match, Team Match, CTF, Tag, Etc. + { + UINT8 player1_em = M_CountBits((UINT32)player1->powers[pw_emeralds], 7); + UINT8 player2_em = M_CountBits((UINT32)player2->powers[pw_emeralds], 7); + + UINT8 player1_rw = M_CountBits((UINT32)player1->ringweapons, NUM_WEAPONS-1); + UINT8 player2_rw = M_CountBits((UINT32)player2->ringweapons, NUM_WEAPONS-1); + + UINT16 player1_am = player1->powers[pw_infinityring] // max 800 + + player1->powers[pw_automaticring] // max 300 + + (player1->powers[pw_bouncering] * 3) // max 100 + + (player1->powers[pw_explosionring] * 6) // max 50 + + (player1->powers[pw_scatterring] * 3) // max 100 + + (player1->powers[pw_grenadering] * 6) // max 50 + + (player1->powers[pw_railring] * 6); // max 50 + UINT16 player2_am = player2->powers[pw_infinityring] // max 800 + + player2->powers[pw_automaticring] // max 300 + + (player2->powers[pw_bouncering] * 3) // max 100 + + (player2->powers[pw_explosionring] * 6) // max 50 + + (player2->powers[pw_scatterring] * 3) // max 100 + + (player2->powers[pw_grenadering] * 6) // max 50 + + (player2->powers[pw_railring] * 6); // max 50 + + // Super trumps everything. + if (player1->powers[pw_super] && !player2->powers[pw_super]) return -1; + else if (player2->powers[pw_super] && !player1->powers[pw_super]) return 1; + + // Emerald count if neither player is Super. + if (player1_em > player2_em) return -1; + else if (player1_em < player2_em) return 1; + + // One has a shield, the other doesn't. + // (the likelihood of a shielded player being worse off than one without one is low.) + if (player1->powers[pw_shield] && !player2->powers[pw_shield]) return -1; + else if (player2->powers[pw_shield] && !player1->powers[pw_shield]) return 1; + + // Ring weapons count + if (player1_rw > player2_rw) return -1; + else if (player1_rw < player2_rw) return 1; + + // Ring ammo if they have the same number of weapons + if (player1_am > player2_am) return -1; + else if (player1_am < player2_am) return 1; + } + + // Identical for our purposes + return 0; +} +#endif + +// Handles random monitor weights via console. +static mobjtype_t P_DoRandomBoxChances(void) +{ + mobjtype_t spawnchance[256]; + INT32 numchoices = 0, i = 0; + + if (!(netgame || multiplayer)) + { + switch (P_RandomKey(10)) + { + case 0: + return MT_RING_ICON; + case 1: + return MT_SNEAKERS_ICON; + case 2: + return MT_INVULN_ICON; + case 3: + return MT_WHIRLWIND_ICON; + case 4: + return MT_ELEMENTAL_ICON; + case 5: + return MT_ATTRACT_ICON; + case 6: + return MT_FORCE_ICON; + case 7: + return MT_ARMAGEDDON_ICON; + case 8: + return MT_1UP_ICON; + case 9: + return MT_EGGMAN_ICON; + } + return MT_NULL; + } + +#define QUESTIONBOXCHANCES(type, cvar) \ +for (i = cvar.value; i; --i) spawnchance[numchoices++] = type + QUESTIONBOXCHANCES(MT_RING_ICON, cv_superring); + QUESTIONBOXCHANCES(MT_SNEAKERS_ICON, cv_supersneakers); + QUESTIONBOXCHANCES(MT_INVULN_ICON, cv_invincibility); + QUESTIONBOXCHANCES(MT_WHIRLWIND_ICON, cv_jumpshield); + QUESTIONBOXCHANCES(MT_ELEMENTAL_ICON, cv_watershield); + QUESTIONBOXCHANCES(MT_ATTRACT_ICON, cv_ringshield); + QUESTIONBOXCHANCES(MT_FORCE_ICON, cv_forceshield); + QUESTIONBOXCHANCES(MT_ARMAGEDDON_ICON, cv_bombshield); + QUESTIONBOXCHANCES(MT_1UP_ICON, cv_1up); + QUESTIONBOXCHANCES(MT_EGGMAN_ICON, cv_eggmanbox); + QUESTIONBOXCHANCES(MT_MIXUP_ICON, cv_teleporters); + QUESTIONBOXCHANCES(MT_RECYCLER_ICON, cv_recycler); +#undef QUESTIONBOXCHANCES + + if (numchoices == 0) return MT_NULL; + return spawnchance[P_RandomKey(numchoices)]; +} + +// +// ACTION ROUTINES +// + +// Function: A_Look +// +// Description: Look for a player and set your target to them. +// +// var1: +// lower 16 bits = look all around +// upper 16 bits = distance limit +// var2 = If 1, only change to seestate. If 2, only play seesound. If 0, do both. +// +void A_Look(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Look", actor)) + return; +#endif + + if (!P_LookForPlayers(actor, locvar1 & 65535, false , FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale))) + return; + + // go into chase state + if (!locvar2) + { + P_SetMobjState(actor, actor->info->seestate); + A_PlaySeeSound(actor); + } + else if (locvar2 == 1) // Only go into seestate + P_SetMobjState(actor, actor->info->seestate); + else if (locvar2 == 2) // Only play seesound + A_PlaySeeSound(actor); +} + +// Function: A_Chase +// +// Description: Chase after your target. +// +// var1: +// 1 = don't check meleestate +// 2 = don't check missilestate +// 3 = don't check meleestate and missilestate +// var2 = unused +// +void A_Chase(mobj_t *actor) +{ + INT32 delta; + INT32 locvar1 = var1; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Chase", actor)) + return; +#endif + + I_Assert(actor != NULL); + I_Assert(!P_MobjWasRemoved(actor)); + + if (actor->reactiontime) + actor->reactiontime--; + + // modify target threshold + if (actor->threshold) + { + if (!actor->target || actor->target->health <= 0) + actor->threshold = 0; + else + actor->threshold--; + } + + // turn towards movement direction if not there yet + if (actor->movedir < NUMDIRS) + { + actor->angle &= (7<<29); + delta = actor->angle - (actor->movedir << 29); + + if (delta > 0) + actor->angle -= ANGLE_45; + else if (delta < 0) + actor->angle += ANGLE_45; + } + + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) + { + // look for a new target + if (P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + P_SetMobjStateNF(actor, actor->info->spawnstate); + return; + } + + // do not attack twice in a row + if (actor->flags2 & MF2_JUSTATTACKED) + { + actor->flags2 &= ~MF2_JUSTATTACKED; + P_NewChaseDir(actor); + return; + } + + // check for melee attack + if (!(locvar1 & 1) && actor->info->meleestate && P_CheckMeleeRange(actor)) + { + if (actor->info->attacksound) + S_StartAttackSound(actor, actor->info->attacksound); + + P_SetMobjState(actor, actor->info->meleestate); + return; + } + + // check for missile attack + if (!(locvar1 & 2) && actor->info->missilestate) + { + if (actor->movecount || !P_CheckMissileRange(actor)) + goto nomissile; + + P_SetMobjState(actor, actor->info->missilestate); + actor->flags2 |= MF2_JUSTATTACKED; + return; + } + +nomissile: + // possibly choose another target + if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)) + && P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + // chase towards player + if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed)) + P_NewChaseDir(actor); +} + +// Function: A_FaceStabChase +// +// Description: Unused variant of A_Chase for Castlebot Facestabber. +// +// var1 = unused +// var2 = unused +// +void A_FaceStabChase(mobj_t *actor) +{ + INT32 delta; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FaceStabChase", actor)) + return; +#endif + + if (actor->reactiontime) + actor->reactiontime--; + + // modify target threshold + if (actor->threshold) + { + if (!actor->target || actor->target->health <= 0) + actor->threshold = 0; + else + actor->threshold--; + } + + // turn towards movement direction if not there yet + if (actor->movedir < NUMDIRS) + { + actor->angle &= (7<<29); + delta = actor->angle - (actor->movedir << 29); + + if (delta > 0) + actor->angle -= ANGLE_45; + else if (delta < 0) + actor->angle += ANGLE_45; + } + + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) + { + // look for a new target + if (P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + P_SetMobjStateNF(actor, actor->info->spawnstate); + return; + } + + // do not attack twice in a row + if (actor->flags2 & MF2_JUSTATTACKED) + { + actor->flags2 &= ~MF2_JUSTATTACKED; + P_NewChaseDir(actor); + return; + } + + // check for melee attack + if (actor->info->meleestate && P_FaceStabCheckMeleeRange(actor)) + { + if (actor->info->attacksound) + S_StartAttackSound(actor, actor->info->attacksound); + + P_SetMobjState(actor, actor->info->meleestate); + return; + } + + // check for missile attack + if (actor->info->missilestate) + { + if (actor->movecount || !P_CheckMissileRange(actor)) + goto nomissile; + + P_SetMobjState(actor, actor->info->missilestate); + actor->flags2 |= MF2_JUSTATTACKED; + return; + } + +nomissile: + // possibly choose another target + if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)) + && P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + // chase towards player + if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed)) + P_NewChaseDir(actor); +} + +static void P_SharpDust(mobj_t *actor, mobjtype_t type, angle_t ang) +{ + mobj_t *dust; + + if (!type || !P_IsObjectOnGround(actor)) + return; + + dust = P_SpawnMobjFromMobj(actor, + -P_ReturnThrustX(actor, ang, 16<angle, actor->radius), + -P_ReturnThrustY(actor, actor->angle, actor->radius), + actor->height/3, + MT_PARTICLE); + flume->destscale = actor->scale*3; + P_SetScale(flume, flume->destscale); + P_SetTarget(&flume->target, actor); + flume->sprite = SPR_JETF; + flume->frame = FF_FULLBRIGHT; + flume->tics = 2; +} + +// Function: A_FaceStabRev +// +// Description: Facestabber rev action +// +// var1 = effective duration +// var2 = effective nextstate +// +void A_FaceStabRev(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FaceStabRev", actor)) + return; +#endif + + if (!actor->target) + { + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + + actor->extravalue1 = 0; + + if (!actor->reactiontime) + { + actor->reactiontime = locvar1; + S_StartSound(actor, actor->info->activesound); + } + else + { + if ((--actor->reactiontime) == 0) + { + S_StartSound(actor, actor->info->attacksound); + P_SetMobjState(actor, locvar2); + } + else + { + P_TryMove(actor, actor->x - P_ReturnThrustX(actor, actor->angle, 2<y - P_ReturnThrustY(actor, actor->angle, 2<target) + { + angle_t visang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); + // Calculate new direction. + angle_t dirang = actor->angle; + angle_t diffang = visang - dirang; + + if (locvar1) // Allow homing? + { + if (diffang > ANGLE_180) + { + angle_t workang = locvar1*(InvAngle(diffang)>>5); + diffang += InvAngle(workang); + } + else + diffang += (locvar1*(diffang>>5)); + } + diffang += ANGLE_45; + + // Check the sight cone. + if (diffang < ANGLE_90) + { + actor->angle = dirang; + if (++actor->extravalue2 < 4) + actor->extravalue2 = 4; + else if (actor->extravalue2 > 26) + actor->extravalue2 = 26; + + if (P_TryMove(actor, + actor->x + P_ReturnThrustX(actor, dirang, actor->extravalue2<y + P_ReturnThrustY(actor, dirang, actor->extravalue2<extravalue1); + fixed_t basesize = FRACUNIT/MAXVAL; + mobj_t *hwork = actor; + INT32 dist = 113; + fixed_t xo = P_ReturnThrustX(actor, actor->angle, dist*basesize); + fixed_t yo = P_ReturnThrustY(actor, actor->angle, dist*basesize); + + while (step > 0) + { + if (!hwork->hnext) + P_SetTarget(&hwork->hnext, P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_FACESTABBERSPEAR)); + hwork = hwork->hnext; + hwork->angle = actor->angle + ANGLE_90; + hwork->destscale = FixedSqrt(step*basesize); + P_SetScale(hwork, hwork->destscale); + hwork->fuse = 2; + P_TeleportMove(hwork, actor->x + xo*(15-step), actor->y + yo*(15-step), actor->z + (actor->height - hwork->height)/2 + (P_MobjFlip(actor)*(8<extravalue1 >= MAXVAL) + actor->extravalue1 -= NUMGRADS; + + if ((step % 5) == 0) + P_SharpDust(actor, MT_SPINDUST, actor->angle); + + P_FaceStabFlume(actor); + return; +#undef MAXVAL +#undef NUMGRADS +#undef NUMSTEPS + } + } + } + + P_SetMobjState(actor, locvar2); + actor->reactiontime = actor->info->reactiontime; +} + +// Function: A_FaceStabMiss +// +// Description: Facestabber miss action +// +// var1 = unused +// var2 = effective nextstate +// +void A_FaceStabMiss(mobj_t *actor) +{ + //INT32 locvar1 = var1; + INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FaceStabMiss", actor)) + return; +#endif + + if (++actor->extravalue1 >= 3) + { + actor->extravalue2 -= 2; + actor->extravalue1 = 0; + S_StartSound(actor, sfx_s3k47); + P_SharpDust(actor, MT_SPINDUST, actor->angle); + } + + if (actor->extravalue2 <= 0 || !P_TryMove(actor, + actor->x + P_ReturnThrustX(actor, actor->angle, actor->extravalue2<y + P_ReturnThrustY(actor, actor->angle, actor->extravalue2<extravalue2 = 0; + P_SetMobjState(actor, locvar2); + } +} + +// Function: A_StatueBurst +// +// Description: For suspicious statues only... +// +// var1 = object to create +// var2 = effective nextstate for created object +// +void A_StatueBurst(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobjtype_t chunktype = (mobjtype_t)actor->info->raisestate; + mobj_t *new; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_StatueBurst", actor)) + return; +#endif + + if (!locvar1 || !(new = P_SpawnMobjFromMobj(actor, 0, 0, 0, locvar1))) + return; + + new->angle = actor->angle; + new->target = actor->target; + if (locvar2) + P_SetMobjState(new, (statenum_t)locvar2); + S_StartSound(new, new->info->attacksound); + S_StopSound(actor); + S_StartSound(actor, sfx_s3k96); + + { + fixed_t a, b; + fixed_t c = (actor->height>>2) - FixedMul(actor->scale, mobjinfo[chunktype].height>>1); + fixed_t v = 4<radius>>1); + mobj_t *spawned; + UINT8 i; + for (i = 0; i < 8; i++) + { + a = ((i & 1) ? r : (-r)); + b = ((i & 2) ? r : (-r)); + if (i == 4) + { + c += (actor->height>>1); + v = 8<fuse = 3*TICRATE; + } + } +} + +// Function: A_JetJawRoam +// +// Description: Roaming routine for JetJaw +// +// var1 = unused +// var2 = unused +// +void A_JetJawRoam(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_JetJawRoam", actor)) + return; +#endif + if (actor->reactiontime) + { + actor->reactiontime--; + P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed*FRACUNIT/4, actor->scale)); + } + else + { + actor->reactiontime = actor->info->reactiontime; + actor->angle += ANGLE_180; + } + + if (P_LookForPlayers(actor, false, false, actor->radius * 16)) + P_SetMobjState(actor, actor->info->seestate); +} + +// Function: A_JetJawChomp +// +// Description: Chase and chomp at the target, as long as it is in view +// +// var1 = unused +// var2 = unused +// +void A_JetJawChomp(mobj_t *actor) +{ + INT32 delta; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_JetJawChomp", actor)) + return; +#endif + + // turn towards movement direction if not there yet + if (actor->movedir < NUMDIRS) + { + actor->angle &= (7<<29); + delta = actor->angle - (actor->movedir << 29); + + if (delta > 0) + actor->angle -= ANGLE_45; + else if (delta < 0) + actor->angle += ANGLE_45; + } + + // Stop chomping if target's dead or you can't see it + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE) + || actor->target->health <= 0 || !P_CheckSight(actor, actor->target)) + { + P_SetMobjStateNF(actor, actor->info->spawnstate); + return; + } + + // chase towards player + if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed)) + P_NewChaseDir(actor); +} + +// Function: A_PointyThink +// +// Description: Thinker function for Pointy +// +// var1 = unused +// var2 = unused +// +void A_PointyThink(mobj_t *actor) +{ + INT32 i; + player_t *player = NULL; + mobj_t *ball; + TVector v; + TVector *res; + angle_t fa; + fixed_t radius = FixedMul(actor->info->radius*actor->info->reactiontime, actor->scale); + boolean firsttime = true; + INT32 sign; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_PointyThink", actor)) + return; +#endif + actor->momx = actor->momy = actor->momz = 0; + + // Find nearest player + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + + if (!players[i].mo) + continue; + + if (!players[i].mo->health) + continue; + + if (!P_CheckSight(actor, players[i].mo)) + continue; + + if (firsttime) + { + firsttime = false; + player = &players[i]; + } + else + { + if (P_AproxDistance(players[i].mo->x - actor->x, players[i].mo->y - actor->y) < + P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y)) + player = &players[i]; + } + } + + if (!player) + return; + + // Okay, we found the closest player. Let's move based on his movement. + P_SetTarget(&actor->target, player->mo); + A_FaceTarget(actor); + + if (P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y) < P_AproxDistance(player->mo->x + player->mo->momx - actor->x, player->mo->y + player->mo->momy - actor->y)) + sign = -1; // Player is moving away + else + sign = 1; // Player is moving closer + + if (player->mo->momx || player->mo->momy) + { + P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y), FixedMul(actor->info->speed*sign, actor->scale)); + + // Rotate our spike balls + actor->lastlook += actor->info->damage; + actor->lastlook %= FINEANGLES/4; + } + + if (!actor->tracer) // For some reason we do not have spike balls... + return; + + // Position spike balls relative to the value of 'lastlook'. + ball = actor->tracer; + + i = 0; + while (ball) + { + fa = actor->lastlook+i; + v[0] = FixedMul(FINECOSINE(fa),radius); + v[1] = 0; + v[2] = FixedMul(FINESINE(fa),radius); + v[3] = FRACUNIT; + + res = VectorMatrixMultiply(v, *RotateXMatrix(FixedAngle(actor->lastlook+i))); + M_Memcpy(&v, res, sizeof (v)); + res = VectorMatrixMultiply(v, *RotateZMatrix(actor->angle+ANGLE_180)); + M_Memcpy(&v, res, sizeof (v)); + + P_UnsetThingPosition(ball); + ball->x = actor->x + v[0]; + ball->y = actor->y + v[1]; + ball->z = actor->z + (actor->height>>1) + v[2]; + P_SetThingPosition(ball); + + ball = ball->tracer; + i += ANGLE_90 >> ANGLETOFINESHIFT; + } +} + +// Function: A_CheckBuddy +// +// Description: Checks if target/tracer exists/has health. If not, the object removes itself. +// +// var1: +// 0 = target +// 1 = tracer +// var2 = unused +// +void A_CheckBuddy(mobj_t *actor) +{ + INT32 locvar1 = var1; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CheckBuddy", actor)) + return; +#endif + if (locvar1 && (!actor->tracer || actor->tracer->health <= 0)) + P_RemoveMobj(actor); + else if (!locvar1 && (!actor->target || actor->target->health <= 0)) + P_RemoveMobj(actor); +} + +// Helper function for the Robo Hood. +// Don't ask me how it works. Nev3r made it with dark majyks. +static void P_ParabolicMove(mobj_t *actor, fixed_t x, fixed_t y, fixed_t z, fixed_t speed) +{ + fixed_t dh; + + x -= actor->x; + y -= actor->y; + z -= actor->z; + + dh = P_AproxDistance(x, y); + + actor->momx = FixedMul(FixedDiv(x, dh), speed); + actor->momy = FixedMul(FixedDiv(y, dh), speed); + + if (!gravity) + return; + + dh = FixedDiv(FixedMul(dh, gravity), speed); + actor->momz = (dh>>1) + FixedDiv(z, dh<<1); +} + +// Function: A_HoodFire +// +// Description: Firing Robo-Hood +// +// var1 = object type to fire +// var2 = unused +// +void A_HoodFire(mobj_t *actor) +{ + mobj_t *arrow; + INT32 locvar1 = var1; + //INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_HoodFire", actor)) + return; +#endif + + // Check target first. + if (!actor->target) + { + actor->reactiontime = actor->info->reactiontime; + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + + A_FaceTarget(actor); + + if (!(arrow = P_SpawnMissile(actor, actor->target, (mobjtype_t)locvar1))) + return; + + // Set a parabolic trajectory for the arrow. + P_ParabolicMove(arrow, actor->target->x, actor->target->y, actor->target->z, arrow->info->speed); +} + +// Function: A_HoodThink +// +// Description: Thinker for Robo-Hood +// +// var1 = unused +// var2 = unused +// +void A_HoodThink(mobj_t *actor) +{ + fixed_t dx, dy, dz, dm; + boolean checksight; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_HoodThink", actor)) + return; +#endif + + // Check target first. + if (!actor->target) + { + actor->reactiontime = actor->info->reactiontime; + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + + dx = (actor->target->x - actor->x), dy = (actor->target->y - actor->y), dz = (actor->target->z - actor->z); + dm = P_AproxDistance(dx, dy); + // Target dangerously close to robohood, retreat then. + if ((dm < 256<info->raisestate); + return; + } + + // If target on sight, look at it. + if ((checksight = P_CheckSight(actor, actor->target))) + { + angle_t dang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); + if (actor->angle >= ANGLE_180) + { + actor->angle = InvAngle(actor->angle)>>1; + actor->angle = InvAngle(actor->angle); + } + else + actor->angle >>= 1; + + if (dang >= ANGLE_180) + { + dang = InvAngle(dang)>>1; + dang = InvAngle(dang); + } + else + dang >>= 1; + + actor->angle += dang; + } + + // Check whether to do anything. + if ((--actor->reactiontime) <= 0) + { + actor->reactiontime = actor->info->reactiontime; + + // If way too far, don't shoot. + if ((dm < (3072<info->missilestate); + return; + } + } +} + +// Function: A_HoodFall +// +// Description: Falling Robo-Hood +// +// var1 = unused +// var2 = unused +// +void A_HoodFall(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_HoodFall", actor)) + return; +#endif + + if (!P_IsObjectOnGround(actor)) + return; + + actor->momx = actor->momy = 0; + actor->reactiontime = actor->info->reactiontime; + P_SetMobjState(actor, actor->info->seestate); +} + +// Function: A_ArrowBonks +// +// Description: Arrow momentum setting on collision +// +// var1 = unused +// var2 = unused +// +void A_ArrowBonks(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_ArrowBonks", actor)) + return; +#endif + + if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz) + || (!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)) + actor->angle += ANGLE_180; + + P_SetObjectMomZ(actor, 8*actor->scale, false); + P_InstaThrust(actor, actor->angle, -6*actor->scale); + + actor->flags = (actor->flags|MF_NOCLIPHEIGHT) & ~MF_NOGRAVITY; + actor->z += P_MobjFlip(actor); +} + +// Function: A_SnailerThink +// +// Description: Thinker function for Snailer +// +// var1 = unused +// var2 = unused +// +void A_SnailerThink(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SnailerThink", actor)) + return; +#endif + + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) + { + // look for a new target + if (!P_LookForPlayers(actor, true, false, 0)) + return; + } + + // We now have a target. Oh bliss, rapture, and contentment! + + if (actor->target->z + actor->target->height > actor->z - FixedMul(32*FRACUNIT, actor->scale) + && actor->target->z < actor->z + actor->height + FixedMul(32*FRACUNIT, actor->scale) + && !(leveltime % (TICRATE*2))) + { + angle_t an; + fixed_t z; + + // Actor shouldn't face target, so we'll do things a bit differently here + + an = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) - actor->angle; + + z = actor->z + actor->height/2; + + if (an > ANGLE_45 && an < ANGLE_315) // fire as close as you can to the target, even if too sharp an angle from your front + { + fixed_t dist; + fixed_t dx, dy; + + dist = P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y); + + if (an > ANGLE_45 && an <= ANGLE_90) // fire at 45 degrees to the left + { + dx = actor->x + P_ReturnThrustX(actor, actor->angle + ANGLE_45, dist); + dy = actor->y + P_ReturnThrustY(actor, actor->angle + ANGLE_45, dist); + } + else if (an >= ANGLE_270 && an < ANGLE_315) // fire at 45 degrees to the right + { + dx = actor->x + P_ReturnThrustX(actor, actor->angle - ANGLE_45, dist); + dy = actor->y + P_ReturnThrustY(actor, actor->angle - ANGLE_45, dist); + } + else // fire straight ahead + { + dx = actor->x + P_ReturnThrustX(actor, actor->angle, dist); + dy = actor->y + P_ReturnThrustY(actor, actor->angle, dist); + } + + P_SpawnPointMissile(actor, dx, dy, actor->target->z, MT_ROCKET, actor->x, actor->y, z); + } + else + P_SpawnXYZMissile(actor, actor->target, MT_ROCKET, actor->x, actor->y, z); + } + + if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->target->z > actor->z) + || (actor->eflags & MFE_VERTICALFLIP && (actor->target->z + actor->target->height) > (actor->z + actor->height))) + actor->momz += FixedMul(actor->info->speed, actor->scale); + else if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->target->z < actor->z) + || (actor->eflags & MFE_VERTICALFLIP && (actor->target->z + actor->target->height) < (actor->z + actor->height))) + actor->momz -= FixedMul(actor->info->speed, actor->scale); + + actor->momz /= 2; +} + +// Function: A_SharpChase +// +// Description: Thinker/Chase routine for Spincushions +// +// var1 = unused +// var2 = unused +// +void A_SharpChase(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SharpChase", actor)) + return; +#endif + + if (actor->reactiontime) + { + INT32 delta; + + actor->reactiontime--; + + // turn towards movement direction if not there yet + if (actor->movedir < NUMDIRS) + { + actor->angle &= (7<<29); + delta = actor->angle - (actor->movedir << 29); + + if (delta > 0) + actor->angle -= ANGLE_45; + else if (delta < 0) + actor->angle += ANGLE_45; + } + + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) + { + // look for a new target + if (P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + + // chase towards player + if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed)) + P_NewChaseDir(actor); + } + else + { + actor->threshold = actor->info->painchance; + P_SetMobjState(actor, actor->info->missilestate); + S_StartSound(actor, actor->info->attacksound); + } +} + +// Function: A_SharpSpin +// +// Description: Spin chase routine for Spincushions +// +// var1 = object # to spawn as dust (if not provided not done) +// var2 = if nonzero, do the old-style spinning using this as the angle difference +// +void A_SharpSpin(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + angle_t oldang = actor->angle; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SharpSpin", actor)) + return; +#endif + + if (actor->threshold && actor->target) + { + angle_t ang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); + P_Thrust(actor, ang, actor->info->speed*actor->scale); + if (locvar2) + actor->angle += locvar2; // ANGLE_22h; + else + actor->angle = ang; + actor->threshold--; + if (leveltime & 1) + S_StartSound(actor, actor->info->painsound); + } + else + { + actor->reactiontime = actor->info->reactiontime; + P_SetMobjState(actor, actor->info->meleestate); + } + + P_SharpDust(actor, locvar1, oldang); +} + +// Function: A_SharpDecel +// +// Description: Slow down the Spincushion +// +// var1 = unused +// var2 = unused +// +void A_SharpDecel(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SharpDecel", actor)) + return; +#endif + + if (actor->momx > 2 || actor->momy > 2) + { + actor->momx >>= 1; + actor->momy >>= 1; + } + else + P_SetMobjState(actor, actor->info->xdeathstate); +} + +// Function: A_CrushstaceanWalk +// +// Description: Crushstacean movement +// +// var1 = speed (actor info's speed if 0) +// var2 = state to switch to when blocked (spawnstate if 0) +// +void A_CrushstaceanWalk(mobj_t *actor) +{ + INT32 locvar1 = (var1 ? var1 : (INT32)actor->info->speed); + INT32 locvar2 = (var2 ? var2 : (INT32)actor->info->spawnstate); + angle_t ang = actor->angle + ((actor->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270); +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CrushstaceanWalk", actor)) + return; +#endif + + actor->reactiontime--; + + if (!P_TryMove(actor, + actor->x + P_ReturnThrustX(actor, ang, locvar1*actor->scale), + actor->y + P_ReturnThrustY(actor, ang, locvar1*actor->scale), + false) + || (actor->reactiontime-- <= 0)) + { + actor->flags2 ^= MF2_AMBUSH; + P_SetMobjState(actor, locvar2); + actor->reactiontime = actor->info->reactiontime; + } +} + +// Function: A_CrushstaceanPunch +// +// Description: Crushstacean attack +// +// var1 = unused +// var2 = state to go to if unsuccessful (spawnstate if 0) +// +void A_CrushstaceanPunch(mobj_t *actor) +{ + //INT32 locvar1 = var1; + INT32 locvar2 = (var2 ? var2 : (INT32)actor->info->spawnstate); +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CrushstaceanPunch", actor)) + return; +#endif + + if (!actor->tracer) + return; + + if (!actor->target) + { + P_SetMobjState(actor, locvar2); + return; + } + + actor->tracer->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); + P_SetMobjState(actor->tracer, actor->tracer->info->missilestate); + actor->tracer->extravalue1 = actor->tracer->extravalue2 = 0; + S_StartSound(actor, actor->info->attacksound); +} + +// Function: A_CrushclawAim +// +// Description: Crushstacean claw aiming +// +// var1 = sideways offset +// var2 = vertical offset +// +void A_CrushclawAim(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobj_t *crab = actor->tracer; + angle_t ang; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CrushclawAim", actor)) + return; +#endif + + if (!crab) + { + P_RemoveMobj(actor); + return; // there is only one step and it is crab + } + + if (crab->target || P_LookForPlayers(crab, true, false, 600*crab->scale)) + ang = R_PointToAngle2(crab->x, crab->y, crab->target->x, crab->target->y); + else + ang = crab->angle + ((crab->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270); + ang -= actor->angle; + +#define anglimit ANGLE_22h +#define angfactor 5 + if (ang < ANGLE_180) + { + if (ang > anglimit) + ang = anglimit; + ang /= angfactor; + } + else + { + ang = InvAngle(ang); + if (ang > anglimit) + ang = anglimit; + ang = InvAngle(ang/angfactor); + } + actor->angle += ang; +#undef anglimit +#undef angfactor + + P_TeleportMove(actor, + crab->x + P_ReturnThrustX(actor, actor->angle, locvar1*crab->scale), + crab->y + P_ReturnThrustY(actor, actor->angle, locvar1*crab->scale), + crab->z + locvar2*crab->scale); + + if (!crab->target || !crab->info->missilestate || (statenum_t)(crab->state-states) == crab->info->missilestate) + return; + + if (((ang + ANG1) < ANG2) || P_AproxDistance(crab->x - crab->target->x, crab->y - crab->target->y) < 333*crab->scale) + P_SetMobjState(crab, crab->info->missilestate); +} + +// Function: A_CrushclawLaunch +// +// Description: Crushstacean claw launching +// +// var1: +// 0 - forwards +// anything else - backwards +// var2 = state to change to when done +// +void A_CrushclawLaunch(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobj_t *crab = actor->tracer; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CrushclawLaunch", actor)) + return; +#endif + + if (!crab) + { + mobj_t *chainnext; + while (actor) + { + chainnext = actor->target; + P_RemoveMobj(actor); + actor = chainnext; + } + return; // there is only one step and it is crab + } + + if (!actor->extravalue1) + { + S_StartSound(actor, actor->info->activesound); + actor->extravalue1 = ((locvar1) ? -1 : 32); + } + else if (actor->extravalue1 != 1) + actor->extravalue1 -= 1; + +#define CSEGS 5 + if (!actor->target) + { + mobj_t *prevchain = actor; + UINT8 i = 0; + for (i = 0; (i < CSEGS); i++) + { + mobj_t *newchain = P_SpawnMobjFromMobj(actor, 0, 0, 0, actor->info->raisestate); + prevchain->target = newchain; + prevchain = newchain; + } + actor->target->angle = R_PointToAngle2(actor->target->x, actor->target->y, crab->target->x, crab->target->y); + } + + if ((!locvar1) && crab->target) + { +#define anglimit ANGLE_22h +#define angfactor 7 + angle_t ang = R_PointToAngle2(actor->target->x, actor->target->y, crab->target->x, crab->target->y) - actor->target->angle; + if (ang < ANGLE_180) + { + if (ang > anglimit) + ang = anglimit; + ang /= angfactor; + } + else + { + ang = InvAngle(ang); + if (ang > anglimit) + ang = anglimit; + ang /= angfactor; + ang = InvAngle(ang); + } + actor->target->angle += ang; + actor->angle = actor->target->angle; + } + + actor->extravalue2 += actor->extravalue1; + + if (!P_TryMove(actor, + actor->target->x + P_ReturnThrustX(actor, actor->target->angle, actor->extravalue2*actor->scale), + actor->target->y + P_ReturnThrustY(actor, actor->target->angle, actor->extravalue2*actor->scale), + true) + && !locvar1) + { + actor->extravalue1 = 0; + actor->extravalue2 = FixedHypot(actor->x - actor->target->x, actor->y - actor->target->y)>>FRACBITS; + P_SetMobjState(actor, locvar2); + S_StopSound(actor); + S_StartSound(actor, sfx_s3k49); + } + else + { + actor->z = actor->target->z; + if ((!locvar1 && (actor->extravalue2 > 256)) || (locvar1 && (actor->extravalue2 < 16))) + { + if (locvar1) // In case of retracting, resume crab and remove the chain. + { + mobj_t *chain = actor->target, *chainnext; + while (chain) + { + chainnext = chain->target; + P_RemoveMobj(chain); + chain = chainnext; + } + actor->extravalue2 = 0; + actor->angle = R_PointToAngle2(crab->x, crab->y, actor->x, actor->y); + P_SetTarget(&actor->target, NULL); + P_SetTarget(&crab->target, NULL); + P_SetMobjState(crab, crab->state->nextstate); + } + actor->extravalue1 = 0; + P_SetMobjState(actor, locvar2); + S_StopSound(actor); + if (!locvar1) + S_StartSound(actor, sfx_s3k64); + } + } + + if (!actor->target) + return; + + { + mobj_t *chain = actor->target->target; + fixed_t dx = (actor->x - actor->target->x)/CSEGS, dy = (actor->y - actor->target->y)/CSEGS, dz = (actor->z - actor->target->z)/CSEGS; + fixed_t idx = dx, idy = dy, idz = dz; + while (chain) + { + P_TeleportMove(chain, actor->target->x + idx, actor->target->y + idy, actor->target->z + idz); + chain->watertop = chain->z; + idx += dx; + idy += dy; + idz += dz; + chain = chain->target; + } + } +#undef CSEGS +} + +// Function: A_VultureVtol +// +// Description: Vulture rising up to match target's height +// +// var1 = unused +// var2 = unused +// +void A_VultureVtol(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_VultureVtol", actor)) + return; +#endif + + if (!actor->target) + return; + + actor->flags |= MF_NOGRAVITY; + actor->flags |= MF_FLOAT; + + A_FaceTarget(actor); + + S_StopSound(actor); + + if (actor->z < actor->target->z+(actor->target->height/4) && actor->z + actor->height < actor->ceilingz) + actor->momz = FixedMul(2*FRACUNIT, actor->scale); + else if (actor->z > (actor->target->z+(actor->target->height/4)*3) && actor->z > actor->floorz) + actor->momz = FixedMul(-2*FRACUNIT, actor->scale); + else + { + // Attack! + actor->momz = 0; + P_SetMobjState(actor, actor->info->missilestate); + S_StartSound(actor, actor->info->activesound); + } +} + +// Function: A_VultureCheck +// +// Description: If the vulture is stopped, look for a new target +// +// var1 = unused +// var2 = unused +// +void A_VultureCheck(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_VultureCheck", actor)) + return; +#endif + + if (actor->momx || actor->momy) + return; + + actor->flags &= ~MF_NOGRAVITY; // Fall down + + if (actor->z <= actor->floorz) + { + actor->angle -= ANGLE_180; // turn around + P_SetMobjState(actor, actor->info->spawnstate); + } +} + +// Function: A_SkimChase +// +// Description: Thinker/Chase routine for Skims +// +// var1 = unused +// var2 = unused +// +void A_SkimChase(mobj_t *actor) +{ + INT32 delta; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SkimChase", actor)) + return; +#endif + if (actor->reactiontime) + actor->reactiontime--; + + // modify target threshold + if (actor->threshold) + { + if (!actor->target || actor->target->health <= 0) + actor->threshold = 0; + else + actor->threshold--; + } + + // turn towards movement direction if not there yet + if (actor->movedir < NUMDIRS) + { + actor->angle &= (7<<29); + delta = actor->angle - (actor->movedir << 29); + + if (delta > 0) + actor->angle -= ANGLE_45; + else if (delta < 0) + actor->angle += ANGLE_45; + } + + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) + { + // look for a new target + P_LookForPlayers(actor, true, false, 0); + + // the spawnstate for skims already calls this function so just return either way + // without changing state + return; + } + + // do not attack twice in a row + if (actor->flags2 & MF2_JUSTATTACKED) + { + actor->flags2 &= ~MF2_JUSTATTACKED; + P_NewChaseDir(actor); + return; + } + + // check for melee attack + if (actor->info->meleestate && P_SkimCheckMeleeRange(actor)) + { + if (actor->info->attacksound) + S_StartAttackSound(actor, actor->info->attacksound); + + P_SetMobjState(actor, actor->info->meleestate); + return; + } + + // check for missile attack + if (actor->info->missilestate) + { + if (actor->movecount || !P_CheckMissileRange(actor)) + goto nomissile; + + P_SetMobjState(actor, actor->info->missilestate); + actor->flags2 |= MF2_JUSTATTACKED; + return; + } + +nomissile: + // possibly choose another target + if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)) + && P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + // chase towards player + if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed)) + P_NewChaseDir(actor); +} + +// Function: A_FaceTarget +// +// Description: Immediately turn to face towards your target. +// +// var1 = unused +// var2 = unused +// +void A_FaceTarget(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FaceTarget", actor)) + return; +#endif + if (!actor->target) + return; + + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); +} + +// Function: A_FaceTracer +// +// Description: Immediately turn to face towards your tracer. +// +// var1 = unused +// var2 = unused +// +void A_FaceTracer(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FaceTracer", actor)) + return; +#endif + if (!actor->tracer) + return; + + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); +} + +// Function: A_LobShot +// +// Description: Lob an object at your target. +// +// var1 = object # to lob +// var2: +// var2 >> 16 = height offset +// var2 & 65535 = airtime +// +void A_LobShot(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2 >> 16; + mobj_t *shot, *hitspot; + angle_t an; + fixed_t z; + fixed_t dist; + fixed_t vertical, horizontal; + fixed_t airtime = var2 & 65535; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_LobShot", actor)) + return; +#endif + if (!actor->target) + return; + + A_FaceTarget(actor); + + if (actor->eflags & MFE_VERTICALFLIP) + { + z = actor->z + actor->height - FixedMul(locvar2*FRACUNIT, actor->scale); + if (actor->type == MT_BLACKEGGMAN) + z -= FixedMul(mobjinfo[locvar1].height, actor->scale/2); + else + z -= FixedMul(mobjinfo[locvar1].height, actor->scale); + } + else + z = actor->z + FixedMul(locvar2*FRACUNIT, actor->scale); + + shot = P_SpawnMobj(actor->x, actor->y, z, locvar1); + + if (actor->type == MT_BLACKEGGMAN) + { + shot->destscale = actor->scale/2; + P_SetScale(shot, actor->scale/2); + } + else + { + shot->destscale = actor->scale; + P_SetScale(shot, actor->scale); + } + + // Keep track of where it's going to land + hitspot = P_SpawnMobj(actor->target->x&(64*FRACUNIT-1), actor->target->y&(64*FRACUNIT-1), actor->target->subsector->sector->floorheight, MT_NULL); + hitspot->tics = airtime; + P_SetTarget(&shot->tracer, hitspot); + + P_SetTarget(&shot->target, actor); // where it came from + + shot->angle = an = actor->angle; + an >>= ANGLETOFINESHIFT; + + dist = P_AproxDistance(actor->target->x - shot->x, actor->target->y - shot->y); + + horizontal = dist / airtime; + vertical = FixedMul((gravity*airtime)/2, shot->scale); + + shot->momx = FixedMul(horizontal, FINECOSINE(an)); + shot->momy = FixedMul(horizontal, FINESINE(an)); + shot->momz = vertical; + +/* Try to adjust when destination is not the same height + if (actor->z != actor->target->z) + { + fixed_t launchhyp; + fixed_t diff; + fixed_t orig; + + diff = actor->z - actor->target->z; + { + launchhyp = P_AproxDistance(horizontal, vertical); + + orig = FixedMul(FixedDiv(vertical, horizontal), diff); + + CONS_Debug(DBG_GAMELOGIC, "orig: %d\n", (orig)>>FRACBITS); + + horizontal = dist / airtime; + vertical = (gravity*airtime)/2; + } + dist -= orig; + shot->momx = FixedMul(horizontal, FINECOSINE(an)); + shot->momy = FixedMul(horizontal, FINESINE(an)); + shot->momz = vertical; +*/ + + if (shot->info->seesound) + S_StartSound(shot, shot->info->seesound); + + if (!(actor->flags & MF_BOSS)) + { + if (ultimatemode) + actor->reactiontime = actor->info->reactiontime*TICRATE; + else + actor->reactiontime = actor->info->reactiontime*TICRATE*2; + } +} + +// Function: A_FireShot +// +// Description: Shoot an object at your target. +// +// var1 = object # to shoot +// var2 = height offset +// +void A_FireShot(mobj_t *actor) +{ + fixed_t z; + INT32 locvar1 = var1; + INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FireShot", actor)) + return; +#endif + if (!actor->target) + return; + + A_FaceTarget(actor); + + if (actor->eflags & MFE_VERTICALFLIP) + z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale); + else + z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale); + + P_SpawnXYZMissile(actor, actor->target, locvar1, actor->x, actor->y, z); + + if (!(actor->flags & MF_BOSS)) + { + if (ultimatemode) + actor->reactiontime = actor->info->reactiontime*TICRATE; + else + actor->reactiontime = actor->info->reactiontime*TICRATE*2; + } +} + +// Function: A_SuperFireShot +// +// Description: Shoot an object at your target that will even stall Super Sonic. +// +// var1 = object # to shoot +// var2 = height offset +// +void A_SuperFireShot(mobj_t *actor) +{ + fixed_t z; + mobj_t *mo; + INT32 locvar1 = var1; + INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SuperFireShot", actor)) + return; +#endif + if (!actor->target) + return; + + A_FaceTarget(actor); + + if (actor->eflags & MFE_VERTICALFLIP) + z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale); + else + z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale); + + mo = P_SpawnXYZMissile(actor, actor->target, locvar1, actor->x, actor->y, z); + + if (mo) + mo->flags2 |= MF2_SUPERFIRE; + + if (!(actor->flags & MF_BOSS)) + { + if (ultimatemode) + actor->reactiontime = actor->info->reactiontime*TICRATE; + else + actor->reactiontime = actor->info->reactiontime*TICRATE*2; + } +} + +// Function: A_BossFireShot +// +// Description: Shoot an object at your target ala Bosses: +// +// var1 = object # to shoot +// var2: +// 0 - Boss 1 Left side +// 1 - Boss 1 Right side +// 2 - Boss 3 Left side upper +// 3 - Boss 3 Left side lower +// 4 - Boss 3 Right side upper +// 5 - Boss 3 Right side lower +// +void A_BossFireShot(mobj_t *actor) +{ + fixed_t x, y, z; + INT32 locvar1 = var1; + INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_BossFireShot", actor)) + return; +#endif + if (!actor->target) + return; + + A_FaceTarget(actor); + + switch (locvar2) + { + case 0: + x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); + y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); + if (actor->eflags & MFE_VERTICALFLIP) + z = actor->z + actor->height - FixedMul(48*FRACUNIT, actor->scale); + else + z = actor->z + FixedMul(48*FRACUNIT, actor->scale); + break; + case 1: + x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); + y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); + if (actor->eflags & MFE_VERTICALFLIP) + z = actor->z + actor->height - FixedMul(48*FRACUNIT, actor->scale); + else + z = actor->z + FixedMul(48*FRACUNIT, actor->scale); + break; + case 2: + x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(56*FRACUNIT, actor->scale)); + y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(56*FRACUNIT, actor->scale)); + if (actor->eflags & MFE_VERTICALFLIP) + z = actor->z + actor->height - FixedMul(42*FRACUNIT, actor->scale); + else + z = actor->z + FixedMul(42*FRACUNIT, actor->scale); + break; + case 3: + x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(58*FRACUNIT, actor->scale)); + y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(58*FRACUNIT, actor->scale)); + if (actor->eflags & MFE_VERTICALFLIP) + z = actor->z + actor->height - FixedMul(30*FRACUNIT, actor->scale); + else + z = actor->z + FixedMul(30*FRACUNIT, actor->scale); + break; + case 4: + x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(56*FRACUNIT, actor->scale)); + y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(56*FRACUNIT, actor->scale)); + if (actor->eflags & MFE_VERTICALFLIP) + z = actor->z + actor->height - FixedMul(42*FRACUNIT, actor->scale); + else + z = actor->z + FixedMul(42*FRACUNIT, actor->scale); + break; + case 5: + x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(58*FRACUNIT, actor->scale)); + y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(58*FRACUNIT, actor->scale)); + if (actor->eflags & MFE_VERTICALFLIP) + z = actor->z + actor->height - FixedMul(30*FRACUNIT, actor->scale); + else + z = actor->z + FixedMul(30*FRACUNIT, actor->scale); + break; + default: + x = actor->x; + y = actor->y; + z = actor->z + actor->height/2; + break; + } + + P_SpawnXYZMissile(actor, actor->target, locvar1, x, y, z); +} + +// Function: A_Boss7FireMissiles +// +// Description: Shoot 4 missiles of a specific object type at your target ala Black Eggman +// +// var1 = object # to shoot +// var2 = firing sound +// +void A_Boss7FireMissiles(mobj_t *actor) +{ + mobj_t dummymo; + INT32 locvar1 = var1; + INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss7FireMissiles", actor)) + return; +#endif + + if (!actor->target) + { + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + + A_FaceTarget(actor); + + S_StartSound(NULL, locvar2); + + // set dummymo's coordinates + dummymo.x = actor->target->x; + dummymo.y = actor->target->y; + dummymo.z = actor->target->z + FixedMul(16*FRACUNIT, actor->scale); // raised height + + P_SpawnXYZMissile(actor, &dummymo, locvar1, + actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)), + actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)), + actor->z + FixedDiv(actor->height, 3*FRACUNIT/2)); + + P_SpawnXYZMissile(actor, &dummymo, locvar1, + actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)), + actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)), + actor->z + FixedDiv(actor->height, 3*FRACUNIT/2)); + + P_SpawnXYZMissile(actor, &dummymo, locvar1, + actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)), + actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)), + actor->z + actor->height/2); + + P_SpawnXYZMissile(actor, &dummymo, locvar1, + actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)), + actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)), + actor->z + actor->height/2); +} + +// Function: A_Boss1Laser +// +// Description: Shoot an object at your target ala Bosses: +// +// var1 = object # to shoot +// var2: +// 0 - Boss 1 Left side +// 1 - Boss 1 Right side +// +void A_Boss1Laser(mobj_t *actor) +{ + fixed_t x, y, z, floorz, speed; + INT32 locvar1 = var1; + INT32 locvar2 = var2; + INT32 i; + angle_t angle; + mobj_t *point; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss1Laser", actor)) + return; +#endif + if (!actor->target) + return; + + switch (locvar2) + { + case 0: + x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); + y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); + if (actor->eflags & MFE_VERTICALFLIP) + z = actor->z + actor->height - FixedMul(56*FRACUNIT, actor->scale) - mobjinfo[locvar1].height; + else + z = actor->z + FixedMul(56*FRACUNIT, actor->scale); + break; + case 1: + x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); + y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale)); + if (actor->eflags & MFE_VERTICALFLIP) + z = actor->z + actor->height - FixedMul(56*FRACUNIT, actor->scale) - mobjinfo[locvar1].height; + else + z = actor->z + FixedMul(56*FRACUNIT, actor->scale); + break; + default: + x = actor->x; + y = actor->y; + z = actor->z + actor->height/2; + break; + } + + if (!(actor->flags2 & MF2_FIRING)) + { + actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y); + if (mobjinfo[locvar1].seesound) + S_StartSound(actor, mobjinfo[locvar1].seesound); + if (!(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)) + { + point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor->angle, actor->radius), y + P_ReturnThrustY(actor, actor->angle, actor->radius), actor->z - actor->height / 2, MT_EGGMOBILE_TARGET); + point->angle = actor->angle; + point->fuse = actor->tics+1; + P_SetTarget(&point->target, actor->target); + P_SetTarget(&actor->target, point); + } + } + /* -- the following was relevant when the MT_EGGMOBILE_TARGET was allowed to move left and right from its path + else if (actor->target && !(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)) + actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);*/ + + if (actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH) + angle = FixedAngle(FixedDiv(actor->tics*160*FRACUNIT, actor->state->tics*FRACUNIT) + 10*FRACUNIT); + else + angle = R_PointToAngle2(z + (mobjinfo[locvar1].height>>1), 0, actor->target->z, R_PointToDist2(x, y, actor->target->x, actor->target->y)); + point = P_SpawnMobj(x, y, z, locvar1); + P_SetTarget(&point->target, actor); + point->angle = actor->angle; + speed = point->radius*2; + point->momz = FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT), speed); + point->momx = FixedMul(FINESINE(angle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(point->angle>>ANGLETOFINESHIFT), speed)); + point->momy = FixedMul(FINESINE(angle>>ANGLETOFINESHIFT), FixedMul(FINESINE(point->angle>>ANGLETOFINESHIFT), speed)); + + for (i = 0; i < 256; i++) + { + mobj_t *mo = P_SpawnMobj(point->x, point->y, point->z, point->type); + mo->angle = point->angle; + P_UnsetThingPosition(mo); + mo->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY; + P_SetThingPosition(mo); + + x = point->x, y = point->y, z = point->z; + if (P_RailThinker(point)) + break; + } + + floorz = P_FloorzAtPos(x, y, z, mobjinfo[MT_EGGMOBILE_FIRE].height); + if (z - floorz < mobjinfo[MT_EGGMOBILE_FIRE].height>>1) + { + point = P_SpawnMobj(x, y, floorz+1, MT_EGGMOBILE_FIRE); + point->target = actor; + point->destscale = 3*FRACUNIT; + point->scalespeed = FRACUNIT>>2; + point->fuse = TICRATE; + } + + if (actor->tics > 1) + actor->flags2 |= MF2_FIRING; + else + actor->flags2 &= ~MF2_FIRING; +} + +// Function: A_FocusTarget +// +// Description: Home in on your target. +// +// var1: +// 0 - accelerative focus with friction +// 1 - steady focus with fixed movement speed +// anything else - don't move +// var2: +// 0 - don't trace target, just move forwards +// & 1 - change horizontal angle +// & 2 - change vertical angle +// +void A_FocusTarget(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FocusTarget", actor)) + return; +#endif + + if (actor->target) + { + fixed_t speed = FixedMul(actor->info->speed, actor->scale); + fixed_t dist = (locvar2 ? R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y) : speed+1); + angle_t hangle = ((locvar2 & 1) ? R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) : actor->angle); + angle_t vangle = ((locvar2 & 2) ? R_PointToAngle2(actor->z , 0, actor->target->z + (actor->target->height>>1), dist) : ANGLE_90); + switch(locvar1) + { + case 0: + { + actor->momx -= actor->momx>>4, actor->momy -= actor->momy>>4, actor->momz -= actor->momz>>4; + actor->momz += FixedMul(FINECOSINE(vangle>>ANGLETOFINESHIFT), speed); + actor->momx += FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hangle>>ANGLETOFINESHIFT), speed)); + actor->momy += FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINESINE(hangle>>ANGLETOFINESHIFT), speed)); + } + break; + case 1: + if (dist > speed) + { + actor->momz = FixedMul(FINECOSINE(vangle>>ANGLETOFINESHIFT), speed); + actor->momx = FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hangle>>ANGLETOFINESHIFT), speed)); + actor->momy = FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINESINE(hangle>>ANGLETOFINESHIFT), speed)); + } + else + { + actor->momx = 0, actor->momy = 0, actor->momz = 0; + actor->z = actor->target->z + (actor->target->height>>1); + P_TryMove(actor, actor->target->x, actor->target->y, true); + } + break; + default: + break; + } + } +} + +// Function: A_Boss4Reverse +// +// Description: Reverse arms direction. +// +// var1 = sfx to play +// var2 = unused +// +void A_Boss4Reverse(mobj_t *actor) +{ + sfxenum_t locvar1 = (sfxenum_t)var1; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss4Reverse", actor)) + return; +#endif + S_StartSound(NULL, locvar1); + actor->reactiontime = 0; + if (actor->movedir == 1) + actor->movedir = 2; + else + actor->movedir = 1; +} + +// Function: A_Boss4SpeedUp +// +// Description: Speed up arms +// +// var1 = sfx to play +// var2 = unused +// +void A_Boss4SpeedUp(mobj_t *actor) +{ + sfxenum_t locvar1 = (sfxenum_t)var1; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss4SpeedUp", actor)) + return; +#endif + S_StartSound(NULL, locvar1); + actor->reactiontime = 2; +} + +// Function: A_Boss4Raise +// +// Description: Raise helmet +// +// var1 = sfx to play +// var2 = unused +// +void A_Boss4Raise(mobj_t *actor) +{ + sfxenum_t locvar1 = (sfxenum_t)var1; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss4Raise", actor)) + return; +#endif + S_StartSound(NULL, locvar1); + actor->reactiontime = 1; +} + +// Function: A_SkullAttack +// +// Description: Fly at the player like a missile. +// +// var1: +// 0 - Fly at the player +// 1 - Fly away from the player +// 2 - Strafe in relation to the player +// var2: +// 0 - Fly horizontally and vertically +// 1 - Fly horizontal-only (momz = 0) +// +#define SKULLSPEED (20*FRACUNIT) + +void A_SkullAttack(mobj_t *actor) +{ + mobj_t *dest; + angle_t an; + INT32 dist; + INT32 speed; + INT32 locvar1 = var1; + INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SkullAttack", actor)) + return; +#endif + if (!actor->target) + return; + + speed = FixedMul(SKULLSPEED, actor->scale); + + dest = actor->target; + actor->flags2 |= MF2_SKULLFLY; + if (actor->info->activesound) + S_StartSound(actor, actor->info->activesound); + A_FaceTarget(actor); + + if (locvar1 == 1) + actor->angle += ANGLE_180; + else if (locvar1 == 2) + actor->angle += (P_RandomChance(FRACUNIT/2)) ? ANGLE_90 : -ANGLE_90; + + an = actor->angle >> ANGLETOFINESHIFT; + + actor->momx = FixedMul(speed, FINECOSINE(an)); + actor->momy = FixedMul(speed, FINESINE(an)); + dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); + dist = dist / speed; + + if (dist < 1) + dist = 1; + + actor->momz = (dest->z + (dest->height>>1) - actor->z) / dist; + + if (locvar1 == 1) + actor->momz = -actor->momz; + if (locvar2 == 1) + actor->momz = 0; +} + +// Function: A_BossZoom +// +// Description: Like A_SkullAttack, but used by Boss 1. +// +// var1 = unused +// var2 = unused +// +void A_BossZoom(mobj_t *actor) +{ + mobj_t *dest; + angle_t an; + INT32 dist; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_BossZoom", actor)) + return; +#endif + if (!actor->target) + return; + + dest = actor->target; + actor->flags2 |= MF2_SKULLFLY; + if (actor->info->attacksound) + S_StartAttackSound(actor, actor->info->attacksound); + A_FaceTarget(actor); + an = actor->angle >> ANGLETOFINESHIFT; + actor->momx = FixedMul(FixedMul(actor->info->speed*5*FRACUNIT, actor->scale), FINECOSINE(an)); + actor->momy = FixedMul(FixedMul(actor->info->speed*5*FRACUNIT, actor->scale), FINESINE(an)); + dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); + dist = dist / FixedMul(actor->info->speed*5*FRACUNIT, actor->scale); + + if (dist < 1) + dist = 1; + actor->momz = (dest->z + (dest->height>>1) - actor->z) / dist; +} + +// Function: A_BossScream +// +// Description: Spawns explosions and plays appropriate sounds around the defeated boss. +// +// var1: +// 0 - Use movecount to spawn explosions evenly +// 1 - Use P_Random to spawn explosions at complete random +// var2 = Object to spawn. Default is MT_BOSSEXPLODE. +// +void A_BossScream(mobj_t *actor) +{ + mobj_t *mo; + fixed_t x, y, z; + angle_t fa; + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobjtype_t explodetype; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_BossScream", actor)) + return; +#endif + switch (locvar1) + { + default: + case 0: + actor->movecount += 4*16; + actor->movecount %= 360; + fa = (FixedAngle(actor->movecount*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK; + break; + case 1: + fa = (FixedAngle(P_RandomKey(360)*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK; + break; + } + x = actor->x + FixedMul(FINECOSINE(fa),actor->radius); + y = actor->y + FixedMul(FINESINE(fa),actor->radius); + + // Determine what mobj to spawn. If undefined or invalid, use MT_BOSSEXPLODE as default. + if (locvar2 <= 0 || locvar2 >= NUMMOBJTYPES) + explodetype = MT_BOSSEXPLODE; + else + explodetype = (mobjtype_t)locvar2; + + if (actor->eflags & MFE_VERTICALFLIP) + z = actor->z + actor->height - mobjinfo[explodetype].height - FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale); + else + z = actor->z + FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale); + + mo = P_SpawnMobj(x, y, z, explodetype); + if (actor->eflags & MFE_VERTICALFLIP) + mo->flags2 |= MF2_OBJECTFLIP; + mo->destscale = actor->scale; + P_SetScale(mo, mo->destscale); + if (actor->info->deathsound) + S_StartSound(mo, actor->info->deathsound); +} + +// Function: A_Scream +// +// Description: Starts the death sound of the object. +// +// var1 = unused +// var2 = unused +// +void A_Scream(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Scream", actor)) + return; +#endif + if (actor->tracer && (actor->tracer->type == MT_SHELL || actor->tracer->type == MT_FIREBALL)) + S_StartScreamSound(actor, sfx_mario2); + else if (actor->info->deathsound) + S_StartScreamSound(actor, actor->info->deathsound); +} + +// Function: A_Pain +// +// Description: Starts the pain sound of the object. +// +// var1 = unused +// var2 = unused +// +void A_Pain(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Pain", actor)) + return; +#endif + if (actor->info->painsound) + S_StartSound(actor, actor->info->painsound); + + actor->flags2 &= ~MF2_FIRING; + actor->flags2 &= ~MF2_SUPERFIRE; +} + +// Function: A_Fall +// +// Description: Changes a dying object's flags to reflect its having fallen to the ground. +// +// var1 = unused +// var2 = unused +// +void A_Fall(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Fall", actor)) + return; +#endif + // actor is on ground, it can be walked over + actor->flags &= ~MF_SOLID; + + // fall through the floor + actor->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; + + // So change this if corpse objects + // are meant to be obstacles. +} + +#define LIVESBOXDISPLAYPLAYER // Use displayplayer instead of closest player + +// Function: A_1upThinker +// +// Description: Used by the 1up box to show the player's face. +// +// var1 = unused +// var2 = unused +// +void A_1upThinker(mobj_t *actor) +{ + INT32 i; + fixed_t dist = INT32_MAX; + fixed_t temp; + INT32 closestplayer = -1; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_1upThinker", actor)) + return; +#endif + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].bot || players[i].spectator) + continue; + + if (!players[i].mo) + continue; + + if ((netgame || multiplayer) && players[i].playerstate != PST_LIVE) + continue; + + temp = P_AproxDistance(players[i].mo->x-actor->x, players[i].mo->y-actor->y); + + if (temp < dist) + { + closestplayer = i; + dist = temp; + } + } + + if (closestplayer == -1 || skins[players[closestplayer].skin].sprites[SPR2_LIFE].numframes == 0) + { // Closest player not found (no players in game?? may be empty dedicated server!), or does not have correct sprite. + if (actor->tracer) { + P_RemoveMobj(actor->tracer); + actor->tracer = NULL; + } + return; + } + + // We're using the overlay, so use the overlay 1up box (no text) + actor->sprite = SPR_TV1P; + + if (!actor->tracer) + { + P_SetTarget(&actor->tracer, P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY)); + P_SetTarget(&actor->tracer->target, actor); + actor->tracer->skin = &skins[players[closestplayer].skin]; // required here to prevent spr2 default showing stand for a single frame + P_SetMobjState(actor->tracer, actor->info->seestate); + + // The overlay is going to be one tic early turning off and on + // because it's going to get its thinker run the frame we spawned it. + // So make it take one tic longer if it just spawned. + ++actor->tracer->tics; + } + + actor->tracer->color = players[closestplayer].mo->color; + actor->tracer->skin = &skins[players[closestplayer].skin]; +} + +// Function: A_MonitorPop +// +// Description: Used by monitors when they explode. +// +// var1 = unused +// var2 = unused +// +void A_MonitorPop(mobj_t *actor) +{ + mobjtype_t item = 0; + mobj_t *newmobj; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_MonitorPop", actor)) + return; +#endif + + // Spawn the "pop" explosion. + if (actor->info->deathsound) + S_StartSound(actor, actor->info->deathsound); + P_SpawnMobjFromMobj(actor, 0, 0, actor->height/4, MT_EXPLODE); + + // We're dead now. De-solidify. + actor->health = 0; + P_UnsetThingPosition(actor); + actor->flags &= ~MF_SOLID; + actor->flags |= MF_NOCLIP; + P_SetThingPosition(actor); + + if (actor->info->damage == MT_UNKNOWN) + { + // MT_UNKNOWN is random. Because it's unknown to us... get it? + item = P_DoRandomBoxChances(); + + if (item == MT_NULL) + { + CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n")); + return; + } + } + else + item = actor->info->damage; + + if (item == 0) + { + CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_MonitorPop\n"); + return; + } + + newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 13*FRACUNIT, item); + P_SetTarget(&newmobj->target, actor->target); // Transfer target + + if (item == MT_1UP_ICON) + { + if (actor->tracer) // Remove the old lives icon. + P_RemoveMobj(actor->tracer); + + if (!newmobj->target + || !newmobj->target->player + || !newmobj->target->skin + || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0) + {} // No lives icon for this player, use the default. + else + { // Spawn the lives icon. + mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY); + P_SetTarget(&livesico->target, newmobj); + P_SetTarget(&newmobj->tracer, livesico); + + livesico->color = newmobj->target->player->mo->color; + livesico->skin = &skins[newmobj->target->player->skin]; + P_SetMobjState(livesico, newmobj->info->seestate); + + // We're using the overlay, so use the overlay 1up sprite (no text) + newmobj->sprite = SPR_TV1P; + } + } +} + +// Function: A_GoldMonitorPop +// +// Description: Used by repeating monitors when they turn off. They don't really pop, but, you know... +// +// var1 = unused +// var2 = unused +// +void A_GoldMonitorPop(mobj_t *actor) +{ + mobjtype_t item = 0; + mobj_t *newmobj; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_GoldMonitorPop", actor)) + return; +#endif + + // Don't spawn the "pop" explosion, because the monitor isn't broken. + if (actor->info->deathsound) + S_StartSound(actor, actor->info->deathsound); + //P_SpawnMobjFromMobj(actor, 0, 0, actor.height/4, MT_EXPLODE); + + // Remove our flags for a bit. + // Players can now stand on top of us. + P_UnsetThingPosition(actor); + actor->flags &= ~(MF_MONITOR|MF_SHOOTABLE); + P_SetThingPosition(actor); + + // Don't count this box in statistics. Sorry. + if (actor->target && actor->target->player) + --actor->target->player->numboxes; + actor->fuse = 0; // Don't let the monitor code screw us up. + + if (actor->info->damage == MT_UNKNOWN) + { + // MT_UNKNOWN is random. Because it's unknown to us... get it? + item = P_DoRandomBoxChances(); + + if (item == MT_NULL) + { + CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n")); + return; + } + } + else + item = actor->info->damage; + + if (item == 0) + { + CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_GoldMonitorPop\n"); + return; + } + + // Note: the icon spawns 1 fracunit higher + newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 14*FRACUNIT, item); + P_SetTarget(&newmobj->target, actor->target); // Transfer target + + if (item == MT_1UP_ICON) + { + if (actor->tracer) // Remove the old lives icon. + P_RemoveMobj(actor->tracer); + + if (!newmobj->target + || !newmobj->target->player + || !newmobj->target->skin + || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0) + {} // No lives icon for this player, use the default. + else + { // Spawn the lives icon. + mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY); + P_SetTarget(&livesico->target, newmobj); + P_SetTarget(&newmobj->tracer, livesico); + + livesico->color = newmobj->target->player->mo->color; + livesico->skin = &skins[newmobj->target->player->skin]; + P_SetMobjState(livesico, newmobj->info->seestate); + + // We're using the overlay, so use the overlay 1up sprite (no text) + newmobj->sprite = SPR_TV1P; + } + } +} + +// Function: A_GoldMonitorRestore +// +// Description: A repeating monitor is coming back to life. Reset monitor flags, etc. +// +// var1 = unused +// var2 = unused +// +void A_GoldMonitorRestore(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_GoldMonitorRestore", actor)) + return; +#endif + + actor->flags |= MF_MONITOR|MF_SHOOTABLE; + actor->health = 1; // Just in case. +} + +// Function: A_GoldMonitorSparkle +// +// Description: Spawns the little sparkly effect around big monitors. Looks pretty, doesn't it? +// +// var1 = unused +// var2 = unused +// +void A_GoldMonitorSparkle(mobj_t *actor) +{ + fixed_t i, ngangle, xofs, yofs; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_GoldMonitorSparkle", actor)) + return; +#endif + + ngangle = FixedAngle(((leveltime * 21) % 360) << FRACBITS); + xofs = FINESINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK) * (actor->radius>>FRACBITS); + yofs = FINECOSINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK) * (actor->radius>>FRACBITS); + + for (i = FRACUNIT*2; i <= FRACUNIT*3; i += FRACUNIT/2) + P_SetObjectMomZ(P_SpawnMobjFromMobj(actor, xofs, yofs, 0, MT_BOXSPARKLE), i, false); +} + +// Function: A_Explode +// +// Description: Explodes an object, doing damage to any objects nearby. The target is used as the cause of the explosion. Damage value is used as explosion range. +// +// var1 = damagetype +// var2 = unused +// +void A_Explode(mobj_t *actor) +{ + INT32 locvar1 = var1; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Explode", actor)) + return; +#endif + P_RadiusAttack(actor, actor->target, actor->info->damage, locvar1); +} + +// Function: A_BossDeath +// +// Description: Possibly trigger special effects when boss dies. +// +// var1 = unused +// var2 = unused +// +void A_BossDeath(mobj_t *mo) +{ + thinker_t *th; + mobj_t *mo2; + line_t junk; + INT32 i; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_BossDeath", mo)) + return; +#endif + + P_LinedefExecute(LE_BOSSDEAD, mo, NULL); + mo->health = 0; + + // Boss is dead (but not necessarily fleeing...) + // Lua may use this to ignore bosses after they start fleeing + mo->flags2 |= MF2_BOSSDEAD; + + // make sure there is a player alive for victory + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && ((players[i].mo && players[i].mo->health) + || ((netgame || multiplayer) && (players[i].lives || players[i].continues)))) + break; + + if (i == MAXPLAYERS) + return; // no one left alive, so do not end game + + // scan the remaining thinkers to see + // if all bosses are dead + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo2 = (mobj_t *)th; + if (mo2 != mo && (mo2->flags & MF_BOSS) && mo2->health > 0) + goto bossjustdie; // other boss not dead - just go straight to dying! + } + + // victory! + P_LinedefExecute(LE_ALLBOSSESDEAD, mo, NULL); + if (mo->flags2 & MF2_BOSSNOTRAP) + { + for (i = 0; i < MAXPLAYERS; i++) + P_DoPlayerExit(&players[i]); + } + else + { + // Bring the egg trap up to the surface + junk.tag = 680; + EV_DoElevator(&junk, elevateHighest, false); + junk.tag = 681; + EV_DoElevator(&junk, elevateUp, false); + junk.tag = 682; + EV_DoElevator(&junk, elevateHighest, false); + } + +bossjustdie: +#ifdef HAVE_BLUA + if (LUAh_BossDeath(mo)) + return; + else if (P_MobjWasRemoved(mo)) + return; +#endif + if (mo->type == MT_BLACKEGGMAN || mo->type == MT_CYBRAKDEMON) + { + mo->flags |= MF_NOCLIP; + mo->flags &= ~MF_SPECIAL; + + S_StartSound(NULL, sfx_befall); + } + else if (mo->type == MT_KOOPA) + { + junk.tag = 650; + EV_DoCeiling(&junk, raiseToHighest); + return; + } + else // eggmobiles + { + // Stop exploding and prepare to run. + P_SetMobjState(mo, mo->info->xdeathstate); + if (P_MobjWasRemoved(mo)) + return; + + P_SetTarget(&mo->target, NULL); + + // Flee! Flee! Find a point to escape to! If none, just shoot upward! + // scan the thinkers to find the runaway point + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo2 = (mobj_t *)th; + + if (mo2->type == MT_BOSSFLYPOINT) + { + // If this one's closer then the last one, go for it. + if (!mo->target || + P_AproxDistance(P_AproxDistance(mo->x - mo2->x, mo->y - mo2->y), mo->z - mo2->z) < + P_AproxDistance(P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y), mo->z - mo->target->z)) + P_SetTarget(&mo->target, mo2); + // Otherwise... Don't! + } + } + + mo->flags |= MF_NOGRAVITY|MF_NOCLIP; + mo->flags |= MF_NOCLIPHEIGHT; + + if (mo->target) + { + mo->angle = R_PointToAngle2(mo->x, mo->y, mo->target->x, mo->target->y); + mo->flags2 |= MF2_BOSSFLEE; + mo->momz = FixedMul(FixedDiv(mo->target->z - mo->z, P_AproxDistance(mo->x-mo->target->x,mo->y-mo->target->y)), FixedMul(2*FRACUNIT, mo->scale)); + } + else + mo->momz = FixedMul(2*FRACUNIT, mo->scale); + } + + if (mo->type == MT_EGGMOBILE2) + { + mo2 = P_SpawnMobj(mo->x + P_ReturnThrustX(mo, mo->angle - ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)), + mo->y + P_ReturnThrustY(mo, mo->angle - ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)), + mo->z + mo->height/2 + ((mo->eflags & MFE_VERTICALFLIP)? FixedMul(8*FRACUNIT, mo->scale)-mobjinfo[MT_BOSSTANK1].height : -FixedMul(8*FRACUNIT, mo->scale)), MT_BOSSTANK1); // Right tank + mo2->angle = mo->angle; + mo2->destscale = mo->scale; + P_SetScale(mo2, mo2->destscale); + if (mo->eflags & MFE_VERTICALFLIP) + { + mo2->eflags |= MFE_VERTICALFLIP; + mo2->flags2 |= MF2_OBJECTFLIP; + } + P_InstaThrust(mo2, mo2->angle - ANGLE_90, FixedMul(4*FRACUNIT, mo2->scale)); + P_SetObjectMomZ(mo2, 4*FRACUNIT, false); + + mo2 = P_SpawnMobj(mo->x + P_ReturnThrustX(mo, mo->angle + ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)), + mo->y + P_ReturnThrustY(mo, mo->angle + ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)), + mo->z + mo->height/2 + ((mo->eflags & MFE_VERTICALFLIP)? FixedMul(8*FRACUNIT, mo->scale)-mobjinfo[MT_BOSSTANK2].height : -FixedMul(8*FRACUNIT, mo->scale)), MT_BOSSTANK2); // Left tank + mo2->angle = mo->angle; + mo2->destscale = mo->scale; + P_SetScale(mo2, mo2->destscale); + if (mo->eflags & MFE_VERTICALFLIP) + { + mo2->eflags |= MFE_VERTICALFLIP; + mo2->flags2 |= MF2_OBJECTFLIP; + } + P_InstaThrust(mo2, mo2->angle + ANGLE_90, FixedMul(4*FRACUNIT, mo2->scale)); + P_SetObjectMomZ(mo2, 4*FRACUNIT, false); + + mo2 = P_SpawnMobj(mo->x, mo->y, + mo->z + ((mo->eflags & MFE_VERTICALFLIP)? mobjinfo[MT_BOSSSPIGOT].height-FixedMul(32*FRACUNIT,mo->scale): mo->height + FixedMul(32*FRACUNIT, mo->scale)), MT_BOSSSPIGOT); + mo2->angle = mo->angle; + mo2->destscale = mo->scale; + P_SetScale(mo2, mo2->destscale); + if (mo->eflags & MFE_VERTICALFLIP) + { + mo2->eflags |= MFE_VERTICALFLIP; + mo2->flags2 |= MF2_OBJECTFLIP; + } + P_SetObjectMomZ(mo2, 4*FRACUNIT, false); + return; + } +} + +// Function: A_CustomPower +// +// Description: Provides a custom powerup. Target (must be a player) is awarded the powerup. Reactiontime of the object is used as an index to the powers array. +// +// var1 = Power index # +// var2 = Power duration in tics +// +void A_CustomPower(mobj_t *actor) +{ + player_t *player; + INT32 locvar1 = var1; + INT32 locvar2 = var2; + boolean spawnshield = false; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CustomPower", actor)) + return; +#endif + if (!actor->target || !actor->target->player) + { + CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); + return; + } + + if (locvar1 >= NUMPOWERS) + { + CONS_Debug(DBG_GAMELOGIC, "Power #%d out of range!\n", locvar1); + return; + } + + player = actor->target->player; + + if (locvar1 == pw_shield && player->powers[pw_shield] != locvar2) + spawnshield = true; + + player->powers[locvar1] = (UINT16)locvar2; + if (actor->info->seesound) + S_StartSound(player->mo, actor->info->seesound); + + if (spawnshield) //workaround for a bug + P_SpawnShieldOrb(player); +} + +// Function: A_GiveWeapon +// +// Description: Gives the player the specified weapon panels. +// +// var1 = Weapon index # +// var2 = unused +// +void A_GiveWeapon(mobj_t *actor) +{ + player_t *player; + INT32 locvar1 = var1; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_GiveWeapon", actor)) + return; +#endif + if (!actor->target || !actor->target->player) + { + CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); + return; + } + + if (locvar1 >= 1<<(NUM_WEAPONS-1)) + { + CONS_Debug(DBG_GAMELOGIC, "Weapon #%d out of range!\n", locvar1); + return; + } + + player = actor->target->player; + + player->ringweapons |= locvar1; + if (actor->info->seesound) + S_StartSound(player->mo, actor->info->seesound); +} + +// Function: A_RingBox +// +// Description: Awards the player 10 rings. +// +// var1 = unused +// var2 = unused +// +void A_RingBox(mobj_t *actor) +{ + player_t *player; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_RingBox", actor)) + return; +#endif + if (!actor->target || !actor->target->player) + { + CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); + return; + } + + player = actor->target->player; + + P_GivePlayerRings(player, actor->info->reactiontime); + if (actor->info->seesound) + S_StartSound(player->mo, actor->info->seesound); +} + +// Function: A_Invincibility +// +// Description: Awards the player invincibility. +// +// var1 = unused +// var2 = unused +// +void A_Invincibility(mobj_t *actor) +{ + player_t *player; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Invincibility", actor)) + return; +#endif + if (!actor->target || !actor->target->player) + { + CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); + return; + } + + player = actor->target->player; + player->powers[pw_invulnerability] = invulntics + 1; + + if (P_IsLocalPlayer(player) && !player->powers[pw_super]) + { + S_StopMusic(); + if (mariomode) + G_GhostAddColor(GHC_INVINCIBLE); + strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14); + S_StartCaption(sfx_None, -1, player->powers[pw_invulnerability]); + S_ChangeMusicInternal((mariomode) ? "_minv" : "_inv", false); + } +} + +// Function: A_SuperSneakers +// +// Description: Awards the player super sneakers. +// +// var1 = unused +// var2 = unused +// +void A_SuperSneakers(mobj_t *actor) +{ + player_t *player; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SuperSneakers", actor)) + return; +#endif + if (!actor->target || !actor->target->player) + { + CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); + return; + } + + player = actor->target->player; + + actor->target->player->powers[pw_sneakers] = sneakertics + 1; + + if (P_IsLocalPlayer(player) && !player->powers[pw_super]) + { + if (S_SpeedMusic(0.0f) && (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC)) + S_SpeedMusic(1.4f); + else + { + S_StopMusic(); + S_ChangeMusicInternal("_shoes", false); + } + strlcpy(S_sfx[sfx_None].caption, "Speed shoes", 12); + S_StartCaption(sfx_None, -1, player->powers[pw_sneakers]); + } +} + +// Function: A_AwardScore +// +// Description: Adds a set amount of points to the player's score. +// +// var1 = unused +// var2 = unused +// +void A_AwardScore(mobj_t *actor) +{ + player_t *player; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_AwardScore", actor)) + return; +#endif + if (!actor->target || !actor->target->player) + { + CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); + return; + } + + player = actor->target->player; + + P_AddPlayerScore(player, actor->info->reactiontime); + if (actor->info->seesound) + S_StartSound(player->mo, actor->info->seesound); +} + +// Function: A_ExtraLife +// +// Description: Awards the player an extra life. +// +// var1 = unused +// var2 = unused +// +void A_ExtraLife(mobj_t *actor) +{ + player_t *player; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_ExtraLife", actor)) + return; +#endif + if (!actor->target || !actor->target->player) + { + CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); + return; + } + + player = actor->target->player; + + if (actor->type == MT_1UP_ICON && actor->tracer) + { + // We're using the overlay, so use the overlay 1up sprite (no text) + actor->sprite = SPR_TV1P; + } + + if (ultimatemode) //I don't THINK so! + { + S_StartSound(player->mo, sfx_lose); + return; + } + + P_GiveCoopLives(player, 1, true); +} + +// Function: A_GiveShield +// +// Description: Awards the player a specified shield. +// +// var1 = Shield type (make with SH_ constants) +// var2 = unused +// +void A_GiveShield(mobj_t *actor) +{ + player_t *player; + UINT16 locvar1 = var1; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_GiveShield", actor)) + return; +#endif + if (!actor->target || !actor->target->player) + { + CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); + return; + } + + player = actor->target->player; + + P_SwitchShield(player, locvar1); + S_StartSound(player->mo, actor->info->seesound); +} + +// Function: A_GravityBox +// +// Description: Awards the player gravity boots. +// +// var1 = unused +// var2 = unused +// +void A_GravityBox(mobj_t *actor) +{ + player_t *player; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_GravityBox", actor)) + return; +#endif + if (!actor->target || !actor->target->player) + { + CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); + return; + } + + player = actor->target->player; + + S_StartSound(player, actor->info->activesound); + + player->powers[pw_gravityboots] = (UINT16)(actor->info->reactiontime + 1); +} + +// Function: A_ScoreRise +// +// Description: Makes the little score logos rise. Speed value sets speed. +// +// var1 = unused +// var2 = unused +// +void A_ScoreRise(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_ScoreRise", actor)) + return; +#endif + // make logo rise! + P_SetObjectMomZ(actor, actor->info->speed, false); +} + +// Function: A_BunnyHop +// +// Description: Makes object hop like a bunny. +// +// var1 = jump strength +// var2 = horizontal movement +// +void A_BunnyHop(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_BunnyHop", actor)) + return; +#endif + if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz) + || (!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)) + { + P_SetObjectMomZ(actor, locvar1*FRACUNIT, false); + P_InstaThrust(actor, actor->angle, FixedMul(locvar2*FRACUNIT, actor->scale)); // Launch the hopping action! PHOOM!! + } +} + +// Function: A_BubbleSpawn +// +// Description: Spawns a randomly sized bubble from the object's location. Only works underwater. +// +// var1 = Distance to look for players. If no player is in this distance, bubbles aren't spawned. (Ambush overrides) +// var2 = unused +// +void A_BubbleSpawn(mobj_t *actor) +{ + INT32 i, locvar1 = var1; + UINT8 prandom; + mobj_t *bubble = NULL; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_BubbleSpawn", actor)) + return; +#endif + if (!(actor->eflags & MFE_UNDERWATER)) + { + // Don't draw or spawn bubbles above water + actor->flags2 |= MF2_DONTDRAW; + return; + } + actor->flags2 &= ~MF2_DONTDRAW; + + if (!(actor->flags2 & MF2_AMBUSH)) + { + // Quick! Look through players! + // Don't spawn bubbles unless a player is relatively close by (var1). + for (i = 0; i < MAXPLAYERS; ++i) + if (playeringame[i] && players[i].mo + && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (locvar1<x, actor->y, actor->z + (actor->height / 2), MT_EXTRALARGEBUBBLE); + else if (prandom > 128) + bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_SMALLBUBBLE); + else if (prandom < 128 && prandom > 96) + bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_MEDIUMBUBBLE); + + if (bubble) + { + bubble->destscale = actor->scale; + P_SetScale(bubble, actor->scale); + } +} + +// Function: A_FanBubbleSpawn +// +// Description: Spawns bubbles from fans, if they're underwater. +// +// var1 = Distance to look for players. If no player is in this distance, bubbles aren't spawned. (Ambush overrides) +// var2 = unused +// +void A_FanBubbleSpawn(mobj_t *actor) +{ + INT32 i, locvar1 = var1; + UINT8 prandom; + mobj_t *bubble = NULL; + fixed_t hz = actor->z + (4*actor->height)/5; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FanBubbleSpawn", actor)) + return; +#endif + if (!(actor->eflags & MFE_UNDERWATER)) + return; + + if (!(actor->flags2 & MF2_AMBUSH)) + { + // Quick! Look through players! + // Don't spawn bubbles unless a player is relatively close by (var2). + for (i = 0; i < MAXPLAYERS; ++i) + if (playeringame[i] && players[i].mo + && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (locvar1<x, actor->y, hz, MT_SMALLBUBBLE); + else if ((prandom & 0xF0) == 0xF0) + bubble = P_SpawnMobj(actor->x, actor->y, hz, MT_MEDIUMBUBBLE); + + if (bubble) + { + bubble->destscale = actor->scale; + P_SetScale(bubble, actor->scale); + } +} + +// Function: A_BubbleRise +// +// Description: Raises a bubble +// +// var1: +// 0 = Bend around the water abit, looking more realistic +// 1 = Rise straight up +// var2 = rising speed +// +void A_BubbleRise(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_BubbleRise", actor)) + return; +#endif + if (actor->type == MT_EXTRALARGEBUBBLE) + P_SetObjectMomZ(actor, FixedDiv(6*FRACUNIT,5*FRACUNIT), false); // make bubbles rise! + else + { + P_SetObjectMomZ(actor, locvar2, true); // make bubbles rise! + + // Move around slightly to make it look like it's bending around the water + if (!locvar1) + { + UINT8 prandom = P_RandomByte(); + if (!(prandom & 0x7)) // *****000 + { + P_InstaThrust(actor, prandom & 0x70 ? actor->angle + ANGLE_90 : actor->angle, + FixedMul(prandom & 0xF0 ? FRACUNIT/2 : -FRACUNIT/2, actor->scale)); + } + else if (!(prandom & 0x38)) // **000*** + { + P_InstaThrust(actor, prandom & 0x70 ? actor->angle - ANGLE_90 : actor->angle - ANGLE_180, + FixedMul(prandom & 0xF0 ? FRACUNIT/2 : -FRACUNIT/2, actor->scale)); + } + } + } +} + +// Function: A_BubbleCheck +// +// Description: Checks if a bubble should be drawn or not. Bubbles are not drawn above water. +// +// var1 = unused +// var2 = unused +// +void A_BubbleCheck(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_BubbleCheck", actor)) + return; +#endif + if (actor->eflags & MFE_UNDERWATER) + actor->flags2 &= ~MF2_DONTDRAW; // underwater so draw + else + actor->flags2 |= MF2_DONTDRAW; // above water so don't draw +} + +// Function: A_AttractChase +// +// Description: Makes a ring chase after a player with a ring shield and also causes spilled rings to flicker. +// +// var1 = unused +// var2 = unused +// +void A_AttractChase(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_AttractChase", actor)) + return; +#endif + if (actor->flags2 & MF2_NIGHTSPULL || !actor->health) + return; + + // spilled rings flicker before disappearing + if (leveltime & 1 && actor->type == (mobjtype_t)actor->info->reactiontime && actor->fuse && actor->fuse < 2*TICRATE) + actor->flags2 |= MF2_DONTDRAW; + else + actor->flags2 &= ~MF2_DONTDRAW; + + // Turn flingrings back into regular rings if attracted. + if (actor->tracer && actor->tracer->player + && !(actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC) && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime) + { + mobj_t *newring; + newring = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->reactiontime); + newring->momx = actor->momx; + newring->momy = actor->momy; + newring->momz = actor->momz; + P_RemoveMobj(actor); + return; + } + + P_LookForShield(actor); // Go find 'em, boy! + + if (!actor->tracer + || !actor->tracer->player + || !actor->tracer->health + || !P_CheckSight(actor, actor->tracer)) // You have to be able to SEE it...sorta + { + // Lost attracted rings don't through walls anymore. + actor->flags &= ~MF_NOCLIP; + P_SetTarget(&actor->tracer, NULL); + return; + } + + // If a FlingRing gets attracted by a shield, change it into a normal ring. + if (actor->type == (mobjtype_t)actor->info->reactiontime) + { + P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->painchance); + P_RemoveMobj(actor); + return; + } + + // Keep stuff from going down inside floors and junk + actor->flags &= ~MF_NOCLIPHEIGHT; + + // Let attracted rings move through walls and such. + actor->flags |= MF_NOCLIP; + + P_Attract(actor, actor->tracer, false); +} + +// Function: A_DropMine +// +// Description: Drops a mine. Raisestate specifies the object # to use for the mine. +// +// var1 = height offset +// var2: +// lower 16 bits = proximity check distance (0 disables) +// upper 16 bits = 0 to check proximity with target, 1 for tracer +// +void A_DropMine(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + fixed_t z; + mobj_t *mine; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_DropMine", actor)) + return; +#endif + + if (locvar2 & 65535) + { + fixed_t dist; + mobj_t *target; + + if (locvar2 >> 16) + target = actor->tracer; + else + target = actor->target; + + if (!target) + return; + + dist = P_AproxDistance(actor->x-target->x, actor->y-target->y)>>FRACBITS; + + if (dist > FixedMul((locvar2 & 65535), actor->scale)) + return; + } + + if (actor->eflags & MFE_VERTICALFLIP) + z = actor->z + actor->height - mobjinfo[actor->info->raisestate].height - FixedMul((locvar1*FRACUNIT) - 12*FRACUNIT, actor->scale); + else + z = actor->z + FixedMul((locvar1*FRACUNIT) - 12*FRACUNIT, actor->scale); + + // Use raisestate instead of MT_MINE + mine = P_SpawnMobj(actor->x, actor->y, z, (mobjtype_t)actor->info->raisestate); + if (actor->eflags & MFE_VERTICALFLIP) + mine->eflags |= MFE_VERTICALFLIP; + mine->momz = actor->momz + actor->pmomz; + + S_StartSound(actor, actor->info->attacksound); +} + +// Function: A_FishJump +// +// Description: Makes the stupid harmless fish in Greenflower Zone jump. +// +// var1 = Jump strength (in FRACBITS), if specified. Otherwise, uses the angle value. +// var2 = unused +// +void A_FishJump(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FishJump", actor)) + return; +#endif + + if (locvar2) + { + fixed_t rad = actor->radius>>FRACBITS; + P_SpawnMobjFromMobj(actor, P_RandomRange(rad, -rad)<z <= actor->floorz) || (actor->z <= actor->watertop - FixedMul((64 << FRACBITS), actor->scale))) + { + fixed_t jumpval; + + if (locvar1) + jumpval = var1; + else + jumpval = FixedMul(AngleFixed(actor->angle)/4, actor->scale); + + if (!jumpval) jumpval = FixedMul(44*(FRACUNIT/4), actor->scale); + actor->momz = jumpval; + P_SetMobjStateNF(actor, actor->info->seestate); + } + + if (actor->momz < 0 + && (actor->state < &states[actor->info->meleestate] || actor->state > &states[actor->info->xdeathstate])) + P_SetMobjStateNF(actor, actor->info->meleestate); +} + +// Function:A_ThrownRing +// +// Description: Thinker for thrown rings/sparkle trail +// +// var1 = unused +// var2 = unused +// +void A_ThrownRing(mobj_t *actor) +{ + INT32 c = 0; + INT32 stop; + player_t *player; + fixed_t dist; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_ThrownRing", actor)) + return; +#endif + + if (leveltime % (TICRATE/7) == 0) + { + mobj_t *ring = NULL; + + if (actor->flags2 & MF2_EXPLOSION) + { + if (actor->momx != 0 || actor->momy != 0) + ring = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMOKE); + // Else spawn nothing because it's totally stationary and constantly smoking would be weird -SH + } + else if (actor->flags2 & MF2_AUTOMATIC) + ring = P_SpawnGhostMobj(actor); + else if (!(actor->flags2 & MF2_RAILRING)) + ring = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SPARK); + + if (ring) + { + /* + P_SetTarget(&ring->target, actor); + ring->color = actor->color; //copy color + */ + ring->destscale = actor->scale; + P_SetScale(ring, actor->scale); + } + } + + // A_GrenadeRing beeping lives once moooooore -SH + if (actor->type == MT_THROWNGRENADE && actor->fuse % TICRATE == 0) + S_StartSound(actor, actor->info->attacksound); + + // decrement bounce ring time + if (actor->flags2 & MF2_BOUNCERING) + { + if (actor->fuse) + actor->fuse--; + else { + P_RemoveMobj(actor); + return; + } + } + + // spilled rings (and thrown bounce) flicker before disappearing + if (leveltime & 1 && actor->fuse > 0 && actor->fuse < 2*TICRATE + && actor->type != MT_THROWNGRENADE) + actor->flags2 |= MF2_DONTDRAW; + else + actor->flags2 &= ~MF2_DONTDRAW; + + if (actor->tracer && actor->tracer->health <= 0) + P_SetTarget(&actor->tracer, NULL); + + // Updated homing ring special capability + // If you have a ring shield, all rings thrown + // at you become homing (except rail)! + if (actor->tracer) + { + // A non-homing ring getting attracted by a + // magnetic player. If he gets too far away, make + // sure to stop the attraction! + if ((!actor->tracer->health) || (actor->tracer->player && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC) + && P_AproxDistance(P_AproxDistance(actor->tracer->x-actor->x, + actor->tracer->y-actor->y), actor->tracer->z-actor->z) > FixedMul(RING_DIST/4, actor->tracer->scale))) + { + P_SetTarget(&actor->tracer, NULL); + } + + if (actor->tracer && (actor->tracer->health) + && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC))// Already found someone to follow. + { + const INT32 temp = actor->threshold; + actor->threshold = 32000; + P_HomingAttack(actor, actor->tracer); + actor->threshold = temp; + return; + } + } + + // first time init, this allow minimum lastlook changes + if (actor->lastlook < 0) + actor->lastlook = P_RandomByte(); + + actor->lastlook %= MAXPLAYERS; + + stop = (actor->lastlook - 1) & PLAYERSMASK; + + for (; ; actor->lastlook = (actor->lastlook + 1) & PLAYERSMASK) + { + // done looking + if (actor->lastlook == stop) + return; + + if (!playeringame[actor->lastlook]) + continue; + + if (c++ == 2) + return; + + player = &players[actor->lastlook]; + + if (!player->mo) + continue; + + if (player->mo->health <= 0) + continue; // dead + + if ((netgame || multiplayer) && player->spectator) + continue; // spectator + + if (actor->target && actor->target->player) + { + if (player->mo == actor->target) + continue; + + // Don't home in on teammates. + if (gametype == GT_CTF + && actor->target->player->ctfteam == player->ctfteam) + continue; + } + + dist = P_AproxDistance(P_AproxDistance(player->mo->x-actor->x, + player->mo->y-actor->y), player->mo->z-actor->z); + + // check distance + if (actor->flags2 & MF2_RAILRING) + { + if (dist > FixedMul(RING_DIST/2, player->mo->scale)) + continue; + } + else if (dist > FixedMul(RING_DIST, player->mo->scale)) + continue; + + // do this after distance check because it's more computationally expensive + if (!P_CheckSight(actor, player->mo)) + continue; // out of sight + + if ((player->powers[pw_shield] & SH_PROTECTELECTRIC) + && dist < FixedMul(RING_DIST/4, player->mo->scale)) + P_SetTarget(&actor->tracer, player->mo); + return; + } + + return; +} + +// Function: A_SetSolidSteam +// +// Description: Makes steam solid so it collides with the player to boost them. +// +// var1 = unused +// var2 = unused +// +void A_SetSolidSteam(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SetSolidSteam", actor)) + return; +#endif + actor->flags &= ~MF_NOCLIP; + actor->flags |= MF_SOLID; + if (!(actor->flags2 & MF2_AMBUSH)) + { + if (P_RandomChance(FRACUNIT/8)) + { + if (actor->info->deathsound) + S_StartSound(actor, actor->info->deathsound); // Hiss! + } + else + { + if (actor->info->painsound) + S_StartSound(actor, actor->info->painsound); + } + } + + P_SetObjectMomZ (actor, 1, true); +} + +// Function: A_UnsetSolidSteam +// +// Description: Makes an object non-solid and also noclip. Used by the steam. +// +// var1 = unused +// var2 = unused +// +void A_UnsetSolidSteam(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_UnsetSolidSteam", actor)) + return; +#endif + actor->flags &= ~MF_SOLID; + actor->flags |= MF_NOCLIP; +} + +// Function: A_SignPlayer +// +// Description: Changes the state of a level end sign to reflect the player that hit it. +// +// var1 = unused +// var2 = unused +// +void A_SignPlayer(mobj_t *actor) +{ + mobj_t *ov; + skin_t *skin; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SignPlayer", actor)) + return; +#endif + if (!actor->target) + return; + + if (!actor->target->player) + return; + + skin = &skins[actor->target->player->skin]; + + if ((actor->target->player->skincolor == skin->prefcolor) && (skin->prefoppositecolor)) // Set it as the skin's preferred oppositecolor? + { + actor->color = skin->prefoppositecolor; + /* + If you're here from the comment above Color_Opposite, + the following line is the one which is dependent on the + array being symmetrical. It gets the opposite of the + opposite of your desired colour just so it can get the + brightness frame for the End Sign. It's not a great + design choice, but it's constant time array access and + the idea that the colours should be OPPOSITES is kind + of in the name. If you have a better idea, feel free + to let me know. ~toast 2016/07/20 + */ + actor->frame += (15 - Color_Opposite[(Color_Opposite[(skin->prefoppositecolor - 1)*2] - 1)*2 + 1]); + } + else if (actor->target->player->skincolor) // Set the sign to be an appropriate background color for this player's skincolor. + { + actor->color = Color_Opposite[(actor->target->player->skincolor - 1)*2]; + actor->frame += (15 - Color_Opposite[(actor->target->player->skincolor - 1)*2 + 1]); + } + + if (skin->sprites[SPR2_SIGN].numframes) + { + // spawn an overlay of the player's face. + ov = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY); + P_SetTarget(&ov->target, actor); + ov->color = actor->target->player->skincolor; + ov->skin = skin; + P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN + } +} + +// Function: A_OverlayThink +// +// Description: Moves the overlay to the position of its target. +// +// var1 = unused +// var2 = invert, z offset +// +void A_OverlayThink(mobj_t *actor) +{ + fixed_t destx, desty; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_OverlayThink", actor)) + return; +#endif + if (!actor->target) + return; + + if (!splitscreen && rendermode != render_soft) + { + angle_t viewingangle; + + if (players[displayplayer].awayviewtics) + viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayer].awayviewmobj->x, players[displayplayer].awayviewmobj->y); + else if (!camera.chase && players[displayplayer].mo) + viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayer].mo->x, players[displayplayer].mo->y); + else + viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, camera.x, camera.y); + + destx = actor->target->x + P_ReturnThrustX(actor->target, viewingangle, FixedMul(FRACUNIT, actor->scale)); + desty = actor->target->y + P_ReturnThrustY(actor->target, viewingangle, FixedMul(FRACUNIT, actor->scale)); + } + else + { + destx = actor->target->x; + desty = actor->target->y; + } + P_UnsetThingPosition(actor); + actor->x = destx; + actor->y = desty; + P_SetThingPosition(actor); + if (actor->eflags & MFE_VERTICALFLIP) + actor->z = actor->target->z + actor->target->height - mobjinfo[actor->type].height - ((var2>>16) ? -1 : 1)*(var2&0xFFFF)*FRACUNIT; + else + actor->z = actor->target->z + ((var2>>16) ? -1 : 1)*(var2&0xFFFF)*FRACUNIT; + actor->angle = actor->target->angle; + actor->eflags = actor->target->eflags; + + actor->momx = actor->target->momx; + actor->momy = actor->target->momy; + actor->momz = actor->target->momz; // assume target has correct momz! Do not use P_SetObjectMomZ! +} + +// Function: A_JetChase +// +// Description: A_Chase for Jettysyns +// +// var1 = unused +// var2 = unused +// +void A_JetChase(mobj_t *actor) +{ + fixed_t thefloor; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_JetChase", actor)) + return; +#endif + + if (actor->flags2 & MF2_AMBUSH) + return; + + if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz + && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale)) + thefloor = actor->watertop; + else + thefloor = actor->floorz; + + if (actor->reactiontime) + actor->reactiontime--; + + if (P_RandomChance(FRACUNIT/32)) + { + actor->momx = actor->momx / 2; + actor->momy = actor->momy / 2; + actor->momz = actor->momz / 2; + } + + // Bounce if too close to floor or ceiling - + // ideal for Jetty-Syns above you on 3d floors + if (actor->momz && ((actor->z - FixedMul((32<scale)) < thefloor) && !((thefloor + FixedMul(32*FRACUNIT, actor->scale) + actor->height) > actor->ceilingz)) + actor->momz = -actor->momz/2; + + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) + { + // look for a new target + if (P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + actor->momx = actor->momy = actor->momz = 0; + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + + // modify target threshold + if (actor->threshold) + { + if (!actor->target || actor->target->health <= 0) + actor->threshold = 0; + else + actor->threshold--; + } + + // turn towards movement direction if not there yet + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); + + if ((multiplayer || netgame) && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))) + if (P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + // If the player is over 3072 fracunits away, then look for another player + if (P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), + actor->target->z - actor->z) > FixedMul(3072*FRACUNIT, actor->scale) && P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale))) + { + return; // got a new target + } + + // chase towards player + if (ultimatemode) + P_Thrust(actor, actor->angle, FixedMul(actor->info->speed/2, actor->scale)); + else + P_Thrust(actor, actor->angle, FixedMul(actor->info->speed/4, actor->scale)); + + // must adjust height + if (ultimatemode) + { + if (actor->z < (actor->target->z + actor->target->height + FixedMul((64<scale))) + actor->momz += FixedMul(FRACUNIT/2, actor->scale); + else + actor->momz -= FixedMul(FRACUNIT/2, actor->scale); + } + else + { + if (actor->z < (actor->target->z + actor->target->height + FixedMul((32<scale))) + actor->momz += FixedMul(FRACUNIT/2, actor->scale); + else + actor->momz -= FixedMul(FRACUNIT/2, actor->scale); + } +} + +// Function: A_JetbThink +// +// Description: Thinker for Jetty-Syn bombers +// +// var1 = unused +// var2 = unused +// +void A_JetbThink(mobj_t *actor) +{ + sector_t *nextsector; + fixed_t thefloor; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_JetbThink", actor)) + return; +#endif + + if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz + && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale)) + thefloor = actor->watertop; + else + thefloor = actor->floorz; + + if (actor->target) + { + A_JetChase(actor); + // check for melee attack + if (actor->info->raisestate + && (actor->z > (actor->floorz + FixedMul((32<scale))) + && P_JetbCheckMeleeRange(actor) && !actor->reactiontime + && (actor->target->z >= actor->floorz)) + { + mobj_t *bomb; + if (actor->info->attacksound) + S_StartAttackSound(actor, actor->info->attacksound); + + // use raisestate instead of MT_MINE + bomb = P_SpawnMobj(actor->x, actor->y, actor->z - FixedMul((32<scale), (mobjtype_t)actor->info->raisestate); + + P_SetTarget(&bomb->target, actor); + bomb->destscale = actor->scale; + P_SetScale(bomb, actor->scale); + actor->reactiontime = TICRATE; // one second + S_StartSound(actor, actor->info->attacksound); + } + } + else if (((actor->z - FixedMul((32<scale)) < thefloor) && !((thefloor + FixedMul((32<scale) + actor->height) > actor->ceilingz)) + actor->z = thefloor+FixedMul((32<scale); + + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) + { + // look for a new target + if (P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + + nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector; + + // Move downwards or upwards to go through a passageway. + if (nextsector->ceilingheight < actor->z + actor->height) + actor->momz -= FixedMul(5*FRACUNIT, actor->scale); + else if (nextsector->floorheight > actor->z) + actor->momz += FixedMul(5*FRACUNIT, actor->scale); +} + +// Function: A_JetgShoot +// +// Description: Firing function for Jetty-Syn gunners. +// +// var1 = unused +// var2 = unused +// +void A_JetgShoot(mobj_t *actor) +{ + fixed_t dist; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_JetgShoot", actor)) + return; +#endif + + if (!actor->target) + return; + + if (actor->reactiontime) + return; + + dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y); + + if (dist > FixedMul(actor->info->painchance*FRACUNIT, actor->scale)) + return; + + if (dist < FixedMul(64*FRACUNIT, actor->scale)) + return; + + A_FaceTarget(actor); + P_SpawnMissile(actor, actor->target, (mobjtype_t)actor->info->raisestate); + + if (ultimatemode) + actor->reactiontime = actor->info->reactiontime*TICRATE; + else + actor->reactiontime = actor->info->reactiontime*TICRATE*2; + + if (actor->info->attacksound) + S_StartSound(actor, actor->info->attacksound); +} + +// Function: A_ShootBullet +// +// Description: Shoots a bullet. Raisestate defines object # to use as projectile. +// +// var1 = unused +// var2 = unused +// +void A_ShootBullet(mobj_t *actor) +{ + fixed_t dist; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_ShootBullet", actor)) + return; +#endif + + if (!actor->target) + return; + + dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), actor->target->z - actor->z); + + if (dist > FixedMul(actor->info->painchance*FRACUNIT, actor->scale)) + return; + + A_FaceTarget(actor); + P_SpawnMissile(actor, actor->target, (mobjtype_t)actor->info->raisestate); + + if (actor->info->attacksound) + S_StartSound(actor, actor->info->attacksound); +} + +// Function: A_MinusDigging +// +// Description: Minus digging in the ground. +// +// var1 = unused +// var2 = unused +// +void A_MinusDigging(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_MinusDigging", actor)) + return; +#endif + actor->flags &= ~MF_SPECIAL; + actor->flags &= ~MF_SHOOTABLE; + + if (!actor->target) + { + A_Look(actor); + return; + } + + if (actor->reactiontime) + { + actor->reactiontime--; + return; + } + + // Dirt trail + P_SpawnGhostMobj(actor); + + actor->flags |= MF_NOCLIPTHING; + var1 = 3; + A_Chase(actor); + actor->flags &= ~MF_NOCLIPTHING; + + // Play digging sound + if (!(leveltime & 15)) + S_StartSound(actor, actor->info->activesound); + + // If we're close enough to our target, pop out of the ground + if (P_AproxDistance(actor->target->x-actor->x, actor->target->y-actor->y) < actor->radius + && abs(actor->target->z - actor->z) < 2*actor->height) + P_SetMobjState(actor, actor->info->missilestate); + + // Snap to ground + if (actor->eflags & MFE_VERTICALFLIP) + actor->z = actor->ceilingz - actor->height; + else + actor->z = actor->floorz; +} + +// Function: A_MinusPopup +// +// Description: Minus popping out of the ground. +// +// var1 = unused +// var2 = unused +// +void A_MinusPopup(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_MinusPopup", actor)) + return; +#endif + P_SetObjectMomZ(actor, 10*FRACUNIT, false); + + actor->flags |= MF_SPECIAL; + actor->flags |= MF_SHOOTABLE; + + // Sound for busting out of the ground. + S_StartSound(actor, actor->info->attacksound); +} + +// Function: A_MinusCheck +// +// Description: If the minus hits the floor, dig back into the ground. +// +// var1 = unused +// var2 = unused +// +void A_MinusCheck(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_MinusCheck", actor)) + return; +#endif + if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz) + || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)) + { + actor->flags &= ~MF_SPECIAL; + actor->flags &= ~MF_SHOOTABLE; + actor->reactiontime = TICRATE; + P_SetMobjState(actor, actor->info->seestate); + return; + } + + // 'Falling' animation + if (P_MobjFlip(actor)*actor->momz < 0 && actor->state < &states[actor->info->meleestate]) + P_SetMobjState(actor, actor->info->meleestate); +} + +// Function: A_ChickenCheck +// +// Description: Resets the chicken once it hits the floor again. +// +// var1 = unused +// var2 = unused +// +void A_ChickenCheck(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_ChickenCheck", actor)) + return; +#endif + if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz) + || (actor->eflags & MFE_VERTICALFLIP && actor->z + actor->height >= actor->ceilingz)) + { + if (!(actor->momx || actor->momy || actor->momz) + && actor->state > &states[actor->info->seestate]) + { + A_Chase(actor); + P_SetMobjState(actor, actor->info->seestate); + } + + actor->momx >>= 2; + actor->momy >>= 2; + } +} + +// Function: A_JetgThink +// +// Description: Thinker for Jetty-Syn Gunners +// +// var1 = unused +// var2 = unused +// +void A_JetgThink(mobj_t *actor) +{ + sector_t *nextsector; + + fixed_t thefloor; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_JetgThink", actor)) + return; +#endif + + if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz + && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale)) + thefloor = actor->watertop; + else + thefloor = actor->floorz; + + if (actor->target) + { + if (P_RandomChance(FRACUNIT/8) && !actor->reactiontime) + P_SetMobjState(actor, actor->info->missilestate); + else + A_JetChase (actor); + } + else if (actor->z - FixedMul((32<scale) < thefloor && !(thefloor + FixedMul((32<scale) + + actor->height > actor->ceilingz)) + { + actor->z = thefloor + FixedMul((32<scale); + } + + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) + { + // look for a new target + if (P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + + nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector; + + // Move downwards or upwards to go through a passageway. + if (nextsector->ceilingheight < actor->z + actor->height) + actor->momz -= FixedMul(5*FRACUNIT, actor->scale); + else if (nextsector->floorheight > actor->z) + actor->momz += FixedMul(5*FRACUNIT, actor->scale); +} + +// Function: A_MouseThink +// +// Description: Thinker for scurrying mice. +// +// var1 = unused +// var2 = unused +// +void A_MouseThink(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_MouseThink", actor)) + return; +#endif + + if (actor->reactiontime) + actor->reactiontime--; + + if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z == actor->floorz) + || (actor->eflags & MFE_VERTICALFLIP && actor->z + actor->height == actor->ceilingz)) + && !actor->reactiontime) + { + if (twodlevel || actor->flags2 & MF2_TWOD) + { + if (P_RandomChance(FRACUNIT/2)) + actor->angle += ANGLE_180; + } + else if (P_RandomChance(FRACUNIT/2)) + actor->angle += ANGLE_90; + else + actor->angle -= ANGLE_90; + + P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed, actor->scale)); + actor->reactiontime = TICRATE/5; + } +} + +// Function: A_DetonChase +// +// Description: Chases a Deton after a player. +// +// var1 = unused +// var2 = unused +// +void A_DetonChase(mobj_t *actor) +{ + angle_t exact; + fixed_t xydist, dist; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_DetonChase", actor)) + return; +#endif + + // modify tracer threshold + if (!actor->tracer || actor->tracer->health <= 0) + actor->threshold = 0; + else + actor->threshold = 1; + + if (!actor->tracer || !(actor->tracer->flags & MF_SHOOTABLE)) + { + // look for a new target + if (P_LookForPlayers(actor, true, true, 0)) + return; // got a new target + + actor->momx = actor->momy = actor->momz = 0; + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + + if (multiplayer && !actor->threshold && P_LookForPlayers(actor, true, true, 0)) + return; // got a new target + + // Face movement direction if not doing so + exact = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); + actor->angle = exact; + /*if (exact != actor->angle) + { + if (exact - actor->angle > ANGLE_180) + { + actor->angle -= actor->info->raisestate; + if (exact - actor->angle < ANGLE_180) + actor->angle = exact; + } + else + { + actor->angle += actor->info->raisestate; + if (exact - actor->angle > ANGLE_180) + actor->angle = exact; + } + }*/ + // movedir is up/down angle: how much it has to go up as it goes over to the player + xydist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y); + exact = R_PointToAngle2(0, 0, xydist, actor->tracer->z - actor->z); + actor->movedir = exact; + /*if (exact != actor->movedir) + { + if (exact - actor->movedir > ANGLE_180) + { + actor->movedir -= actor->info->raisestate; + if (exact - actor->movedir < ANGLE_180) + actor->movedir = exact; + } + else + { + actor->movedir += actor->info->raisestate; + if (exact - actor->movedir > ANGLE_180) + actor->movedir = exact; + } + }*/ + + // check for melee attack + if (actor->tracer) + { + if (P_AproxDistance(actor->tracer->x-actor->x, actor->tracer->y-actor->y) < actor->radius+actor->tracer->radius) + { + if (!((actor->tracer->z > actor->z + actor->height) || (actor->z > actor->tracer->z + actor->tracer->height))) + { + P_ExplodeMissile(actor); + return; + } + } + } + + // chase towards player + if ((dist = P_AproxDistance(xydist, actor->tracer->z-actor->z)) + > FixedMul((actor->info->painchance << FRACBITS), actor->scale)) + { + P_SetTarget(&actor->tracer, NULL); // Too far away + return; + } + + if (actor->reactiontime == 0) + { + actor->reactiontime = actor->info->reactiontime; + return; + } + + if (actor->reactiontime > 1) + { + actor->reactiontime--; + return; + } + + if (actor->reactiontime > 0) + { + actor->reactiontime = -42; + + if (actor->info->seesound) + S_StartScreamSound(actor, actor->info->seesound); + } + + if (actor->reactiontime == -42) + { + fixed_t xyspeed; + + actor->reactiontime = -42; + + exact = actor->movedir>>ANGLETOFINESHIFT; + xyspeed = FixedMul(FixedMul(actor->tracer->player->normalspeed,3*FRACUNIT/4), FINECOSINE(exact)); + actor->momz = FixedMul(FixedMul(actor->tracer->player->normalspeed,3*FRACUNIT/4), FINESINE(exact)); + + exact = actor->angle>>ANGLETOFINESHIFT; + actor->momx = FixedMul(xyspeed, FINECOSINE(exact)); + actor->momy = FixedMul(xyspeed, FINESINE(exact)); + + // Variable re-use + xyspeed = (P_AproxDistance(actor->tracer->x - actor->x, P_AproxDistance(actor->tracer->y - actor->y, actor->tracer->z - actor->z))>>(FRACBITS+6)); + + if (xyspeed < 1) + xyspeed = 1; + + if (leveltime % xyspeed == 0) + S_StartSound(actor, sfx_deton); + } +} + +// Function: A_CapeChase +// +// Description: Set an object's location to its target or tracer. +// +// var1: +// 0 = Use target +// 1 = Use tracer +// upper 16 bits = Z offset +// var2: +// upper 16 bits = forward/backward offset +// lower 16 bits = sideways offset +// +void A_CapeChase(mobj_t *actor) +{ + mobj_t *chaser; + fixed_t foffsetx, foffsety, boffsetx, boffsety; + INT32 locvar1 = var1; + INT32 locvar2 = var2; + angle_t angle; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CapeChase", actor)) + return; +#endif + + CONS_Debug(DBG_GAMELOGIC, "A_CapeChase called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); + + if (locvar1 & 65535) + chaser = actor->tracer; + else + chaser = actor->target; + + if (!chaser || (chaser->health <= 0)) + { + if (chaser) + CONS_Debug(DBG_GAMELOGIC, "Hmm, the guy I'm chasing (object type %d) has no health.. so I'll die too!\n", chaser->type); + + P_RemoveMobj(actor); + return; + } + + angle = (chaser->player ? chaser->player->drawangle : chaser->angle); + + foffsetx = P_ReturnThrustX(chaser, angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale)); + foffsety = P_ReturnThrustY(chaser, angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale)); + + boffsetx = P_ReturnThrustX(chaser, angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale)); + boffsety = P_ReturnThrustY(chaser, angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale)); + + P_UnsetThingPosition(actor); + actor->x = chaser->x + foffsetx + boffsetx; + actor->y = chaser->y + foffsety + boffsety; + if (chaser->eflags & MFE_VERTICALFLIP) + { + actor->eflags |= MFE_VERTICALFLIP; + actor->flags2 |= MF2_OBJECTFLIP; + actor->z = chaser->z + chaser->height - actor->height - FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale); + } + else + { + actor->eflags &= ~MFE_VERTICALFLIP; + actor->flags2 &= ~MF2_OBJECTFLIP; + actor->z = chaser->z + FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale); + } + actor->angle = angle; + P_SetThingPosition(actor); +} + +// Function: A_RotateSpikeBall +// +// Description: Rotates a spike ball around its target/tracer. +// +// var1: +// 0 = Use target +// 1 = Use tracer +// var2 = unused +// +void A_RotateSpikeBall(mobj_t *actor) +{ + INT32 locvar1 = var1; + const fixed_t radius = FixedMul(12*actor->info->speed, actor->scale); +#ifdef HAVE_BLUA + if (LUA_CallAction("A_RotateSpikeBall", actor)) + return; +#endif + + if (!((!locvar1 && (actor->target)) || (locvar1 && (actor->tracer))))// This should NEVER happen. + { + CONS_Debug(DBG_GAMELOGIC, "A_RotateSpikeBall: Spikeball has no target\n"); + P_RemoveMobj(actor); + return; + } + + if (!actor->info->speed) + { + CONS_Debug(DBG_GAMELOGIC, "A_RotateSpikeBall: Object has no speed.\n"); + return; + } + + actor->angle += FixedAngle(actor->info->speed); + P_UnsetThingPosition(actor); + { + const angle_t fa = actor->angle>>ANGLETOFINESHIFT; + if (!locvar1) + { + actor->x = actor->target->x + FixedMul(FINECOSINE(fa),radius); + actor->y = actor->target->y + FixedMul(FINESINE(fa),radius); + actor->z = actor->target->z + actor->target->height/2; + } + else + { + actor->x = actor->tracer->x + FixedMul(FINECOSINE(fa),radius); + actor->y = actor->tracer->y + FixedMul(FINESINE(fa),radius); + actor->z = actor->tracer->z + actor->tracer->height/2; + } + P_SetThingPosition(actor); + } +} + +// Function: A_UnidusBall +// +// Description: Rotates a spike ball around its target. +// +// var1: +// 0 = Don't throw +// 1 = Throw +// 2 = Throw when target leaves MF2_SKULLFLY. +// var2 = unused +// +void A_UnidusBall(mobj_t *actor) +{ + INT32 locvar1 = var1; + boolean canthrow = false; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_UnidusBall", actor)) + return; +#endif + + actor->angle += ANGLE_11hh; + + if (actor->movecount) + { + if (P_AproxDistance(actor->momx, actor->momy) < FixedMul(actor->info->damage/2, actor->scale)) + P_ExplodeMissile(actor); + return; + } + + if (!actor->target || !actor->target->health) + { + CONS_Debug(DBG_GAMELOGIC, "A_UnidusBall: Removing unthrown spikeball from nonexistant Unidus\n"); + P_RemoveMobj(actor); + return; + } + + P_UnsetThingPosition(actor); + { + const angle_t angle = actor->movedir + FixedAngle(actor->info->speed*(leveltime%360)); + const UINT16 fa = angle>>ANGLETOFINESHIFT; + + actor->x = actor->target->x + FixedMul(FINECOSINE(fa),actor->threshold); + actor->y = actor->target->y + FixedMul( FINESINE(fa),actor->threshold); + actor->z = actor->target->z + actor->target->height/2 - actor->height/2; + + if (locvar1 == 1 && actor->target->target) + { + const angle_t tang = R_PointToAngle2(actor->target->x, actor->target->y, actor->target->target->x, actor->target->target->y); + const angle_t mina = tang-ANGLE_11hh; + canthrow = (angle-mina < FixedAngle(actor->info->speed*3)); + } + } + P_SetThingPosition(actor); + + if (locvar1 == 1 && canthrow) + { + if (P_AproxDistance(actor->target->target->x - actor->target->x, actor->target->target->y - actor->target->y) > FixedMul(MISSILERANGE>>1, actor->scale) + || !P_CheckSight(actor, actor->target->target)) + return; + + actor->movecount = actor->info->damage>>FRACBITS; + actor->flags &= ~(MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING); + P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, actor->target->target->x, actor->target->target->y), FixedMul(actor->info->damage, actor->scale)); + } + else if (locvar1 == 2) + { + boolean skull = (actor->target->flags2 & MF2_SKULLFLY) == MF2_SKULLFLY; + if (actor->target->state == &states[actor->target->info->painstate]) + { + P_KillMobj(actor, NULL, NULL, 0); + return; + } + switch(actor->extravalue2) + { + case 0: // at least one frame where not dashing + if (!skull) ++actor->extravalue2; + else break; + /* FALLTHRU */ + case 1: // at least one frame where ARE dashing + if (skull) ++actor->extravalue2; + else break; + /* FALLTHRU */ + case 2: // not dashing again? + if (skull) break; + // launch. + { + mobj_t *target = actor->target; + if (actor->target->target) + target = actor->target->target; + actor->movecount = actor->info->damage>>FRACBITS; + actor->flags &= ~(MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING); + P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, target->x, target->y), FixedMul(actor->info->damage, actor->scale)); + } + default: // from our compiler appeasement program (CAP). + break; + } + } +} + +// Function: A_RockSpawn +// +// Spawns rocks at a specified interval +// +// var1 = unused +// var2 = unused +void A_RockSpawn(mobj_t *actor) +{ + mobj_t *mo; + mobjtype_t type; + INT32 i = P_FindSpecialLineFromTag(12, (INT16)actor->threshold, -1); + line_t *line; + fixed_t dist; + fixed_t randomoomph; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_RockSpawn", actor)) + return; +#endif + + if (i == -1) + { + CONS_Debug(DBG_GAMELOGIC, "A_RockSpawn: Unable to find parameter line 12 (tag %d)!\n", actor->threshold); + return; + } + + line = &lines[i]; + + if (!(sides[line->sidenum[0]].textureoffset >> FRACBITS)) + { + CONS_Debug(DBG_GAMELOGIC, "A_RockSpawn: No X-offset detected! (tag %d)!\n", actor->threshold); + return; + } + + dist = P_AproxDistance(line->dx, line->dy)/16; + + if (dist < 1) + dist = 1; + + type = MT_ROCKCRUMBLE1 + (sides[line->sidenum[0]].rowoffset >> FRACBITS); + + if (line->flags & ML_NOCLIMB) + randomoomph = P_RandomByte() * (FRACUNIT/32); + else + randomoomph = 0; + + mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FALLINGROCK); + P_SetMobjState(mo, mobjinfo[type].spawnstate); + mo->angle = R_PointToAngle2(line->v2->x, line->v2->y, line->v1->x, line->v1->y); + + P_InstaThrust(mo, mo->angle, dist + randomoomph); + mo->momz = dist + randomoomph; + + var1 = sides[line->sidenum[0]].textureoffset >> FRACBITS; + A_SetTics(actor); +} + +// +// Function: A_SlingAppear +// +// Appears a sling. +// +// var1 = unused +// var2 = unused +// +void A_SlingAppear(mobj_t *actor) +{ + UINT8 mlength = 4; + mobj_t *spawnee, *hprev; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SlingAppear", actor)) + return; +#endif + + P_UnsetThingPosition(actor); + actor->flags &= ~(MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_NOCLIPHEIGHT); + P_SetThingPosition(actor); + actor->lastlook = 128; + actor->movecount = actor->lastlook; + actor->threshold = 0; + actor->movefactor = actor->threshold; + actor->friction = 128; + + hprev = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLGRABCHAIN); + P_SetTarget(&hprev->tracer, actor); + P_SetTarget(&hprev->hprev, actor); + P_SetTarget(&actor->hnext, hprev); + hprev->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT; + hprev->movecount = mlength; + + mlength--; + + while (mlength > 0) + { + spawnee = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLMACECHAIN); + P_SetTarget(&spawnee->tracer, actor); + P_SetTarget(&spawnee->hprev, hprev); + P_SetTarget(&hprev->hnext, spawnee); + hprev = spawnee; + + spawnee->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT; + spawnee->movecount = mlength; + + mlength--; + } +} + +// Function: A_SetFuse +// +// Description: Sets the actor's fuse timer if not set already. May also change state when fuse reaches the last tic, otherwise by default the actor will die or disappear. (Replaces A_SnowBall) +// +// var1 = fuse timer duration (in tics). +// var2: +// lower 16 bits = if > 0, state to change to when fuse = 1 +// upper 16 bits: 0 = (default) don't set fuse unless 0, 1 = force change, 2 = force no change +// +void A_SetFuse(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SetFuse", actor)) + return; +#endif + + if ((!actor->fuse || (locvar2 >> 16)) && (locvar2 >> 16) != 2) // set the actor's fuse value + actor->fuse = locvar1; + + if (actor->fuse == 1 && (locvar2 & 65535)) // change state on the very last tic (fuse is handled before actions in P_MobjThinker) + { + actor->fuse = 0; // don't die/disappear the next tic! + P_SetMobjState(actor, locvar2 & 65535); + } +} + +// Function: A_CrawlaCommanderThink +// +// Description: Thinker for Crawla Commander. +// +// var1 = shoot bullets? +// var2 = "pogo mode" speed +// +void A_CrawlaCommanderThink(mobj_t *actor) +{ + fixed_t dist; + sector_t *nextsector; + fixed_t thefloor; + INT32 locvar1 = var1; + INT32 locvar2 = var2; + boolean hovermode = (actor->health > 1 || actor->fuse); +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CrawlaCommanderThink", actor)) + return; +#endif + + if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz + && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale)) + thefloor = actor->watertop; + else + thefloor = actor->floorz; + + if (!actor->fuse && actor->flags2 & MF2_FRET) + { + if (actor->info->painsound) + S_StartSound(actor, actor->info->painsound); + + actor->fuse = TICRATE/2; + actor->momz = 0; + + P_InstaThrust(actor, actor->angle-ANGLE_180, FixedMul(5*FRACUNIT, actor->scale)); + } + + if (actor->reactiontime > 0) + actor->reactiontime--; + + if (actor->fuse < 2) + { + actor->fuse = 0; + actor->flags2 &= ~MF2_FRET; + } + + // Hover mode + if (hovermode) + { + if (actor->z < thefloor + FixedMul(16*FRACUNIT, actor->scale)) + actor->momz += FixedMul(FRACUNIT, actor->scale); + else if (actor->z < thefloor + FixedMul(32*FRACUNIT, actor->scale)) + actor->momz += FixedMul(FRACUNIT/2, actor->scale); + else + actor->momz += FixedMul(16, actor->scale); + } + + if (!actor->target) + { + // look for a new target + if (P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + if (actor->state != &states[actor->info->spawnstate]) + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + + dist = P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y); + + if (actor->target->player && (!hovermode || actor->reactiontime <= 2*TICRATE)) + { + if (dist < FixedMul(64<<(FRACBITS+(hovermode ? 1 : 0)), actor->scale) + && ((actor->target->player->pflags & PF_JUMPED) || (actor->target->player->pflags & PF_SPINNING))) + { + // Auugh! She's trying to kill you! Strafe! STRAAAAFFEEE!! + P_InstaThrust(actor, actor->angle - ANGLE_180, FixedMul(20*FRACUNIT, actor->scale)); + return; + } + } + + if (locvar1) + { + if (actor->health < 2 && P_RandomChance(FRACUNIT/128)) + P_SpawnMissile(actor, actor->target, locvar1); + } + + // Face the player + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); + + if (actor->threshold && dist > FixedMul(256*FRACUNIT, actor->scale)) + actor->momx = actor->momy = 0; + + if (actor->reactiontime && actor->reactiontime <= 2*TICRATE && dist > actor->target->radius - FixedMul(FRACUNIT, actor->scale)) + { + actor->threshold = 0; + + // Roam around, somewhat in the player's direction. + actor->angle += (P_RandomByte()<<10); + actor->angle -= (P_RandomByte()<<10); + + if (hovermode) + { + fixed_t mom; + P_Thrust(actor, actor->angle, 2*actor->scale); + mom = P_AproxDistance(actor->momx, actor->momy); + if (mom > 20*actor->scale) + { + mom += 20*actor->scale; + mom >>= 1; + P_InstaThrust(actor, R_PointToAngle2(0, 0, actor->momx, actor->momy), mom); + } + } + } + else if (!actor->reactiontime) + { + if (hovermode && !(actor->flags2 & MF2_FRET)) // Hover Mode + { + if (dist < FixedMul(512*FRACUNIT, actor->scale)) + { + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); + P_InstaThrust(actor, actor->angle, FixedMul(40*FRACUNIT, actor->scale)); + actor->threshold = 1; + if (actor->info->attacksound) + S_StartSound(actor, actor->info->attacksound); + } + } + actor->reactiontime = 3*TICRATE + (P_RandomByte()>>2); + } + + if (actor->health == 1) + P_Thrust(actor, actor->angle, 1); + + // Pogo Mode + if (!hovermode && actor->z <= actor->floorz) + { + if (actor->info->activesound) + S_StartSound(actor, actor->info->activesound); + + if (dist < FixedMul(256*FRACUNIT, actor->scale)) + { + actor->momz = FixedMul(locvar2, actor->scale); + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); + P_InstaThrust(actor, actor->angle, FixedMul(locvar2/8, actor->scale)); + // pogo on player + } + else + { + UINT8 prandom = P_RandomByte(); + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); + P_InstaThrust(actor, actor->angle, FixedDiv(FixedMul(locvar2, actor->scale), 3*FRACUNIT/2)); + actor->momz = FixedMul(locvar2, actor->scale); // Bounce up in air + } + } + + nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector; + + // Move downwards or upwards to go through a passageway. + if (nextsector->floorheight > actor->z && nextsector->floorheight - actor->z < FixedMul(128*FRACUNIT, actor->scale)) + actor->momz += (nextsector->floorheight - actor->z) / 4; +} + +// Function: A_RingExplode +// +// Description: An explosion ring exploding +// +// var1 = unused +// var2 = unused +// +void A_RingExplode(mobj_t *actor) +{ + mobj_t *mo2; + thinker_t *th; + angle_t d; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_RingExplode", actor)) + return; +#endif + + for (d = 0; d < 16; d++) + P_SpawnParaloop(actor->x, actor->y, actor->z + actor->height, FixedMul(actor->info->painchance, actor->scale), 16, MT_NIGHTSPARKLE, S_NULL, d*(ANGLE_22h), true); + + S_StartSound(actor, sfx_prloop); + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo2 = (mobj_t *)th; + + if (mo2 == actor) // Don't explode yourself! Endless loop! + continue; + + if (P_AproxDistance(P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y), mo2->z - actor->z) > FixedMul(actor->info->painchance, actor->scale)) + continue; + + if (mo2->flags & MF_SHOOTABLE) + { + actor->flags2 |= MF2_DEBRIS; + P_DamageMobj(mo2, actor, actor->target, 1, 0); + continue; + } + } + return; +} + +// Function: A_OldRingExplode +// +// Description: An explosion ring exploding, 1.09.4 style +// +// var1 = object # to explode as debris +// var2 = unused +// +void A_OldRingExplode(mobj_t *actor) { + UINT8 i; + mobj_t *mo; + const fixed_t ns = FixedMul(20 * FRACUNIT, actor->scale); + INT32 locvar1 = var1; + //INT32 locvar2 = var2; + boolean changecolor = (actor->target && actor->target->player); +#ifdef HAVE_BLUA + if (LUA_CallAction("A_OldRingExplode", actor)) + return; +#endif + + for (i = 0; i < 32; i++) + { + const angle_t fa = (i*FINEANGLES/16) & FINEMASK; + + mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1); + P_SetTarget(&mo->target, actor->target); // Transfer target so player gets the points + + mo->momx = FixedMul(FINECOSINE(fa),ns); + mo->momy = FixedMul(FINESINE(fa),ns); + + if (i > 15) + { + if (i & 1) + mo->momz = ns; + else + mo->momz = -ns; + } + + mo->flags2 |= MF2_DEBRIS; + mo->fuse = TICRATE/5; + + if (changecolor) + { + if (gametype != GT_CTF) + mo->color = actor->target->color; //copy color + else if (actor->target->player->ctfteam == 2) + mo->color = skincolor_bluering; + } + } + + mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1); + + P_SetTarget(&mo->target, actor->target); + mo->momz = ns; + mo->flags2 |= MF2_DEBRIS; + mo->fuse = TICRATE/5; + + if (changecolor) + { + if (gametype != GT_CTF) + mo->color = actor->target->color; //copy color + else if (actor->target->player->ctfteam == 2) + mo->color = skincolor_bluering; + } + + mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1); + + P_SetTarget(&mo->target, actor->target); + mo->momz = -ns; + mo->flags2 |= MF2_DEBRIS; + mo->fuse = TICRATE/5; + + if (changecolor) + { + if (gametype != GT_CTF) + mo->color = actor->target->color; //copy color + else if (actor->target->player->ctfteam == 2) + mo->color = skincolor_bluering; + } +} + +// Function: A_MixUp +// +// Description: Mix up all of the player positions. +// +// var1 = unused +// var2 = unused +// +void A_MixUp(mobj_t *actor) +{ + boolean teleported[MAXPLAYERS]; + INT32 i, numplayers = 0, prandom = 0; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_MixUp", actor)) + return; +#else + (void)actor; +#endif + + if (!multiplayer) + return; + + // No mix-up monitors in hide and seek or time only race. + // The random factor is okay for other game modes, but in these, it is cripplingly unfair. + if (gametype == GT_HIDEANDSEEK || gametype == GT_RACE) + { + S_StartSound(actor, sfx_lose); + return; + } + + numplayers = 0; + memset(teleported, 0, sizeof (teleported)); + + // Count the number of players in the game + // and grab their xyz coords + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE + && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE) + { + if ((netgame || multiplayer) && players[i].spectator) // Ignore spectators + continue; + + numplayers++; + } + + if (numplayers <= 1) // Not enough players to mix up. + { + S_StartSound(actor, sfx_lose); + return; + } + else if (numplayers == 2) // Special case -- simple swap + { + fixed_t x, y, z; + angle_t angle; + INT32 one = -1, two = 0; // default value 0 to make the compiler shut up + + // Zoom tube stuff + mobj_t *tempthing = NULL; //tracer + UINT16 carry1,carry2; //carry + INT32 transspeed; //player speed + + // Starpost stuff + INT16 starpostx, starposty, starpostz; + INT32 starpostnum; + tic_t starposttime; + angle_t starpostangle; + + INT32 mflags2; + + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE + && !players[i].exiting && !players[i].powers[pw_super]) + { + if ((netgame || multiplayer) && players[i].spectator) // Ignore spectators + continue; + + if (one == -1) + one = i; + else + { + two = i; + break; + } + } + + //get this done first! + tempthing = players[one].mo->tracer; + P_SetTarget(&players[one].mo->tracer, players[two].mo->tracer); + P_SetTarget(&players[two].mo->tracer, tempthing); + + //zoom tubes use player->speed to determine direction and speed + transspeed = players[one].speed; + players[one].speed = players[two].speed; + players[two].speed = transspeed; + + //set flags variables now but DON'T set them. + carry1 = (players[one].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[one].powers[pw_carry]); + carry2 = (players[two].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[two].powers[pw_carry]); + + x = players[one].mo->x; + y = players[one].mo->y; + z = players[one].mo->z; + angle = players[one].mo->angle; + + starpostx = players[one].starpostx; + starposty = players[one].starposty; + starpostz = players[one].starpostz; + starpostangle = players[one].starpostangle; + starpostnum = players[one].starpostnum; + starposttime = players[one].starposttime; + + mflags2 = players[one].mo->flags2; + + P_MixUp(players[one].mo, players[two].mo->x, players[two].mo->y, players[two].mo->z, players[two].mo->angle, + players[two].starpostx, players[two].starposty, players[two].starpostz, + players[two].starpostnum, players[two].starposttime, players[two].starpostangle, + players[two].mo->flags2); + + P_MixUp(players[two].mo, x, y, z, angle, starpostx, starposty, starpostz, + starpostnum, starposttime, starpostangle, + mflags2); + + //carry set after mixup. Stupid P_ResetPlayer() takes away some of the stuff we look for... + //but not all of it! So we need to make sure they aren't set wrong or anything. + players[one].powers[pw_carry] = carry2; + players[two].powers[pw_carry] = carry1; + + teleported[one] = true; + teleported[two] = true; + } + else + { + fixed_t position[MAXPLAYERS][3]; + angle_t anglepos[MAXPLAYERS]; + INT32 pindex[MAXPLAYERS], counter = 0, teleportfrom = 0; + + // Zoom tube stuff + mobj_t *transtracer[MAXPLAYERS]; //tracer + //pflags_t transflag[MAXPLAYERS]; //cyan pink white pink cyan + UINT16 transcarry[MAXPLAYERS]; //player carry + INT32 transspeed[MAXPLAYERS]; //player speed + + // Star post stuff + INT16 spposition[MAXPLAYERS][3]; + INT32 starpostnum[MAXPLAYERS]; + tic_t starposttime[MAXPLAYERS]; + angle_t starpostangle[MAXPLAYERS]; + + INT32 flags2[MAXPLAYERS]; + + for (i = 0; i < MAXPLAYERS; i++) + { + position[i][0] = position[i][1] = position[i][2] = anglepos[i] = pindex[i] = -1; + teleported[i] = false; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && players[i].playerstate == PST_LIVE + && players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE) + { + if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators + continue; + + position[counter][0] = players[i].mo->x; + position[counter][1] = players[i].mo->y; + position[counter][2] = players[i].mo->z; + pindex[counter] = i; + anglepos[counter] = players[i].mo->angle; + players[i].mo->momx = players[i].mo->momy = players[i].mo->momz = + players[i].rmomx = players[i].rmomy = 1; + players[i].cmomx = players[i].cmomy = 0; + + transcarry[counter] = (players[i].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[i].powers[pw_carry]); + transspeed[counter] = players[i].speed; + transtracer[counter] = players[i].mo->tracer; + + spposition[counter][0] = players[i].starpostx; + spposition[counter][1] = players[i].starposty; + spposition[counter][2] = players[i].starpostz; + starpostnum[counter] = players[i].starpostnum; + starposttime[counter] = players[i].starposttime; + starpostangle[counter] = players[i].starpostangle; + + flags2[counter] = players[i].mo->flags2; + + counter++; + } + } + + counter = 0; + + // Mix them up! + for (;;) + { + if (counter > 255) // fail-safe to avoid endless loop + break; + prandom = P_RandomByte(); + prandom %= numplayers; // I love modular arithmetic, don't you? + if (prandom) // Make sure it's not a useless mix + break; + counter++; + } + + counter = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && players[i].playerstate == PST_LIVE + && players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE) + { + if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators + continue; + + teleportfrom = (counter + prandom) % numplayers; + + //speed and tracer come before... + players[i].speed = transspeed[teleportfrom]; + P_SetTarget(&players[i].mo->tracer, transtracer[teleportfrom]); + + P_MixUp(players[i].mo, position[teleportfrom][0], position[teleportfrom][1], position[teleportfrom][2], anglepos[teleportfrom], + spposition[teleportfrom][0], spposition[teleportfrom][1], spposition[teleportfrom][2], + starpostnum[teleportfrom], starposttime[teleportfrom], starpostangle[teleportfrom], + flags2[teleportfrom]); + + //...carry after. same reasoning. + players[i].powers[pw_carry] = transcarry[teleportfrom]; + + teleported[i] = true; + counter++; + } + } + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (teleported[i]) + { + if (playeringame[i] && players[i].playerstate == PST_LIVE + && players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE) + { + if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators + continue; + + P_SetThingPosition(players[i].mo); + +#ifdef ESLOPE + players[i].mo->floorz = P_GetFloorZ(players[i].mo, players[i].mo->subsector->sector, players[i].mo->x, players[i].mo->y, NULL); + players[i].mo->ceilingz = P_GetCeilingZ(players[i].mo, players[i].mo->subsector->sector, players[i].mo->x, players[i].mo->y, NULL); +#else + players[i].mo->floorz = players[i].mo->subsector->sector->floorheight; + players[i].mo->ceilingz = players[i].mo->subsector->sector->ceilingheight; +#endif + + P_CheckPosition(players[i].mo, players[i].mo->x, players[i].mo->y); + } + } + } + + // Play the 'bowrwoosh!' sound + S_StartSound(NULL, sfx_mixup); +} + +// Function: A_RecyclePowers +// +// Description: Take all player's powers, and swap 'em. +// +// var1 = unused +// var2 = unused +// +void A_RecyclePowers(mobj_t *actor) +{ + INT32 i, j, k, numplayers = 0; + +#ifdef WEIGHTEDRECYCLER + UINT8 beneficiary = 255; +#endif + UINT8 playerslist[MAXPLAYERS]; + UINT8 postscramble[MAXPLAYERS]; + + UINT16 powers[MAXPLAYERS][NUMPOWERS]; + INT32 weapons[MAXPLAYERS]; + INT32 weaponheld[MAXPLAYERS]; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_RecyclePowers", actor)) + return; +#endif + +#if !defined(WEIGHTEDRECYCLER) && !defined(HAVE_BLUA) + // actor is used in all scenarios but this one, funny enough + (void)actor; +#endif + + if (!multiplayer) + { + S_StartSound(actor, sfx_lose); + return; + } + + numplayers = 0; + + // Count the number of players in the game + for (i = 0, j = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE + && !players[i].exiting && !((netgame || multiplayer) && players[i].spectator)) + { +#ifndef WEIGHTEDRECYCLER + if (players[i].powers[pw_super]) + continue; // Ignore super players +#endif + + numplayers++; + postscramble[j] = playerslist[j] = (UINT8)i; + +#ifdef WEIGHTEDRECYCLER + // The guy who started the recycle gets the best result + if (actor && actor->target && actor->target->player && &players[i] == actor->target->player) + beneficiary = (UINT8)i; +#endif + + // Save powers + for (k = 0; k < NUMPOWERS; k++) + powers[i][k] = players[i].powers[k]; + //1.1: ring weapons too + weapons[i] = players[i].ringweapons; + weaponheld[i] = players[i].currentweapon; + + j++; + } + } + + if (numplayers <= 1) + { + S_StartSound(actor, sfx_lose); + return; //nobody to touch! + } + + //shuffle the post scramble list, whee! + // hardcoded 0-1 to 1-0 for two players + if (numplayers == 2) + { + postscramble[0] = playerslist[1]; + postscramble[1] = playerslist[0]; + } + else + for (j = 0; j < numplayers; j++) + { + UINT8 tempint; + + i = j + ((P_RandomByte() + leveltime) % (numplayers - j)); + tempint = postscramble[j]; + postscramble[j] = postscramble[i]; + postscramble[i] = tempint; + } + +#ifdef WEIGHTEDRECYCLER + //the joys of qsort... + if (beneficiary != 255) { + qsort(playerslist, numplayers, sizeof(UINT8), P_RecycleCompare); + + // now, make sure the benificiary is in the best slot + // swap out whatever poor sap was going to get the best items + for (i = 0; i < numplayers; i++) + { + if (postscramble[i] == beneficiary) + { + postscramble[i] = postscramble[0]; + postscramble[0] = beneficiary; + break; + } + } + } +#endif + + // now assign! + for (i = 0; i < numplayers; i++) + { + UINT8 send_pl = playerslist[i]; + UINT8 recv_pl = postscramble[i]; + + // debugF + CONS_Debug(DBG_GAMELOGIC, "sending player %hu's items to %hu\n", (UINT16)send_pl, (UINT16)recv_pl); + + for (j = 0; j < NUMPOWERS; j++) + { + if (j == pw_flashing || j == pw_underwater || j == pw_spacetime || j == pw_carry + || j == pw_tailsfly || j == pw_extralife || j == pw_nocontrol || j == pw_super) + continue; + players[recv_pl].powers[j] = powers[send_pl][j]; + } + + //1.1: weapon rings too + players[recv_pl].ringweapons = weapons[send_pl]; + players[recv_pl].currentweapon = weaponheld[send_pl]; + + P_SpawnShieldOrb(&players[recv_pl]); + if (P_IsLocalPlayer(&players[recv_pl])) + P_RestoreMusic(&players[recv_pl]); + P_FlashPal(&players[recv_pl], PAL_RECYCLE, 10); + } + + S_StartSound(NULL, sfx_gravch); //heh, the sound effect I used is already in +} + +// Function: A_Boss1Chase +// +// Description: Like A_Chase, but for Boss 1. +// +// var1 = unused +// var2 = unused +// +void A_Boss1Chase(mobj_t *actor) +{ + INT32 delta; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss1Chase", actor)) + return; +#endif + + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) + { + // look for a new target + if (P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + P_SetMobjStateNF(actor, actor->info->spawnstate); + return; + } + + if (actor->reactiontime) + actor->reactiontime--; + + // turn towards movement direction if not there yet + if (actor->movedir < NUMDIRS) + { + actor->angle &= (7<<29); + delta = actor->angle - (actor->movedir << 29); + + if (delta > 0) + actor->angle -= ANGLE_45; + else if (delta < 0) + actor->angle += ANGLE_45; + } + + // do not attack twice in a row + if (actor->flags2 & MF2_JUSTATTACKED) + { + actor->flags2 &= ~MF2_JUSTATTACKED; + P_NewChaseDir(actor); + return; + } + + if (actor->movecount) + goto nomissile; + + if (!P_CheckMissileRange(actor)) + goto nomissile; + + if (actor->reactiontime <= 0) + { + if (actor->health > actor->info->damage) + { + if (P_RandomChance(FRACUNIT/2)) + P_SetMobjState(actor, actor->info->missilestate); + else + P_SetMobjState(actor, actor->info->meleestate); + } + else + { + P_LinedefExecute(LE_PINCHPHASE, actor, NULL); + P_SetMobjState(actor, actor->info->raisestate); + } + + actor->flags2 |= MF2_JUSTATTACKED; + actor->reactiontime = actor->info->reactiontime; + return; + } + + // ? +nomissile: + // possibly choose another target + if (multiplayer && P_RandomChance(FRACUNIT/128)) + { + if (P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + } + + if (actor->flags & MF_FLOAT && !(actor->flags2 & MF2_SKULLFLY)) + { // Float up/down to your target's position. Stay above them, but not out of jump range. + fixed_t target_min = actor->target->floorz+FixedMul(64*FRACUNIT, actor->scale); + if (target_min < actor->target->z - actor->height) + target_min = actor->target->z - actor->height; + if (target_min < actor->floorz+FixedMul(33*FRACUNIT, actor->scale)) + target_min = actor->floorz+FixedMul(33*FRACUNIT, actor->scale); + if (actor->z > target_min+FixedMul(16*FRACUNIT, actor->scale)) + actor->momz = FixedMul((-actor->info->speed<<(FRACBITS-1)), actor->scale); + else if (actor->z < target_min) + actor->momz = FixedMul(actor->info->speed<<(FRACBITS-1), actor->scale); + else + actor->momz = FixedMul(actor->momz,7*FRACUNIT/8); + } + + // chase towards player + if (P_AproxDistance(actor->target->x-actor->x, actor->target->y-actor->y) > actor->radius+actor->target->radius) + { + if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed)) + P_NewChaseDir(actor); + } + // too close, don't want to chase. + else if (--actor->movecount < 0) + { + // A mini-A_FaceTarget based on P_NewChaseDir. + // Yes, it really is this simple when you get down to it. + fixed_t deltax, deltay; + + deltax = actor->target->x - actor->x; + deltay = actor->target->y - actor->y; + + actor->movedir = diags[((deltay < 0)<<1) + (deltax > 0)]; + actor->movecount = P_RandomByte() & 15; + } +} + +// Function: A_Boss2Chase +// +// Description: Really doesn't 'chase', but rather goes in a circle. +// +// var1 = unused +// var2 = unused +// +void A_Boss2Chase(mobj_t *actor) +{ + fixed_t radius; + boolean reverse = false; + INT32 speedvar; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss2Chase", actor)) + return; +#endif + + if (actor->health <= 0) + return; + + // Startup randomness + if (actor->reactiontime <= -666) + actor->reactiontime = 2*TICRATE + P_RandomByte(); + + // When reactiontime hits zero, he will go the other way + if (--actor->reactiontime <= 0) + { + reverse = true; + actor->reactiontime = 2*TICRATE + P_RandomByte(); + } + + P_SetTarget(&actor->target, P_GetClosestAxis(actor)); + + if (!actor->target) // This should NEVER happen. + { + CONS_Debug(DBG_GAMELOGIC, "Boss2 has no target!\n"); + A_BossDeath(actor); + return; + } + + radius = actor->target->radius; + + if (reverse) + { + actor->watertop = -actor->watertop; + actor->extravalue1 = 18; + if (actor->flags2 & MF2_AMBUSH) + actor->extravalue1 -= (actor->info->spawnhealth - actor->health)*2; + actor->extravalue2 = actor->extravalue1; + } + + // Turnaround + if (actor->extravalue1 > 0) + { + --actor->extravalue1; + + // Set base angle + { + const angle_t fa = (actor->target->angle + FixedAngle(actor->watertop))>>ANGLETOFINESHIFT; + const fixed_t fc = FixedMul(FINECOSINE(fa),radius); + const fixed_t fs = FixedMul(FINESINE(fa),radius); + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x + fc, actor->target->y + fs); + } + + // Now turn you around! + // Note that the start position is the final position, we move it back around + // to intermediary positions... + actor->angle -= FixedAngle(FixedMul(FixedDiv(180<extravalue2<extravalue1<flags2 & MF2_AMBUSH) + speedvar = actor->health; + else + speedvar = actor->info->spawnhealth; + + actor->target->angle += // Don't use FixedAngleC! + FixedAngle(FixedDiv(FixedMul(actor->watertop, (actor->info->spawnhealth*(FRACUNIT/4)*3)), speedvar*FRACUNIT)); + + P_UnsetThingPosition(actor); + { + const angle_t fa = actor->target->angle>>ANGLETOFINESHIFT; + const fixed_t fc = FixedMul(FINECOSINE(fa),radius); + const fixed_t fs = FixedMul(FINESINE(fa),radius); + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x + fc, actor->target->y + fs); + actor->x = actor->target->x + fc; + actor->y = actor->target->y + fs; + } + P_SetThingPosition(actor); + + // Spray goo once every second + if (leveltime % (speedvar*15/10)-1 == 0) + { + const fixed_t ns = FixedMul(3 * FRACUNIT, actor->scale); + mobj_t *goop; + fixed_t fz = actor->z+actor->height+FixedMul(24*FRACUNIT, actor->scale); + angle_t fa; + // actor->movedir is used to determine the last + // direction goo was sprayed in. There are 8 possible + // directions to spray. (45-degree increments) + + actor->movedir++; + actor->movedir %= NUMDIRS; + fa = (actor->movedir*FINEANGLES/8) & FINEMASK; + + goop = P_SpawnMobj(actor->x, actor->y, fz, actor->info->painchance); + goop->momx = FixedMul(FINECOSINE(fa),ns); + goop->momy = FixedMul(FINESINE(fa),ns); + goop->momz = FixedMul(4*FRACUNIT, actor->scale); + goop->fuse = 10*TICRATE; + + if (actor->info->attacksound) + S_StartAttackSound(actor, actor->info->attacksound); + + if (P_RandomChance(FRACUNIT/2)) + { + goop->momx *= 2; + goop->momy *= 2; + } + else if (P_RandomChance(129*FRACUNIT/256)) + { + goop->momx *= 3; + goop->momy *= 3; + } + + actor->flags2 |= MF2_JUSTATTACKED; + } + } +} + +// Function: A_Boss2Pogo +// +// Description: Pogo part of Boss 2 AI. +// +// var1 = unused +// var2 = unused +// +void A_Boss2Pogo(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss2Pogo", actor)) + return; +#endif + if (actor->z <= actor->floorz + FixedMul(8*FRACUNIT, actor->scale) && actor->momz <= 0) + { + if (actor->state != &states[actor->info->raisestate]) + P_SetMobjState(actor, actor->info->raisestate); + // Pogo Mode + } + else if (actor->momz < 0 && actor->reactiontime) + { + const fixed_t ns = FixedMul(3 * FRACUNIT, actor->scale); + mobj_t *goop; + fixed_t fz = actor->z+actor->height+FixedMul(24*FRACUNIT, actor->scale); + angle_t fa; + INT32 i; + // spray in all 8 directions! + for (i = 0; i < 8; i++) + { + actor->movedir++; + actor->movedir %= NUMDIRS; + fa = (actor->movedir*FINEANGLES/8) & FINEMASK; + + goop = P_SpawnMobj(actor->x, actor->y, fz, actor->info->painchance); + goop->momx = FixedMul(FINECOSINE(fa),ns); + goop->momy = FixedMul(FINESINE(fa),ns); + goop->momz = FixedMul(4*FRACUNIT, actor->scale); + + goop->fuse = 10*TICRATE; + } + actor->reactiontime = 0; // we already shot goop, so don't do it again! + if (actor->info->attacksound) + S_StartAttackSound(actor, actor->info->attacksound); + actor->flags2 |= MF2_JUSTATTACKED; + } +} + +// Function: A_Boss2TakeDamage +// +// Description: Special function for Boss 2 so you can't just sit and destroy him. +// +// var1 = Invincibility duration +// var2 = unused +// +void A_Boss2TakeDamage(mobj_t *actor) +{ + INT32 locvar1 = var1; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss2TakeDamage", actor)) + return; +#endif + A_Pain(actor); + actor->reactiontime = 1; // turn around + if (locvar1 == 0) // old A_Invincibilerize behavior + actor->movecount = TICRATE; + else + actor->movecount = locvar1; // become flashing invulnerable for this long. +} + +// Function: A_Boss7Chase +// +// Description: Like A_Chase, but for Black Eggman +// +// var1 = unused +// var2 = unused +// +void A_Boss7Chase(mobj_t *actor) +{ + INT32 delta; + INT32 i; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss7Chase", actor)) + return; +#endif + + if (actor->z != actor->floorz) + return; + + // Self-adjust if stuck on the edge + if (actor->tracer) + { + if (P_AproxDistance(actor->x - actor->tracer->x, actor->y - actor->tracer->y) > 128*FRACUNIT - actor->radius) + P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y), FRACUNIT); + } + + if (actor->flags2 & MF2_FRET) + { + P_SetMobjState(actor, S_BLACKEGG_DESTROYPLAT1); + S_StartSound(0, sfx_s3k53); + actor->flags2 &= ~MF2_FRET; + return; + } + + // turn towards movement direction if not there yet + if (actor->movedir < NUMDIRS) + { + actor->angle &= (7<<29); + delta = actor->angle - (actor->movedir << 29); + + if (delta > 0) + actor->angle -= ANGLE_45; + else if (delta < 0) + actor->angle += ANGLE_45; + } + + // Is a player on top of us? + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + + if (!players[i].mo) + continue; + + if (players[i].mo->health <= 0) + continue; + + if (P_AproxDistance(players[i].mo->x - actor->x, players[i].mo->y - actor->y) > actor->radius) + continue; + + if (players[i].mo->z > actor->z + actor->height - 2*FRACUNIT + && players[i].mo->z < actor->z + actor->height + 32*FRACUNIT) + { + // Punch him! + P_SetMobjState(actor, actor->info->meleestate); + S_StartSound(0, sfx_begrnd); // warning sound + return; + } + } + + if (actor->health <= actor->info->damage + && actor->target + && actor->target->player + && (actor->target->player->powers[pw_carry] == CR_GENERIC)) + { + A_FaceTarget(actor); + P_SetMobjState(actor, S_BLACKEGG_SHOOT1); + actor->movecount = TICRATE + P_RandomByte()/2; + return; + } + + if (actor->reactiontime) + actor->reactiontime--; + + if (actor->reactiontime <= 0 && actor->z == actor->floorz) + { + // Here, we'll call P_RandomByte() and decide what kind of attack to do + switch(actor->threshold) + { + case 0: // Lob cannon balls + if (actor->z < 1056*FRACUNIT) + { + A_FaceTarget(actor); + P_SetMobjState(actor, actor->info->xdeathstate); + actor->movecount = 7*TICRATE + P_RandomByte(); + break; + } + actor->threshold++; + /* FALLTHRU */ + case 1: // Chaingun Goop + A_FaceTarget(actor); + P_SetMobjState(actor, S_BLACKEGG_SHOOT1); + + if (actor->health > actor->info->damage) + actor->movecount = TICRATE + P_RandomByte()/3; + else + actor->movecount = TICRATE + P_RandomByte()/2; + break; + case 2: // Homing Missile + A_FaceTarget(actor); + P_SetMobjState(actor, actor->info->missilestate); + S_StartSound(0, sfx_beflap); + break; + } + + actor->threshold++; + actor->threshold %= 3; + return; + } + + // possibly choose another target + if (multiplayer && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)) + && P_BossTargetPlayer(actor, false)) + return; // got a new target + + if (leveltime & 1) + { + // chase towards player + if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed)) + P_NewChaseDir(actor); + } +} + +// Function: A_GoopSplat +// +// Description: Black Eggman goop hits a target and sticks around for awhile. +// +// var1 = unused +// var2 = unused +// +void A_GoopSplat(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_GoopSplat", actor)) + return; +#endif + P_UnsetThingPosition(actor); + if (sector_list) + { + P_DelSeclist(sector_list); + sector_list = NULL; + } + actor->flags = MF_SPECIAL; // Not a typo + P_SetThingPosition(actor); +} + +// Function: A_Boss2PogoSFX +// +// Description: Pogoing for Boss 2 +// +// var1 = pogo jump strength +// var2 = idle pogo speed +// +void A_Boss2PogoSFX(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss2PogoSFX", actor)) + return; +#endif + + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) + { + // look for a new target + if (P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + return; + } + + // Boing! + if (P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) < FixedMul(256*FRACUNIT, actor->scale)) + { + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); + P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed, actor->scale)); + // pogo on player + } + else + { + UINT8 prandom = P_RandomByte(); + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); + P_InstaThrust(actor, actor->angle, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale)); + } + if (actor->info->activesound) S_StartSound(actor, actor->info->activesound); + actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air + actor->reactiontime = 1; +} + +// Function: A_Boss2PogoTarget +// +// Description: Pogoing for Boss 2, tries to actually land on the player directly. +// +// var1 = pogo jump strength +// var2 = idle pogo speed +// +void A_Boss2PogoTarget(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss2PogoTarget", actor)) + return; +#endif + + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE) || (actor->target->player && actor->target->player->powers[pw_flashing]) + || P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) >= FixedMul(512*FRACUNIT, actor->scale)) + { + // look for a new target + if (P_LookForPlayers(actor, true, false, 512*FRACUNIT)) + ; // got a new target + else if (P_LookForPlayers(actor, true, false, 0)) + ; // got a new target + else + return; + } + + // Target hit, retreat! + if (actor->target->player->powers[pw_flashing] > TICRATE || actor->flags2 & MF2_FRET) + { + UINT8 prandom = P_RandomByte(); + actor->z++; // unstick from the floor + actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); // Pick a direction, and randomize it. + P_InstaThrust(actor, actor->angle+ANGLE_180, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale)); // Move at wandering speed + } + // Try to land on top of the player. + else if (P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) < FixedMul(512*FRACUNIT, actor->scale)) + { + fixed_t airtime, gravityadd, zoffs; + + // check gravity in the sector (for later math) + P_CheckGravity(actor, true); + gravityadd = actor->momz; + + actor->z++; // unstick from the floor + actor->momz = FixedMul(locvar1 + (locvar1>>2), actor->scale); // Bounce up in air + + /*badmath = 0; + airtime = 0; + do { + badmath += momz; + momz += gravityadd; + airtime++; + } while(badmath > 0); + airtime = 2*airtime<momz<<1, gravityadd)<<1; // going from 0 to 0 is much simpler + zoffs = (P_GetPlayerHeight(actor->target->player)>>1) + (actor->target->floorz - actor->floorz); // offset by the difference in floor height plus half the player height, + airtime = FixedDiv((-actor->momz - FixedSqrt(FixedMul(actor->momz,actor->momz)+zoffs)), gravityadd)<<1; // to try and land on their head rather than on their feet + + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); + P_InstaThrust(actor, actor->angle, FixedDiv(P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y), airtime)); + } + // Wander semi-randomly towards the player to get closer. + else + { + UINT8 prandom = P_RandomByte(); + actor->z++; // unstick from the floor + actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); // Pick a direction, and randomize it. + P_InstaThrust(actor, actor->angle, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale)); // Move at wandering speed + } + // Boing! + if (actor->info->activesound) S_StartSound(actor, actor->info->activesound); + + if (actor->info->missilestate) // spawn the pogo stick collision box + { + mobj_t *pogo = P_SpawnMobj(actor->x, actor->y, actor->z - mobjinfo[actor->info->missilestate].height, (mobjtype_t)actor->info->missilestate); + pogo->target = actor; + } + + actor->reactiontime = 1; +} + +// Function: A_EggmanBox +// +// Description: Harms the player +// +// var1 = unused +// var2 = unused +// +void A_EggmanBox(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_EggmanBox", actor)) + return; +#endif + if (!actor->target || !actor->target->player) + { + CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n"); + return; + } + + P_DamageMobj(actor->target, actor, actor, 1, 0); // Ow! +} + +// Function: A_TurretFire +// +// Description: Initiates turret fire. +// +// var1 = object # to repeatedly fire +// var2 = distance threshold +// +void A_TurretFire(mobj_t *actor) +{ + INT32 count = 0; + fixed_t dist; + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_TurretFire", actor)) + return; +#endif + + if (locvar2) + dist = FixedMul(locvar2*FRACUNIT, actor->scale); + else + dist = FixedMul(2048*FRACUNIT, actor->scale); + + if (!locvar1) + locvar1 = MT_TURRETLASER; + + while (P_SupermanLook4Players(actor) && count < MAXPLAYERS) + { + if (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) < dist) + { + actor->flags2 |= MF2_FIRING; + actor->extravalue1 = locvar1; + break; + } + + count++; + } +} + +// Function: A_SuperTurretFire +// +// Description: Initiates turret fire that even stops Super Sonic. +// +// var1 = object # to repeatedly fire +// var2 = distance threshold +// +void A_SuperTurretFire(mobj_t *actor) +{ + INT32 count = 0; + fixed_t dist; + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SuperTurretFire", actor)) + return; +#endif + + if (locvar2) + dist = FixedMul(locvar2*FRACUNIT, actor->scale); + else + dist = FixedMul(2048*FRACUNIT, actor->scale); + + if (!locvar1) + locvar1 = MT_TURRETLASER; + + while (P_SupermanLook4Players(actor) && count < MAXPLAYERS) + { + if (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) < dist) + { + actor->flags2 |= MF2_FIRING; + actor->flags2 |= MF2_SUPERFIRE; + actor->extravalue1 = locvar1; + break; + } + + count++; + } +} + +// Function: A_TurretStop +// +// Description: Stops the turret fire. +// +// var1 = Don't play activesound? +// var2 = unused +// +void A_TurretStop(mobj_t *actor) +{ + INT32 locvar1 = var1; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_TurretStop", actor)) + return; +#endif + + actor->flags2 &= ~MF2_FIRING; + actor->flags2 &= ~MF2_SUPERFIRE; + + if (actor->target && actor->info->activesound && !locvar1) + S_StartSound(actor, actor->info->activesound); +} + +// Function: A_SparkFollow +// +// Description: Used by the hyper sparks to rotate around their target. +// +// var1 = unused +// var2 = unused +// +void A_SparkFollow(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SparkFollow", actor)) + return; +#endif + + if ((!actor->target || (actor->target->health <= 0)) + || (actor->target->player && !actor->target->player->powers[pw_super])) + { + P_RemoveMobj(actor); + return; + } + + actor->angle += FixedAngle(actor->info->damage*FRACUNIT); + P_UnsetThingPosition(actor); + { + const angle_t fa = actor->angle>>ANGLETOFINESHIFT; + actor->x = actor->target->x + FixedMul(FINECOSINE(fa),FixedMul(actor->info->speed, actor->scale)); + actor->y = actor->target->y + FixedMul(FINESINE(fa),FixedMul(actor->info->speed, actor->scale)); + if (actor->target->eflags & MFE_VERTICALFLIP) + actor->z = actor->target->z + actor->target->height - FixedDiv(actor->target->height,3*FRACUNIT); + else + actor->z = actor->target->z + FixedDiv(actor->target->height,3*FRACUNIT) - actor->height; + } + P_SetThingPosition(actor); +} + +// Function: A_BuzzFly +// +// Description: Makes an object slowly fly after a player, in the manner of a Buzz. +// +// var1 = sfx to play +// var2 = length of sfx, set to threshold if played +// +void A_BuzzFly(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_BuzzFly", actor)) + return; +#endif + if (actor->flags2 & MF2_AMBUSH) + return; + + if (actor->reactiontime) + actor->reactiontime--; + + // modify target threshold + if (actor->threshold) + { + if (!actor->target || actor->target->health <= 0) + actor->threshold = 0; + else + actor->threshold--; + } + + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) + { + // look for a new target + if (P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + actor->momz = actor->momy = actor->momx = 0; + P_SetMobjState(actor, actor->info->spawnstate); + return; + } + + // turn towards movement direction if not there yet + actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); + + if (actor->target->health <= 0 || (!actor->threshold && !P_CheckSight(actor, actor->target))) + { + if ((multiplayer || netgame) && P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale))) + return; // got a new target + + actor->momx = actor->momy = actor->momz = 0; + P_SetMobjState(actor, actor->info->spawnstate); // Go back to looking around + return; + } + + // If the player is over 3072 fracunits away, then look for another player + if (P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), + actor->target->z - actor->z) > FixedMul(3072*FRACUNIT, actor->scale)) + { + if (multiplayer || netgame) + P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)); // maybe get a new target + + return; + } + + // chase towards player + { + INT32 dist, realspeed; + const fixed_t mf = 5*(FRACUNIT/4); + + if (ultimatemode) + realspeed = FixedMul(FixedMul(actor->info->speed,mf), actor->scale); + else + realspeed = FixedMul(actor->info->speed, actor->scale); + + dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, + actor->target->y - actor->y), actor->target->z - actor->z); + + if (dist < 1) + dist = 1; + + actor->momx = FixedMul(FixedDiv(actor->target->x - actor->x, dist), realspeed); + actor->momy = FixedMul(FixedDiv(actor->target->y - actor->y, dist), realspeed); + actor->momz = FixedMul(FixedDiv(actor->target->z - actor->z, dist), realspeed); + + if (actor->z+actor->momz >= actor->waterbottom && actor->watertop > actor->floorz + && actor->z+actor->momz > actor->watertop - FixedMul(256*FRACUNIT, actor->scale) + && actor->z+actor->momz <= actor->watertop) + { + actor->momz = 0; + actor->z = actor->watertop; + } + } + + if (locvar1 != sfx_None && !actor->threshold) + { + S_StartSound(actor, locvar1); + actor->threshold = locvar2; + } +} + +// Function: A_GuardChase +// +// Description: Modified A_Chase for Egg Guard +// +// var1 = unused +// var2 = unused +// +void A_GuardChase(mobj_t *actor) +{ + INT32 delta; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_GuardChase", actor)) + return; +#endif + + if (actor->reactiontime) + actor->reactiontime--; + + if (actor->threshold != 42) // In formation... + { + fixed_t speed; + + if (!actor->tracer || !actor->tracer->health) + { + P_SetTarget(&actor->tracer, NULL); + actor->threshold = 42; + P_SetMobjState(actor, actor->info->painstate); + actor->flags |= MF_SPECIAL|MF_SHOOTABLE; + return; + } + + speed = actor->extravalue1*actor->scale; + + if (actor->flags2 & MF2_AMBUSH) + speed <<= 1; + + if (speed + && !P_TryMove(actor, + actor->x + P_ReturnThrustX(actor, actor->angle, speed), + actor->y + P_ReturnThrustY(actor, actor->angle, speed), + false) + && speed > 0) // can't be the same check as previous so that P_TryMove gets to happen. + { + if (actor->spawnpoint && ((actor->spawnpoint->options & (MTF_EXTRA|MTF_OBJECTSPECIAL)) == MTF_OBJECTSPECIAL)) + actor->angle += ANGLE_90; + else if (actor->spawnpoint && ((actor->spawnpoint->options & (MTF_EXTRA|MTF_OBJECTSPECIAL)) == MTF_EXTRA)) + actor->angle -= ANGLE_90; + else + actor->angle += ANGLE_180; + } + + if (actor->extravalue1 < actor->info->speed) + actor->extravalue1++; + } + else // Break ranks! + { + // turn towards movement direction if not there yet + if (actor->movedir < NUMDIRS) + { + actor->angle &= (7<<29); + delta = actor->angle - (actor->movedir << 29); + + if (delta > 0) + actor->angle -= ANGLE_45; + else if (delta < 0) + actor->angle += ANGLE_45; + } + + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) + { + // look for a new target + if (P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + P_SetMobjStateNF(actor, actor->info->spawnstate); + return; + } + + // possibly choose another target + if (multiplayer && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)) + && P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + // chase towards player + if (--actor->movecount < 0 || !P_Move(actor, (actor->flags2 & MF2_AMBUSH) ? actor->info->speed * 2 : actor->info->speed)) + { + P_NewChaseDir(actor); + actor->movecount += 5; // Increase tics before change in direction allowed. + } + } + + // Now that we've moved, its time for our shield to move! + // Otherwise it'll never act as a proper overlay. + if (actor->tracer && actor->tracer->state + && actor->tracer->state->action.acp1) + { + var1 = actor->tracer->state->var1, var2 = actor->tracer->state->var2; + actor->tracer->state->action.acp1(actor->tracer); + } +} + +// Function: A_EggShield +// +// Description: Modified A_Chase for Egg Guard's shield +// +// var1 = unused +// var2 = unused +// +void A_EggShield(mobj_t *actor) +{ + INT32 i; + player_t *player; + fixed_t blockdist; + fixed_t newx, newy; + fixed_t movex, movey; + angle_t angle; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_EggShield", actor)) + return; +#endif + + if (!actor->target || !actor->target->health) + { + P_RemoveMobj(actor); + return; + } + + newx = actor->target->x + P_ReturnThrustX(actor, actor->target->angle, FixedMul(FRACUNIT, actor->scale)); + newy = actor->target->y + P_ReturnThrustY(actor, actor->target->angle, FixedMul(FRACUNIT, actor->scale)); + + movex = newx - actor->x; + movey = newy - actor->y; + + actor->angle = actor->target->angle; + if (actor->target->eflags & MFE_VERTICALFLIP) + { + actor->eflags |= MFE_VERTICALFLIP; + actor->z = actor->target->z + actor->target->height - actor->height; + } + else + actor->z = actor->target->z; + + actor->destscale = actor->target->destscale; + P_SetScale(actor, actor->target->scale); + + actor->floorz = actor->target->floorz; + actor->ceilingz = actor->target->ceilingz; + + if (!movex && !movey) + return; + + P_UnsetThingPosition(actor); + actor->x = newx; + actor->y = newy; + P_SetThingPosition(actor); + + // Search for players to push + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + + player = &players[i]; + + if (!player->mo) + continue; + + if (player->mo->z > actor->z + actor->height) + continue; + + if (player->mo->z + player->mo->height < actor->z) + continue; + + blockdist = actor->radius + player->mo->radius; + + if (abs(actor->x - player->mo->x) >= blockdist || abs(actor->y - player->mo->y) >= blockdist) + continue; // didn't hit it + + angle = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle; + + if (angle > ANGLE_90 && angle < ANGLE_270) + continue; + + // Blocked by the shield + player->mo->momx += movex; + player->mo->momy += movey; + return; + } +} + + +// Function: A_SetReactionTime +// +// Description: Sets the object's reaction time. +// +// var1 = 1 (use value in var2); 0 (use info table value) +// var2 = if var1 = 1, then value to set +// +void A_SetReactionTime(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SetReactionTime", actor)) + return; +#endif + if (var1) + actor->reactiontime = var2; + else + actor->reactiontime = actor->info->reactiontime; +} + +// Function: A_Boss1Spikeballs +// +// Description: Boss 1 spikeball spawning loop. +// +// var1 = ball number +// var2 = total balls +// +void A_Boss1Spikeballs(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobj_t *ball; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss1Spikeballs", actor)) + return; +#endif + + ball = P_SpawnMobj(actor->x, actor->y, actor->z, MT_EGGMOBILE_BALL); + P_SetTarget(&ball->target, actor); + ball->movedir = FixedAngle(FixedMul(FixedDiv(locvar1<threshold = ball->radius + actor->radius + ball->info->painchance; + + S_StartSound(ball, ball->info->seesound); + var1 = ball->state->var1, var2 = ball->state->var2; + ball->state->action.acp1(ball); +} + +// Function: A_Boss3TakeDamage +// +// Description: Called when Boss 3 takes damage. +// +// var1 = movecount value +// var2 = unused +// +void A_Boss3TakeDamage(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss3TakeDamage", actor)) + return; +#endif + actor->movecount = var1; + + if (actor->target && actor->target->spawnpoint) + actor->threshold = actor->target->spawnpoint->extrainfo; +} + +// Function: A_Boss3Path +// +// Description: Does pathfinding along Boss 3's nodes. +// +// var1 = unused +// var2 = unused +// +void A_Boss3Path(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss3Path", actor)) + return; +#endif + + if (actor->tracer && actor->tracer->health && actor->tracer->movecount) + actor->movecount |= 1; + else if (actor->movecount & 1) + actor->movecount = 0; + + if (actor->movecount & 2) // We've reached a firing point? + { + // Wait here and pretend to be angry or something. + actor->momx = 0; + actor->momy = 0; + actor->momz = 0; + P_SetTarget(&actor->target, actor->tracer->target); + var1 = 0, var2 = 0; + A_FaceTarget(actor); + if (actor->tracer->state == &states[actor->tracer->info->missilestate]) + P_SetMobjState(actor, actor->info->missilestate); + return; + } + else if (actor->threshold >= 0) // Traveling mode + { + thinker_t *th; + mobj_t *mo2; + fixed_t dist, dist2; + fixed_t speed; + + P_SetTarget(&actor->target, NULL); + + // scan the thinkers + // to find a point that matches + // the number + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo2 = (mobj_t *)th; + if (mo2->type == MT_BOSS3WAYPOINT && mo2->spawnpoint && mo2->spawnpoint->angle == actor->threshold) + { + P_SetTarget(&actor->target, mo2); + break; + } + } + + if (!actor->target) // Should NEVER happen + { + CONS_Debug(DBG_GAMELOGIC, "Error: Boss 3 Dummy was unable to find specified waypoint: %d\n", actor->threshold); + return; + } + + dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), actor->target->z - actor->z); + + if (dist < 1) + dist = 1; + + if (actor->tracer && ((actor->tracer->movedir) + || (actor->tracer->health <= actor->tracer->info->damage))) + speed = actor->info->speed * 2; + else + speed = actor->info->speed; + + actor->momx = FixedMul(FixedDiv(actor->target->x - actor->x, dist), speed); + actor->momy = FixedMul(FixedDiv(actor->target->y - actor->y, dist), speed); + actor->momz = FixedMul(FixedDiv(actor->target->z - actor->z, dist), speed); + + if (actor->momx != 0 || actor->momy != 0) + actor->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy); + + dist2 = P_AproxDistance(P_AproxDistance(actor->target->x - (actor->x + actor->momx), actor->target->y - (actor->y + actor->momy)), actor->target->z - (actor->z + actor->momz)); + + if (dist2 < 1) + dist2 = 1; + + if ((dist >> FRACBITS) <= (dist2 >> FRACBITS)) + { + // If further away, set XYZ of mobj to waypoint location + P_UnsetThingPosition(actor); + actor->x = actor->target->x; + actor->y = actor->target->y; + actor->z = actor->target->z; + actor->momx = actor->momy = actor->momz = 0; + P_SetThingPosition(actor); + + if (actor->threshold == 0) + { + P_RemoveMobj(actor); // Cycle completed. Dummy removed. + return; + } + + // Set to next waypoint in sequence + if (actor->target->spawnpoint) + { + // From the center point, choose one of the five paths + if (actor->target->spawnpoint->angle == 0) + { + P_RemoveMobj(actor); // Cycle completed. Dummy removed. + return; + } + else + actor->threshold = actor->target->spawnpoint->extrainfo; + + // If the deaf flag is set, go into firing mode + if (actor->target->spawnpoint->options & MTF_AMBUSH) + actor->movecount |= 2; + } + else // This should never happen, as well + CONS_Debug(DBG_GAMELOGIC, "Error: Boss 3 Dummy waypoint has no spawnpoint associated with it.\n"); + } + } +} + +// Function: A_LinedefExecute +// +// Description: Object's location is used to set the calling sector. The tag used is var1. Optionally, if var2 is set, the actor's angle (multiplied by var2) is added to the tag number as well. +// +// var1 = tag +// var2 = add angle to tag (optional) +// +void A_LinedefExecute(mobj_t *actor) +{ + INT32 tagnum; + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_LinedefExecute", actor)) + return; +#endif + + tagnum = locvar1; + // state numbers option is no more, custom states cannot be guaranteed to stay the same number anymore, now that they can be defined by names instead + + if (locvar2) + tagnum += locvar2*(AngleFixed(actor->angle)>>FRACBITS); + + CONS_Debug(DBG_GAMELOGIC, "A_LinedefExecute: Running mobjtype %d's sector with tag %d\n", actor->type, tagnum); + + // tag 32768 displayed in map editors is actually tag -32768, tag 32769 is -32767, 65535 is -1 etc. + P_LinedefExecute((INT16)tagnum, actor, actor->subsector->sector); +} + +// Function: A_PlaySeeSound +// +// Description: Plays the object's seesound. +// +// var1 = unused +// var2 = unused +// +void A_PlaySeeSound(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_PlaySeeSound", actor)) + return; +#endif + if (actor->info->seesound) + S_StartScreamSound(actor, actor->info->seesound); +} + +// Function: A_PlayAttackSound +// +// Description: Plays the object's attacksound. +// +// var1 = unused +// var2 = unused +// +void A_PlayAttackSound(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_PlayAttackSound", actor)) + return; +#endif + if (actor->info->attacksound) + S_StartAttackSound(actor, actor->info->attacksound); +} + +// Function: A_PlayActiveSound +// +// Description: Plays the object's activesound. +// +// var1 = unused +// var2 = unused +// +void A_PlayActiveSound(mobj_t *actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_PlayActiveSound", actor)) + return; +#endif + if (actor->info->activesound) + S_StartSound(actor, actor->info->activesound); +} + +// Function: A_SmokeTrailer +// +// Description: Adds smoke trails to an object. +// +// var1 = object # to spawn as smoke +// var2 = unused +// +void A_SmokeTrailer(mobj_t *actor) +{ + mobj_t *th; + INT32 locvar1 = var1; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SmokeTrailer", actor)) + return; +#endif + + if (leveltime % 4) + return; + + // add the smoke behind the rocket + if (actor->eflags & MFE_VERTICALFLIP) + { + th = P_SpawnMobj(actor->x-actor->momx, actor->y-actor->momy, actor->z + actor->height - FixedMul(mobjinfo[locvar1].height, actor->scale), locvar1); + th->flags2 |= MF2_OBJECTFLIP; + } + else + th = P_SpawnMobj(actor->x-actor->momx, actor->y-actor->momy, actor->z, locvar1); + P_SetObjectMomZ(th, FRACUNIT, false); + th->destscale = actor->scale; + P_SetScale(th, actor->scale); + th->tics -= P_RandomByte() & 3; + if (th->tics < 1) + th->tics = 1; +} + +// Function: A_SpawnObjectAbsolute +// +// Description: Spawns an object at an absolute position +// +// var1: +// var1 >> 16 = x +// var1 & 65535 = y +// var2: +// var2 >> 16 = z +// var2 & 65535 = type +// +void A_SpawnObjectAbsolute(mobj_t *actor) +{ + INT16 x, y, z; // Want to be sure we can use negative values + mobjtype_t type; + mobj_t *mo; + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SpawnObjectAbsolute", actor)) + return; +#endif + + x = (INT16)(locvar1>>16); + y = (INT16)(locvar1&65535); + z = (INT16)(locvar2>>16); + type = (mobjtype_t)(locvar2&65535); + + mo = P_SpawnMobj(x<angle = actor->angle; + + if (actor->eflags & MFE_VERTICALFLIP) + mo->flags2 |= MF2_OBJECTFLIP; +} + +// Function: A_SpawnObjectRelative +// +// Description: Spawns an object relative to the location of the actor +// +// var1: +// var1 >> 16 = x +// var1 & 65535 = y +// var2: +// var2 >> 16 = z +// var2 & 65535 = type +// +void A_SpawnObjectRelative(mobj_t *actor) +{ + INT16 x, y, z; // Want to be sure we can use negative values + mobjtype_t type; + mobj_t *mo; + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SpawnObjectRelative", actor)) + return; +#endif + + CONS_Debug(DBG_GAMELOGIC, "A_SpawnObjectRelative called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); + + x = (INT16)(locvar1>>16); + y = (INT16)(locvar1&65535); + z = (INT16)(locvar2>>16); + type = (mobjtype_t)(locvar2&65535); + + // Spawn objects correctly in reverse gravity. + // NOTE: Doing actor->z + actor->height is the bottom of the object while the object has reverse gravity. - Flame + mo = P_SpawnMobj(actor->x + FixedMul(x<scale), + actor->y + FixedMul(y<scale), + (actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[type].height) - FixedMul(z<scale)) : (actor->z + FixedMul(z<scale)), type); + + // Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn + mo->angle = actor->angle; + + if (actor->eflags & MFE_VERTICALFLIP) + mo->flags2 |= MF2_OBJECTFLIP; + +} + +// Function: A_ChangeAngleRelative +// +// Description: Changes the angle to a random relative value between the min and max. Set min and max to the same value to eliminate randomness +// +// var1 = min +// var2 = max +// +void A_ChangeAngleRelative(mobj_t *actor) +{ + // Oh god, the old code /sucked/. Changed this and the absolute version to get a random range using amin and amax instead of + // getting a random angle from the _entire_ spectrum and then clipping. While we're at it, do the angle conversion to the result + // rather than the ranges, so <0 and >360 work as possible values. -Red + INT32 locvar1 = var1; + INT32 locvar2 = var2; + //angle_t angle = (P_RandomByte()+1)<<24; + const fixed_t amin = locvar1*FRACUNIT; + const fixed_t amax = locvar2*FRACUNIT; + //const angle_t amin = FixedAngle(locvar1*FRACUNIT); + //const angle_t amax = FixedAngle(locvar2*FRACUNIT); +#ifdef HAVE_BLUA + if (LUA_CallAction("A_ChangeAngleRelative", actor)) + return; +#endif + +#ifdef PARANOIA + if (amin > amax) + I_Error("A_ChangeAngleRelative: var1 is greater then var2"); +#endif +/* + if (angle < amin) + angle = amin; + if (angle > amax) + angle = amax;*/ + + actor->angle += FixedAngle(P_RandomRange(amin, amax)); +} + +// Function: A_ChangeAngleAbsolute +// +// Description: Changes the angle to a random absolute value between the min and max. Set min and max to the same value to eliminate randomness +// +// var1 = min +// var2 = max +// +void A_ChangeAngleAbsolute(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + //angle_t angle = (P_RandomByte()+1)<<24; + const fixed_t amin = locvar1*FRACUNIT; + const fixed_t amax = locvar2*FRACUNIT; + //const angle_t amin = FixedAngle(locvar1*FRACUNIT); + //const angle_t amax = FixedAngle(locvar2*FRACUNIT); +#ifdef HAVE_BLUA + if (LUA_CallAction("A_ChangeAngleAbsolute", actor)) + return; +#endif + +#ifdef PARANOIA + if (amin > amax) + I_Error("A_ChangeAngleAbsolute: var1 is greater then var2"); +#endif +/* + if (angle < amin) + angle = amin; + if (angle > amax) + angle = amax;*/ + + actor->angle = FixedAngle(P_RandomRange(amin, amax)); +} + +// Function: A_PlaySound +// +// Description: Plays a sound +// +// var1 = sound # to play +// var2: +// 0 = Play sound without an origin +// 1 = Play sound using calling object as origin +// +void A_PlaySound(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_PlaySound", actor)) + return; +#endif + + S_StartSound(locvar2 ? actor : NULL, locvar1); +} + +// Function: A_FindTarget +// +// Description: Finds the nearest/furthest mobj of the specified type and sets actor->target to it. +// +// var1 = mobj type +// var2 = if (0) nearest; else furthest; +// +void A_FindTarget(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobj_t *targetedmobj = NULL; + thinker_t *th; + mobj_t *mo2; + fixed_t dist1 = 0, dist2 = 0; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FindTarget", actor)) + return; +#endif + + CONS_Debug(DBG_GAMELOGIC, "A_FindTarget called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); + + // scan the thinkers + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo2 = (mobj_t *)th; + + if (mo2->type == (mobjtype_t)locvar1) + { + if (mo2->player && (mo2->player->spectator || mo2->player->pflags & PF_INVIS)) + continue; // Ignore spectators + if ((mo2->player || mo2->flags & MF_ENEMY) && mo2->health <= 0) + continue; // Ignore dead things + if (targetedmobj == NULL) + { + targetedmobj = mo2; + dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y); + } + else + { + dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y); + + if ((!locvar2 && dist1 < dist2) || (locvar2 && dist1 > dist2)) + { + targetedmobj = mo2; + dist2 = dist1; + } + } + } + } + + if (!targetedmobj) + { + CONS_Debug(DBG_GAMELOGIC, "A_FindTarget: Unable to find the specified object to target.\n"); + return; // Oops, nothing found.. + } + + CONS_Debug(DBG_GAMELOGIC, "A_FindTarget: Found a target.\n"); + + P_SetTarget(&actor->target, targetedmobj); +} + +// Function: A_FindTracer +// +// Description: Finds the nearest/furthest mobj of the specified type and sets actor->tracer to it. +// +// var1 = mobj type +// var2 = if (0) nearest; else furthest; +// +void A_FindTracer(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobj_t *targetedmobj = NULL; + thinker_t *th; + mobj_t *mo2; + fixed_t dist1 = 0, dist2 = 0; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FindTracer", actor)) + return; +#endif + + CONS_Debug(DBG_GAMELOGIC, "A_FindTracer called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); + + // scan the thinkers + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo2 = (mobj_t *)th; + + if (mo2->type == (mobjtype_t)locvar1) + { + if (mo2->player && (mo2->player->spectator || mo2->player->pflags & PF_INVIS)) + continue; // Ignore spectators + if ((mo2->player || mo2->flags & MF_ENEMY) && mo2->health <= 0) + continue; // Ignore dead things + if (targetedmobj == NULL) + { + targetedmobj = mo2; + dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y); + } + else + { + dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y); + + if ((!locvar2 && dist1 < dist2) || (locvar2 && dist1 > dist2)) + { + targetedmobj = mo2; + dist2 = dist1; + } + } + } + } + + if (!targetedmobj) + { + CONS_Debug(DBG_GAMELOGIC, "A_FindTracer: Unable to find the specified object to target.\n"); + return; // Oops, nothing found.. + } + + CONS_Debug(DBG_GAMELOGIC, "A_FindTracer: Found a target.\n"); + + P_SetTarget(&actor->tracer, targetedmobj); +} + +// Function: A_SetTics +// +// Description: Sets the animation tics of an object +// +// var1 = tics to set to +// var2 = if this is set, and no var1 is supplied, the mobj's threshold value will be used. +// +void A_SetTics(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SetTics", actor)) + return; +#endif + + if (locvar1) + actor->tics = locvar1; + else if (locvar2) + actor->tics = actor->threshold; +} + +// Function: A_SetRandomTics +// +// Description: Sets the animation tics of an object to a random value +// +// var1 = lower bound +// var2 = upper bound +// +void A_SetRandomTics(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SetRandomTics", actor)) + return; +#endif + + actor->tics = P_RandomRange(locvar1, locvar2); +} + +// Function: A_ChangeColorRelative +// +// Description: Changes the color of an object +// +// var1 = if (var1 > 0), find target and add its color value to yours +// var2 = if (var1 = 0), color value to add +// +void A_ChangeColorRelative(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_ChangeColorRelative", actor)) + return; +#endif + + if (locvar1) + { + // Have you ever seen anything so hideous? + if (actor->target) + actor->color = (UINT8)(actor->color + actor->target->color); + } + else + actor->color = (UINT8)(actor->color + locvar2); +} + +// Function: A_ChangeColorAbsolute +// +// Description: Changes the color of an object by an absolute value. Note: 0 is default colormap. +// +// var1 = if (var1 > 0), set your color to your target's color +// var2 = if (var1 = 0), color value to set to +// +void A_ChangeColorAbsolute(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_ChangeColorAbsolute", actor)) + return; +#endif + + if (locvar1) + { + if (actor->target) + actor->color = actor->target->color; + } + else + actor->color = (UINT8)locvar2; +} + +// Function: A_MoveRelative +// +// Description: Moves an object (wrapper for P_Thrust) +// +// var1 = angle +// var2 = force +// +void A_MoveRelative(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_MoveRelative", actor)) + return; +#endif + + P_Thrust(actor, actor->angle+FixedAngle(locvar1*FRACUNIT), FixedMul(locvar2*FRACUNIT, actor->scale)); +} + +// Function: A_MoveAbsolute +// +// Description: Moves an object (wrapper for P_InstaThrust) +// +// var1 = angle +// var2 = force +// +void A_MoveAbsolute(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_MoveAbsolute", actor)) + return; +#endif + + P_InstaThrust(actor, FixedAngle(locvar1*FRACUNIT), FixedMul(locvar2*FRACUNIT, actor->scale)); +} + +// Function: A_Thrust +// +// Description: Pushes the object horizontally at its current angle. +// +// var1 = amount of force +// var2 = If 1, xy momentum is lost. If 0, xy momentum is kept +// +void A_Thrust(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Thrust", actor)) + return; +#endif + + if (!locvar1) + CONS_Debug(DBG_GAMELOGIC, "A_Thrust: Var1 not specified!\n"); + + if (locvar2) + P_InstaThrust(actor, actor->angle, FixedMul(locvar1*FRACUNIT, actor->scale)); + else + P_Thrust(actor, actor->angle, FixedMul(locvar1*FRACUNIT, actor->scale)); +} + +// Function: A_ZThrust +// +// Description: Pushes the object up or down. +// +// var1 = amount of force +// var2: +// lower 16 bits = If 1, xy momentum is lost. If 0, xy momentum is kept +// upper 16 bits = If 1, z momentum is lost. If 0, z momentum is kept +// +void A_ZThrust(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_ZThrust", actor)) + return; +#endif + + if (!locvar1) + CONS_Debug(DBG_GAMELOGIC, "A_ZThrust: Var1 not specified!\n"); + + if (locvar2 & 65535) + actor->momx = actor->momy = 0; + + if (actor->eflags & MFE_VERTICALFLIP) + actor->z--; + else + actor->z++; + + P_SetObjectMomZ(actor, locvar1*FRACUNIT, !(locvar2 >> 16)); +} + +// Function: A_SetTargetsTarget +// +// Description: Sets your target to the object who your target is targeting. Yikes! If it happens to be NULL, you're just out of luck. +// +// var1: (Your target) +// 0 = target +// 1 = tracer +// var2: (Your target's target) +// 0 = target/tracer's target +// 1 = target/tracer's tracer +// +void A_SetTargetsTarget(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobj_t *oldtarg = NULL, *newtarg = NULL; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SetTargetsTarget", actor)) + return; +#endif + + // actor's target + if (locvar1) // or tracer + oldtarg = actor->tracer; + else + oldtarg = actor->target; + + if (P_MobjWasRemoved(oldtarg)) + return; + + // actor's target's target! + if (locvar2) // or tracer + newtarg = oldtarg->tracer; + else + newtarg = oldtarg->target; + + if (P_MobjWasRemoved(newtarg)) + return; + + // set actor's new target + if (locvar1) // or tracer + P_SetTarget(&actor->tracer, newtarg); + else + P_SetTarget(&actor->target, newtarg); +} + +// Function: A_SetObjectFlags +// +// Description: Sets the flags of an object +// +// var1 = flag value to set +// var2: +// if var2 == 2, add the flag to the current flags +// else if var2 == 1, remove the flag from the current flags +// else if var2 == 0, set the flags to the exact value +// +void A_SetObjectFlags(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + boolean unlinkthings = false; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SetObjectFlags", actor)) + return; +#endif + + if (locvar2 == 2) + locvar1 = actor->flags | locvar1; + else if (locvar2 == 1) + locvar1 = actor->flags & ~locvar1; + + if ((UINT32)(locvar1 & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (actor->flags & (MF_NOBLOCKMAP|MF_NOSECTOR))) // Blockmap/sector status has changed, so reset the links + unlinkthings = true; + + if (unlinkthings) { + P_UnsetThingPosition(actor); + if (sector_list) + { + P_DelSeclist(sector_list); + sector_list = NULL; + } + } + + actor->flags = locvar1; + + if (unlinkthings) + P_SetThingPosition(actor); +} + +// Function: A_SetObjectFlags2 +// +// Description: Sets the flags2 of an object +// +// var1 = flag value to set +// var2: +// if var2 == 2, add the flag to the current flags +// else if var2 == 1, remove the flag from the current flags +// else if var2 == 0, set the flags to the exact value +// +void A_SetObjectFlags2(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SetObjectFlags2", actor)) + return; +#endif + + if (locvar2 == 2) + actor->flags2 |= locvar1; + else if (locvar2 == 1) + actor->flags2 &= ~locvar1; + else + actor->flags2 = locvar1; +} + +// Function: A_BossJetFume +// +// Description: Spawns jet fumes/other attachment miscellany for the boss. To only be used when he is spawned. +// +// var1: +// 0 - Triple jet fume pattern +// 1 - Boss 3's propeller +// 2 - Metal Sonic jet fume +// 3 - Boss 4 jet flame +// var2 = unused +// +void A_BossJetFume(mobj_t *actor) +{ + mobj_t *filler; + INT32 locvar1 = var1; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_BossJetFume", actor)) + return; +#endif + + if (locvar1 == 0) // Boss1 jet fumes + { + fixed_t jetx, jety, jetz; + + jetx = actor->x + P_ReturnThrustX(actor, actor->angle, -FixedMul(64*FRACUNIT, actor->scale)); + jety = actor->y + P_ReturnThrustY(actor, actor->angle, -FixedMul(64*FRACUNIT, actor->scale)); + if (actor->eflags & MFE_VERTICALFLIP) + jetz = actor->z + actor->height - FixedMul(38*FRACUNIT + mobjinfo[MT_JETFUME1].height, actor->scale); + else + jetz = actor->z + FixedMul(38*FRACUNIT, actor->scale); + + filler = P_SpawnMobj(jetx, jety, jetz, MT_JETFUME1); + P_SetTarget(&filler->target, actor); + filler->destscale = actor->scale; + P_SetScale(filler, filler->destscale); + if (actor->eflags & MFE_VERTICALFLIP) + filler->flags2 |= MF2_OBJECTFLIP; + filler->fuse = 56; + + if (actor->eflags & MFE_VERTICALFLIP) + jetz = actor->z + actor->height - FixedMul(12*FRACUNIT + mobjinfo[MT_JETFUME1].height, actor->scale); + else + jetz = actor->z + FixedMul(12*FRACUNIT, actor->scale); + + filler = P_SpawnMobj(jetx + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)), + jety + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)), + jetz, MT_JETFUME1); + P_SetTarget(&filler->target, actor); + filler->destscale = actor->scale; + P_SetScale(filler, filler->destscale); + if (actor->eflags & MFE_VERTICALFLIP) + filler->flags2 |= MF2_OBJECTFLIP; + filler->fuse = 57; + + filler = P_SpawnMobj(jetx + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)), + jety + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)), + jetz, MT_JETFUME1); + P_SetTarget(&filler->target, actor); + filler->destscale = actor->scale; + P_SetScale(filler, filler->destscale); + if (actor->eflags & MFE_VERTICALFLIP) + filler->flags2 |= MF2_OBJECTFLIP; + filler->fuse = 58; + + P_SetTarget(&actor->tracer, filler); + } + else if (locvar1 == 1) // Boss 3 propeller + { + fixed_t jetx, jety, jetz; + + jetx = actor->x + P_ReturnThrustX(actor, actor->angle, -FixedMul(60*FRACUNIT, actor->scale)); + jety = actor->y + P_ReturnThrustY(actor, actor->angle, -FixedMul(60*FRACUNIT, actor->scale)); + if (actor->eflags & MFE_VERTICALFLIP) + jetz = actor->z + actor->height - FixedMul(17*FRACUNIT + mobjinfo[MT_PROPELLER].height, actor->scale); + else + jetz = actor->z + FixedMul(17*FRACUNIT, actor->scale); + + filler = P_SpawnMobj(jetx, jety, jetz, MT_PROPELLER); + P_SetTarget(&filler->target, actor); + filler->destscale = actor->scale; + P_SetScale(filler, filler->destscale); + if (actor->eflags & MFE_VERTICALFLIP) + filler->flags2 |= MF2_OBJECTFLIP; + filler->angle = actor->angle - ANGLE_180; + + P_SetTarget(&actor->tracer, filler); + } + else if (locvar1 == 2) // Metal Sonic jet fumes + { + filler = P_SpawnMobj(actor->x, actor->y, actor->z, MT_JETFUME1); + P_SetTarget(&filler->target, actor); + filler->fuse = 59; + P_SetTarget(&actor->tracer, filler); + filler->destscale = actor->scale/2; + P_SetScale(filler, filler->destscale); + if (actor->eflags & MFE_VERTICALFLIP) + filler->flags2 |= MF2_OBJECTFLIP; + } + else if (locvar1 == 3) // Boss 4 jet flame + { + fixed_t jetz; + if (actor->eflags & MFE_VERTICALFLIP) + jetz = actor->z + actor->height + FixedMul(50*FRACUNIT - mobjinfo[MT_JETFLAME].height, actor->scale); + else + jetz = actor->z - FixedMul(50*FRACUNIT, actor->scale); + filler = P_SpawnMobj(actor->x, actor->y, jetz, MT_JETFLAME); + P_SetTarget(&filler->target, actor); + // Boss 4 already uses its tracer for other things + filler->destscale = actor->scale; + P_SetScale(filler, filler->destscale); + if (actor->eflags & MFE_VERTICALFLIP) + filler->flags2 |= MF2_OBJECTFLIP; + } +} + +// Function: A_RandomState +// +// Description: Chooses one of the two state numbers supplied randomly. +// +// var1 = state number 1 +// var2 = state number 2 +// +void A_RandomState(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_RandomState", actor)) + return; +#endif + + P_SetMobjState(actor, P_RandomChance(FRACUNIT/2) ? locvar1 : locvar2); +} + +// Function: A_RandomStateRange +// +// Description: Chooses a random state within the range supplied. +// +// var1 = Minimum state number to choose. +// var2 = Maximum state number to use. +// +void A_RandomStateRange(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_RandomStateRange", actor)) + return; +#endif + + P_SetMobjState(actor, P_RandomRange(locvar1, locvar2)); +} + +// Function: A_DualAction +// +// Description: Calls two actions. Be careful, if you reference the same state this action is called from, you can create an infinite loop. +// +// var1 = state # to use 1st action from +// var2 = state # to use 2nd action from +// +void A_DualAction(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_DualAction", actor)) + return; +#endif + + CONS_Debug(DBG_GAMELOGIC, "A_DualAction called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); + + var1 = states[locvar1].var1; + var2 = states[locvar1].var2; +#ifdef HAVE_BLUA + astate = &states[locvar1]; +#endif + + CONS_Debug(DBG_GAMELOGIC, "A_DualAction: Calling First Action (state %d)...\n", locvar1); + states[locvar1].action.acp1(actor); + + var1 = states[locvar2].var1; + var2 = states[locvar2].var2; +#ifdef HAVE_BLUA + astate = &states[locvar2]; +#endif + + CONS_Debug(DBG_GAMELOGIC, "A_DualAction: Calling Second Action (state %d)...\n", locvar2); + states[locvar2].action.acp1(actor); +} + +// Function: A_RemoteAction +// +// Description: var1 is the remote object. var2 is the state reference for calling the action called on var1. var1 becomes the actor's target, the action (var2) is called on var1. actor's target is restored +// +// var1 = remote object (-2 uses tracer, -1 uses target) +// var2 = state reference for calling an action +// +void A_RemoteAction(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobj_t *originaltarget = actor->target; // Hold on to the target for later. +#ifdef HAVE_BLUA + if (LUA_CallAction("A_RemoteAction", actor)) + return; +#endif + + // If >=0, find the closest target. + if (locvar1 >= 0) + { + ///* DO A_FINDTARGET STUFF */// + mobj_t *targetedmobj = NULL; + thinker_t *th; + mobj_t *mo2; + fixed_t dist1 = 0, dist2 = 0; + + // scan the thinkers + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo2 = (mobj_t *)th; + + if (mo2->type == (mobjtype_t)locvar1) + { + if (targetedmobj == NULL) + { + targetedmobj = mo2; + dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y); + } + else + { + dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y); + + if ((locvar2 && dist1 < dist2) || (!locvar2 && dist1 > dist2)) + { + targetedmobj = mo2; + dist2 = dist1; + } + } + } + } + + if (!targetedmobj) + { + CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Unable to find the specified object to target.\n"); + return; // Oops, nothing found.. + } + + CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Found a target.\n"); + + P_SetTarget(&actor->target, targetedmobj); + + ///* END A_FINDTARGET STUFF */// + } + + // If -2, use the tracer as the target + else if (locvar1 == -2) + P_SetTarget(&actor->target, actor->tracer); + // if -1 or anything else, just use the target. + + if (actor->target) + { + // Steal the var1 and var2 from "locvar2" + var1 = states[locvar2].var1; + var2 = states[locvar2].var2; +#ifdef HAVE_BLUA + astate = &states[locvar2]; +#endif + + CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Calling action on %p\n" + "var1 is %d\nvar2 is %d\n", actor->target, var1, var2); + states[locvar2].action.acp1(actor->target); + } + + P_SetTarget(&actor->target, originaltarget); // Restore the original target. +} + +// Function: A_ToggleFlameJet +// +// Description: Turns a flame jet on and off. +// +// var1 = unused +// var2 = unused +// +void A_ToggleFlameJet(mobj_t* actor) +{ +#ifdef HAVE_BLUA + if (LUA_CallAction("A_ToggleFlameJet", actor)) + return; +#endif + // threshold - off delay + // movecount - on timer + + if (actor->flags2 & MF2_FIRING) + { + actor->flags2 &= ~MF2_FIRING; + + if (actor->threshold) + actor->tics = actor->threshold; + } + else + { + actor->flags2 |= MF2_FIRING; + + if (actor->movecount) + actor->tics = actor->movecount; + } +} + +// Function: A_OrbitNights +// +// Description: Used by Chaos Emeralds to orbit around Nights (aka Super Sonic.) +// +// var1 = Angle adjustment (aka orbit speed) +// var2 = Lower four bits: height offset, Upper 4 bits = set if object is Nightopian Helper +// +void A_OrbitNights(mobj_t* actor) +{ + INT32 ofs = (var2 & 0xFFFF); + boolean ishelper = (var2 & 0xFFFF0000); +#ifdef HAVE_BLUA + if (LUA_CallAction("A_OrbitNights", actor)) + return; +#endif + + if (!actor->target + || (actor->target->player && + // if NiGHTS special stage and not NiGHTSmode. + (((maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap) && !(actor->target->player->powers[pw_carry] == CR_NIGHTSMODE)) + // Also remove this object if they no longer have a NiGHTS helper + || (ishelper && !actor->target->player->powers[pw_nights_helper])))) + { + P_RemoveMobj(actor); + return; + } + else + { + actor->extravalue1 += var1; + P_UnsetThingPosition(actor); + { + const angle_t fa = (angle_t)actor->extravalue1 >> ANGLETOFINESHIFT; + const angle_t ofa = ((angle_t)actor->extravalue1 + (ofs*ANG1)) >> ANGLETOFINESHIFT; + + const fixed_t fc = FixedMul(FINECOSINE(fa),FixedMul(32*FRACUNIT, actor->scale)); + const fixed_t fh = FixedMul(FINECOSINE(ofa),FixedMul(20*FRACUNIT, actor->scale)); + const fixed_t fs = FixedMul(FINESINE(fa),FixedMul(32*FRACUNIT, actor->scale)); + + actor->x = actor->target->x + fc; + actor->y = actor->target->y + fs; + actor->z = actor->target->z + fh + FixedMul(16*FRACUNIT, actor->scale); + + // Semi-lazy hack + actor->angle = (angle_t)actor->extravalue1 + ANGLE_90; + } + P_SetThingPosition(actor); + + if (ishelper && actor->target->player) // Flash a helper that's about to be removed. + { + if ((actor->target->player->powers[pw_nights_helper] < TICRATE) + && (actor->target->player->powers[pw_nights_helper] & 1)) + actor->flags2 |= MF2_DONTDRAW; + else + actor->flags2 &= ~MF2_DONTDRAW; + } + } +} + +// Function: A_GhostMe +// +// Description: Spawns a "ghost" mobj of this actor, ala spindash trails and the minus's digging "trails" +// +// var1 = duration in tics +// var2 = unused +// +void A_GhostMe(mobj_t *actor) +{ + INT32 locvar1 = var1; + mobj_t *ghost; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_GhostMe", actor)) + return; +#endif + ghost = P_SpawnGhostMobj(actor); + if (ghost && locvar1 > 0) + ghost->fuse = locvar1; +} + +// Function: A_SetObjectState +// +// Description: Changes the state of an object's target/tracer. +// +// var1 = state number +// var2: +// 0 = target +// 1 = tracer +// +void A_SetObjectState(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobj_t *target; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SetObjectState", actor)) + return; +#endif + + if ((!locvar2 && !actor->target) || (locvar2 && !actor->tracer)) + { + if (cv_debug) + CONS_Printf("A_SetObjectState: No target to change state!\n"); + return; + } + + if (!locvar2) // target + target = actor->target; + else // tracer + target = actor->tracer; + + if (target->health > 0) + { + if (!target->player) + P_SetMobjState(target, locvar1); + else + P_SetPlayerMobjState(target, locvar1); + } +} + +// Function: A_SetObjectTypeState +// +// Description: Changes the state of all active objects of a certain type in a certain range of the actor. +// +// var1 = state number +// var2: +// lower 16 bits = type +// upper 16 bits = range (if == 0, across whole map) +// +void A_SetObjectTypeState(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + const UINT16 loc2lw = (UINT16)(locvar2 & 65535); + const UINT16 loc2up = (UINT16)(locvar2 >> 16); + + thinker_t *th; + mobj_t *mo2; + fixed_t dist = 0; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SetObjectTypeState", actor)) + return; +#endif + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo2 = (mobj_t *)th; + + if (mo2->type == (mobjtype_t)loc2lw) + { + dist = P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y); + + if (mo2->health > 0) + { + if (loc2up == 0) + P_SetMobjState(mo2, locvar1); + else + { + if (dist <= FixedMul(loc2up*FRACUNIT, actor->scale)) + P_SetMobjState(mo2, locvar1); + } + } + } + } +} + +// Function: A_KnockBack +// +// Description: Knocks back the object's target at its current speed. +// +// var1: +// 0 = target +// 1 = tracer +// var2 = unused +// +void A_KnockBack(mobj_t *actor) +{ + INT32 locvar1 = var1; + mobj_t *target; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_KnockBack", actor)) + return; +#endif + + if (!locvar1) + target = actor->target; + else + target = actor->tracer; + + if (!target) + { + if(cv_debug) + CONS_Printf("A_KnockBack: No target!\n"); + return; + } + + target->momx *= -1; + target->momy *= -1; +} + +// Function: A_PushAway +// +// Description: Pushes an object's target away from the calling object. +// +// var1 = amount of force +// var2: +// lower 16 bits = If 1, xy momentum is lost. If 0, xy momentum is kept +// upper 16 bits = 0 - target, 1 - tracer +// +void A_PushAway(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobj_t *target; // target + angle_t an; // actor to target angle +#ifdef HAVE_BLUA + if (LUA_CallAction("A_PushAway", actor)) + return; +#endif + + if ((!(locvar2 >> 16) && !actor->target) || ((locvar2 >> 16) && !actor->tracer)) + return; + + if (!locvar1) + CONS_Printf("A_Thrust: Var1 not specified!\n"); + + if (!(locvar2 >> 16)) // target + target = actor->target; + else // tracer + target = actor->tracer; + + an = R_PointToAngle2(actor->x, actor->y, target->x, target->y); + + if (locvar2 & 65535) + P_InstaThrust(target, an, FixedMul(locvar1*FRACUNIT, actor->scale)); + else + P_Thrust(target, an, FixedMul(locvar1*FRACUNIT, actor->scale)); +} + +// Function: A_RingDrain +// +// Description: Drain targeted player's rings. +// +// var1 = ammount of drained rings +// var2 = unused +// +void A_RingDrain(mobj_t *actor) +{ + INT32 locvar1 = var1; + player_t *player; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_RingDrain", actor)) + return; +#endif + + if (!actor->target || !actor->target->player) + { + if(cv_debug) + CONS_Printf("A_RingDrain: No player targeted!\n"); + return; + } + + player = actor->target->player; + P_GivePlayerRings(player, -min(locvar1, player->rings)); +} + +// Function: A_SplitShot +// +// Description: Shoots 2 missiles that hit next to the player. +// +// var1 = target x-y-offset +// var2: +// lower 16 bits = missile type +// upper 16 bits = height offset +// +void A_SplitShot(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + const UINT16 loc2lw = (UINT16)(locvar2 & 65535); + const UINT16 loc2up = (UINT16)(locvar2 >> 16); + const fixed_t offs = (fixed_t)(locvar1*FRACUNIT); + const fixed_t hoffs = (fixed_t)(loc2up*FRACUNIT); +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SplitShot", actor)) + return; +#endif + + A_FaceTarget(actor); + { + const angle_t an = (actor->angle + ANGLE_90) >> ANGLETOFINESHIFT; + const fixed_t fasin = FINESINE(an); + const fixed_t facos = FINECOSINE(an); + fixed_t xs = FixedMul(facos,FixedMul(offs, actor->scale)); + fixed_t ys = FixedMul(fasin,FixedMul(offs, actor->scale)); + fixed_t z; + + if (actor->eflags & MFE_VERTICALFLIP) + z = actor->z + actor->height - FixedMul(hoffs, actor->scale); + else + z = actor->z + FixedMul(hoffs, actor->scale); + + P_SpawnPointMissile(actor, actor->target->x+xs, actor->target->y+ys, actor->target->z, loc2lw, actor->x, actor->y, z); + P_SpawnPointMissile(actor, actor->target->x-xs, actor->target->y-ys, actor->target->z, loc2lw, actor->x, actor->y, z); + } +} + +// Function: A_MissileSplit +// +// Description: If the object is a missile it will create a new missile with an alternate flight path owned by the one who shot the former missile. +// +// var1 = splitting missile type +// var2 = splitting angle +// +void A_MissileSplit(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_MissileSplit", actor)) + return; +#endif + if (actor->eflags & MFE_VERTICALFLIP) + P_SpawnAlteredDirectionMissile(actor, locvar1, actor->x, actor->y, actor->z+actor->height, locvar2); + else + P_SpawnAlteredDirectionMissile(actor, locvar1, actor->x, actor->y, actor->z, locvar2); +} + +// Function: A_MultiShot +// +// Description: Shoots objects horizontally that spread evenly in all directions. +// +// var1: +// lower 16 bits = number of missiles +// upper 16 bits = missile type # +// var2 = height offset +// +void A_MultiShot(mobj_t *actor) +{ + fixed_t z, xr, yr; + INT32 locvar1 = var1; + INT32 locvar2 = var2; + const UINT16 loc1lw = (UINT16)(locvar1 & 65535); + const UINT16 loc1up = (UINT16)(locvar1 >> 16); + INT32 count = 0; + fixed_t ad; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_MultiShot", actor)) + return; +#endif + + if (actor->target) + A_FaceTarget(actor); + + if(loc1lw > 90) + ad = FixedMul(90*FRACUNIT, actor->scale); + else + ad = FixedMul(loc1lw*FRACUNIT, actor->scale); + + if (actor->eflags & MFE_VERTICALFLIP) + z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale); + else + z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale); + xr = FixedMul((P_SignedRandom()/3)<scale); // please note p_mobj.c's P_Rand() abuse + yr = FixedMul((P_SignedRandom()/3)<scale); // of rand(), RAND_MAX and signness mess + + while(count <= loc1lw && loc1lw >= 1) + { + const angle_t fa = FixedAngleC(count*FRACUNIT*360, ad)>>ANGLETOFINESHIFT; + const fixed_t rc = FINECOSINE(fa); + const fixed_t rs = FINESINE(fa); + const fixed_t xrc = FixedMul(xr, rc); + const fixed_t yrs = FixedMul(yr, rs); + const fixed_t xrs = FixedMul(xr, rs); + const fixed_t yrc = FixedMul(yr, rc); + + P_SpawnPointMissile(actor, xrc-yrs+actor->x, xrs+yrc+actor->y, z, loc1up, actor->x, actor->y, z); + count++; + } + + if (!(actor->flags & MF_BOSS)) + { + if (ultimatemode) + actor->reactiontime = actor->info->reactiontime*TICRATE; + else + actor->reactiontime = actor->info->reactiontime*TICRATE*2; + } +} + +// Function: A_InstaLoop +// +// Description: Makes the object move along a 2d (view angle, z) polygon. +// +// var1: +// lower 16 bits = current step +// upper 16 bits = maximum step # +// var2 = force +// +void A_InstaLoop(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + fixed_t force = max(locvar2, 1)*FRACUNIT; // defaults to 1 if var2 < 1 + const UINT16 loc1lw = (UINT16)(locvar1 & 65535); + const UINT16 loc1up = (UINT16)(locvar1 >> 16); + const angle_t fa = FixedAngleC(loc1lw*FRACUNIT*360, loc1up*FRACUNIT)>>ANGLETOFINESHIFT; + const fixed_t ac = FINECOSINE(fa); + const fixed_t as = FINESINE(fa); +#ifdef HAVE_BLUA + if (LUA_CallAction("A_InstaLoop", actor)) + return; +#endif + + P_InstaThrust(actor, actor->angle, FixedMul(ac, FixedMul(force, actor->scale))); + P_SetObjectMomZ(actor, FixedMul(as, force), false); +} + +// Function: A_Custom3DRotate +// +// Description: Rotates the actor around its target in 3 dimensions. +// +// var1: +// lower 16 bits = radius in fracunits +// upper 16 bits = vertical offset +// var2: +// lower 16 bits = vertical rotation speed in 1/10 fracunits per tic +// upper 16 bits = horizontal rotation speed in 1/10 fracunits per tic +// +void A_Custom3DRotate(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + + const UINT16 loc1lw = (UINT16)(locvar1 & 65535); + const UINT16 loc1up = (UINT16)(locvar1 >> 16); + const UINT16 loc2lw = (UINT16)(locvar2 & 65535); + const UINT16 loc2up = (UINT16)(locvar2 >> 16); + + const fixed_t radius = FixedMul(loc1lw*FRACUNIT, actor->scale); + const fixed_t hOff = FixedMul(loc1up*FRACUNIT, actor->scale); + const fixed_t hspeed = FixedMul(loc2up*FRACUNIT/10, actor->scale); + const fixed_t vspeed = FixedMul(loc2lw*FRACUNIT/10, actor->scale); +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Custom3DRotate", actor)) + return; +#endif + + if (actor->target->health == 0) + { + P_RemoveMobj(actor); + return; + } + + if (!actor->target) // This should NEVER happen. + { + if (cv_debug) + CONS_Printf("Error: Object has no target\n"); + P_RemoveMobj(actor); + return; + } + if (hspeed==0 && vspeed==0) + { + CONS_Printf("Error: A_Custom3DRotate: Object has no speed.\n"); + return; + } + + actor->angle += FixedAngle(hspeed); + actor->movedir += FixedAngle(vspeed); + P_UnsetThingPosition(actor); + { + const angle_t fa = actor->angle>>ANGLETOFINESHIFT; + + if (vspeed == 0 && hspeed != 0) + { + actor->x = actor->target->x + FixedMul(FINECOSINE(fa),radius); + actor->y = actor->target->y + FixedMul(FINESINE(fa),radius); + actor->z = actor->target->z + actor->target->height/2 - actor->height/2 + hOff; + } + else + { + const angle_t md = actor->movedir>>ANGLETOFINESHIFT; + actor->x = actor->target->x + FixedMul(FixedMul(FINESINE(md),FINECOSINE(fa)),radius); + actor->y = actor->target->y + FixedMul(FixedMul(FINESINE(md),FINESINE(fa)),radius); + actor->z = actor->target->z + FixedMul(FINECOSINE(md),radius) + actor->target->height/2 - actor->height/2 + hOff; + } + } + P_SetThingPosition(actor); +} + +// Function: A_SearchForPlayers +// +// Description: Checks if the actor has targeted a vulnerable player. If not a new player will be searched all around. If no players are available the object can call a specific state. (Useful for not moving enemies) +// +// var1: +// if var1 == 0, if necessary call state with same state number as var2 +// else, do not call a specific state if no players are available +// var2 = state number +// +void A_SearchForPlayers(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SearchForPlayers", actor)) + return; +#endif + + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) + { + // look for a new target + if (P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + if(locvar1==0) + { + P_SetMobjStateNF(actor, locvar2); + return; + } + } +} + +// Function: A_CheckRandom +// +// Description: Calls a state by chance. +// +// var1: +// lower 16 bits = denominator +// upper 16 bits = numerator (defaults to 1 if zero) +// var2 = state number +// +void A_CheckRandom(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + fixed_t chance = FRACUNIT; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CheckRandom", actor)) + return; +#endif + if ((locvar1 & 0xFFFF) == 0) + return; + + // The PRNG doesn't suck anymore, OK? + if (locvar1 >> 16) + chance *= (locvar1 >> 16); + chance /= (locvar1 & 0xFFFF); + + if (P_RandomChance(chance)) + P_SetMobjState(actor, locvar2); +} + +// Function: A_CheckTargetRings +// +// Description: Calls a state depending on the ammount of rings currently owned by targeted players. +// +// var1 = if player rings >= var1 call state +// var2 = state number +// +void A_CheckTargetRings(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CheckTargetRings", actor)) + return; +#endif + + if (!(actor->target) || !(actor->target->player)) + return; + + if (actor->target->player->rings >= locvar1) + P_SetMobjState(actor, locvar2); +} + +// Function: A_CheckRings +// +// Description: Calls a state depending on the ammount of rings currently owned by all players. +// +// var1 = if player rings >= var1 call state +// var2 = state number +// +void A_CheckRings(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + INT32 i, cntr = 0; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CheckRings", actor)) + return; +#endif + + for (i = 0; i < MAXPLAYERS; i++) + cntr += players[i].rings; + + if (cntr >= locvar1) + P_SetMobjState(actor, locvar2); +} + +// Function: A_CheckTotalRings +// +// Description: Calls a state depending on the maximum ammount of rings owned by all players during this try. +// +// var1 = if total player rings >= var1 call state +// var2 = state number +// +void A_CheckTotalRings(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + + INT32 i, cntr = 0; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CheckTotalRings", actor)) + return; +#endif + + for (i = 0; i < MAXPLAYERS; i++) + cntr += players[i].totalring; + + if (cntr >= locvar1) + P_SetMobjState(actor, locvar2); +} + +// Function: A_CheckHealth +// +// Description: Calls a state depending on the object's current health. +// +// var1 = if health <= var1 call state +// var2 = state number +// +void A_CheckHealth(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CheckHealth", actor)) + return; +#endif + + if (actor->health <= locvar1) + P_SetMobjState(actor, locvar2); +} + +// Function: A_CheckRange +// +// Description: Calls a state if the object's target is in range. +// +// var1: +// lower 16 bits = range +// upper 16 bits = 0 - target, 1 - tracer +// var2 = state number +// +void A_CheckRange(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + fixed_t dist; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CheckRange", actor)) + return; +#endif + + if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer)) + return; + + if (!(locvar1 >> 16)) //target + dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y); + else //tracer + dist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y); + + if (dist <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale)) + P_SetMobjState(actor, locvar2); +} + +// Function: A_CheckHeight +// +// Description: Calls a state if the object and it's target have a height offset <= var1 compared to each other. +// +// var1: +// lower 16 bits = height offset +// upper 16 bits = 0 - target, 1 - tracer +// var2 = state number +// +void A_CheckHeight(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + fixed_t height; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CheckHeight", actor)) + return; +#endif + + if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer)) + return; + + if (!(locvar1 >> 16)) // target + height = abs(actor->target->z - actor->z); + else // tracer + height = abs(actor->tracer->z - actor->z); + + if (height <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale)) + P_SetMobjState(actor, locvar2); +} + +// Function: A_CheckTrueRange +// +// Description: Calls a state if the object's target is in true range. (Checks height, too.) +// +// var1: +// lower 16 bits = range +// upper 16 bits = 0 - target, 1 - tracer +// var2 = state number +// +void A_CheckTrueRange(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + fixed_t height; // vertical range + fixed_t dist; // horizontal range + fixed_t l; // true range +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CheckTrueRange", actor)) + return; +#endif + + if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer)) + return; + + if (!(locvar1 >> 16)) // target + { + height = actor->target->z - actor->z; + dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y); + + } + else // tracer + { + height = actor->tracer->z - actor->z; + dist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y); + } + + l = P_AproxDistance(dist, height); + + if (l <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale)) + P_SetMobjState(actor, locvar2); + +} + +// Function: A_CheckThingCount +// +// Description: Calls a state depending on the number of active things in range. +// +// var1: +// lower 16 bits = number of things +// upper 16 bits = thing type +// var2: +// lower 16 bits = state to call +// upper 16 bits = range (if == 0, check whole map) +// +void A_CheckThingCount(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + + const UINT16 loc1lw = (UINT16)(locvar1 & 65535); + const UINT16 loc1up = (UINT16)(locvar1 >> 16); + const UINT16 loc2lw = (UINT16)(locvar2 & 65535); + const UINT16 loc2up = (UINT16)(locvar2 >> 16); + + INT32 count = 0; + thinker_t *th; + mobj_t *mo2; + fixed_t dist = 0; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CheckThingCount", actor)) + return; +#endif + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo2 = (mobj_t *)th; + + if (mo2->type == (mobjtype_t)loc1up) + { + dist = P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y); + + if (loc2up == 0) + count++; + else + { + if (dist <= FixedMul(loc2up*FRACUNIT, actor->scale)) + count++; + } + } + } + + if(loc1lw <= count) + P_SetMobjState(actor, loc2lw); +} + +// Function: A_CheckAmbush +// +// Description: Calls a state if the actor is behind its targeted player. +// +// var1: +// 0 = target +// 1 = tracer +// var2 = state number +// +void A_CheckAmbush(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + angle_t at; // angle target is currently facing + angle_t atp; // actor to target angle + angle_t an; // angle between at and atp + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CheckAmbush", actor)) + return; +#endif + + if ((!locvar1 && !actor->target) || (locvar1 && !actor->tracer)) + return; + + if (!locvar1) // target + { + at = actor->target->angle; + atp = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); + } + else // tracer + { + at = actor->tracer->angle; + atp = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); + } + + an = atp - at; + + if (an > ANGLE_180) // flip angle if bigger than 180 + an = InvAngle(an); + + if (an < ANGLE_90+ANGLE_22h) // within an angle of 112.5 from each other? + P_SetMobjState(actor, locvar2); +} + +// Function: A_CheckCustomValue +// +// Description: Calls a state depending on the object's custom value. +// +// var1 = if custom value >= var1, call state +// var2 = state number +// +void A_CheckCustomValue(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CheckCustomValue", actor)) + return; +#endif + + if (actor->cusval >= locvar1) + P_SetMobjState(actor, locvar2); +} + +// Function: A_CheckCusValMemo +// +// Description: Calls a state depending on the object's custom memory value. +// +// var1 = if memory value >= var1, call state +// var2 = state number +// +void A_CheckCusValMemo(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CheckCusValMemo", actor)) + return; +#endif + + if (actor->cvmem >= locvar1) + P_SetMobjState(actor, locvar2); +} + +// Function: A_SetCustomValue +// +// Description: Changes the custom value of an object. +// +// var1 = manipulating value +// var2: +// if var2 == 5, multiply the custom value by var1 +// else if var2 == 4, divide the custom value by var1 +// else if var2 == 3, apply modulo var1 to the custom value +// else if var2 == 2, add var1 to the custom value +// else if var2 == 1, substract var1 from the custom value +// else if var2 == 0, replace the custom value with var1 +// +void A_SetCustomValue(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SetCustomValue", actor)) + return; +#endif + + if (cv_debug) + CONS_Printf("Init custom value is %d\n", actor->cusval); + + if (locvar1 == 0 && locvar2 == 4) + return; // DON'T DIVIDE BY ZERO + + // no need for a "temp" value here, just modify the cusval directly + if (locvar2 == 5) // multiply + actor->cusval *= locvar1; + else if (locvar2 == 4) // divide + actor->cusval /= locvar1; + else if (locvar2 == 3) // modulo + actor->cusval %= locvar1; + else if (locvar2 == 2) // add + actor->cusval += locvar1; + else if (locvar2 == 1) // subtract + actor->cusval -= locvar1; + else // replace + actor->cusval = locvar1; + + if(cv_debug) + CONS_Printf("New custom value is %d\n", actor->cusval); +} + +// Function: A_UseCusValMemo +// +// Description: Memorizes or recalls a current custom value. +// +// var1: +// if var1 == 1, manipulate memory value +// else, recall memory value replacing the custom value +// var2: +// if var2 == 5, mem = mem*cv || cv = cv*mem +// else if var2 == 4, mem = mem/cv || cv = cv/mem +// else if var2 == 3, mem = mem%cv || cv = cv%mem +// else if var2 == 2, mem += cv || cv += mem +// else if var2 == 1, mem -= cv || cv -= mem +// else mem = cv || cv = mem +// +void A_UseCusValMemo(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + + INT32 temp = actor->cusval; // value being manipulated + INT32 tempM = actor->cvmem; // value used to manipulate temp with +#ifdef HAVE_BLUA + if (LUA_CallAction("A_UseCusValMemo", actor)) + return; +#endif + + if (locvar1 == 1) // cvmem being changed using cusval + { + temp = actor->cvmem; + tempM = actor->cusval; + } + else // cusval being changed with cvmem + { + temp = actor->cusval; + tempM = actor->cvmem; + } + + if (tempM == 0 && locvar2 == 4) + return; // DON'T DIVIDE BY ZERO + + // now get new value for cusval/cvmem using the other + if (locvar2 == 5) // multiply + temp *= tempM; + else if (locvar2 == 4) // divide + temp /= tempM; + else if (locvar2 == 3) // modulo + temp %= tempM; + else if (locvar2 == 2) // add + temp += tempM; + else if (locvar2 == 1) // subtract + temp -= tempM; + else // replace + temp = tempM; + + // finally, give cusval/cvmem the new value! + if (locvar1 == 1) + actor->cvmem = temp; + else + actor->cusval = temp; +} + +// Function: A_RelayCustomValue +// +// Description: Manipulates the custom value of the object's target/tracer. +// +// var1: +// lower 16 bits: +// if var1 == 0, use own custom value +// else, use var1 value +// upper 16 bits = 0 - target, 1 - tracer +// var2: +// if var2 == 5, multiply the target's custom value by var1 +// else if var2 == 4, divide the target's custom value by var1 +// else if var2 == 3, apply modulo var1 to the target's custom value +// else if var2 == 2, add var1 to the target's custom value +// else if var2 == 1, substract var1 from the target's custom value +// else if var2 == 0, replace the target's custom value with var1 +// +void A_RelayCustomValue(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + + INT32 temp; // reference value - var1 lower 16 bits changes this + INT32 tempT; // target's value - changed to tracer if var1 upper 16 bits set, then modified to become final value +#ifdef HAVE_BLUA + if (LUA_CallAction("A_RelayCustomValue", actor)) + return; +#endif + + if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer)) + return; + + // reference custom value + if ((locvar1 & 65535) == 0) + temp = actor->cusval; // your own custom value + else + temp = (locvar1 & 65535); // var1 value + + if (!(locvar1 >> 16)) // target's custom value + tempT = actor->target->cusval; + else // tracer's custom value + tempT = actor->tracer->cusval; + + if (temp == 0 && locvar2 == 4) + return; // DON'T DIVIDE BY ZERO + + // now get new cusval using target's and the reference + if (locvar2 == 5) // multiply + tempT *= temp; + else if (locvar2 == 4) // divide + tempT /= temp; + else if (locvar2 == 3) // modulo + tempT %= temp; + else if (locvar2 == 2) // add + tempT += temp; + else if (locvar2 == 1) // subtract + tempT -= temp; + else // replace + tempT = temp; + + // finally, give target/tracer the new cusval! + if (!(locvar1 >> 16)) // target + actor->target->cusval = tempT; + else // tracer + actor->tracer->cusval = tempT; +} + +// Function: A_CusValAction +// +// Description: Calls an action from a reference state applying custom value parameters. +// +// var1 = state # to use action from +// var2: +// if var2 == 5, only replace new action's var2 with memory value +// else if var2 == 4, only replace new action's var1 with memory value +// else if var2 == 3, replace new action's var2 with custom value and var1 with memory value +// else if var2 == 2, replace new action's var1 with custom value and var2 with memory value +// else if var2 == 1, only replace new action's var2 with custom value +// else if var2 == 0, only replace new action's var1 with custom value +// +void A_CusValAction(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CusValAction", actor)) + return; +#endif + + if (locvar2 == 5) + { + var1 = states[locvar1].var1; + var2 = (INT32)actor->cvmem; + } + else if (locvar2 == 4) + { + var1 = (INT32)actor->cvmem; + var2 = states[locvar1].var2; + } + else if (locvar2 == 3) + { + var1 = (INT32)actor->cvmem; + var2 = (INT32)actor->cusval; + } + else if (locvar2 == 2) + { + var1 = (INT32)actor->cusval; + var2 = (INT32)actor->cvmem; + } + else if (locvar2 == 1) + { + var1 = states[locvar1].var1; + var2 = (INT32)actor->cusval; + } + else + { + var1 = (INT32)actor->cusval; + var2 = states[locvar1].var2; + } + +#ifdef HAVE_BLUA + astate = &states[locvar1]; +#endif + states[locvar1].action.acp1(actor); +} + +// Function: A_ForceStop +// +// Description: Actor immediately stops its current movement. +// +// var1: +// if var1 == 0, stop x-y-z-movement +// else, stop x-y-movement only +// var2 = unused +// +void A_ForceStop(mobj_t *actor) +{ + INT32 locvar1 = var1; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_ForceStop", actor)) + return; +#endif + + actor->momx = actor->momy = 0; + if (locvar1 == 0) + actor->momz = 0; +} + +// Function: A_ForceWin +// +// Description: Makes all players win the level. +// +// var1 = unused +// var2 = unused +// +void A_ForceWin(mobj_t *actor) +{ + INT32 i; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_ForceWin", actor)) + return; +#else + (void)actor; +#endif + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && ((players[i].mo && players[i].mo->health) + || ((netgame || multiplayer) && (players[i].lives || players[i].continues)))) + break; + } + + if (i == MAXPLAYERS) + return; + + for (i = 0; i < MAXPLAYERS; i++) + P_DoPlayerExit(&players[i]); +} + +// Function: A_SpikeRetract +// +// Description: Toggles actor solid flag. +// +// var1: +// if var1 == 0, actor no collide +// else, actor solid +// var2 = unused +// +void A_SpikeRetract(mobj_t *actor) +{ + INT32 locvar1 = var1; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SpikeRetract", actor)) + return; +#endif + + if (actor->flags & MF_NOBLOCKMAP) + return; + + if (locvar1 == 0) + { + actor->flags &= ~MF_SOLID; + actor->flags |= MF_NOCLIPTHING; + } + else + { + actor->flags |= MF_SOLID; + actor->flags &= ~MF_NOCLIPTHING; + } + if (actor->flags & MF_SOLID) + P_CheckPosition(actor, actor->x, actor->y); +} + +// Function: A_InfoState +// +// Description: Set mobj state to one predefined in mobjinfo. +// +// var1: +// if var1 == 0, set actor to spawnstate +// else if var1 == 1, set actor to seestate +// else if var1 == 2, set actor to meleestate +// else if var1 == 3, set actor to missilestate +// else if var1 == 4, set actor to deathstate +// else if var1 == 5, set actor to xdeathstate +// else if var1 == 6, set actor to raisestate +// var2 = unused +// +void A_InfoState(mobj_t *actor) +{ + INT32 locvar1 = var1; + switch (locvar1) + { + case 0: + if (actor->state != &states[actor->info->spawnstate]) + P_SetMobjState(actor, actor->info->spawnstate); + break; + case 1: + if (actor->state != &states[actor->info->seestate]) + P_SetMobjState(actor, actor->info->seestate); + break; + case 2: + if (actor->state != &states[actor->info->meleestate]) + P_SetMobjState(actor, actor->info->meleestate); + break; + case 3: + if (actor->state != &states[actor->info->missilestate]) + P_SetMobjState(actor, actor->info->missilestate); + break; + case 4: + if (actor->state != &states[actor->info->deathstate]) + P_SetMobjState(actor, actor->info->deathstate); + break; + case 5: + if (actor->state != &states[actor->info->xdeathstate]) + P_SetMobjState(actor, actor->info->xdeathstate); + break; + case 6: + if (actor->state != &states[actor->info->raisestate]) + P_SetMobjState(actor, actor->info->raisestate); + break; + default: + break; + } +} + +// Function: A_Repeat +// +// Description: Returns to state var2 until animation has been used var1 times, then continues to nextstate. +// +// var1 = repeat count +// var2 = state to return to if extravalue2 > 0 +// +void A_Repeat(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Repeat", actor)) + return; +#endif + + if (locvar1 && (!actor->extravalue2 || actor->extravalue2 > locvar1)) + actor->extravalue2 = locvar1; + + if (--actor->extravalue2 > 0) + P_SetMobjState(actor, locvar2); +} + +// Function: A_SetScale +// +// Description: Changes the scale of the actor or its target/tracer +// +// var1 = new scale (1*FRACUNIT = 100%) +// var2: +// upper 16 bits: 0 = actor, 1 = target, 2 = tracer +// lower 16 bits: 0 = instant change, 1 = smooth change +// +void A_SetScale(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobj_t *target; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SetScale", actor)) + return; +#endif + + if (locvar1 <= 0) + { + if(cv_debug) + CONS_Printf("A_SetScale: Valid scale not specified!\n"); + return; + } + + if ((locvar2>>16) == 1) + target = actor->target; + else if ((locvar2>>16) == 2) + target = actor->tracer; + else // default to yourself! + target = actor; + + if (!target) + { + if(cv_debug) + CONS_Printf("A_SetScale: No target!\n"); + return; + } + + target->destscale = locvar1; // destination scale + if (!(locvar2 & 65535)) + P_SetScale(target, locvar1); // this instantly changes current scale to var1 if used, if not destscale will alter scale to var1 anyway +} + +// Function: A_RemoteDamage +// +// Description: Damages, kills or even removes either the actor or its target/tracer. Actor acts as the inflictor/source unless harming itself +// +// var1 = Mobj affected: 0 - actor, 1 - target, 2 - tracer +// var2 = Action: 0 - Damage, 1 - Kill, 2 - Remove +// +void A_RemoteDamage(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobj_t *target; // we MUST have a target + mobj_t *source = NULL; // on the other hand we don't necessarily need a source +#ifdef HAVE_BLUA + if (LUA_CallAction("A_RemoteDamage", actor)) + return; +#endif + if (locvar1 == 1) + target = actor->target; + else if (locvar1 == 2) + target = actor->tracer; + else // default to yourself! + target = actor; + + if (locvar1 == 1 || locvar1 == 2) + source = actor; + + if (!target) + { + if(cv_debug) + CONS_Printf("A_RemoteDamage: No target!\n"); + return; + } + + if (locvar2 == 1) // Kill mobj! + { + if (target->player) // players die using P_DamageMobj instead for some reason + P_DamageMobj(target, source, source, 1, DMG_INSTAKILL); + else + P_KillMobj(target, source, source, 0); + } + else if (locvar2 == 2) // Remove mobj! + { + if (target->player) //don't remove players! + return; + + P_RemoveMobj(target); + } + else // default: Damage mobj! + P_DamageMobj(target, source, source, 1, 0); +} + +// Function: A_HomingChase +// +// Description: Actor chases directly towards its destination object +// +// var1 = speed multiple +// var2 = destination: 0 = target, 1 = tracer +// +void A_HomingChase(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobj_t *dest; + fixed_t dist; + fixed_t speedmul; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_HomingChase", actor)) + return; +#endif + + if (locvar2 == 1) + dest = actor->tracer; + else //default + dest = actor->target; + + if (!dest || !dest->health) + return; + + actor->angle = R_PointToAngle2(actor->x, actor->y, dest->x, dest->y); + + dist = P_AproxDistance(P_AproxDistance(dest->x - actor->x, dest->y - actor->y), dest->z - actor->z); + + if (dist < 1) + dist = 1; + + speedmul = FixedMul(locvar1, actor->scale); + + actor->momx = FixedMul(FixedDiv(dest->x - actor->x, dist), speedmul); + actor->momy = FixedMul(FixedDiv(dest->y - actor->y, dist), speedmul); + actor->momz = FixedMul(FixedDiv(dest->z - actor->z, dist), speedmul); +} + +// Function: A_TrapShot +// +// Description: Fires a missile in a particular direction and angle rather than AT something, Trapgoyle-style! +// +// var1: +// lower 16 bits = object # to fire +// upper 16 bits = front offset +// var2: +// lower 15 bits = vertical angle variable +// 16th bit: +// - 0: use vertical angle variable as vertical angle in degrees +// - 1: mimic P_SpawnXYZMissile +// use z of actor minus z of missile as vertical distance to cover during momz calculation +// use vertical angle variable as horizontal distance to cover during momz calculation +// upper 16 bits = height offset +// +void A_TrapShot(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + boolean oldstyle = (locvar2 & 32768) ? true : false; + mobjtype_t type = (mobjtype_t)(locvar1 & 65535); + mobj_t *missile; + INT16 frontoff = (INT16)(locvar1 >> 16); + INT16 vertoff = (INT16)(locvar2 >> 16); + fixed_t x, y, z; + fixed_t speed; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_TrapShot", actor)) + return; +#endif + + x = actor->x + P_ReturnThrustX(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale)); + y = actor->y + P_ReturnThrustY(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale)); + + if (actor->eflags & MFE_VERTICALFLIP) + z = actor->z + actor->height - FixedMul(vertoff*FRACUNIT, actor->scale) - FixedMul(mobjinfo[type].height, actor->scale); + else + z = actor->z + FixedMul(vertoff*FRACUNIT, actor->scale); + + CONS_Debug(DBG_GAMELOGIC, "A_TrapShot: missile no. = %d, front offset = %d, vertical angle = %d, z offset = %d\n", + type, frontoff, (INT16)(locvar2 & 65535), vertoff); + + missile = P_SpawnMobj(x, y, z, type); + + if (actor->eflags & MFE_VERTICALFLIP) + missile->flags2 |= MF2_OBJECTFLIP; + + missile->destscale = actor->scale; + P_SetScale(missile, actor->scale); + + if (missile->info->seesound) + S_StartSound(missile, missile->info->seesound); + + P_SetTarget(&missile->target, actor); + missile->angle = actor->angle; + + speed = FixedMul(missile->info->speed, missile->scale); + + if (oldstyle) + { + missile->momx = FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed); + missile->momy = FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed); + // The below line basically mimics P_SpawnXYZMissile's momz calculation. + missile->momz = (actor->z + ((actor->eflags & MFE_VERTICALFLIP) ? actor->height : 0) - z) / ((fixed_t)(locvar2 & 32767)*FRACUNIT / speed); + P_CheckMissileSpawn(missile); + } + else + { + angle_t vertang = FixedAngle(((INT16)(locvar2 & 32767))*FRACUNIT); + if (actor->eflags & MFE_VERTICALFLIP) + vertang = InvAngle(vertang); // flip firing angle + missile->momx = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed)); + missile->momy = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed)); + missile->momz = FixedMul(FINESINE(vertang>>ANGLETOFINESHIFT), speed); + } +} + +// Function: A_VileTarget +// +// Description: Spawns an object directly on the target, and sets this object as the actor's tracer. +// Originally used by Archviles to summon a pillar of hellfire, hence the name. +// +// var1 = mobj to spawn +// var2 = If 0, target only the actor's target. Else, target every player, period. +// +void A_VileTarget(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobj_t *fog; + mobjtype_t fogtype; + INT32 i; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_VileTarget", actor)) + return; +#endif + + if (!actor->target) + return; + + A_FaceTarget(actor); + + // Determine object to spawn + if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES) + fogtype = MT_CYBRAKDEMON_TARGET_RETICULE; + else + fogtype = (mobjtype_t)locvar1; + + if (!locvar2) + { + fog = P_SpawnMobj(actor->target->x, + actor->target->y, + actor->target->z + ((actor->target->eflags & MFE_VERTICALFLIP) ? actor->target->height - mobjinfo[fogtype].height : 0), + fogtype); + if (actor->target->eflags & MFE_VERTICALFLIP) + { + fog->eflags |= MFE_VERTICALFLIP; + fog->flags2 |= MF2_OBJECTFLIP; + } + fog->destscale = actor->target->scale; + P_SetScale(fog, fog->destscale); + + P_SetTarget(&actor->tracer, fog); + P_SetTarget(&fog->target, actor); + P_SetTarget(&fog->tracer, actor->target); + A_VileFire(fog); + } + else + { + // Our "Archvile" here is actually Oprah. "YOU GET A TARGET! YOU GET A TARGET! YOU ALL GET A TARGET!" + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + + if (!players[i].mo) + continue; + + if (!players[i].mo->health) + continue; + + fog = P_SpawnMobj(players[i].mo->x, + players[i].mo->y, + players[i].mo->z + ((players[i].mo->eflags & MFE_VERTICALFLIP) ? players[i].mo->height - mobjinfo[fogtype].height : 0), + fogtype); + if (players[i].mo->eflags & MFE_VERTICALFLIP) + { + fog->eflags |= MFE_VERTICALFLIP; + fog->flags2 |= MF2_OBJECTFLIP; + } + fog->destscale = players[i].mo->scale; + P_SetScale(fog, fog->destscale); + + if (players[i].mo == actor->target) // We only care to track the fog targeting who we REALLY hate right now + P_SetTarget(&actor->tracer, fog); + P_SetTarget(&fog->target, actor); + P_SetTarget(&fog->tracer, players[i].mo); + A_VileFire(fog); + } + } +} + +// Function: A_VileAttack +// +// Description: Instantly hurts the actor's target, if it's in the actor's line of sight. +// Originally used by Archviles to cause explosions where their hellfire pillars were, hence the name. +// +// var1 = sound to play +// var2: +// Lower 16 bits = optional explosion object +// Upper 16 bits = If 0, attack only the actor's target. Else, attack all the players. All of them. +// +void A_VileAttack(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + sfxenum_t soundtoplay; + mobjtype_t explosionType = MT_NULL; + mobj_t *fire; + INT32 i; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_VileAttack", actor)) + return; +#endif + + if (!actor->target) + return; + + A_FaceTarget(actor); + + if (locvar1 <= 0 || locvar1 >= NUMSFX) + soundtoplay = sfx_brakrx; + else + soundtoplay = (sfxenum_t)locvar1; + + if ((locvar2 & 0xFFFF) > 0 && (locvar2 & 0xFFFF) <= NUMMOBJTYPES) + { + explosionType = (mobjtype_t)(locvar2 & 0xFFFF); + } + + if (!(locvar2 & 0xFFFF0000)) { + if (!P_CheckSight(actor, actor->target)) + return; + + S_StartSound(actor, soundtoplay); + P_DamageMobj(actor->target, actor, actor, 1, 0); + //actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; // How id did it + actor->target->momz += FixedMul(10*FRACUNIT, actor->scale)*P_MobjFlip(actor->target); // How we're doing it + if (explosionType != MT_NULL) + { + P_SpawnMobj(actor->target->x, actor->target->y, actor->target->z, explosionType); + } + + // Extra attack. This was for additional damage in Doom. Doesn't really belong in SRB2, but the heck with it, it's here anyway. + fire = actor->tracer; + + if (!fire) + return; + + // move the fire between the vile and the player + //fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]); + //fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]); + P_TeleportMove(fire, + actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)), + actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)), + fire->z); + P_RadiusAttack(fire, actor, 70*FRACUNIT, 0); + } + else + { + // Oprahvile strikes again, but this time, she brings HOT PAIN + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + + if (!players[i].mo) + continue; + + if (!players[i].mo->health) + continue; + + if (!P_CheckSight(actor, players[i].mo)) + continue; + + S_StartSound(actor, soundtoplay); + P_DamageMobj(players[i].mo, actor, actor, 1, 0); + //actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; // How id did it + players[i].mo->momz += FixedMul(10*FRACUNIT, actor->scale)*P_MobjFlip(players[i].mo); // How we're doing it + if (explosionType != MT_NULL) + { + P_SpawnMobj(players[i].mo->x, players[i].mo->y, players[i].mo->z, explosionType); + } + + // Extra attack. This was for additional damage in Doom. Doesn't really belong in SRB2, but the heck with it, it's here anyway. + // However, it ONLY applies to the actor's target. Nobody else matters! + if (actor->target != players[i].mo) + continue; + + fire = actor->tracer; + + if (!fire) + continue; + + // move the fire between the vile and the player + //fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]); + //fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]); + P_TeleportMove(fire, + actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)), + actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)), + fire->z); + P_RadiusAttack(fire, actor, 70*FRACUNIT, 0); + } + } + +} + +// Function: A_VileFire +// +// Description: Kind of like A_CapeChase; keeps this object in front of its tracer, unless its target can't see it. +// Originally used by Archviles to keep their hellfire pillars on top of the player, hence the name (although it was just "A_Fire" there; added "Vile" to make it more specific). +// Added some functionality to optionally draw a line directly to the enemy doing the targetting. Y'know, to hammer things in a bit. +// +// var1 = sound to play +// var2: +// Lower 16 bits = mobj to spawn (0 doesn't spawn a line at all) +// Upper 16 bits = # to spawn (default is 8) +// +void A_VileFire(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + mobj_t *dest; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_VileFire", actor)) + return; +#endif + + dest = actor->tracer; + if (!dest) + return; + + // don't move it if the vile lost sight + if (!P_CheckSight(actor->target, dest)) + return; + + // keep to same scale and gravity as tracer ALWAYS + actor->destscale = dest->scale; + P_SetScale(actor, actor->destscale); + if (dest->eflags & MFE_VERTICALFLIP) + { + actor->eflags |= MFE_VERTICALFLIP; + actor->flags2 |= MF2_OBJECTFLIP; + } + else + { + actor->eflags &= ~MFE_VERTICALFLIP; + actor->flags2 &= ~MF2_OBJECTFLIP; + } + + P_UnsetThingPosition(actor); + actor->x = dest->x + P_ReturnThrustX(actor, dest->angle, FixedMul(24*FRACUNIT, actor->scale)); + actor->y = dest->y + P_ReturnThrustY(actor, dest->angle, FixedMul(24*FRACUNIT, actor->scale)); + actor->z = dest->z + ((actor->eflags & MFE_VERTICALFLIP) ? dest->height-actor->height : 0); + P_SetThingPosition(actor); + + // Play sound, if one's specified + if (locvar1 > 0 && locvar1 < NUMSFX) + S_StartSound(actor, (sfxenum_t)locvar1); + + // Now draw the line to the actor's target + if (locvar2 & 0xFFFF) + { + mobjtype_t lineMobj; + UINT16 numLineMobjs; + fixed_t distX; + fixed_t distY; + fixed_t distZ; + UINT16 i; + + lineMobj = (mobjtype_t)(locvar2 & 0xFFFF); + numLineMobjs = (UINT16)(locvar2 >> 16); + if (numLineMobjs == 0) { + numLineMobjs = 8; + } + + // Get distance for each step + distX = (actor->target->x - actor->x) / numLineMobjs; + distY = (actor->target->y - actor->y) / numLineMobjs; + distZ = ((actor->target->z + FixedMul(actor->target->height/2, actor->target->scale)) - (actor->z + FixedMul(actor->height/2, actor->scale))) / numLineMobjs; + + for (i = 1; i <= numLineMobjs; i++) + { + P_SpawnMobj(actor->x + (distX * i), actor->y + (distY * i), actor->z + (distZ * i) + FixedMul(actor->height/2, actor->scale), lineMobj); + } + } +} + +// Function: A_BrakChase +// +// Description: Chase after your target, but speed and attack are tied to health. +// +// Every time this is called, generate a random number from a 1/4 to 3/4 of mobj's spawn health. +// If health is above that value, use missilestate to attack. +// If health is at or below that value, use meleestate to attack (default to missile state if not available). +// +// Likewise, state will linearly speed up as health goes down. +// Upper bound will be the frame's normal length. +// Lower bound defaults to 1 tic (technically 0, but we round up), unless a lower bound is specified in var1. +// +// var1 = lower-bound of frame length, in tics +// var2 = optional sound to play +// +void A_BrakChase(mobj_t *actor) +{ + INT32 delta; + INT32 lowerbound; + INT32 newtics; + INT32 locvar1 = var1; + INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_BrakChase", actor)) + return; +#endif + + // Set new tics NOW, in case the state changes while we're doing this and we try applying this to the painstate or something silly + if (actor->tics > 1 && locvar1 < actor->tics) // Not much point, otherwise + { + if (locvar1 < 0) + lowerbound = 0; + else + lowerbound = locvar1; + + newtics = (((actor->tics - lowerbound) * actor->health) / actor->info->spawnhealth) + lowerbound; + if (newtics < 1) + newtics = 1; + + actor->tics = newtics; + } + + if (actor->reactiontime) + { + actor->reactiontime--; + if (actor->reactiontime == 0 && actor->type == MT_CYBRAKDEMON) + S_StartSound(0, sfx_bewar1 + P_RandomKey(4)); + } + + // modify target threshold + if (actor->threshold) + { + if (!actor->target || actor->target->health <= 0) + actor->threshold = 0; + else + actor->threshold--; + } + + // turn towards movement direction if not there yet + if (actor->movedir < NUMDIRS) + { + actor->angle &= (7<<29); + delta = actor->angle - (actor->movedir << 29); + + if (delta > 0) + actor->angle -= ANGLE_45; + else if (delta < 0) + actor->angle += ANGLE_45; + } + + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) + { + // look for a new target + if (P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + P_SetMobjStateNF(actor, actor->info->spawnstate); + return; + } + + // do not attack twice in a row + if (actor->flags2 & MF2_JUSTATTACKED) + { + actor->flags2 &= ~MF2_JUSTATTACKED; + P_NewChaseDir(actor); + return; + } + + // Check if we can attack + if (P_CheckMissileRange(actor) && !actor->movecount) + { + // Check if we should use "melee" attack first. (Yes, this still runs outside of melee range. Quiet, you.) + if (actor->info->meleestate + && actor->health <= P_RandomRange(actor->info->spawnhealth/4, (actor->info->spawnhealth * 3)/4)) // Guaranteed true if <= 1/4 health, guaranteed false if > 3/4 health + { + if (actor->info->attacksound) + S_StartAttackSound(actor, actor->info->attacksound); + + P_SetMobjState(actor, actor->info->meleestate); + actor->flags2 |= MF2_JUSTATTACKED; + return; + } + // Else, check for missile attack. + else if (actor->info->missilestate) + { + P_SetMobjState(actor, actor->info->missilestate); + actor->flags2 |= MF2_JUSTATTACKED; + return; + } + } + + // possibly choose another target + if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)) + && P_LookForPlayers(actor, true, false, 0)) + return; // got a new target + + // chase towards player + if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed)) + P_NewChaseDir(actor); + + // Optionally play a sound effect + if (locvar2 > 0 && locvar2 < NUMSFX) + S_StartSound(actor, (sfxenum_t)locvar2); + + // make active sound + if (actor->type != MT_CYBRAKDEMON && actor->info->activesound && P_RandomChance(3*FRACUNIT/256)) + { + S_StartSound(actor, actor->info->activesound); + } +} + +// Function: A_BrakFireShot +// +// Description: Shoot an object at your target, offset to match where Brak's gun is. +// Also, sets Brak's reaction time; behaves normally otherwise. +// +// var1 = object # to shoot +// var2 = unused +// +void A_BrakFireShot(mobj_t *actor) +{ + fixed_t x, y, z; + INT32 locvar1 = var1; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_BrakFireShot", actor)) + return; +#endif + if (!actor->target) + return; + + A_FaceTarget(actor); + + x = actor->x + + P_ReturnThrustX(actor, actor->angle, FixedMul(64*FRACUNIT, actor->scale)) + + P_ReturnThrustX(actor, actor->angle+ANGLE_270, FixedMul(32*FRACUNIT, actor->scale)); + y = actor->y + + P_ReturnThrustY(actor, actor->angle, FixedMul(64*FRACUNIT, actor->scale)) + + P_ReturnThrustY(actor, actor->angle+ANGLE_270, FixedMul(32*FRACUNIT, actor->scale)); + if (actor->eflags & MFE_VERTICALFLIP) + z = actor->z + actor->height - FixedMul(144*FRACUNIT, actor->scale); + else + z = actor->z + FixedMul(144*FRACUNIT, actor->scale); + + P_SpawnXYZMissile(actor, actor->target, locvar1, x, y, z); + + if (!(actor->flags & MF_BOSS)) + { + if (ultimatemode) + actor->reactiontime = actor->info->reactiontime*TICRATE; + else + actor->reactiontime = actor->info->reactiontime*TICRATE*2; + } +} + +// Function: A_BrakLobShot +// +// Description: Lobs an object at the floor about a third of the way toward your target. +// Implication is it'll bounce the rest of the way. +// (You can also just aim straight at the target, but whatever) +// Formula grabbed from http://en.wikipedia.org/wiki/Trajectory_of_a_projectile#Angle_required_to_hit_coordinate_.28x.2Cy.29 +// +// var1 = object # to lob +// var2: +// Lower 16 bits: height offset to shoot from, from the actor's bottom (none that "airtime" malarky) +// Upper 16 bits: if 0, aim 1/3 of the way. Else, aim directly at target. +// + +void A_BrakLobShot(mobj_t *actor) +{ + fixed_t v; // Velocity to shoot object + fixed_t a1, a2, aToUse; // Velocity squared + fixed_t g; // Gravity + fixed_t x; // Horizontal difference + INT32 x_int; // x! But in integer form! + fixed_t y; // Vertical difference (yes that's normally z in SRB2 shut up) + INT32 y_int; // y! But in integer form! + INT32 intHypotenuse; // x^2 + y^2. Frequently overflows fixed point, hence why we need integers proper. + fixed_t fixedHypotenuse; // However, we can work around that and still get a fixed-point number. + angle_t theta; // Angle of attack + mobjtype_t typeOfShot; + mobj_t *shot; // Object to shoot + fixed_t newTargetX; // If not aiming directly + fixed_t newTargetY; // If not aiming directly + INT32 locvar1 = var1; + INT32 locvar2 = var2 & 0x0000FFFF; + INT32 aimDirect = var2 & 0xFFFF0000; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_BrakLobShot", actor)) + return; +#endif + + if (!actor->target) + return; // Don't even bother if we've got nothing to aim at. + + // Look up actor's current gravity situation + if (actor->subsector->sector->gravity) + g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000))); + else + g = gravity; + + // Look up distance between actor and its target + x = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y); + if (!aimDirect) + { + // Distance should actually be a third of the way over + x = FixedDiv(x, 3<x + P_ReturnThrustX(actor, actor->angle, x); + newTargetY = actor->y + P_ReturnThrustY(actor, actor->angle, x); + x = P_AproxDistance(newTargetX - actor->x, newTargetY - actor->y); + // Look up height difference between actor and the ground 1/3 of the way to its target + y = P_FloorzAtPos(newTargetX, newTargetY, actor->target->z, actor->target->height) - (actor->z + FixedMul(locvar2*FRACUNIT, actor->scale)); + } + else + { + // Look up height difference between actor and its target + y = actor->target->z - (actor->z + FixedMul(locvar2*FRACUNIT, actor->scale)); + } + + // Get x^2 + y^2. Have to do it in a roundabout manner, because this overflows fixed_t way too easily otherwise. + x_int = x>>FRACBITS; + y_int = y>>FRACBITS; + intHypotenuse = (x_int*x_int) + (y_int*y_int); + fixedHypotenuse = FixedSqrt(intHypotenuse) *256; + + // a = g(y+/-sqrt(x^2+y^2)). a1 can be +, a2 can be -. + a1 = FixedMul(g,y+fixedHypotenuse); + a2 = FixedMul(g,y-fixedHypotenuse); + + // Determine which one isn't actually an imaginary number (or the smaller of the two, if both are real), and use that for v. + if (a1 < 0 || a2 < 0) + { + if (a1 < 0 && a2 < 0) + { + //Somehow, v^2 is negative in both cases. v is therefore imaginary and something is horribly wrong. Abort! + return; + } + // Just find which one's NOT negative, and use that + aToUse = max(a1,a2); + } + else + { + // Both are positive; use whichever's smaller so it can decay faster + aToUse = min(a1,a2); + } + v = FixedSqrt(aToUse); + // Okay, so we know the velocity. Let's actually find theta. + // We can cut the "+/- sqrt" part out entirely, since v was calculated specifically for it to equal zero. So: + //theta = tantoangle[FixedDiv(aToUse,FixedMul(g,x)) >> DBITS]; + theta = tantoangle[SlopeDiv(aToUse,FixedMul(g,x))]; + + // Okay, complicated math done. Let's fire our object already, sheesh. + A_FaceTarget(actor); + if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES) + typeOfShot = MT_CANNONBALL; + else typeOfShot = (mobjtype_t)locvar1; + shot = P_SpawnMobj(actor->x, actor->y, actor->z + FixedMul(locvar2*FRACUNIT, actor->scale), typeOfShot); + if (shot->info->seesound) + S_StartSound(shot, shot->info->seesound); + P_SetTarget(&shot->target, actor); // where it came from + + shot->angle = actor->angle; + + // Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle. + shot->momx = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINECOSINE(shot->angle >> ANGLETOFINESHIFT)); + shot->momy = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINESINE(shot->angle >> ANGLETOFINESHIFT)); + // Then the vertical axis. No angle-correction needed here. + shot->momz = FixedMul(v, FINESINE(theta >> ANGLETOFINESHIFT)); + // I hope that's all that's needed, ugh +} + +// Function: A_NapalmScatter +// +// Description: Scatters a specific number of projectiles around in a circle. +// Intended for use with objects that are affected by gravity; would be kind of silly otherwise. +// +// var1: +// Lower 16 bits: object # to lob (TODO: come up with a default) +// Upper 16 bits: Number to lob (default 8) +// var2: +// Lower 16 bits: distance to toss them (No default - 0 does just that - but negatives will revert to 128) +// Upper 16 bits: airtime in tics (default 16) +// +void A_NapalmScatter(mobj_t *actor) +{ + mobjtype_t typeOfShot = var1 & 0x0000FFFF; // Type + INT32 numToShoot = (var1 & 0xFFFF0000) >> 16; // How many + fixed_t distance = (var2 & 0x0000FFFF) << FRACBITS; // How far + fixed_t airtime = var2 & 0xFFFF0000; // How long until impact (assuming no obstacles) + fixed_t vx; // Horizontal momentum + fixed_t vy; // Vertical momentum + fixed_t g; // Gravity + INT32 i; // for-loop cursor + mobj_t *mo; // each and every spawned napalm burst + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_NapalmScatter", actor)) + return; +#endif + + // Some quick sanity-checking + if (typeOfShot >= NUMMOBJTYPES) // I'd add a <0 check, too, but 0x0000FFFF isn't negative in this case + typeOfShot = MT_NULL; + if (numToShoot <= 0) // Presumably you forgot to set var1 up; else, why are you calling this to shoot nothing? + numToShoot = 8; + else if (numToShoot > 8192) // If you seriously need this many objects spawned, stop and ask yourself "Why am I doing this?" + numToShoot = 8192; + if (distance < 0) // Presumably you thought this was an unsigned integer, you naive fool + distance = 32767<subsector->sector->gravity) + g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000))); + else + g = gravity; + + // vy = (g*(airtime-1))/2 + vy = FixedMul(g,(airtime-(1<>1; + // vx = distance/airtime + vx = FixedDiv(distance, airtime); + + for (i = 0; ix, actor->y, actor->z, typeOfShot); + P_SetTarget(&mo->target, actor->target); // Transfer target so Brak doesn't hit himself like an idiot + + mo->angle = fa << ANGLETOFINESHIFT; + mo->momx = FixedMul(FINECOSINE(fa),vx); + mo->momy = FixedMul(FINESINE(fa),vx); + mo->momz = vy; + } +} + +// Function: A_SpawnFreshCopy +// +// Description: Spawns a copy of the mobj. x, y, z, angle, scale, target and tracer carry over; everything else starts anew. +// Mostly writing this because I want to do multiple actions to pass these along in a single frame instead of several. +// +// var1 = unused +// var2 = unused +// +void A_SpawnFreshCopy(mobj_t *actor) +{ + mobj_t *newObject; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SpawnFreshCopy", actor)) + return; +#endif + + newObject = P_SpawnMobjFromMobj(actor, 0, 0, 0, actor->type); + newObject->flags2 = actor->flags2 & MF2_AMBUSH; + newObject->angle = actor->angle; + newObject->color = actor->color; + P_SetTarget(&newObject->target, actor->target); + P_SetTarget(&newObject->tracer, actor->tracer); + + if (newObject->info->seesound) + S_StartSound(newObject, newObject->info->seesound); +} + +// Internal Flicky spawning function. +mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers) +{ + mobj_t *flicky; + + if (!flickytype) + { + if (!mapheaderinfo[gamemap-1] || !mapheaderinfo[gamemap-1]->numFlickies) // No mapheader, no shoes, no service. + return NULL; + else + { + INT32 prandom = P_RandomKey(mapheaderinfo[gamemap-1]->numFlickies); + flickytype = mapheaderinfo[gamemap-1]->flickies[prandom]; + } + } + + flicky = P_SpawnMobjFromMobj(actor, 0, 0, 0, flickytype); + flicky->angle = actor->angle; + + if (flickytype == MT_SEED) + flicky->z += P_MobjFlip(actor)*(actor->height - flicky->height)/2; + + if (actor->eflags & MFE_UNDERWATER) + momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT)); + + P_SetObjectMomZ(flicky, momz, false); + flicky->movedir = (P_RandomChance(FRACUNIT/2) ? -1 : 1); + flicky->fuse = P_RandomRange(595, 700); // originally 300, 350 + flicky->threshold = 0; + + if (lookforplayers) + P_LookForPlayers(flicky, true, false, 0); + + return flicky; +} + +// Function: A_FlickySpawn +// +// Description: Flicky spawning function. +// +// var1: +// lower 16 bits: if 0, spawns random flicky based on level header. Else, spawns the designated thing type. +// upper 16 bits: if 0, no sound is played. Else, A_Scream is called. +// var2 = upwards thrust for spawned flicky. If zero, default value is provided. +// +void A_FlickySpawn(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickySpawn", actor)) + return; +#endif + + if (locvar1 >> 16) { + A_Scream(actor); // A shortcut for the truly lazy. + locvar1 &= 65535; + } + + P_InternalFlickySpawn(actor, locvar1, ((locvar2) ? locvar2 : 8*FRACUNIT), true); +} + +// Internal Flicky color setting +void P_InternalFlickySetColor(mobj_t *actor, UINT8 extrainfo) +{ + UINT8 flickycolors[] = { + SKINCOLOR_RED, + SKINCOLOR_CYAN, + SKINCOLOR_BLUE, + SKINCOLOR_VAPOR, + SKINCOLOR_PURPLE, + SKINCOLOR_BUBBLEGUM, + SKINCOLOR_NEON, + SKINCOLOR_BLACK, + SKINCOLOR_BEIGE, + SKINCOLOR_LAVENDER, + SKINCOLOR_RUBY, + SKINCOLOR_SALMON, + SKINCOLOR_SUNSET, + SKINCOLOR_ORANGE, + SKINCOLOR_YELLOW, + }; + + if (extrainfo == 0) + actor->color = flickycolors[P_RandomKey(sizeof(flickycolors))]; + else if (extrainfo-1 < sizeof(flickycolors)) + actor->color = flickycolors[extrainfo-1]; +} + +// Function: A_FlickyCenter +// +// Description: Place flickies in-level. +// +// var1: +// Lower 16 bits = if 0, spawns random flicky based on level header. Else, spawns the designated thing type. +// Bits 17-20 = Flicky color, up to 15. Applies to fish. +// Bit 21 = Flag MF_SLIDEME (see below) +// Bit 22 = Flag MF_GRENADEBOUNCE (see below) +// Bit 23 = Flag MF_NOCLIPTHING (see below) +// +// If actor is placed from a spawnpoint (map Thing), the Thing's properties take precedence. +// +// var2 = maximum default distance away from spawn the flickies are allowed to travel. If angle != 0, then that's the radius. +// +// If MTF_EXTRA (MF_SLIDEME): is flagged, Flickies move aimlessly. Else, orbit around the target. +// If MTF_OBJECTSPECIAL (MF_GRENADEBOUNCE): Flickies stand in-place without gravity (unless they hop, then gravity is applied.) +// If MTF_AMBUSH (MF_NOCLIPTHING): is flagged, Flickies hop. +// +void A_FlickyCenter(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + UINT16 flickytype = (locvar1 & 0xFFFF); + UINT8 flickycolor = ((locvar1 >> 16) & 0xFF); + UINT8 flickyflags = ((locvar1 >> 20) & 0xF); +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyCenter", actor)) + return; +#endif + + if (!actor->tracer) + { + mobj_t *flicky = P_InternalFlickySpawn(actor, locvar1, 1, false); + P_SetTarget(&flicky->target, actor); + P_SetTarget(&actor->tracer, flicky); + + if (actor->spawnpoint) + { + actor->flags &= ~(MF_SLIDEME|MF_GRENADEBOUNCE|MF_NOCLIPTHING); + actor->flags |= ( + ((actor->spawnpoint->options & MTF_EXTRA) ? MF_SLIDEME : 0) + | ((actor->spawnpoint->options & MTF_OBJECTSPECIAL) ? MF_GRENADEBOUNCE : 0) + | ((actor->spawnpoint->options & MTF_AMBUSH) ? MF_NOCLIPTHING : 0) + ); + actor->extravalue1 = abs(actor->spawnpoint->angle) * FRACUNIT; + actor->extravalue2 = actor->spawnpoint->extrainfo; + actor->friction = actor->spawnpoint->x*FRACUNIT; + actor->movefactor = actor->spawnpoint->y*FRACUNIT; + actor->watertop = actor->spawnpoint->z*FRACUNIT; + } + else + { + actor->flags &= ~(MF_SLIDEME|MF_GRENADEBOUNCE|MF_NOCLIPTHING); + actor->flags |= ( + ((flickyflags & 1) ? MF_SLIDEME : 0) + | ((flickyflags & 2) ? MF_GRENADEBOUNCE : 0) + | ((flickyflags & 4) ? MF_NOCLIPTHING : 0) + ); + actor->extravalue1 = abs(locvar2); + actor->extravalue2 = flickycolor; + actor->friction = actor->x; + actor->movefactor = actor->y; + actor->watertop = actor->z; + locvar1 = flickytype; + } + + if (actor->flags & MF_GRENADEBOUNCE) // in-place + actor->tracer->fuse = 0; + else if (actor->flags & MF_SLIDEME) // aimless + { + actor->tracer->fuse = 0; // less than 2*TICRATE means move aimlessly. + actor->tracer->angle = P_RandomKey(180)*ANG2; + } + else //orbit + { + actor->tracer->fuse = FRACUNIT; + // Impose default home radius if flicky orbits around player + if (!actor->extravalue1) + actor->extravalue1 = 512 * FRACUNIT; + } + + if (locvar1 == MT_FLICKY_08) + P_InternalFlickySetColor(actor->tracer, actor->extravalue2); + + actor->extravalue2 = 0; + } + + if (!(actor->flags & MF_SLIDEME) && !(actor->flags & MF_GRENADEBOUNCE)) + { + fixed_t originx = actor->friction; + fixed_t originy = actor->movefactor; + fixed_t originz = actor->watertop; + + actor->tracer->fuse = FRACUNIT; + + P_LookForPlayers(actor, true, false, locvar2); + + if (actor->target && P_AproxDistance(actor->target->x - originx, actor->target->y - originy) < actor->extravalue1) + { + actor->extravalue2 = 1; + P_TeleportMove(actor, actor->target->x, actor->target->y, actor->target->z); + } + else if(actor->extravalue2) + { + actor->extravalue2 = 0; + P_TeleportMove(actor, originx, originy, originz); + } + } +} + +// Internal Flicky bubbling function. +void P_InternalFlickyBubble(mobj_t *actor) +{ + if (actor->eflags & MFE_UNDERWATER) + { + mobj_t *overlay; + + if (!((actor->z + 3*actor->height/2) < actor->watertop) || !mobjinfo[actor->type].raisestate || actor->tracer) + return; + + overlay = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY); + P_SetMobjStateNF(overlay, mobjinfo[actor->type].raisestate); + P_SetTarget(&actor->tracer, overlay); + P_SetTarget(&overlay->target, actor); + return; + } + + if (!actor->tracer || P_MobjWasRemoved(actor->tracer)) + return; + + P_RemoveMobj(actor->tracer); + P_SetTarget(&actor->tracer, NULL); +} + +// Function: A_FlickyAim +// +// Description: Flicky aiming function. +// +// var1 = how far around the target (in angle constants) the flicky should look +// var2 = distance from target to aim for +// +void A_FlickyAim(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + boolean flickyhitwall = false; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyAim", actor)) + return; +#endif + + if (actor->momx == actor->momy && actor->momy == 0) + flickyhitwall = true; + + P_InternalFlickyBubble(actor); + P_InstaThrust(actor, 0, 0); + + if (!actor->target) + { + P_LookForPlayers(actor, true, false, 0); + actor->angle = P_RandomKey(36)*ANG10; + return; + } + + if (actor->fuse > 2*TICRATE) + { + angle_t posvar; + fixed_t chasevar, chasex, chasey; + + if (flickyhitwall) + actor->movedir *= -1; + + posvar = ((R_PointToAngle2(actor->target->x, actor->target->y, actor->x, actor->y) + actor->movedir*locvar1) >> ANGLETOFINESHIFT) & FINEMASK; + chasevar = FixedSqrt(max(FRACUNIT, P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y) - locvar2)) + locvar2; + + chasex = actor->target->x + FixedMul(FINECOSINE(posvar), chasevar); + chasey = actor->target->y + FixedMul(FINESINE(posvar), chasevar); + + if (P_AproxDistance(chasex - actor->x, chasey - actor->y)) + actor->angle = R_PointToAngle2(actor->x, actor->y, chasex, chasey); + } + else if (flickyhitwall) + { + actor->angle += ANGLE_180; + actor->threshold = 0; + } +} + +//Internal Flicky flying function. Also usuable as an underwater swim thrust. +void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez) +{ + angle_t vertangle; + + flyspeed = FixedMul(flyspeed, actor->scale); + actor->flags |= MF_NOGRAVITY; + + var1 = ANG30; + var2 = 32*FRACUNIT; + A_FlickyAim(actor); + + chasez *= 8; + if (!actor->target || !(actor->fuse > 2*TICRATE)) + chasez += ((actor->eflags & MFE_VERTICALFLIP) ? actor->ceilingz - 24*FRACUNIT : actor->floorz + 24*FRACUNIT); + else + { + fixed_t add = actor->target->z + (actor->target->height - actor->height)/2; + if (add > (actor->ceilingz - 24*actor->scale - actor->height)) + add = actor->ceilingz - 24*actor->scale - actor->height; + else if (add < (actor->floorz + 24*actor->scale)) + add = actor->floorz + 24*actor->scale; + chasez += add; + } + + if (!targetdist) + targetdist = 16*FRACUNIT; //Default! + + if (actor->target && abs(chasez - actor->z) > targetdist) + targetdist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y); + + if (actor->target + && P_IsFlickyCenter(actor->target->type) + && (actor->target->flags & MF_SLIDEME)) + vertangle = 0; + else + vertangle = (R_PointToAngle2(0, actor->z, targetdist, chasez) >> ANGLETOFINESHIFT) & FINEMASK; + + P_InstaThrust(actor, actor->angle, FixedMul(FINECOSINE(vertangle), flyspeed)); + actor->momz = FixedMul(FINESINE(vertangle), flyspeed); +} + +// Function: A_FlickyFly +// +// Description: Flicky flying function. +// +// var1 = how fast to fly +// var2 = how far ahead the target should be considered +// +// If MTF_EXTRA and MTF_OBJECTSPECIAL are flagged, Flicky will always fly at same Z height. +// +void A_FlickyFly(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyFly", actor)) + return; +#endif + P_InternalFlickyFly(actor, locvar1, locvar2, + FINECOSINE((((actor->fuse % 36) * ANG10) >> ANGLETOFINESHIFT) & FINEMASK) + ); +} + +// Function: A_FlickySoar +// +// Description: Flicky soaring function - specific to puffin. +// +// var1 = how fast to fly +// var2 = how far ahead the target should be considered +// +// If MTF_EXTRA and MTF_OBJECTSPECIAL are flagged, Flicky will always fly at same Z height. +// +void A_FlickySoar(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickySoar", actor)) + return; +#endif + P_InternalFlickyFly(actor, locvar1, locvar2, + 2*(FRACUNIT/2 - abs(FINECOSINE((((actor->fuse % 144) * 5*ANG1/2) >> ANGLETOFINESHIFT) & FINEMASK))) + ); + + if (P_MobjFlip(actor)*actor->momz > 0 && actor->frame == 1 && actor->sprite == SPR_FL10) + actor->frame = 3; +} + +//Function: A_FlickyCoast +// +// Description: Flicky swim-coasting function. +// +// var1 = speed to change state upon reaching +// var2 = state to change to upon slowing down +// the spawnstate of the mobj = state to change to when above water +// +void A_FlickyCoast(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyCoast", actor)) + return; +#endif + if (actor->eflags & MFE_UNDERWATER) + { + actor->momx = (11*actor->momx)/12; + actor->momy = (11*actor->momy)/12; + actor->momz = (11*actor->momz)/12; + + if (P_AproxDistance(P_AproxDistance(actor->momx, actor->momy), actor->momz) < locvar1) + P_SetMobjState(actor, locvar2); + + return; + } + + actor->flags &= ~MF_NOGRAVITY; + P_SetMobjState(actor, mobjinfo[actor->type].spawnstate); +} + +// Internal Flicky hopping function. +void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle) +{ + if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz) + || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz))) + { + if (momz) + { + if (actor->eflags & MFE_UNDERWATER) + momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT)); + P_SetObjectMomZ(actor, momz, false); + } + P_InstaThrust(actor, angle, FixedMul(momh, actor->scale)); + } +} + +// Function: A_FlickyHop +// +// Description: Flicky hopping function. +// +// var1 = vertical thrust +// var2 = horizontal thrust +// +void A_FlickyHop(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyHop", actor)) + return; +#endif + P_InternalFlickyHop(actor, locvar1, locvar2, actor->angle); +} + +// Function: A_FlickyFlounder +// +// Description: Flicky floundering function. +// +// var1 = intended vertical thrust +// var2 = intended horizontal thrust +// +void A_FlickyFlounder(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + angle_t hopangle; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyFlounder", actor)) + return; +#endif + locvar1 *= (P_RandomKey(2) + 1); + locvar2 *= (P_RandomKey(2) + 1); + hopangle = (actor->angle + (P_RandomKey(9) - 4)*ANG2); + P_InternalFlickyHop(actor, locvar1, locvar2, hopangle); +} + +// Function: A_FlickyCheck +// +// Description: Flicky airtime check function. +// +// var1 = state to change to upon touching the floor +// var2 = state to change to upon falling +// the meleestate of the mobj = state to change to when underwater +// +void A_FlickyCheck(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyCheck", actor)) + return; +#endif + if (actor->target + && P_IsFlickyCenter(actor->target->type) + && (actor->target->flags & MF_GRENADEBOUNCE)) + { + if (!(actor->target->flags & MF_NOCLIPTHING)) // no hopping + { + actor->momz = 0; + actor->flags |= MF_NOGRAVITY; + } + actor->flags |= MF_NOCLIP | MF_NOBLOCKMAP | MF_SCENERY; + P_SetMobjState(actor, mobjinfo[actor->type].seestate); + } + else if (locvar2 && P_MobjFlip(actor)*actor->momz < 1) + P_SetMobjState(actor, locvar2); + else if (locvar1 && ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz) + || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz))) + P_SetMobjState(actor, locvar1); + else if (mobjinfo[actor->type].meleestate && (actor->eflags & MFE_UNDERWATER)) + P_SetMobjState(actor, mobjinfo[actor->type].meleestate); + P_InternalFlickyBubble(actor); +} + +// Function: A_FlickyHeightCheck +// +// Description: Flicky height check function. +// +// var1 = state to change to when falling below height relative to target +// var2 = height relative to target to change state at +// +void A_FlickyHeightCheck(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyHeightCheck", actor)) + return; +#endif + if (actor->target + && P_IsFlickyCenter(actor->target->type) + && (actor->target->flags & MF_GRENADEBOUNCE)) + { + if (!(actor->target->flags & MF_NOCLIPTHING)) // no hopping + { + actor->momz = 0; + actor->flags |= MF_NOGRAVITY; + } + actor->flags |= MF_NOCLIP | MF_NOBLOCKMAP | MF_SCENERY; + P_SetMobjState(actor, mobjinfo[actor->type].seestate); + } + else if (locvar1 && actor->target && P_MobjFlip(actor)*actor->momz < 1 + && ((P_MobjFlip(actor)*((actor->z + actor->height/2) - (actor->target->z + actor->target->height/2)) < locvar2) + || (actor->z - actor->height < actor->floorz) || (actor->z + 2*actor->height > actor->ceilingz))) + P_SetMobjState(actor, locvar1); + P_InternalFlickyBubble(actor); +} + +// Function: A_FlickyFlutter +// +// Description: Flicky fluttering function - specific to chicken. +// +// var1 = state to change to upon touching the floor +// var2 = state to change to upon falling +// the meleestate of the mobj = state to change to when underwater +// +void A_FlickyFlutter(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlickyFlutter", actor)) + return; +#endif + var1 = locvar1; + var2 = locvar2; + A_FlickyCheck(actor); + + var1 = ANG30; + var2 = 32*FRACUNIT; + A_FlickyAim(actor); + + P_InstaThrust(actor, actor->angle, 2*actor->scale); + if (P_MobjFlip(actor)*actor->momz < -FRACUNIT/2) + actor->momz = -P_MobjFlip(actor)*actor->scale/2; +} + +#undef FLICKYHITWALL + +// Function: A_FlameParticle +// +// Description: Creates the mobj's painchance at a random position around the object's radius. +// +// var1 = unused +// var2 = unused +// +void A_FlameParticle(mobj_t *actor) +{ + mobjtype_t type = (mobjtype_t)(mobjinfo[actor->type].painchance); + fixed_t rad, hei; + mobj_t *particle; + //INT32 locvar1 = var1; + //INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlameParticle", actor)) + return; +#endif + + if (!type) + return; + + rad = actor->radius>>FRACBITS; + hei = actor->height>>FRACBITS; + particle = P_SpawnMobjFromMobj(actor, + P_RandomRange(rad, -rad)<frame = actor->frame; + + if (!(locvar1 & 1)) + { + fade->fuse = 15; + fade->flags2 |= MF2_BOSSNOTRAP; + } + else + fade->fuse = 20; + + if (!(locvar1 & 2)) + P_SetTarget(&actor->tracer, fade); +} + +// Function: A_Boss5Jump +// +// Description: Makes an object jump in an arc to land on their tracer precicely. +// Adapted from A_BrakLobShot, see there for explanation. +// +// var1 = unused +// var2 = unused +// +void A_Boss5Jump(mobj_t *actor) +{ + fixed_t v; // Velocity to jump at + fixed_t a1, a2, aToUse; // Velocity squared + fixed_t g; // Gravity + fixed_t x; // Horizontal difference + INT32 x_int; // x! But in integer form! + fixed_t y; // Vertical difference (yes that's normally z in SRB2 shut up) + INT32 y_int; // y! But in integer form! + INT32 intHypotenuse; // x^2 + y^2. Frequently overflows fixed point, hence why we need integers proper. + fixed_t fixedHypotenuse; // However, we can work around that and still get a fixed-point number. + angle_t theta; // Angle of attack + // INT32 locvar1 = var1; + // INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Boss5Jump", actor)) + return; +#endif + + if (!actor->tracer) + return; // Don't even bother if we've got nothing to aim at. + + // Look up actor's current gravity situation + if (actor->subsector->sector->gravity) + g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000))); + else + g = gravity; + + // Look up distance between actor and its tracer + x = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y); + // Look up height difference between actor and its tracer + y = actor->tracer->z - actor->z; + + // Get x^2 + y^2. Have to do it in a roundabout manner, because this overflows fixed_t way too easily otherwise. + x_int = x>>FRACBITS; + y_int = y>>FRACBITS; + intHypotenuse = (x_int*x_int) + (y_int*y_int); + fixedHypotenuse = FixedSqrt(intHypotenuse) *256; + + // a = g(y+/-sqrt(x^2+y^2)). a1 can be +, a2 can be -. + a1 = FixedMul(g,y+fixedHypotenuse); + a2 = FixedMul(g,y-fixedHypotenuse); + + // Determine which one isn't actually an imaginary number (or the smaller of the two, if both are real), and use that for v. + if (a1 < 0 || a2 < 0) + { + if (a1 < 0 && a2 < 0) + { + //Somehow, v^2 is negative in both cases. v is therefore imaginary and something is horribly wrong. Abort! + return; + } + // Just find which one's NOT negative, and use that + aToUse = max(a1,a2); + } + else + { + // Both are positive; use whichever's smaller so it can decay faster + aToUse = min(a1,a2); + } + v = FixedSqrt(aToUse); + // Okay, so we know the velocity. Let's actually find theta. + // We can cut the "+/- sqrt" part out entirely, since v was calculated specifically for it to equal zero. So: + //theta = tantoangle[FixedDiv(aToUse,FixedMul(g,x)) >> DBITS]; + theta = tantoangle[SlopeDiv(aToUse,FixedMul(g,x))]; + + // Okay, complicated math done. Let's make this object jump already. + A_FaceTracer(actor); + + if (actor->eflags & MFE_VERTICALFLIP) + actor->z--; + else + actor->z++; + + // Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle. + fixedHypotenuse = FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)); // variable reuse + actor->momx = FixedMul(fixedHypotenuse, FINECOSINE(actor->angle >> ANGLETOFINESHIFT)); + actor->momy = FixedMul(fixedHypotenuse, FINESINE(actor->angle >> ANGLETOFINESHIFT)); + // Then the vertical axis. No angle-correction needed here. + actor->momz = FixedMul(v, FINESINE(theta >> ANGLETOFINESHIFT)); + // I hope that's all that's needed, ugh +} + +// Function: A_LightBeamReset +// Description: Resets momentum and position for DSZ's projecting light beams +// +// var1 = unused +// var2 = unused +// +void A_LightBeamReset(mobj_t *actor) +{ + // INT32 locvar1 = var1; + // INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_LightBeamReset", actor)) + return; +#endif + + actor->destscale = FRACUNIT + P_SignedRandom()*FRACUNIT/256; + P_SetScale(actor, actor->destscale); + + if (!actor->spawnpoint) + return; // this can't work properly welp + + actor->momx = -(P_SignedRandom()*FINESINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/128; + actor->momy = (P_SignedRandom()*FINECOSINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/128; + actor->momz = (P_SignedRandom()*FRACUNIT)/128; + + P_TeleportMove(actor, + actor->spawnpoint->x*FRACUNIT - (P_SignedRandom()*FINESINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/2, + actor->spawnpoint->y*FRACUNIT + (P_SignedRandom()*FINECOSINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/2, + actor->spawnpoint->z*FRACUNIT + (P_SignedRandom()*FRACUNIT)/2); +} + +// Function: A_MineExplode +// Description: Handles the explosion of a DSZ mine. +// +// var1 = unused +// var2 = unused +// +void A_MineExplode(mobj_t *actor) +{ + // INT32 locvar1 = var1; + // INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_MineExplode", actor)) + return; +#endif + + A_Scream(actor); + actor->flags = MF_NOGRAVITY|MF_NOCLIP; + + quake.epicenter = NULL; + quake.radius = 512*FRACUNIT; + quake.intensity = 8*FRACUNIT; + quake.time = TICRATE/3; + + P_RadiusAttack(actor, actor->tracer, 192*FRACUNIT, DMG_CANHURTSELF); + P_MobjCheckWater(actor); + + { +#define dist 64 + UINT8 i; + mobjtype_t type = ((actor->eflags & MFE_UNDERWATER) ? MT_UWEXPLODE : MT_BOSSEXPLODE); + S_StartSound(actor, ((actor->eflags & MFE_UNDERWATER) ? sfx_s3k57 : sfx_s3k4e)); + P_SpawnMobj(actor->x, actor->y, actor->z, type); + for (i = 0; i < 16; i++) + { + mobj_t *b = P_SpawnMobj(actor->x+P_RandomRange(-dist, dist)*FRACUNIT, + actor->y+P_RandomRange(-dist, dist)*FRACUNIT, + actor->z+P_RandomRange(((actor->eflags & MFE_UNDERWATER) ? -dist : 0), dist)*FRACUNIT, + type); + fixed_t dx = b->x - actor->x, dy = b->y - actor->y, dz = b->z - actor->z; + fixed_t dm = P_AproxDistance(dz, P_AproxDistance(dy, dx)); + b->momx = FixedDiv(dx, dm)*3; + b->momy = FixedDiv(dy, dm)*3; + b->momz = FixedDiv(dz, dm)*3; + if ((actor->watertop == INT32_MAX) || (b->z + b->height > actor->watertop)) + b->flags &= ~MF_NOGRAVITY; + } +#undef dist + + if (actor->watertop != INT32_MAX) + P_SpawnMobj(actor->x, actor->y, actor->watertop, MT_SPLISH); + } +} + +// Function: A_MineRange +// Description: If the target gets too close, change the state to meleestate. +// +// var1 = Distance to alert at +// var2 = unused +// +void A_MineRange(mobj_t *actor) +{ + fixed_t dm; + INT32 locvar1 = var1; + // INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_MineRange", actor)) + return; +#endif + + if (!actor->target) + return; + + dm = P_AproxDistance(actor->z - actor->target->z, P_AproxDistance(actor->y - actor->target->y, actor->x - actor->target->x)); + if ((dm>>FRACBITS) < locvar1) + P_SetMobjState(actor, actor->info->meleestate); +} + +// Function: A_ConnectToGround +// Description: Create a palm tree trunk/mine chain. +// +// var1 = Object type to connect to ground +// var2 = Object type to place on ground +// +void A_ConnectToGround(mobj_t *actor) +{ + mobj_t *work; + fixed_t workz; + fixed_t workh; + SINT8 dir; + angle_t ang; + INT32 locvar1 = var1; + INT32 locvar2 = var2; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_ConnectToGround", actor)) + return; +#endif + + if (actor->subsector->sector->ffloors) + P_AdjustMobjFloorZ_FFloors(actor, actor->subsector->sector, 2); + + if (actor->flags2 & MF2_OBJECTFLIP) + { + workz = actor->ceilingz - (actor->z + actor->height); + dir = -1; + } + else + { + workz = actor->floorz - actor->z; + dir = 1; + } + + if (locvar2) + { + workh = FixedMul(mobjinfo[locvar2].height, actor->scale); + if (actor->flags2 & MF2_OBJECTFLIP) + workz -= workh; + work = P_SpawnMobjFromMobj(actor, 0, 0, workz, locvar2); + workz += dir*workh; + } + + if (!locvar1) + return; + + if (!(workh = FixedMul(mobjinfo[locvar1].height, actor->scale))) + return; + + if (actor->flags2 & MF2_OBJECTFLIP) + workz -= workh; + + ang = actor->angle + ANGLE_45; + while (dir*workz < 0) + { + work = P_SpawnMobjFromMobj(actor, 0, 0, workz, locvar1); + if (work) + work->angle = ang; + ang += ANGLE_90; + workz += dir*workh; + } + + if (workz != 0) + actor->z += workz; +} + +// Function: A_SpawnParticleRelative +// +// Description: Spawns a particle effect relative to the location of the actor +// +// var1: +// var1 >> 16 = x +// var1 & 65535 = y +// var2: +// var2 >> 16 = z +// var2 & 65535 = state +// +void A_SpawnParticleRelative(mobj_t *actor) +{ + INT16 x, y, z; // Want to be sure we can use negative values + statenum_t state; + mobj_t *mo; + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SpawnParticleRelative", actor)) + return; +#endif + + CONS_Debug(DBG_GAMELOGIC, "A_SpawnParticleRelative called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); + + x = (INT16)(locvar1>>16); + y = (INT16)(locvar1&65535); + z = (INT16)(locvar2>>16); + state = (statenum_t)(locvar2&65535); + + // Spawn objects correctly in reverse gravity. + // NOTE: Doing actor->z + actor->height is the bottom of the object while the object has reverse gravity. - Flame + mo = P_SpawnMobj(actor->x + FixedMul(x<scale), + actor->y + FixedMul(y<scale), + (actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[MT_PARTICLE].height) - FixedMul(z<scale)) : (actor->z + FixedMul(z<scale)), MT_PARTICLE); + + // Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn + mo->angle = actor->angle; + + if (actor->eflags & MFE_VERTICALFLIP) + mo->flags2 |= MF2_OBJECTFLIP; + + P_SetMobjState(mo, state); +} + +// Function: A_MultiShotDist +// +// Description: Spawns multiple shots based on player proximity +// +// var1 = same as A_MultiShot +// var2 = same as A_MultiShot +// +void A_MultiShotDist(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_MultiShotDist", actor)) + return; +#endif + + { + UINT8 i; + // Quick! Look through players! + // Don't spawn dust unless a player is relatively close by (var1). + for (i = 0; i < MAXPLAYERS; ++i) + if (playeringame[i] && players[i].mo + && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (1600<> 16 = mobjtype of child +// var2 & 65535 = vertical momentum +// var2: +// var2 >> 16 = forward offset +// var2 & 65535 = vertical offset +// +void A_WhoCaresIfYourSonIsABee(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; + fixed_t foffsetx; + fixed_t foffsety; + mobj_t *son; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_WhoCaresIfYourSonIsABee", actor)) + return; +#endif + + A_FaceTarget(actor); + + if (actor->extravalue1) + actor->extravalue1--; + + if (actor->info->attacksound) + S_StartSound(actor, actor->info->attacksound); + + foffsetx = P_ReturnThrustX(actor, actor->angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale)); + foffsety = P_ReturnThrustY(actor, actor->angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale)); + + if (!(son = P_SpawnMobjFromMobj(actor, foffsetx, foffsety, (locvar2&65535)*FRACUNIT, (mobjtype_t)(locvar1 >> 16)))) + return; + + P_SetObjectMomZ(son, (locvar1 & 65535)<tracer, actor); + P_SetTarget(&son->target, actor->target); +} + +// Function: A_ParentTriesToSleep +// +// Description: If extravalue1 is less than or equal to var1, go to var2. +// +// var1 = state to go to when extravalue1 +// var2 = unused +// +void A_ParentTriesToSleep(mobj_t *actor) +{ + INT32 locvar1 = var1; + //INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_ParentTriesToSleep", actor)) + return; +#endif + + if (actor->extravalue1) + { + if (actor->info->seesound) + S_StartSound(actor, actor->info->seesound); + actor->reactiontime = 0; + P_SetMobjState(actor, locvar1); + } + else if (!actor->reactiontime) + { + actor->reactiontime = 1; + if (actor->info->activesound) // more like INactivesound doy hoy hoy + S_StartSound(actor, actor->info->activesound); + } +} + + +// Function: A_CryingToMomma +// +// Description: If you're a child, let your parent know something's happened to you through extravalue1. Also, prepare to die. +// +// var1 = unused +// var2 = unused +// +void A_CryingToMomma(mobj_t *actor) +{ + //INT32 locvar1 = var1; + //INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CryingToMomma", actor)) + return; +#endif + + if (actor->tracer) + actor->tracer->extravalue1++; + + actor->momx = actor->momy = actor->momz = 0; + + P_UnsetThingPosition(actor); + if (sector_list) + { + P_DelSeclist(sector_list); + sector_list = NULL; + } + actor->flags = MF_NOBLOCKMAP|MF_NOCLIPTHING; + P_SetThingPosition(actor); +} + +// Function: A_CheckFlags2 +// +// Description: If actor->flags2 & var1, goto var2. +// +// var1 = mask +// var2 = state to go +// +void A_CheckFlags2(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_CheckFlags2", actor)) + return; +#endif + + if (actor->flags2 & locvar1) + P_SetMobjState(actor, (statenum_t)locvar2); +}