diff --git a/bin/Mingw/Release/.gitignore b/bin/Mingw/Release/.gitignore index 834f313e3..3458ff764 100644 --- a/bin/Mingw/Release/.gitignore +++ b/bin/Mingw/Release/.gitignore @@ -1,3 +1,4 @@ *.exe *.mo r_opengl.dll +*.bat diff --git a/src/Makefile b/src/Makefile index 322e67bfe..f2d532cd5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,3 +1,4 @@ + # GNU Make makefile for SRB2 ############################################################################# # Copyright (C) 1998-2000 by DooM Legacy Team. @@ -357,7 +358,8 @@ endif ifdef PROFILEMODE # build with profiling information - CFLAGS:=-pg $(CFLAGS) + CFLAGS+=-pg + LDFLAGS+=-pg endif ifdef ZDEBUG diff --git a/src/b_bot.c b/src/b_bot.c index 543bcb183..5f884896f 100644 --- a/src/b_bot.c +++ b/src/b_bot.c @@ -275,8 +275,7 @@ void B_RespawnBot(INT32 playernum) player->accelstart = sonic->player->accelstart; player->thrustfactor = sonic->player->thrustfactor; player->normalspeed = sonic->player->normalspeed; - player->pflags |= PF_AUTOBRAKE; - player->pflags &= ~PF_DIRECTIONCHAR; + player->pflags |= PF_AUTOBRAKE|(sonic->player->pflags & PF_DIRECTIONCHAR); P_TeleportMove(tails, x, y, z); if (player->charability == CA_FLY) diff --git a/src/config.h.in b/src/config.h.in index 7c9ebe6cb..174b34430 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -16,7 +16,7 @@ #define ASSET_HASH_RINGS_DTA "${SRB2_ASSET_rings.dta_HASH}" #define ASSET_HASH_ZONES_DTA "${SRB2_ASSET_zones.dta_HASH}" #ifdef USE_PATCH_DTA -#define ASSET_HASH_PATCH_DTA "${SRB2_ASSET_patch.dta_HASH}" +#define ASSET_HASH_PATCH_PK3 "${SRB2_ASSET_patch.pk3_HASH}" #endif #define SRB2_COMP_REVISION "${SRB2_COMP_REVISION}" @@ -36,7 +36,7 @@ #define ASSET_HASH_PLAYER_DTA "cfca0f1c73023cbbd8f844f45480f799" #define ASSET_HASH_RINGS_DTA "85901ad4bf94637e5753d2ac2c03ea26" #ifdef USE_PATCH_DTA -#define ASSET_HASH_PATCH_DTA "dbbf8bc6121618ee3be2d5b14650429b" +#define ASSET_HASH_PATCH_PK3 "dbbf8bc6121618ee3be2d5b14650429b" #endif #endif diff --git a/src/console.c b/src/console.c index b785aa18a..9bc01cf19 100644 --- a/src/console.c +++ b/src/console.c @@ -312,7 +312,7 @@ static void CON_SetupColormaps(void) colset(lgreenmap, 97, 98, 106); colset(bluemap, 146, 147, 155); colset(redmap, 210, 32, 39); - colset(graymap, 8, 10, 15); + colset(graymap, 6, 8, 14); colset(orangemap, 51, 52, 57); colset(skymap, 129, 130, 133); colset(purplemap, 160, 161, 163); diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 5832b2c27..92da2492e 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -395,8 +395,7 @@ static void ExtraDataTicker(void) DEBFILE(va("player %d kicked [gametic=%u] reason as follows:\n", i, gametic)); } CONS_Alert(CONS_WARNING, M_GetText("Got unknown net command [%s]=%d (max %d)\n"), sizeu1(curpos - bufferstart), *curpos, bufferstart[0]); - D_FreeTextcmd(gametic); - return; + break; } } } @@ -513,7 +512,8 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->powers[j] = (UINT16)SHORT(players[i].powers[j]); // Score is resynched in the rspfirm resync packet - rsp->rings = LONG(players[i].rings); + rsp->rings = SHORT(players[i].rings); + rsp->spheres = SHORT(players[i].spheres); rsp->lives = players[i].lives; rsp->continues = players[i].continues; rsp->scoreadd = players[i].scoreadd; @@ -643,7 +643,8 @@ static void resynch_read_player(resynch_pak *rsp) players[i].powers[j] = (UINT16)SHORT(rsp->powers[j]); // Score is resynched in the rspfirm resync packet - players[i].rings = LONG(rsp->rings); + players[i].rings = SHORT(rsp->rings); + players[i].spheres = SHORT(rsp->spheres); players[i].lives = rsp->lives; players[i].continues = rsp->continues; players[i].scoreadd = rsp->scoreadd; @@ -2254,7 +2255,7 @@ static void Command_connect(void) // Assume we connect directly. boolean viams = false; - if (COM_Argc() < 2) + if (COM_Argc() < 2 || *COM_Argv(1) == 0) { CONS_Printf(M_GetText( "Connect (port): connect to a server\n" @@ -2377,11 +2378,11 @@ static void CL_RemovePlayer(INT32 playernum) if (gametype == GT_CTF) P_PlayerFlagBurst(&players[playernum], false); // Don't take the flag with you! - // If in a special stage, redistribute the player's rings across + // If in a special stage, redistribute the player's spheres across // the remaining players. if (G_IsSpecialStage(gamemap)) { - INT32 i, count, increment, rings; + INT32 i, count, increment, spheres; for (i = 0, count = 0; i < MAXPLAYERS; i++) { @@ -2390,19 +2391,19 @@ static void CL_RemovePlayer(INT32 playernum) } count--; - rings = players[playernum].rings; - increment = rings/count; + spheres = players[playernum].spheres; + increment = spheres/count; for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] && i != playernum) { - if (rings < increment) - P_GivePlayerRings(&players[i], rings); + if (spheres < increment) + P_GivePlayerSpheres(&players[i], spheres); else - P_GivePlayerRings(&players[i], increment); + P_GivePlayerSpheres(&players[i], increment); - rings -= increment; + spheres -= increment; } } } @@ -3326,7 +3327,7 @@ void SV_StopServer(void) localtextcmd[0] = 0; localtextcmd2[0] = 0; - for (i = 0; i < BACKUPTICS; i++) + for (i = firstticstosend; i < firstticstosend + BACKUPTICS; i++) D_Clearticcmd(i); consoleplayer = 0; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index bdf332665..bdb85a76c 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -164,7 +164,8 @@ typedef struct UINT16 powers[NUMPOWERS]; // Score is resynched in the confirm resync packet - INT32 rings; + INT16 rings; + INT16 spheres; SINT8 lives; SINT8 continues; UINT8 scoreadd; diff --git a/src/d_main.c b/src/d_main.c index 05aa3e675..95af1f754 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -734,11 +734,6 @@ void D_StartTitle(void) CON_ToggleOff(); // Reset the palette -#ifdef HWRENDER - if (rendermode == render_opengl) - HWR_SetPaletteColor(0); - else -#endif if (rendermode != render_none) V_SetPaletteLump("PLAYPAL"); } @@ -843,7 +838,7 @@ static void IdentifyVersion(void) #ifdef USE_PATCH_DTA // Add our crappy patches to fix our bugs - D_AddFile(va(pandf,srb2waddir,"patch.dta")); + D_AddFile(va(pandf,srb2waddir,"patch.pk3")); #endif #if !defined (HAVE_SDL) || defined (HAVE_MIXER) @@ -1037,19 +1032,10 @@ void D_SRB2Main(void) if (M_CheckParm("-password") && M_IsNextParm()) D_SetPassword(M_GetNextParm()); - else - { - size_t z; - char junkpw[25]; - for (z = 0; z < 24; z++) - junkpw[z] = (char)(rand() & 64)+32; - junkpw[24] = '\0'; - D_SetPassword(junkpw); - } // add any files specified on the command line with -file wadfile // to the wad list - if (!(M_CheckParm("-connect"))) + if (!(M_CheckParm("-connect") && !M_CheckParm("-server"))) { if (M_CheckParm("-file")) { @@ -1129,7 +1115,7 @@ void D_SRB2Main(void) //W_VerifyFileMD5(1, ASSET_HASH_ZONES_DTA); // zones.dta //W_VerifyFileMD5(2, ASSET_HASH_PLAYER_DTA); // player.dta #ifdef USE_PATCH_DTA - W_VerifyFileMD5(3, ASSET_HASH_PATCH_DTA); // patch.dta + //W_VerifyFileMD5(3, ASSET_HASH_PATCH_PK3); // patch.pk3 #endif // don't check music.dta because people like to modify it, and it doesn't matter if they do @@ -1138,7 +1124,7 @@ void D_SRB2Main(void) mainwads = 3; // there are 3 wads not to unload #ifdef USE_PATCH_DTA - ++mainwads; // patch.dta adds one more + ++mainwads; // patch.pk3 adds one more #endif #ifdef DEVELOP ++mainwads; // music_new, too @@ -1204,7 +1190,15 @@ void D_SRB2Main(void) R_Init(); // setting up sound - CONS_Printf("S_Init(): Setting up sound.\n"); + if (dedicated) + { + nosound = true; + nomidimusic = nodigimusic = true; + } + else + { + CONS_Printf("S_Init(): Setting up sound.\n"); + } if (M_CheckParm("-nosound")) nosound = true; if (M_CheckParm("-nomusic")) // combines -nomidimusic and -nodigmusic @@ -1316,7 +1310,7 @@ void D_SRB2Main(void) } } - if (autostart || netgame || M_CheckParm("+connect") || M_CheckParm("-connect")) + if (autostart || netgame) { gameaction = ga_nothing; @@ -1350,8 +1344,7 @@ void D_SRB2Main(void) } } - if (server && !M_CheckParm("+map") && !M_CheckParm("+connect") - && !M_CheckParm("-connect")) + if (server && !M_CheckParm("+map")) { // Prevent warping to nonexistent levels if (W_CheckNumForName(G_BuildMapName(pstartmap)) == LUMPERROR) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index e01178155..f3fb1f6ae 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -309,8 +309,8 @@ consvar_t cv_overtime = {"overtime", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, consvar_t cv_rollingdemos = {"rollingdemos", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -static CV_PossibleValue_t timetic_cons_t[] = {{0, "Normal"}, {1, "Centiseconds"}, {2, "Mania"}, {3, "Tics"}, {0, NULL}}; -consvar_t cv_timetic = {"timerres", "Normal", CV_SAVE, timetic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // use tics in display +static CV_PossibleValue_t timetic_cons_t[] = {{0, "Classic"}, {1, "Centiseconds"}, {2, "Mania"}, {3, "Tics"}, {0, NULL}}; +consvar_t cv_timetic = {"timerres", "Classic", CV_SAVE, timetic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t powerupdisplay_cons_t[] = {{0, "Never"}, {1, "First-person only"}, {2, "Always"}, {0, NULL}}; consvar_t cv_powerupdisplay = {"powerupdisplay", "First-person only", CV_SAVE, powerupdisplay_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -2682,8 +2682,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) // Clear player score and rings if a spectator. if (players[playernum].spectator) { - players[playernum].score = 0; - players[playernum].rings = 0; + players[playernum].score = players[playernum].rings = 0; if (players[playernum].mo) players[playernum].mo->health = 1; } @@ -2725,10 +2724,12 @@ static void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, #define BASESALT "basepasswordstorage" static UINT8 adminpassmd5[16]; +static boolean adminpasswordset = false; void D_SetPassword(const char *pw) { D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &adminpassmd5); + adminpasswordset = true; } // Remote Administration @@ -2797,6 +2798,12 @@ static void Got_Login(UINT8 **cp, INT32 playernum) READMEM(*cp, sentmd5, 16); + if (!adminpasswordset) + { + CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[playernum]); + return; + } + if (client) return; @@ -3930,28 +3937,7 @@ static void Command_ExitLevel_f(void) else if (gamestate != GS_LEVEL || demoplayback) CONS_Printf(M_GetText("You must be in a level to use this.\n")); else - { - if ((netgame || multiplayer) - && ((mapheaderinfo[gamemap-1]->nextlevel <= 0) - || (mapheaderinfo[gamemap-1]->nextlevel > NUMMAPS) - || !(mapvisited[mapheaderinfo[gamemap-1]->nextlevel-1]))) - { - UINT8 i; - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && players[i].exiting) - break; - } - - if (i == MAXPLAYERS) - { - CONS_Printf(M_GetText("Someone must finish the level for you to use this.\n")); - return; - } - } - SendNetXCmd(XD_EXITLEVEL, NULL, 0); - } } static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum) @@ -4066,7 +4052,7 @@ static void Command_RestartAudio_f(void) I_ShutdownSound(); I_StartupSound(); I_InitMusic(); - + // These must be called or no sound and music until manually set. I_SetSfxVolume(cv_soundvolume.value); @@ -4074,7 +4060,7 @@ static void Command_RestartAudio_f(void) I_SetMIDIMusicVolume(cv_midimusicvolume.value); if (Playing()) // Gotta make sure the player is in a level P_RestoreMusic(&players[consoleplayer]); - + } /** Quits a game and returns to the title screen. diff --git a/src/d_netfil.c b/src/d_netfil.c index 614d2064e..889de9466 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -950,15 +950,37 @@ filestatus_t checkfilemd5(char *filename, const UINT8 *wantedmd5sum) return FS_FOUND; // will never happen, but makes the compiler shut up } +// Rewritten by Monster Iestyn to be less stupid +// Note: if completepath is true, "filename" is modified, but only if FS_FOUND is going to be returned +// (Don't worry about WinCE's version of filesearch, nobody cares about that OS anymore) filestatus_t findfile(char *filename, const UINT8 *wantedmd5sum, boolean completepath) { - filestatus_t homecheck = filesearch(filename, srb2home, wantedmd5sum, false, 10); - if (homecheck == FS_FOUND) - return filesearch(filename, srb2home, wantedmd5sum, completepath, 10); + filestatus_t homecheck; // store result of last file search + boolean badmd5 = false; // store whether md5 was bad from either of the first two searches (if nothing was found in the third) - homecheck = filesearch(filename, srb2path, wantedmd5sum, false, 10); - if (homecheck == FS_FOUND) - return filesearch(filename, srb2path, wantedmd5sum, completepath, 10); + // first, check SRB2's "home" directory + homecheck = filesearch(filename, srb2home, wantedmd5sum, completepath, 10); - return filesearch(filename, ".", wantedmd5sum, completepath, 10); + if (homecheck == FS_FOUND) // we found the file, so return that we have :) + return FS_FOUND; + else if (homecheck == FS_MD5SUMBAD) // file has a bad md5; move on and look for a file with the right md5 + badmd5 = true; + // if not found at all, just move on without doing anything + + // next, check SRB2's "path" directory + homecheck = filesearch(filename, srb2path, wantedmd5sum, completepath, 10); + + if (homecheck == FS_FOUND) // we found the file, so return that we have :) + return FS_FOUND; + else if (homecheck == FS_MD5SUMBAD) // file has a bad md5; move on and look for a file with the right md5 + badmd5 = true; + // if not found at all, just move on without doing anything + + // finally check "." directory + homecheck = filesearch(filename, ".", wantedmd5sum, completepath, 10); + + if (homecheck != FS_NOTFOUND) // if not found this time, fall back on the below return statement + return homecheck; // otherwise return the result we got + + return (badmd5 ? FS_MD5SUMBAD : FS_NOTFOUND); // md5 sum bad or file not found } diff --git a/src/d_player.h b/src/d_player.h index f919eddb0..159b8499c 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -324,7 +324,8 @@ typedef struct player_s angle_t drawangle; // player's ring count - INT32 rings; + INT16 rings; + INT16 spheres; SINT8 pity; // i pity the fool. INT32 currentweapon; // current weapon selected. @@ -462,7 +463,7 @@ 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) - INT16 finishedrings; // The rings you had left upon finishing the mare + INT16 finishedspheres; // The spheres you had left upon finishing the mare UINT32 marescore; // score for this nights stage UINT32 lastmarescore; // score for the last mare UINT8 lastmare; // previous mare diff --git a/src/dehacked.c b/src/dehacked.c index a7ba72a6c..c4d0bc104 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -853,25 +853,27 @@ static const struct { const char *name; const mobjtype_t type; } FLICKYTYPES[] = { - {"BLUEBIRD", MT_FLICKY_01}, - {"RABBIT", MT_FLICKY_02}, - {"CHICKEN", MT_FLICKY_03}, - {"SEAL", MT_FLICKY_04}, - {"PIG", MT_FLICKY_05}, - {"CHIPMUNK", MT_FLICKY_06}, - {"PENGUIN", MT_FLICKY_07}, - {"FISH", MT_FLICKY_08}, - {"RAM", MT_FLICKY_09}, - {"PUFFIN", MT_FLICKY_10}, - {"COW", MT_FLICKY_11}, - {"RAT", MT_FLICKY_12}, - {"BEAR", MT_FLICKY_13}, - {"DOVE", MT_FLICKY_14}, - {"CAT", MT_FLICKY_15}, - {"CANARY", MT_FLICKY_16}, + {"BLUEBIRD", MT_FLICKY_01}, // Flicky (Flicky) + {"RABBIT", MT_FLICKY_02}, // Pocky (1) + {"CHICKEN", MT_FLICKY_03}, // Cucky (1) + {"SEAL", MT_FLICKY_04}, // Rocky (1) + {"PIG", MT_FLICKY_05}, // Picky (1) + {"CHIPMUNK", MT_FLICKY_06}, // Ricky (1) + {"PENGUIN", MT_FLICKY_07}, // Pecky (1) + {"FISH", MT_FLICKY_08}, // Nicky (CD) + {"RAM", MT_FLICKY_09}, // Flocky (CD) + {"PUFFIN", MT_FLICKY_10}, // Wicky (CD) + {"COW", MT_FLICKY_11}, // Macky (SRB2) + {"RAT", MT_FLICKY_12}, // Micky (2) + {"BEAR", MT_FLICKY_13}, // Becky (2) + {"DOVE", MT_FLICKY_14}, // Docky (CD) + {"CAT", MT_FLICKY_15}, // Nyannyan (Flicky) + {"CANARY", MT_FLICKY_16}, // Lucky (CD) {"a", 0}, // End of normal flickies - a lower case character so will never fastcmp valid with uppercase tmp - //{"FLICKER", MT_FLICKER}, - {"SEED", MT_SEED}, + //{"FLICKER", MT_FLICKER}, // Flacky (SRB2) + {"SPIDER", MT_SECRETFLICKY_01}, // Sticky (SRB2) + {"BAT", MT_SECRETFLICKY_02}, // Backy (SRB2) + {"SEED", MT_SEED}, // Seed (CD) {NULL, 0} }; @@ -1616,200 +1618,220 @@ typedef struct */ static actionpointer_t actionpointers[] = { - {{A_Explode}, "A_EXPLODE"}, - {{A_Pain}, "A_PAIN"}, - {{A_Fall}, "A_FALL"}, - {{A_MonitorPop}, "A_MONITORPOP"}, - {{A_GoldMonitorPop}, "A_GOLDMONITORPOP"}, - {{A_GoldMonitorRestore}, "A_GOLDMONITORRESTORE"}, - {{A_GoldMonitorSparkle}, "A_GOLDMONITORSPARKLE"}, - {{A_Look}, "A_LOOK"}, - {{A_Chase}, "A_CHASE"}, - {{A_FaceStabChase}, "A_FACESTABCHASE"}, - {{A_FaceTarget}, "A_FACETARGET"}, - {{A_FaceTracer}, "A_FACETRACER"}, - {{A_Scream}, "A_SCREAM"}, - {{A_BossDeath}, "A_BOSSDEATH"}, - {{A_CustomPower}, "A_CUSTOMPOWER"}, - {{A_GiveWeapon}, "A_GIVEWEAPON"}, - {{A_RingBox}, "A_RINGBOX"}, - {{A_Invincibility}, "A_INVINCIBILITY"}, - {{A_SuperSneakers}, "A_SUPERSNEAKERS"}, - {{A_BunnyHop}, "A_BUNNYHOP"}, - {{A_BubbleSpawn}, "A_BUBBLESPAWN"}, - {{A_FanBubbleSpawn}, "A_FANBUBBLESPAWN"}, - {{A_BubbleRise}, "A_BUBBLERISE"}, - {{A_BubbleCheck}, "A_BUBBLECHECK"}, - {{A_AwardScore}, "A_AWARDSCORE"}, - {{A_ExtraLife}, "A_EXTRALIFE"}, - {{A_GiveShield}, "A_GIVESHIELD"}, - {{A_GravityBox}, "A_GRAVITYBOX"}, - {{A_ScoreRise}, "A_SCORERISE"}, - {{A_ParticleSpawn}, "A_PARTICLESPAWN"}, - {{A_AttractChase}, "A_ATTRACTCHASE"}, - {{A_DropMine}, "A_DROPMINE"}, - {{A_FishJump}, "A_FISHJUMP"}, - {{A_ThrownRing}, "A_THROWNRING"}, - {{A_SetSolidSteam}, "A_SETSOLIDSTEAM"}, - {{A_UnsetSolidSteam}, "A_UNSETSOLIDSTEAM"}, - {{A_SignPlayer}, "A_SIGNPLAYER"}, - {{A_OverlayThink}, "A_OVERLAYTHINK"}, - {{A_JetChase}, "A_JETCHASE"}, - {{A_JetbThink}, "A_JETBTHINK"}, - {{A_JetgThink}, "A_JETGTHINK"}, - {{A_JetgShoot}, "A_JETGSHOOT"}, - {{A_ShootBullet}, "A_SHOOTBULLET"}, - {{A_MinusDigging}, "A_MINUSDIGGING"}, - {{A_MinusPopup}, "A_MINUSPOPUP"}, - {{A_MinusCheck}, "A_MINUSCHECK"}, - {{A_ChickenCheck}, "A_CHICKENCHECK"}, - {{A_MouseThink}, "A_MOUSETHINK"}, - {{A_DetonChase}, "A_DETONCHASE"}, - {{A_CapeChase}, "A_CAPECHASE"}, - {{A_RotateSpikeBall}, "A_ROTATESPIKEBALL"}, - {{A_SlingAppear}, "A_SLINGAPPEAR"}, - {{A_UnidusBall}, "A_UNIDUSBALL"}, - {{A_RockSpawn}, "A_ROCKSPAWN"}, - {{A_SetFuse}, "A_SETFUSE"}, - {{A_CrawlaCommanderThink}, "A_CRAWLACOMMANDERTHINK"}, - {{A_SmokeTrailer}, "A_SMOKETRAILER"}, - {{A_RingExplode}, "A_RINGEXPLODE"}, - {{A_OldRingExplode}, "A_OLDRINGEXPLODE"}, - {{A_MixUp}, "A_MIXUP"}, - {{A_RecyclePowers}, "A_RECYCLEPOWERS"}, - {{A_Boss1Chase}, "A_BOSS1CHASE"}, - {{A_FocusTarget}, "A_FOCUSTARGET"}, - {{A_Boss2Chase}, "A_BOSS2CHASE"}, - {{A_Boss2Pogo}, "A_BOSS2POGO"}, - {{A_BossZoom}, "A_BOSSZOOM"}, - {{A_BossScream}, "A_BOSSSCREAM"}, - {{A_Boss2TakeDamage}, "A_BOSS2TAKEDAMAGE"}, - {{A_Boss7Chase}, "A_BOSS7CHASE"}, - {{A_GoopSplat}, "A_GOOPSPLAT"}, - {{A_Boss2PogoSFX}, "A_BOSS2POGOSFX"}, - {{A_Boss2PogoTarget}, "A_BOSS2POGOTARGET"}, - {{A_BossJetFume}, "A_BOSSJETFUME"}, - {{A_EggmanBox}, "A_EGGMANBOX"}, - {{A_TurretFire}, "A_TURRETFIRE"}, - {{A_SuperTurretFire}, "A_SUPERTURRETFIRE"}, - {{A_TurretStop}, "A_TURRETSTOP"}, - {{A_JetJawRoam}, "A_JETJAWROAM"}, - {{A_JetJawChomp}, "A_JETJAWCHOMP"}, - {{A_PointyThink}, "A_POINTYTHINK"}, - {{A_CheckBuddy}, "A_CHECKBUDDY"}, - {{A_HoodThink}, "A_HOODTHINK"}, - {{A_ArrowCheck}, "A_ARROWCHECK"}, - {{A_SnailerThink}, "A_SNAILERTHINK"}, - {{A_SharpChase}, "A_SHARPCHASE"}, - {{A_SharpSpin}, "A_SHARPSPIN"}, - {{A_VultureVtol}, "A_VULTUREVTOL"}, - {{A_VultureCheck}, "A_VULTURECHECK"}, - {{A_SkimChase}, "A_SKIMCHASE"}, - {{A_1upThinker}, "A_1UPTHINKER"}, - {{A_SkullAttack}, "A_SKULLATTACK"}, - {{A_LobShot}, "A_LOBSHOT"}, - {{A_FireShot}, "A_FIRESHOT"}, - {{A_SuperFireShot}, "A_SUPERFIRESHOT"}, - {{A_BossFireShot}, "A_BOSSFIRESHOT"}, - {{A_Boss7FireMissiles}, "A_BOSS7FIREMISSILES"}, - {{A_Boss1Laser}, "A_BOSS1LASER"}, - {{A_Boss4Reverse}, "A_BOSS4REVERSE"}, - {{A_Boss4SpeedUp}, "A_BOSS4SPEEDUP"}, - {{A_Boss4Raise}, "A_BOSS4RAISE"}, - {{A_SparkFollow}, "A_SPARKFOLLOW"}, - {{A_BuzzFly}, "A_BUZZFLY"}, - {{A_GuardChase}, "A_GUARDCHASE"}, - {{A_EggShield}, "A_EGGSHIELD"}, - {{A_SetReactionTime}, "A_SETREACTIONTIME"}, - {{A_Boss1Spikeballs}, "A_BOSS1SPIKEBALLS"}, - {{A_Boss3TakeDamage}, "A_BOSS3TAKEDAMAGE"}, - {{A_Boss3Path}, "A_BOSS3PATH"}, - {{A_LinedefExecute}, "A_LINEDEFEXECUTE"}, - {{A_PlaySeeSound}, "A_PLAYSEESOUND"}, - {{A_PlayAttackSound}, "A_PLAYATTACKSOUND"}, - {{A_PlayActiveSound}, "A_PLAYACTIVESOUND"}, - {{A_SpawnObjectAbsolute}, "A_SPAWNOBJECTABSOLUTE"}, - {{A_SpawnObjectRelative}, "A_SPAWNOBJECTRELATIVE"}, - {{A_ChangeAngleRelative}, "A_CHANGEANGLERELATIVE"}, - {{A_ChangeAngleAbsolute}, "A_CHANGEANGLEABSOLUTE"}, - {{A_PlaySound}, "A_PLAYSOUND"}, - {{A_FindTarget}, "A_FINDTARGET"}, - {{A_FindTracer}, "A_FINDTRACER"}, - {{A_SetTics}, "A_SETTICS"}, - {{A_SetRandomTics}, "A_SETRANDOMTICS"}, - {{A_ChangeColorRelative}, "A_CHANGECOLORRELATIVE"}, - {{A_ChangeColorAbsolute}, "A_CHANGECOLORABSOLUTE"}, - {{A_MoveRelative}, "A_MOVERELATIVE"}, - {{A_MoveAbsolute}, "A_MOVEABSOLUTE"}, - {{A_Thrust}, "A_THRUST"}, - {{A_ZThrust}, "A_ZTHRUST"}, - {{A_SetTargetsTarget}, "A_SETTARGETSTARGET"}, - {{A_SetObjectFlags}, "A_SETOBJECTFLAGS"}, - {{A_SetObjectFlags2}, "A_SETOBJECTFLAGS2"}, - {{A_RandomState}, "A_RANDOMSTATE"}, - {{A_RandomStateRange}, "A_RANDOMSTATERANGE"}, - {{A_DualAction}, "A_DUALACTION"}, - {{A_RemoteAction}, "A_REMOTEACTION"}, - {{A_ToggleFlameJet}, "A_TOGGLEFLAMEJET"}, - {{A_OrbitNights}, "A_ORBITNIGHTS"}, - {{A_GhostMe}, "A_GHOSTME"}, - {{A_SetObjectState}, "A_SETOBJECTSTATE"}, - {{A_SetObjectTypeState}, "A_SETOBJECTTYPESTATE"}, - {{A_KnockBack}, "A_KNOCKBACK"}, - {{A_PushAway}, "A_PUSHAWAY"}, - {{A_RingDrain}, "A_RINGDRAIN"}, - {{A_SplitShot}, "A_SPLITSHOT"}, - {{A_MissileSplit}, "A_MISSILESPLIT"}, - {{A_MultiShot}, "A_MULTISHOT"}, - {{A_InstaLoop}, "A_INSTALOOP"}, - {{A_Custom3DRotate}, "A_CUSTOM3DROTATE"}, - {{A_SearchForPlayers}, "A_SEARCHFORPLAYERS"}, - {{A_CheckRandom}, "A_CHECKRANDOM"}, - {{A_CheckTargetRings}, "A_CHECKTARGETRINGS"}, - {{A_CheckRings}, "A_CHECKRINGS"}, - {{A_CheckTotalRings}, "A_CHECKTOTALRINGS"}, - {{A_CheckHealth}, "A_CHECKHEALTH"}, - {{A_CheckRange}, "A_CHECKRANGE"}, - {{A_CheckHeight}, "A_CHECKHEIGHT"}, - {{A_CheckTrueRange}, "A_CHECKTRUERANGE"}, - {{A_CheckThingCount}, "A_CHECKTHINGCOUNT"}, - {{A_CheckAmbush}, "A_CHECKAMBUSH"}, - {{A_CheckCustomValue}, "A_CHECKCUSTOMVALUE"}, - {{A_CheckCusValMemo}, "A_CHECKCUSVALMEMO"}, - {{A_SetCustomValue}, "A_SETCUSTOMVALUE"}, - {{A_UseCusValMemo}, "A_USECUSVALMEMO"}, - {{A_RelayCustomValue}, "A_RELAYCUSTOMVALUE"}, - {{A_CusValAction}, "A_CUSVALACTION"}, - {{A_ForceStop}, "A_FORCESTOP"}, - {{A_ForceWin}, "A_FORCEWIN"}, - {{A_SpikeRetract}, "A_SPIKERETRACT"}, - {{A_InfoState}, "A_INFOSTATE"}, - {{A_Repeat}, "A_REPEAT"}, - {{A_SetScale}, "A_SETSCALE"}, - {{A_RemoteDamage}, "A_REMOTEDAMAGE"}, - {{A_HomingChase}, "A_HOMINGCHASE"}, - {{A_TrapShot}, "A_TRAPSHOT"}, - {{A_VileTarget}, "A_VILETARGET"}, - {{A_VileAttack}, "A_VILEATTACK"}, - {{A_VileFire}, "A_VILEFIRE"}, - {{A_BrakChase}, "A_BRAKCHASE"}, - {{A_BrakFireShot}, "A_BRAKFIRESHOT"}, - {{A_BrakLobShot}, "A_BRAKLOBSHOT"}, - {{A_NapalmScatter}, "A_NAPALMSCATTER"}, - {{A_SpawnFreshCopy}, "A_SPAWNFRESHCOPY"}, - {{A_FlickySpawn}, "A_FLICKYSPAWN"}, - {{A_FlickyAim}, "A_FLICKYAIM"}, - {{A_FlickyFly}, "A_FLICKYFLY"}, - {{A_FlickySoar}, "A_FLICKYSOAR"}, - {{A_FlickyCoast}, "A_FLICKYCOAST"}, - {{A_FlickyHop}, "A_FLICKYHOP"}, - {{A_FlickyFlounder}, "A_FLICKYFLOUNDER"}, - {{A_FlickyCheck}, "A_FLICKYCHECK"}, - {{A_FlickyHeightCheck}, "A_FLICKYHEIGHTCHECK"}, - {{A_FlickyFlutter}, "A_FLICKYFLUTTER"}, - {{A_FlameParticle}, "A_FLAMEPARTICLE"}, - {{A_FadeOverlay}, "A_FADEOVERLAY"}, - {{A_Boss5Jump}, "A_BOSS5JUMP"}, + {{A_Explode}, "A_EXPLODE"}, + {{A_Pain}, "A_PAIN"}, + {{A_Fall}, "A_FALL"}, + {{A_MonitorPop}, "A_MONITORPOP"}, + {{A_GoldMonitorPop}, "A_GOLDMONITORPOP"}, + {{A_GoldMonitorRestore}, "A_GOLDMONITORRESTORE"}, + {{A_GoldMonitorSparkle}, "A_GOLDMONITORSPARKLE"}, + {{A_Look}, "A_LOOK"}, + {{A_Chase}, "A_CHASE"}, + {{A_FaceStabChase}, "A_FACESTABCHASE"}, + {{A_FaceStabRev}, "A_FACESTABREV"}, + {{A_FaceStabHurl}, "A_FACESTABHURL"}, + {{A_FaceStabMiss}, "A_FACESTABMISS"}, + {{A_StatueBurst}, "A_STATUEBURST"}, + {{A_FaceTarget}, "A_FACETARGET"}, + {{A_FaceTracer}, "A_FACETRACER"}, + {{A_Scream}, "A_SCREAM"}, + {{A_BossDeath}, "A_BOSSDEATH"}, + {{A_CustomPower}, "A_CUSTOMPOWER"}, + {{A_GiveWeapon}, "A_GIVEWEAPON"}, + {{A_RingBox}, "A_RINGBOX"}, + {{A_Invincibility}, "A_INVINCIBILITY"}, + {{A_SuperSneakers}, "A_SUPERSNEAKERS"}, + {{A_BunnyHop}, "A_BUNNYHOP"}, + {{A_BubbleSpawn}, "A_BUBBLESPAWN"}, + {{A_FanBubbleSpawn}, "A_FANBUBBLESPAWN"}, + {{A_BubbleRise}, "A_BUBBLERISE"}, + {{A_BubbleCheck}, "A_BUBBLECHECK"}, + {{A_AwardScore}, "A_AWARDSCORE"}, + {{A_ExtraLife}, "A_EXTRALIFE"}, + {{A_GiveShield}, "A_GIVESHIELD"}, + {{A_GravityBox}, "A_GRAVITYBOX"}, + {{A_ScoreRise}, "A_SCORERISE"}, + {{A_AttractChase}, "A_ATTRACTCHASE"}, + {{A_DropMine}, "A_DROPMINE"}, + {{A_FishJump}, "A_FISHJUMP"}, + {{A_ThrownRing}, "A_THROWNRING"}, + {{A_SetSolidSteam}, "A_SETSOLIDSTEAM"}, + {{A_UnsetSolidSteam}, "A_UNSETSOLIDSTEAM"}, + {{A_SignPlayer}, "A_SIGNPLAYER"}, + {{A_OverlayThink}, "A_OVERLAYTHINK"}, + {{A_JetChase}, "A_JETCHASE"}, + {{A_JetbThink}, "A_JETBTHINK"}, + {{A_JetgThink}, "A_JETGTHINK"}, + {{A_JetgShoot}, "A_JETGSHOOT"}, + {{A_ShootBullet}, "A_SHOOTBULLET"}, + {{A_MinusDigging}, "A_MINUSDIGGING"}, + {{A_MinusPopup}, "A_MINUSPOPUP"}, + {{A_MinusCheck}, "A_MINUSCHECK"}, + {{A_ChickenCheck}, "A_CHICKENCHECK"}, + {{A_MouseThink}, "A_MOUSETHINK"}, + {{A_DetonChase}, "A_DETONCHASE"}, + {{A_CapeChase}, "A_CAPECHASE"}, + {{A_RotateSpikeBall}, "A_ROTATESPIKEBALL"}, + {{A_SlingAppear}, "A_SLINGAPPEAR"}, + {{A_UnidusBall}, "A_UNIDUSBALL"}, + {{A_RockSpawn}, "A_ROCKSPAWN"}, + {{A_SetFuse}, "A_SETFUSE"}, + {{A_CrawlaCommanderThink}, "A_CRAWLACOMMANDERTHINK"}, + {{A_SmokeTrailer}, "A_SMOKETRAILER"}, + {{A_RingExplode}, "A_RINGEXPLODE"}, + {{A_OldRingExplode}, "A_OLDRINGEXPLODE"}, + {{A_MixUp}, "A_MIXUP"}, + {{A_RecyclePowers}, "A_RECYCLEPOWERS"}, + {{A_Boss1Chase}, "A_BOSS1CHASE"}, + {{A_FocusTarget}, "A_FOCUSTARGET"}, + {{A_Boss2Chase}, "A_BOSS2CHASE"}, + {{A_Boss2Pogo}, "A_BOSS2POGO"}, + {{A_BossZoom}, "A_BOSSZOOM"}, + {{A_BossScream}, "A_BOSSSCREAM"}, + {{A_Boss2TakeDamage}, "A_BOSS2TAKEDAMAGE"}, + {{A_Boss7Chase}, "A_BOSS7CHASE"}, + {{A_GoopSplat}, "A_GOOPSPLAT"}, + {{A_Boss2PogoSFX}, "A_BOSS2POGOSFX"}, + {{A_Boss2PogoTarget}, "A_BOSS2POGOTARGET"}, + {{A_BossJetFume}, "A_BOSSJETFUME"}, + {{A_EggmanBox}, "A_EGGMANBOX"}, + {{A_TurretFire}, "A_TURRETFIRE"}, + {{A_SuperTurretFire}, "A_SUPERTURRETFIRE"}, + {{A_TurretStop}, "A_TURRETSTOP"}, + {{A_JetJawRoam}, "A_JETJAWROAM"}, + {{A_JetJawChomp}, "A_JETJAWCHOMP"}, + {{A_PointyThink}, "A_POINTYTHINK"}, + {{A_CheckBuddy}, "A_CHECKBUDDY"}, + {{A_HoodFire}, "A_HOODFIRE"}, + {{A_HoodThink}, "A_HOODTHINK"}, + {{A_HoodFall}, "A_HOODFALL"}, + {{A_ArrowBonks}, "A_ARROWBONKS"}, + {{A_SnailerThink}, "A_SNAILERTHINK"}, + {{A_SharpChase}, "A_SHARPCHASE"}, + {{A_SharpSpin}, "A_SHARPSPIN"}, + {{A_SharpDecel}, "A_SHARPDECEL"}, + {{A_CrushstaceanWalk}, "A_CRUSHSTACEANWALK"}, + {{A_CrushstaceanPunch}, "A_CRUSHSTACEANPUNCH"}, + {{A_CrushclawAim}, "A_CRUSHCLAWAIM"}, + {{A_CrushclawLaunch}, "A_CRUSHCLAWLAUNCH"}, + {{A_VultureVtol}, "A_VULTUREVTOL"}, + {{A_VultureCheck}, "A_VULTURECHECK"}, + {{A_SkimChase}, "A_SKIMCHASE"}, + {{A_1upThinker}, "A_1UPTHINKER"}, + {{A_SkullAttack}, "A_SKULLATTACK"}, + {{A_LobShot}, "A_LOBSHOT"}, + {{A_FireShot}, "A_FIRESHOT"}, + {{A_SuperFireShot}, "A_SUPERFIRESHOT"}, + {{A_BossFireShot}, "A_BOSSFIRESHOT"}, + {{A_Boss7FireMissiles}, "A_BOSS7FIREMISSILES"}, + {{A_Boss1Laser}, "A_BOSS1LASER"}, + {{A_Boss4Reverse}, "A_BOSS4REVERSE"}, + {{A_Boss4SpeedUp}, "A_BOSS4SPEEDUP"}, + {{A_Boss4Raise}, "A_BOSS4RAISE"}, + {{A_SparkFollow}, "A_SPARKFOLLOW"}, + {{A_BuzzFly}, "A_BUZZFLY"}, + {{A_GuardChase}, "A_GUARDCHASE"}, + {{A_EggShield}, "A_EGGSHIELD"}, + {{A_SetReactionTime}, "A_SETREACTIONTIME"}, + {{A_Boss1Spikeballs}, "A_BOSS1SPIKEBALLS"}, + {{A_Boss3TakeDamage}, "A_BOSS3TAKEDAMAGE"}, + {{A_Boss3Path}, "A_BOSS3PATH"}, + {{A_LinedefExecute}, "A_LINEDEFEXECUTE"}, + {{A_PlaySeeSound}, "A_PLAYSEESOUND"}, + {{A_PlayAttackSound}, "A_PLAYATTACKSOUND"}, + {{A_PlayActiveSound}, "A_PLAYACTIVESOUND"}, + {{A_SpawnObjectAbsolute}, "A_SPAWNOBJECTABSOLUTE"}, + {{A_SpawnObjectRelative}, "A_SPAWNOBJECTRELATIVE"}, + {{A_ChangeAngleRelative}, "A_CHANGEANGLERELATIVE"}, + {{A_ChangeAngleAbsolute}, "A_CHANGEANGLEABSOLUTE"}, + {{A_PlaySound}, "A_PLAYSOUND"}, + {{A_FindTarget}, "A_FINDTARGET"}, + {{A_FindTracer}, "A_FINDTRACER"}, + {{A_SetTics}, "A_SETTICS"}, + {{A_SetRandomTics}, "A_SETRANDOMTICS"}, + {{A_ChangeColorRelative}, "A_CHANGECOLORRELATIVE"}, + {{A_ChangeColorAbsolute}, "A_CHANGECOLORABSOLUTE"}, + {{A_MoveRelative}, "A_MOVERELATIVE"}, + {{A_MoveAbsolute}, "A_MOVEABSOLUTE"}, + {{A_Thrust}, "A_THRUST"}, + {{A_ZThrust}, "A_ZTHRUST"}, + {{A_SetTargetsTarget}, "A_SETTARGETSTARGET"}, + {{A_SetObjectFlags}, "A_SETOBJECTFLAGS"}, + {{A_SetObjectFlags2}, "A_SETOBJECTFLAGS2"}, + {{A_RandomState}, "A_RANDOMSTATE"}, + {{A_RandomStateRange}, "A_RANDOMSTATERANGE"}, + {{A_DualAction}, "A_DUALACTION"}, + {{A_RemoteAction}, "A_REMOTEACTION"}, + {{A_ToggleFlameJet}, "A_TOGGLEFLAMEJET"}, + {{A_OrbitNights}, "A_ORBITNIGHTS"}, + {{A_GhostMe}, "A_GHOSTME"}, + {{A_SetObjectState}, "A_SETOBJECTSTATE"}, + {{A_SetObjectTypeState}, "A_SETOBJECTTYPESTATE"}, + {{A_KnockBack}, "A_KNOCKBACK"}, + {{A_PushAway}, "A_PUSHAWAY"}, + {{A_RingDrain}, "A_RINGDRAIN"}, + {{A_SplitShot}, "A_SPLITSHOT"}, + {{A_MissileSplit}, "A_MISSILESPLIT"}, + {{A_MultiShot}, "A_MULTISHOT"}, + {{A_InstaLoop}, "A_INSTALOOP"}, + {{A_Custom3DRotate}, "A_CUSTOM3DROTATE"}, + {{A_SearchForPlayers}, "A_SEARCHFORPLAYERS"}, + {{A_CheckRandom}, "A_CHECKRANDOM"}, + {{A_CheckTargetRings}, "A_CHECKTARGETRINGS"}, + {{A_CheckRings}, "A_CHECKRINGS"}, + {{A_CheckTotalRings}, "A_CHECKTOTALRINGS"}, + {{A_CheckHealth}, "A_CHECKHEALTH"}, + {{A_CheckRange}, "A_CHECKRANGE"}, + {{A_CheckHeight}, "A_CHECKHEIGHT"}, + {{A_CheckTrueRange}, "A_CHECKTRUERANGE"}, + {{A_CheckThingCount}, "A_CHECKTHINGCOUNT"}, + {{A_CheckAmbush}, "A_CHECKAMBUSH"}, + {{A_CheckCustomValue}, "A_CHECKCUSTOMVALUE"}, + {{A_CheckCusValMemo}, "A_CHECKCUSVALMEMO"}, + {{A_SetCustomValue}, "A_SETCUSTOMVALUE"}, + {{A_UseCusValMemo}, "A_USECUSVALMEMO"}, + {{A_RelayCustomValue}, "A_RELAYCUSTOMVALUE"}, + {{A_CusValAction}, "A_CUSVALACTION"}, + {{A_ForceStop}, "A_FORCESTOP"}, + {{A_ForceWin}, "A_FORCEWIN"}, + {{A_SpikeRetract}, "A_SPIKERETRACT"}, + {{A_InfoState}, "A_INFOSTATE"}, + {{A_Repeat}, "A_REPEAT"}, + {{A_SetScale}, "A_SETSCALE"}, + {{A_RemoteDamage}, "A_REMOTEDAMAGE"}, + {{A_HomingChase}, "A_HOMINGCHASE"}, + {{A_TrapShot}, "A_TRAPSHOT"}, + {{A_VileTarget}, "A_VILETARGET"}, + {{A_VileAttack}, "A_VILEATTACK"}, + {{A_VileFire}, "A_VILEFIRE"}, + {{A_BrakChase}, "A_BRAKCHASE"}, + {{A_BrakFireShot}, "A_BRAKFIRESHOT"}, + {{A_BrakLobShot}, "A_BRAKLOBSHOT"}, + {{A_NapalmScatter}, "A_NAPALMSCATTER"}, + {{A_SpawnFreshCopy}, "A_SPAWNFRESHCOPY"}, + {{A_FlickySpawn}, "A_FLICKYSPAWN"}, + {{A_FlickyAim}, "A_FLICKYAIM"}, + {{A_FlickyFly}, "A_FLICKYFLY"}, + {{A_FlickySoar}, "A_FLICKYSOAR"}, + {{A_FlickyCoast}, "A_FLICKYCOAST"}, + {{A_FlickyHop}, "A_FLICKYHOP"}, + {{A_FlickyFlounder}, "A_FLICKYFLOUNDER"}, + {{A_FlickyCheck}, "A_FLICKYCHECK"}, + {{A_FlickyHeightCheck}, "A_FLICKYHEIGHTCHECK"}, + {{A_FlickyFlutter}, "A_FLICKYFLUTTER"}, + {{A_FlameParticle}, "A_FLAMEPARTICLE"}, + {{A_FadeOverlay}, "A_FADEOVERLAY"}, + {{A_Boss5Jump}, "A_BOSS5JUMP"}, + {{A_LightBeamReset}, "A_LIGHTBEAMRESET"}, + {{A_MineExplode}, "A_MINEEXPLODE"}, + {{A_MineRange}, "A_MINERANGE"}, + {{A_ConnectToGround}, "A_CONNECTTOGROUND"}, + {{A_SpawnParticleRelative}, "A_SPAWNPARTICLERELATIVE"}, + {{A_MultiShotDist}, "A_MULTISHOTDIST"}, + {{A_WhoCaresIfYourSonIsABee},"A_WHOCARESIFYOURSONISABEE"}, + {{A_ParentTriesToSleep}, "A_PARENTTRIESTOSLEEP"}, + {{A_CryingToMomma}, "A_CRYINGTOMOMMA"}, + {{A_CheckFlags2}, "A_CHECKFLAGS2"}, {{NULL}, "NONE"}, @@ -2648,11 +2670,21 @@ static void readmaincfg(MYFILE *f) value = get_number(word2); sstage_start = (INT16)value; - sstage_end = (INT16)(sstage_start+6); // 7 special stages total + sstage_end = (INT16)(sstage_start+7); // 7 special stages total plus one weirdo } - else if (fastcmp(word, "USENIGHTSSS")) + else if (fastcmp(word, "SMPSTAGE_START")) { - useNightsSS = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y'); + // Support using the actual map name, + // i.e., Level AB, Level FZ, etc. + + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z') + value = M_MapNumber(word2[0], word2[1]); + else + value = get_number(word2); + + smpstage_start = (INT16)value; + smpstage_end = (INT16)(smpstage_start+6); // 7 special stages total } else if (fastcmp(word, "REDTEAM")) { @@ -3577,10 +3609,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_RBUZZFLY1", "S_RBUZZFLY2", - // AquaBuzz - "S_BBUZZFLY1", - "S_BBUZZFLY2", - // Jetty-Syn Bomber "S_JETBLOOK1", "S_JETBLOOK2", @@ -3617,7 +3645,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_DETON13", "S_DETON14", "S_DETON15", - "S_DETON16", // Skim Mine Dropper "S_SKIM1", @@ -3659,14 +3686,40 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_TURRETPOPDOWN7", "S_TURRETPOPDOWN8", - // Sharp - "S_SHARP_ROAM1", - "S_SHARP_ROAM2", - "S_SHARP_AIM1", - "S_SHARP_AIM2", - "S_SHARP_AIM3", - "S_SHARP_AIM4", - "S_SHARP_SPIN", + // Spincushion + "S_SPINCUSHION_LOOK", + "S_SPINCUSHION_CHASE1", + "S_SPINCUSHION_CHASE2", + "S_SPINCUSHION_CHASE3", + "S_SPINCUSHION_CHASE4", + "S_SPINCUSHION_AIM1", + "S_SPINCUSHION_AIM2", + "S_SPINCUSHION_AIM3", + "S_SPINCUSHION_AIM4", + "S_SPINCUSHION_AIM5", + "S_SPINCUSHION_SPIN1", + "S_SPINCUSHION_SPIN2", + "S_SPINCUSHION_SPIN3", + "S_SPINCUSHION_SPIN4", + "S_SPINCUSHION_STOP1", + "S_SPINCUSHION_STOP2", + "S_SPINCUSHION_STOP3", + "S_SPINCUSHION_STOP4", + + // Crushstacean + "S_CRUSHSTACEAN_ROAM1", + "S_CRUSHSTACEAN_ROAM2", + "S_CRUSHSTACEAN_ROAM3", + "S_CRUSHSTACEAN_ROAM4", + "S_CRUSHSTACEAN_ROAMPAUSE", + "S_CRUSHSTACEAN_PUNCH1", + "S_CRUSHSTACEAN_PUNCH2", + "S_CRUSHCLAW_AIM", + "S_CRUSHCLAW_OUT", + "S_CRUSHCLAW_STAY", + "S_CRUSHCLAW_IN", + "S_CRUSHCLAW_WAIT", + "S_CRUSHCHAIN", // Jet Jaw "S_JETJAW_ROAM1", @@ -3715,11 +3768,12 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit // Robo-Hood "S_ROBOHOOD_LOOK", - "S_ROBOHOOD_STND", - "S_ROBOHOOD_SHOOT", - "S_ROBOHOOD_JUMP", + "S_ROBOHOOD_STAND", + "S_ROBOHOOD_FIRE1", + "S_ROBOHOOD_FIRE2", + "S_ROBOHOOD_JUMP1", "S_ROBOHOOD_JUMP2", - "S_ROBOHOOD_FALL", + "S_ROBOHOOD_JUMP3", // CastleBot FaceStabber "S_FACESTABBER_STND1", @@ -3732,6 +3786,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_FACESTABBER_CHARGE2", "S_FACESTABBER_CHARGE3", "S_FACESTABBER_CHARGE4", + "S_FACESTABBER_PAIN", + "S_FACESTABBER_DIE1", + "S_FACESTABBER_DIE2", + "S_FACESTABBER_DIE3", + "S_FACESTABBERSPEAR", // Egg Guard "S_EGGGUARD_STND", @@ -3749,6 +3808,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit // Egg Shield for Egg Guard "S_EGGSHIELD", + "S_EGGSHIELDBREAK", // Green Snapper "S_GSNAPPER_STND", @@ -3806,13 +3866,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_UNIDUS_BALL", // Boss Explosion - "S_BPLD1", - "S_BPLD2", - "S_BPLD3", - "S_BPLD4", - "S_BPLD5", - "S_BPLD6", - "S_BPLD7", + "S_BOSSEXPLODE", // S3&K Boss Explosion "S_SONIC3KBOSSEXPLOSION1", @@ -4317,17 +4371,27 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_RING", // Blue Sphere for special stages - "S_BLUEBALL", - "S_BLUEBALLSPARK", + "S_BLUESPHERE", + "S_BLUESPHEREBONUS", + "S_BLUESPHERESPARK", + + // Bomb Sphere + "S_BOMBSPHERE1", + "S_BOMBSPHERE2", + "S_BOMBSPHERE3", + "S_BOMBSPHERE4", + + // NiGHTS Chip + "S_NIGHTSCHIP", + "S_NIGHTSCHIPBONUS", + + // NiGHTS Star + "S_NIGHTSSTAR", + "S_NIGHTSSTARXMAS", // Gravity Wells for special stages "S_GRAVWELLGREEN", - "S_GRAVWELLGREEN2", - "S_GRAVWELLGREEN3", - "S_GRAVWELLRED", - "S_GRAVWELLRED2", - "S_GRAVWELLRED3", // Individual Team Rings "S_TEAMRING", @@ -4376,14 +4440,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_CEMG6", "S_CEMG7", - // Emeralds (for hunt) - "S_EMER1", - - "S_FAN", - "S_FAN2", - "S_FAN3", - "S_FAN4", - "S_FAN5", + // Emerald hunt shards + "S_SHRD1", + "S_SHRD2", + "S_SHRD3", // Bubble Source "S_BUBBLES1", @@ -4446,16 +4506,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_SIGN52", // Eggman "S_SIGN53", - // Steam Riser - "S_STEAM1", - "S_STEAM2", - "S_STEAM3", - "S_STEAM4", - "S_STEAM5", - "S_STEAM6", - "S_STEAM7", - "S_STEAM8", - // Spike Ball "S_SPIKEBALL1", "S_SPIKEBALL2", @@ -4503,14 +4553,18 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_STARPOST_ENDSPIN", // Big floating mine - "S_BIGMINE1", - "S_BIGMINE2", - "S_BIGMINE3", - "S_BIGMINE4", - "S_BIGMINE5", - "S_BIGMINE6", - "S_BIGMINE7", - "S_BIGMINE8", + "S_BIGMINE_IDLE", + "S_BIGMINE_ALERT1", + "S_BIGMINE_ALERT2", + "S_BIGMINE_ALERT3", + "S_BIGMINE_SET1", + "S_BIGMINE_SET2", + "S_BIGMINE_SET3", + "S_BIGMINE_BLAST1", + "S_BIGMINE_BLAST2", + "S_BIGMINE_BLAST3", + "S_BIGMINE_BLAST4", + "S_BIGMINE_BLAST5", // Cannon Launcher "S_CANNONLAUNCHER1", @@ -4642,6 +4696,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_THUNDERCOIN_ICON1", "S_THUNDERCOIN_ICON2", + // --- + "S_ROCKET", "S_LASER", @@ -4671,22 +4727,17 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit // Arrow "S_ARROW", - "S_ARROWUP", - "S_ARROWDOWN", + "S_ARROWBONK", // Trapgoyle Demon fire - "S_DEMONFIRE1", - "S_DEMONFIRE2", - "S_DEMONFIRE3", - "S_DEMONFIRE4", - "S_DEMONFIRE5", - "S_DEMONFIRE6", + "S_DEMONFIRE", // GFZ flowers "S_GFZFLOWERA", "S_GFZFLOWERB", "S_GFZFLOWERC", + "S_BLUEBERRYBUSH", "S_BERRYBUSH", "S_BUSH", @@ -4701,10 +4752,28 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_POLYGONTREE", "S_BUSHTREE", "S_BUSHREDTREE", + "S_SPRINGTREE", - // THZ Plant - "S_THZFLOWERA", - "S_THZFLOWERB", + // THZ flowers + "S_THZFLOWERA", // THZ1 Steam flower + "S_THZFLOWERB", // THZ1 Spin flower (red) + "S_THZFLOWERC", // THZ1 Spin flower (yellow) + + // THZ Steam Whistle tree/bush + "S_THZTREE", + "S_THZTREEBRANCH1", + "S_THZTREEBRANCH2", + "S_THZTREEBRANCH3", + "S_THZTREEBRANCH4", + "S_THZTREEBRANCH5", + "S_THZTREEBRANCH6", + "S_THZTREEBRANCH7", + "S_THZTREEBRANCH8", + "S_THZTREEBRANCH9", + "S_THZTREEBRANCH10", + "S_THZTREEBRANCH11", + "S_THZTREEBRANCH12", + "S_THZTREEBRANCH13", // THZ Alarm "S_ALARM1", @@ -4742,18 +4811,33 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit // Blue Crystal "S_BLUECRYSTAL1", + // Kelp, + "S_KELP", + + // DSZ Stalagmites + "S_DSZSTALAGMITE", + "S_DSZ2STALAGMITE", + + // DSZ Light beam + "S_LIGHTBEAM1", + "S_LIGHTBEAM2", + "S_LIGHTBEAM3", + "S_LIGHTBEAM4", + "S_LIGHTBEAM5", + "S_LIGHTBEAM6", + "S_LIGHTBEAM7", + "S_LIGHTBEAM8", + "S_LIGHTBEAM9", + "S_LIGHTBEAM10", + "S_LIGHTBEAM11", + "S_LIGHTBEAM12", + // CEZ Chain "S_CEZCHAIN", // Flame - "S_FLAME1", - "S_FLAME2", - "S_FLAME3", - "S_FLAME4", - "S_FLAME5", - "S_FLAME6", + "S_FLAME", "S_FLAMEPARTICLE", - "S_FLAMEREST", // Eggman Statue @@ -4768,6 +4852,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_BIGMACECHAIN", "S_SMALLMACE", "S_BIGMACE", + "S_SMALLGRABCHAIN", + "S_BIGGRABCHAIN", // Yellow spring on a ball "S_YELLOWSPRINGBALL", @@ -4819,7 +4905,23 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_BIGFIREBAR15", "S_BIGFIREBAR16", - "S_CEZFLOWER1", + "S_CEZFLOWER", + "S_CEZPOLE", + "S_CEZBANNER", + "S_PINETREE", + "S_CEZBUSH1", + "S_CEZBUSH2", + "S_CANDLE", + "S_CANDLEPRICKET", + "S_FLAMEHOLDER", + "S_FIRETORCH", + "S_WAVINGFLAG", + "S_WAVINGFLAGSEG", + "S_CRAWLASTATUE", + "S_FACESTABBERSTATUE", + "S_SUSPICIOUSFACESTABBERSTATUE_WAIT", + "S_SUSPICIOUSFACESTABBERSTATUE_BURST1", + "S_SUSPICIOUSFACESTABBERSTATUE_BURST2", // Big Tumbleweed "S_BIGTUMBLEWEED", @@ -4920,8 +5022,57 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_LAMPPOST2", // with snow "S_HANGSTAR", // Xmas GFZ bushes + "S_XMASBLUEBERRYBUSH", "S_XMASBERRYBUSH", "S_XMASBUSH", + // FHZ + "S_FHZICE1", + "S_FHZICE2", + + // Halloween Scenery + // Pumpkins + "S_JACKO1", + "S_JACKO1OVERLAY_1", + "S_JACKO1OVERLAY_2", + "S_JACKO1OVERLAY_3", + "S_JACKO1OVERLAY_4", + "S_JACKO2", + "S_JACKO2OVERLAY_1", + "S_JACKO2OVERLAY_2", + "S_JACKO2OVERLAY_3", + "S_JACKO2OVERLAY_4", + "S_JACKO3", + "S_JACKO3OVERLAY_1", + "S_JACKO3OVERLAY_2", + "S_JACKO3OVERLAY_3", + "S_JACKO3OVERLAY_4", + // Dr Seuss Trees + "S_HHZTREE_TOP", + "S_HHZTREE_TRUNK", + "S_HHZTREE_LEAF", + // Mushroom + "S_HHZSHROOM_1", + "S_HHZSHROOM_2", + "S_HHZSHROOM_3", + "S_HHZSHROOM_4", + "S_HHZSHROOM_5", + "S_HHZSHROOM_6", + "S_HHZSHROOM_7", + "S_HHZSHROOM_8", + "S_HHZSHROOM_9", + "S_HHZSHROOM_10", + "S_HHZSHROOM_11", + "S_HHZSHROOM_12", + "S_HHZSHROOM_13", + "S_HHZSHROOM_14", + "S_HHZSHROOM_15", + "S_HHZSHROOM_16", + // Misc + "S_HHZGRASS", + "S_HHZTENT1", + "S_HHZTENT2", + "S_HHZSTALAGMITE_TALL", + "S_HHZSTALAGMITE_SHORT", // Botanic Serenity's loads of scenery states "S_BSZTALLFLOWER_RED", @@ -5362,19 +5513,64 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_FLICKY_16_FLAP2", "S_FLICKY_16_FLAP3", + // Spider + "S_SECRETFLICKY_01_OUT", + "S_SECRETFLICKY_01_AIM", + "S_SECRETFLICKY_01_HOP", + "S_SECRETFLICKY_01_UP", + "S_SECRETFLICKY_01_DOWN", + + // Bat + "S_SECRETFLICKY_02_OUT", + "S_SECRETFLICKY_02_FLAP1", + "S_SECRETFLICKY_02_FLAP2", + "S_SECRETFLICKY_02_FLAP3", + + // Fan + "S_FAN", + "S_FAN2", + "S_FAN3", + "S_FAN4", + "S_FAN5", + + // Steam Riser + "S_STEAM1", + "S_STEAM2", + "S_STEAM3", + "S_STEAM4", + "S_STEAM5", + "S_STEAM6", + "S_STEAM7", + "S_STEAM8", + + // Bumpers + "S_BUMPER", + "S_BUMPERHIT", + + // Balloons + "S_BALLOON", + "S_BALLOONPOP1", + "S_BALLOONPOP2", + "S_BALLOONPOP3", + "S_BALLOONPOP4", + "S_BALLOONPOP5", + "S_BALLOONPOP6", + + // Yellow Spring "S_YELLOWSPRING", "S_YELLOWSPRING2", "S_YELLOWSPRING3", "S_YELLOWSPRING4", "S_YELLOWSPRING5", + // Red Spring "S_REDSPRING", "S_REDSPRING2", "S_REDSPRING3", "S_REDSPRING4", "S_REDSPRING5", - // Blue Springs + // Blue Spring "S_BLUESPRING", "S_BLUESPRING2", "S_BLUESPRING3", @@ -5401,6 +5597,16 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_RDIAG7", "S_RDIAG8", + // Blue Diagonal Spring + "S_BDIAG1", + "S_BDIAG2", + "S_BDIAG3", + "S_BDIAG4", + "S_BDIAG5", + "S_BDIAG6", + "S_BDIAG7", + "S_BDIAG8", + // Yellow Side Spring "S_YHORIZ1", "S_YHORIZ2", @@ -5506,7 +5712,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_SEED", "S_PARTICLE", - "S_PARTICLEGEN", // Score Logos "S_SCRA", // 100 @@ -5520,6 +5725,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_SCRI", // 4000 (mario) "S_SCRJ", // 8000 (mario) "S_SCRK", // 1UP (mario) + "S_SCRL", // 10 // Drowning Timer Numbers "S_ZERO1", @@ -5818,9 +6024,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_NIGHTSCORE90_2", "S_NIGHTSCORE100_2", - "S_NIGHTSWING", - "S_NIGHTSWING_XMAS", - // NiGHTS Paraloop Powerups "S_NIGHTSSUPERLOOP", "S_NIGHTSDRILLREFILL", @@ -5838,14 +6041,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_ORBITEM6", "S_ORBITEM7", "S_ORBITEM8", - "S_ORBITEM9", - "S_ORBITEM10", - "S_ORBITEM11", - "S_ORBITEM12", - "S_ORBITEM13", - "S_ORBITEM14", - "S_ORBITEM15", - "S_ORBITEM16", + "S_ORBIDYA1", + "S_ORBIDYA2", + "S_ORBIDYA3", + "S_ORBIDYA4", + "S_ORBIDYA5", // "Flicky" helper "S_NIGHTOPIANHELPER1", @@ -5858,6 +6058,141 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_NIGHTOPIANHELPER8", "S_NIGHTOPIANHELPER9", + // Nightopian + "S_PIAN0", + "S_PIAN1", + "S_PIAN2", + "S_PIAN3", + "S_PIAN4", + "S_PIAN5", + "S_PIAN6", + "S_PIANSING", + + // Shleep + "S_SHLEEP1", + "S_SHLEEP2", + "S_SHLEEP3", + "S_SHLEEP4", + "S_SHLEEPBOUNCE1", + "S_SHLEEPBOUNCE2", + "S_SHLEEPBOUNCE3", + + // Secret badniks and hazards, shhhh + "S_PENGUINATOR_LOOK", + "S_PENGUINATOR_WADDLE1", + "S_PENGUINATOR_WADDLE2", + "S_PENGUINATOR_WADDLE3", + "S_PENGUINATOR_WADDLE4", + "S_PENGUINATOR_SLIDE1", + "S_PENGUINATOR_SLIDE2", + "S_PENGUINATOR_SLIDE3", + "S_PENGUINATOR_SLIDE4", + "S_PENGUINATOR_SLIDE5", + + "S_POPHAT_LOOK", + "S_POPHAT_SHOOT1", + "S_POPHAT_SHOOT2", + "S_POPHAT_SHOOT3", + + "S_HIVEELEMENTAL_LOOK", + "S_HIVEELEMENTAL_PREPARE1", + "S_HIVEELEMENTAL_PREPARE2", + "S_HIVEELEMENTAL_SHOOT1", + "S_HIVEELEMENTAL_SHOOT2", + "S_HIVEELEMENTAL_DORMANT", + "S_HIVEELEMENTAL_PAIN", + "S_HIVEELEMENTAL_DIE1", + "S_HIVEELEMENTAL_DIE2", + "S_HIVEELEMENTAL_DIE3", + + "S_BUMBLEBORE_SPAWN", + "S_BUMBLEBORE_LOOK1", + "S_BUMBLEBORE_LOOK2", + "S_BUMBLEBORE_FLY1", + "S_BUMBLEBORE_FLY2", + "S_BUMBLEBORE_RAISE", + "S_BUMBLEBORE_FALL1", + "S_BUMBLEBORE_FALL2", + "S_BUMBLEBORE_STUCK1", + "S_BUMBLEBORE_STUCK2", + "S_BUMBLEBORE_DIE", + + "S_BBUZZFLY1", + "S_BBUZZFLY2", + + "S_SMASHSPIKE_FLOAT", + "S_SMASHSPIKE_EASE1", + "S_SMASHSPIKE_EASE2", + "S_SMASHSPIKE_FALL", + "S_SMASHSPIKE_STOMP1", + "S_SMASHSPIKE_STOMP2", + "S_SMASHSPIKE_RISE1", + "S_SMASHSPIKE_RISE2", + + "S_CACO_LOOK", + "S_CACO_WAKE1", + "S_CACO_WAKE2", + "S_CACO_WAKE3", + "S_CACO_WAKE4", + "S_CACO_ROAR", + "S_CACO_CHASE", + "S_CACO_CHASE_REPEAT", + "S_CACO_RANDOM", + "S_CACO_PREPARE_SOUND", + "S_CACO_PREPARE1", + "S_CACO_PREPARE2", + "S_CACO_PREPARE3", + "S_CACO_SHOOT_SOUND", + "S_CACO_SHOOT1", + "S_CACO_SHOOT2", + "S_CACO_CLOSE", + "S_CACO_DIE_FLAGS", + "S_CACO_DIE_GIB1", + "S_CACO_DIE_GIB2", + "S_CACO_DIE_SCREAM", + "S_CACO_DIE_SHATTER", + "S_CACO_DIE_FALL", + "S_CACOSHARD_RANDOMIZE", + "S_CACOSHARD1_1", + "S_CACOSHARD1_2", + "S_CACOSHARD2_1", + "S_CACOSHARD2_2", + "S_CACOFIRE1", + "S_CACOFIRE2", + "S_CACOFIRE3", + "S_CACOFIRE_EXPLODE1", + "S_CACOFIRE_EXPLODE2", + "S_CACOFIRE_EXPLODE3", + "S_CACOFIRE_EXPLODE4", + + "S_SPINBOBERT_MOVE_FLIPUP", + "S_SPINBOBERT_MOVE_UP", + "S_SPINBOBERT_MOVE_FLIPDOWN", + "S_SPINBOBERT_MOVE_DOWN", + "S_SPINBOBERT_FIRE_MOVE", + "S_SPINBOBERT_FIRE_GHOST", + "S_SPINBOBERT_FIRE_TRAIL1", + "S_SPINBOBERT_FIRE_TRAIL2", + "S_SPINBOBERT_FIRE_TRAIL3", + + "S_HANGSTER_LOOK", + "S_HANGSTER_SWOOP1", + "S_HANGSTER_SWOOP2", + "S_HANGSTER_ARC1", + "S_HANGSTER_ARC2", + "S_HANGSTER_ARC3", + "S_HANGSTER_FLY1", + "S_HANGSTER_FLY2", + "S_HANGSTER_FLY3", + "S_HANGSTER_FLY4", + "S_HANGSTER_FLYREPEAT", + "S_HANGSTER_ARCUP1", + "S_HANGSTER_ARCUP2", + "S_HANGSTER_ARCUP3", + "S_HANGSTER_RETURN1", + "S_HANGSTER_RETURN2", + "S_HANGSTER_RETURN3", + "S_CRUMBLE1", "S_CRUMBLE2", @@ -5897,6 +6232,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_WPLD5", "S_WPLD6", + "S_DUST1", + "S_DUST2", + "S_DUST3", + "S_DUST4", + "S_ROCKSPAWN", "S_ROCKCRUMBLEA", @@ -5933,32 +6273,35 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_TAILSOVERLAY", // c: // Enemies - "MT_BLUECRAWLA", - "MT_REDCRAWLA", - "MT_GFZFISH", // Greenflower Fish - "MT_GOLDBUZZ", - "MT_REDBUZZ", - "MT_AQUABUZZ", + "MT_BLUECRAWLA", // Crawla (Blue) + "MT_REDCRAWLA", // Crawla (Red) + "MT_GFZFISH", // SDURF + "MT_GOLDBUZZ", // Buzz (Gold) + "MT_REDBUZZ", // Buzz (Red) "MT_JETTBOMBER", // Jetty-Syn Bomber "MT_JETTGUNNER", // Jetty-Syn Gunner "MT_CRAWLACOMMANDER", // Crawla Commander "MT_DETON", // Deton "MT_SKIM", // Skim mine dropper - "MT_TURRET", - "MT_POPUPTURRET", - "MT_SHARP", // Sharp + "MT_TURRET", // Industrial Turret + "MT_POPUPTURRET", // Pop-Up Turret + "MT_SPINCUSHION", // Spincushion + "MT_CRUSHSTACEAN", // Crushstacean + "MT_CRUSHCLAW", // Big meaty claw + "MT_CRUSHCHAIN", // Chain "MT_JETJAW", // Jet Jaw "MT_SNAILER", // Snailer - "MT_VULTURE", // Vulture + "MT_VULTURE", // BASH "MT_POINTY", // Pointy "MT_POINTYBALL", // Pointy Ball "MT_ROBOHOOD", // Robo-Hood - "MT_FACESTABBER", // CastleBot FaceStabber + "MT_FACESTABBER", // Castlebot Facestabber + "MT_FACESTABBERSPEAR", // Castlebot Facestabber spear aura "MT_EGGGUARD", // Egg Guard - "MT_EGGSHIELD", // Egg Shield for Egg Guard + "MT_EGGSHIELD", // Egg Guard's shield "MT_GSNAPPER", // Green Snapper "MT_MINUS", // Minus - "MT_SPRINGSHELL", // Spring Shell (no drop) + "MT_SPRINGSHELL", // Spring Shell "MT_YELLOWSHELL", // Spring Shell (yellow) "MT_UNIDUS", // Unidus "MT_UNIBALL", // Unidus Ball @@ -6025,7 +6368,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s // Collectible Items "MT_RING", "MT_FLINGRING", // Lost ring - "MT_BLUEBALL", // Blue sphere replacement for special stages + "MT_BLUESPHERE", // Blue sphere for special stages + "MT_BOMBSPHERE", "MT_REDTEAMRING", //Rings collectable by red team. "MT_BLUETEAMRING", //Rings collectable by blue team. "MT_TOKEN", // Special Stage Token @@ -6045,28 +6389,31 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s // Springs and others "MT_FAN", - "MT_STEAM", // Steam riser - "MT_BLUESPRING", + "MT_STEAM", + "MT_BUMPER", + "MT_BALLOON", + "MT_YELLOWSPRING", "MT_REDSPRING", - "MT_YELLOWDIAG", // Yellow Diagonal Spring - "MT_REDDIAG", // Red Diagonal Spring - "MT_YELLOWHORIZ", // Yellow Side Spring - "MT_REDHORIZ", // Red Side Spring - "MT_BLUEHORIZ", // Blue Side Spring + "MT_BLUESPRING", + "MT_YELLOWDIAG", + "MT_REDDIAG", + "MT_BLUEDIAG", + "MT_YELLOWHORIZ", + "MT_REDHORIZ", + "MT_BLUEHORIZ", // Interactive Objects "MT_BUBBLES", // Bubble source "MT_SIGN", // Level end sign "MT_SPIKEBALL", // Spike Ball - "MT_SPECIALSPIKEBALL", "MT_SPINFIRE", "MT_SPIKE", "MT_WALLSPIKE", "MT_WALLSPIKEBASE", "MT_STARPOST", "MT_BIGMINE", - "MT_BIGAIRMINE", + "MT_BLASTEXECUTOR", "MT_CANNONLAUNCHER", // Monitor miscellany @@ -6152,8 +6499,11 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_GFZFLOWER1", "MT_GFZFLOWER2", "MT_GFZFLOWER3", + + "MT_BLUEBERRYBUSH", "MT_BERRYBUSH", "MT_BUSH", + // Trees (both GFZ and misc) "MT_GFZTREE", "MT_GFZBERRYTREE", @@ -6165,10 +6515,14 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_POLYGONTREE", "MT_BUSHTREE", "MT_BUSHREDTREE", + "MT_SPRINGTREE", // Techno Hill Scenery "MT_THZFLOWER1", "MT_THZFLOWER2", + "MT_THZFLOWER3", + "MT_THZTREE", // Steam whistle tree/bush + "MT_THZTREEBRANCH", // branch of said tree "MT_ALARM", // Deep Sea Scenery @@ -6181,6 +6535,10 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_CORAL2", // Coral 2 "MT_CORAL3", // Coral 3 "MT_BLUECRYSTAL", // Blue Crystal + "MT_KELP", // Kelp + "MT_DSZSTALAGMITE", // Deep Sea 1 Stalagmite + "MT_DSZ2STALAGMITE", // Deep Sea 2 Stalagmite + "MT_LIGHTBEAM", // DSZ Light beam // Castle Eggman Scenery "MT_CHAIN", // CEZ Chain @@ -6198,11 +6556,27 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_BIGMACECHAIN", // Big Mace Chain "MT_SMALLMACE", // Small Mace "MT_BIGMACE", // Big Mace + "MT_SMALLGRABCHAIN", // Small Grab Chain + "MT_BIGGRABCHAIN", // Big Grab Chain "MT_YELLOWSPRINGBALL", // Yellow spring on a ball "MT_REDSPRINGBALL", // Red spring on a ball "MT_SMALLFIREBAR", // Small Firebar "MT_BIGFIREBAR", // Big Firebar - "MT_CEZFLOWER", + "MT_CEZFLOWER", // Flower + "MT_CEZPOLE", // Pole + "MT_CEZBANNER", // Banner + "MT_PINETREE", // Pine Tree + "MT_CEZBUSH1", // Bush 1 + "MT_CEZBUSH2", // Bush 2 + "MT_CANDLE", // Candle + "MT_CANDLEPRICKET", // Candle pricket + "MT_FLAMEHOLDER", // Flame holder + "MT_FIRETORCH", // Fire torch + "MT_WAVINGFLAG", // Waving flag + "MT_WAVINGFLAGSEG", // Waving flag segment + "MT_CRAWLASTATUE", // Crawla statue + "MT_FACESTABBERSTATUE", // Facestabber statue + "MT_SUSPICIOUSFACESTABBERSTATUE", // :eggthinking: // Arid Canyon Scenery "MT_BIGTUMBLEWEED", @@ -6254,8 +6628,28 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_LAMPPOST2", // with snow "MT_HANGSTAR", // Xmas GFZ bushes + "MT_XMASBLUEBERRYBUSH", "MT_XMASBERRYBUSH", "MT_XMASBUSH", + // FHZ + "MT_FHZICE1", + "MT_FHZICE2", + + // Halloween Scenery + // Pumpkins + "MT_JACKO1", + "MT_JACKO2", + "MT_JACKO3", + // Dr Seuss Trees + "MT_HHZTREE_TOP", + "MT_HHZTREE_PART", + // Misc + "MT_HHZSHROOM", + "MT_HHZGRASS", + "MT_HHZTENTACLE1", + "MT_HHZTENTACLE2", + "MT_HHZSTALAGMITE_TALL", + "MT_HHZSTALAGMITE_SHORT", // Botanic Serenity "MT_BSZTALLFLOWER_RED", @@ -6342,6 +6736,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_FLICKY_14", // Dove "MT_FLICKY_15", // Cat "MT_FLICKY_16", // Canary + "MT_SECRETFLICKY_01", // Spider + "MT_SECRETFLICKY_02", // Bat + "MT_SEED", // Environmental Effects "MT_RAIN", // Rain @@ -6354,7 +6751,6 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_WATERZAP", "MT_SPINDUST", // Spindash dust "MT_TFOG", - "MT_SEED", "MT_PARTICLE", "MT_PARTICLEGEN", // For fans, etc. @@ -6377,6 +6773,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_AWATERH", // Ambient Water Sound 8 "MT_RANDOMAMBIENT", "MT_RANDOMAMBIENT2", + "MT_MACHINEAMBIENCE", "MT_CORK", @@ -6435,7 +6832,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_HOOPCOLLIDE", // Collision detection for NiGHTS hoops "MT_HOOPCENTER", // Center of a hoop "MT_NIGHTSCORE", - "MT_NIGHTSWING", + "MT_NIGHTSCHIP", // NiGHTS Chip + "MT_NIGHTSSTAR", // NiGHTS Star "MT_NIGHTSSUPERLOOP", "MT_NIGHTSDRILLREFILL", "MT_NIGHTSHELPER", @@ -6443,6 +6841,27 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_NIGHTSLINKFREEZE", "MT_EGGCAPSULE", "MT_NIGHTOPIANHELPER", // the actual helper object that orbits you + "MT_PIAN", // decorative singing friend + "MT_SHLEEP", // almost-decorative sleeping enemy + + // Secret badniks and hazards, shhhh + "MT_PENGUINATOR", + "MT_POPHAT", + "MT_POPSHOT", + + "MT_HIVEELEMENTAL", + "MT_BUMBLEBORE", + + "MT_BUBBLEBUZZ", + + "MT_SMASHINGSPIKEBALL", + "MT_CACOLANTERN", + "MT_CACOSHARD", + "MT_CACOFIRE", + "MT_SPINBOBERT", + "MT_SPINBOBERT_FIRE1", + "MT_SPINBOBERT_FIRE2", + "MT_HANGSTER", // Utility Objects "MT_TELEPORTMAN", @@ -6464,6 +6883,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_SPARK", //spark "MT_EXPLODE", // Robot Explosion "MT_UWEXPLODE", // Underwater Explosion + "MT_DUST", "MT_ROCKSPAWNER", "MT_FALLINGROCK", "MT_ROCKCRUMBLE1", @@ -6535,8 +6955,8 @@ static const char *const MOBJFLAG2_LIST[] = { "SCATTER", // Thrown ring has scatter properties "BEYONDTHEGRAVE", // Source of this missile has died and has since respawned. "SLIDEPUSH", // MF_PUSHABLE that pushes continuously. - "CLASSICPUSH", // Drops straight down when object has negative Z. - "STANDONME", // While not pushable, stand on me anyway. + "CLASSICPUSH", // Drops straight down when object has negative momz. + "INVERTAIMABLE", // Flips whether it's targetable by A_LookForEnemies (enemies no, decoys yes) "INFLOAT", // Floating to a height for a move, don't auto float to target's height. "DEBRIS", // Splash ring from explosion ring "NIGHTSPULL", // Attracted from a paraloop @@ -6554,7 +6974,6 @@ static const char *const MOBJFLAG2_LIST[] = { "AMBUSH", // Alternate behaviour typically set by MTF_AMBUSH "LINKDRAW", // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position) "SHIELD", // Thinker calls P_AddShield/P_ShieldLook (must be partnered with MF_SCENERY to use) - "MACEROTATE", // Thinker calls P_MaceRotate around tracer NULL }; @@ -7144,6 +7563,7 @@ struct { {"DMG_CRUSHED",DMG_CRUSHED}, {"DMG_SPECTATOR",DMG_SPECTATOR}, //// Masks + {"DMG_CANHURTSELF",DMG_CANHURTSELF}, {"DMG_DEATHMASK",DMG_DEATHMASK}, // Gametypes, for use with global var "gametype" diff --git a/src/doomdata.h b/src/doomdata.h index c0586fd65..5ee39c5a8 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -46,6 +46,9 @@ enum ML_BLOCKMAP, // LUT, motion clipping, walls/grid element }; +// Extra flag for objects. +#define MTF_EXTRA 1 + // Reverse gravity flag for objects. #define MTF_OBJECTFLIP 2 diff --git a/src/doomdef.h b/src/doomdef.h index 32771163e..b09aaa415 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -569,6 +569,6 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// Handle touching sector specials in P_PlayerAfterThink instead of P_PlayerThink. /// \note Required for proper collision with moving sloped surfaces that have sector specials on them. -//#define SECTORSPECIALSAFTERTHINK +#define SECTORSPECIALSAFTERTHINK #endif // __DOOMDEF__ diff --git a/src/doomstat.h b/src/doomstat.h index d4735f6b2..24b9e5753 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -126,15 +126,13 @@ extern INT32 secondarydisplayplayer; // for splitscreen // Maps of special importance extern INT16 spstage_start; -extern INT16 sstage_start; -extern INT16 sstage_end; +extern INT16 sstage_start, sstage_end, smpstage_start, smpstage_end; extern INT16 titlemap; extern boolean hidetitlepics; extern INT16 bootmap; //bootmap for loading a map on startup extern boolean looptitle; -extern boolean useNightsSS; // CTF colors. extern UINT8 skincolor_redteam, skincolor_blueteam, skincolor_redring, skincolor_bluering; @@ -175,7 +173,7 @@ extern cutscene_t *cutscenes[128]; extern INT16 nextmapoverride; extern boolean skipstats; -extern UINT32 totalrings; // Total # of rings in a level +extern UINT32 ssspheres; // Total # of spheres in a level // Fun extra stuff extern INT16 lastmap; // Last level you were at (returning from special stages). diff --git a/src/f_finale.c b/src/f_finale.c index db62ddf09..151889c4c 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -987,7 +987,6 @@ static const char *credits[] = { "\1Programming", "Alam \"GBC\" Arias", "Logan \"GBA\" Arias", - "Colette \"fickle\" Bordelon", "Callum Dickinson", "Scott \"Graue\" Feeney", "Nathan \"Jazz\" Giroux", @@ -1345,7 +1344,7 @@ void F_GameEvaluationDrawer(void) ++timesBeatenUltimate; if (M_UpdateUnlockablesAndExtraEmblems()) - S_StartSound(NULL, sfx_ncitem); + S_StartSound(NULL, sfx_s3k68); G_SaveGameData(); } diff --git a/src/g_game.c b/src/g_game.c index 61f300c67..81044b716 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -116,20 +116,18 @@ INT32 secondarydisplayplayer; // for splitscreen tic_t gametic; tic_t levelstarttic; // gametic at level start -UINT32 totalrings; // for intermission +UINT32 ssspheres; // old special stage INT16 lastmap; // last level you were at (returning from special stages) tic_t timeinmap; // Ticker for time spent in level (used for levelcard display) INT16 spstage_start; -INT16 sstage_start; -INT16 sstage_end; +INT16 sstage_start, sstage_end, smpstage_start, smpstage_end; INT16 titlemap = 0; boolean hidetitlepics = false; INT16 bootmap; //bootmap for loading a map on startup boolean looptitle = false; -boolean useNightsSS = false; UINT8 skincolor_redteam = SKINCOLOR_RED; UINT8 skincolor_blueteam = SKINCOLOR_BLUE; @@ -2201,7 +2199,7 @@ void G_PlayerReborn(INT32 player) p->pflags |= PF_JUMPDOWN; p->playerstate = PST_LIVE; - p->rings = 0; // 0 rings + p->rings = p->spheres = 0; // 0 rings p->panim = PA_IDLE; // standing animation //if ((netgame || multiplayer) && !p->spectator) -- moved into P_SpawnPlayer to account for forced changes there @@ -2798,7 +2796,11 @@ INT32 G_GetGametypeByName(const char *gametypestr) // boolean G_IsSpecialStage(INT32 mapnum) { - if (gametype == GT_COOP && modeattacking != ATTACKING_RECORD && mapnum >= sstage_start && mapnum <= sstage_end) + if (gametype != GT_COOP || modeattacking == ATTACKING_RECORD) + return false; + if (mapnum >= sstage_start && mapnum <= sstage_end) + return true; + if (mapnum >= smpstage_start && mapnum <= smpstage_end) return true; return false; @@ -3023,21 +3025,14 @@ static void G_DoCompleted(void) { token--; - if (!(emeralds & EMERALD1)) - nextmap = (INT16)(sstage_start - 1); // Special Stage 1 - else if (!(emeralds & EMERALD2)) - nextmap = (INT16)(sstage_start); // Special Stage 2 - else if (!(emeralds & EMERALD3)) - nextmap = (INT16)(sstage_start + 1); // Special Stage 3 - else if (!(emeralds & EMERALD4)) - nextmap = (INT16)(sstage_start + 2); // Special Stage 4 - else if (!(emeralds & EMERALD5)) - nextmap = (INT16)(sstage_start + 3); // Special Stage 5 - else if (!(emeralds & EMERALD6)) - nextmap = (INT16)(sstage_start + 4); // Special Stage 6 - else if (!(emeralds & EMERALD7)) - nextmap = (INT16)(sstage_start + 5); // Special Stage 7 - else + for (i = 0; i < 7; i++) + if (!(emeralds & (1<dispoffset, affects ordering but not drawing + float z1, z2; } gr_vissprite_t; // -------- diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c index 267666749..dfb2c4351 100644 --- a/src/hardware/hw_light.c +++ b/src/hardware/hw_light.c @@ -62,7 +62,7 @@ static dynlights_t *dynlights = &view_dynlights[0]; light_t lspr[NUMLIGHTS] = { // type offset x, y coronas color, c_size,light color,l_radius, sqr radius computed at init - // UNDEFINED: 0 + // NOLIGHT: 0 { UNDEFINED_SPR, 0.0f, 0.0f, 0x00000000, 24.0f, 0x00000000, 0.0f, 0.0f}, // weapons // RINGSPARK_L @@ -151,10 +151,9 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_POSS &lspr[NOLIGHT], // SPR_SPOS &lspr[NOLIGHT], // SPR_FISH - &lspr[NOLIGHT], // SPR_BUZZ Graue 03-10-2004 - &lspr[NOLIGHT], // SPR_RBUZ Graue 03-10-2004 + &lspr[NOLIGHT], // SPR_BUZZ + &lspr[NOLIGHT], // SPR_RBUZ &lspr[NOLIGHT], // SPR_JETB - &lspr[NOLIGHT], // SPR_JETW &lspr[NOLIGHT], // SPR_JETG &lspr[NOLIGHT], // SPR_CCOM &lspr[NOLIGHT], // SPR_DETN @@ -162,19 +161,20 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_TRET &lspr[NOLIGHT], // SPR_TURR &lspr[NOLIGHT], // SPR_SHRP + &lspr[NOLIGHT], // SPR_CRAB &lspr[NOLIGHT], // SPR_JJAW &lspr[NOLIGHT], // SPR_SNLR &lspr[NOLIGHT], // SPR_VLTR &lspr[NOLIGHT], // SPR_PNTY &lspr[NOLIGHT], // SPR_ARCH &lspr[NOLIGHT], // SPR_CBFS + &lspr[JETLIGHT_L], // SPR_STAB &lspr[NOLIGHT], // SPR_SPSH &lspr[NOLIGHT], // SPR_ESHI &lspr[NOLIGHT], // SPR_GSNP &lspr[NOLIGHT], // SPR_MNUS &lspr[NOLIGHT], // SPR_SSHL &lspr[NOLIGHT], // SPR_UNID - &lspr[NOLIGHT], // SPR_BBUZ // Generic Boos Items &lspr[JETLIGHT_L], // SPR_JETF // Boss jet fumes @@ -227,18 +227,18 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_RING &lspr[NOLIGHT], // SPR_TRNG &lspr[NOLIGHT], // SPR_TOKE - &lspr[REDBALL_L], // SPR_RFLG - &lspr[BLUEBALL_L], // SPR_BFLG - &lspr[NOLIGHT], // SPR_NWNG + &lspr[REDBALL_L], // SPR_RFLG + &lspr[BLUEBALL_L], // SPR_BFLG + &lspr[NOLIGHT], // SPR_SPHR + &lspr[NOLIGHT], // SPR_NCHP + &lspr[NOLIGHT], // SPR_NSTR &lspr[NOLIGHT], // SPR_EMBM &lspr[NOLIGHT], // SPR_CEMG - &lspr[NOLIGHT], // SPR_EMER + &lspr[NOLIGHT], // SPR_SHRD // Interactive Objects - &lspr[NOLIGHT], // SPR_FANS &lspr[NOLIGHT], // SPR_BBLS &lspr[NOLIGHT], // SPR_SIGN - &lspr[NOLIGHT], // SPR_STEM &lspr[NOLIGHT], // SPR_SPIK &lspr[NOLIGHT], // SPR_SFLM &lspr[NOLIGHT], // SPR_USPK @@ -294,17 +294,19 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_FWR4 &lspr[NOLIGHT], // SPR_BUS1 &lspr[NOLIGHT], // SPR_BUS2 + &lspr[NOLIGHT], // SPR_BUS3 // Trees (both GFZ and misc) &lspr[NOLIGHT], // SPR_TRE1 &lspr[NOLIGHT], // SPR_TRE2 &lspr[NOLIGHT], // SPR_TRE3 &lspr[NOLIGHT], // SPR_TRE4 &lspr[NOLIGHT], // SPR_TRE5 + &lspr[NOLIGHT], // SPR_TRE6 // Techno Hill Scenery &lspr[NOLIGHT], // SPR_THZP &lspr[NOLIGHT], // SPR_FWR5 - &lspr[REDBALL_L], // SPR_ALRM + &lspr[REDBALL_L], // SPR_ALRM // Deep Sea Scenery &lspr[NOLIGHT], // SPR_GARG @@ -327,6 +329,15 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_RSPB &lspr[REDBALL_L], // SPR_SFBR &lspr[REDBALL_L], // SPR_BFBR + &lspr[NOLIGHT], // SPR_BANR + &lspr[NOLIGHT], // SPR_PINE + &lspr[NOLIGHT], // SPR_CEZB + &lspr[REDBALL_L], // SPR_CNDL + &lspr[NOLIGHT], // SPR_FLMH + &lspr[REDBALL_L], // SPR_CTRC + &lspr[NOLIGHT], // SPR_CFLG + &lspr[NOLIGHT], // SPR_CSTA + &lspr[NOLIGHT], // SPR_CBBS // Arid Canyon Scenery &lspr[NOLIGHT], // SPR_BTBL @@ -347,12 +358,25 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_XMS3 &lspr[NOLIGHT], // SPR_XMS4 &lspr[NOLIGHT], // SPR_XMS5 + &lspr[NOLIGHT], // SPR_FHZI + + // Halloween Scenery + &lspr[RINGLIGHT_L], // SPR_PUMK + &lspr[NOLIGHT], // SPR_HHPL + &lspr[NOLIGHT], // SPR_SHRM + &lspr[NOLIGHT], // SPR_HHZM // Botanic Serenity Scenery &lspr[NOLIGHT], // SPR_BSZ1 &lspr[NOLIGHT], // SPR_BSZ2 &lspr[NOLIGHT], // SPR_BSZ3 - &lspr[NOLIGHT], // SPR_BSZ4 + //&lspr[NOLIGHT], -- SPR_BSZ4 + &lspr[NOLIGHT], // SPR_BST1 + &lspr[NOLIGHT], // SPR_BST2 + &lspr[NOLIGHT], // SPR_BST3 + &lspr[NOLIGHT], // SPR_BST4 + &lspr[NOLIGHT], // SPR_BST5 + &lspr[NOLIGHT], // SPR_BST6 &lspr[NOLIGHT], // SPR_BSZ5 &lspr[NOLIGHT], // SPR_BSZ6 &lspr[NOLIGHT], // SPR_BSZ7 @@ -375,8 +399,8 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_FIRS &lspr[NOLIGHT], // SPR_BUBS &lspr[NOLIGHT], // SPR_ZAPS - &lspr[INVINCIBLE_L], // SPR_IVSP - &lspr[SUPERSPARK_L], // SPR_SSPK + &lspr[INVINCIBLE_L], // SPR_IVSP + &lspr[SUPERSPARK_L], // SPR_SSPK &lspr[NOLIGHT], // SPR_GOAL @@ -398,13 +422,20 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_FL14 &lspr[NOLIGHT], // SPR_FL15 &lspr[NOLIGHT], // SPR_FL16 + &lspr[NOLIGHT], // SPR_FS01 + &lspr[NOLIGHT], // SPR_FS02 // Springs + &lspr[NOLIGHT], // SPR_FANS + &lspr[NOLIGHT], // SPR_STEM + &lspr[NOLIGHT], // SPR_BUMP + &lspr[NOLIGHT], // SPR_BLON &lspr[NOLIGHT], // SPR_SPRY &lspr[NOLIGHT], // SPR_SPRR - &lspr[NOLIGHT], // SPR_SPRB Graue + &lspr[NOLIGHT], // SPR_SPRB &lspr[NOLIGHT], // SPR_YSPR &lspr[NOLIGHT], // SPR_RSPR + &lspr[NOLIGHT], // SPR_BSPR &lspr[NOLIGHT], // SPR_SSWY &lspr[NOLIGHT], // SPR_SSWR &lspr[NOLIGHT], // SPR_SSWB @@ -420,7 +451,7 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_DUST &lspr[NOLIGHT], // SPR_FPRT &lspr[SUPERSPARK_L], // SPR_TFOG - &lspr[NIGHTSLIGHT_L], // SPR_SEED // Sonic CD flower seed + &lspr[NIGHTSLIGHT_L], // SPR_SEED &lspr[NOLIGHT], // SPR_PRTL // Game Indicators @@ -459,25 +490,43 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_GOOM &lspr[NOLIGHT], // SPR_BGOM &lspr[REDBALL_L], // SPR_FFWR - &lspr[SMALLREDBALL_L], // SPR_FBLL + &lspr[SMALLREDBALL_L], // SPR_FBLL &lspr[NOLIGHT], // SPR_SHLL - &lspr[REDBALL_L], // SPR_PUMA + &lspr[REDBALL_L], // SPR_PUMA &lspr[NOLIGHT], // SPR_HAMM &lspr[NOLIGHT], // SPR_KOOP - &lspr[REDBALL_L], // SPR_BFLM + &lspr[REDBALL_L], // SPR_BFLM &lspr[NOLIGHT], // SPR_MAXE &lspr[NOLIGHT], // SPR_MUS1 &lspr[NOLIGHT], // SPR_MUS2 &lspr[NOLIGHT], // SPR_TOAD // NiGHTS Stuff - &lspr[SUPERSONIC_L], // SPR_NDRN // NiGHTS drone + &lspr[SUPERSONIC_L], // SPR_NDRN // NiGHTS drone &lspr[NOLIGHT], // SPR_NSPK &lspr[NOLIGHT], // SPR_NBMP &lspr[NOLIGHT], // SPR_HOOP &lspr[NOLIGHT], // SPR_HSCR &lspr[NOLIGHT], // SPR_NPRU &lspr[NOLIGHT], // SPR_CAPS + &lspr[INVINCIBLE_L], // SPR_IDYA + &lspr[NOLIGHT], // SPR_NTPN + &lspr[NOLIGHT], // SPR_SHLP + + // Secret badniks and hazards, shhhh + &lspr[NOLIGHT], // SPR_PENG + &lspr[NOLIGHT], // SPR_POPH, + &lspr[NOLIGHT], // SPR_HIVE + &lspr[NOLIGHT], // SPR_BUMB, + &lspr[NOLIGHT], // SPR_BBUZ + &lspr[NOLIGHT], // SPR_FMCE, + &lspr[NOLIGHT], // SPR_HMCE, + &lspr[NOLIGHT], // SPR_CACO, + &lspr[BLUEBALL_L], // SPR_BAL2, + &lspr[NOLIGHT], // SPR_SBOB, + &lspr[BLUEBALL_L], // SPR_SBFL, + &lspr[BLUEBALL_L], // SPR_SBSK, + &lspr[NOLIGHT], // SPR_BATT, // Debris &lspr[RINGSPARK_L], // SPR_SPRK @@ -485,6 +534,7 @@ light_t *t_lspr[NUMSPRITES] = &lspr[SUPERSPARK_L], // SPR_BOM2 &lspr[SUPERSPARK_L], // SPR_BOM3 &lspr[NOLIGHT], // SPR_BOM4 + &lspr[REDBALL_L], // SPR_BMNB // Crumbly rocks &lspr[NOLIGHT], // SPR_ROIA @@ -504,9 +554,6 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_ROIO &lspr[NOLIGHT], // SPR_ROIP - // Blue Spheres - &lspr[NOLIGHT], // SPR_BBAL - // Gravity Well Objects &lspr[NOLIGHT], // SPR_GWLG &lspr[NOLIGHT], // SPR_GWLR diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index ea76f8f3d..119be6b46 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -2115,27 +2115,34 @@ static void HWR_StoreWallRange(double startfrac, double endfrac) } else { -#ifdef ESLOPE // P.S. this is better-organized than the old version - fixed_t offs = sides[(newline ? newline : rover->master)->sidenum[0]].rowoffset; - grTex = HWR_GetTexture(texnum); - - wallVerts[3].t = (*rover->topheight - h + offs) * grTex->scaleY; - wallVerts[2].t = (*rover->topheight - hS + offs) * grTex->scaleY; - wallVerts[0].t = (*rover->topheight - l + offs) * grTex->scaleY; - wallVerts[1].t = (*rover->topheight - lS + offs) * grTex->scaleY; -#else - grTex = HWR_GetTexture(texnum); + fixed_t texturevpeg; + // Wow, how was this missing from OpenGL for so long? + // ...Oh well, anyway, Lower Unpegged now changes pegging of FOFs like in software + // -- Monster Iestyn 26/06/18 if (newline) { - wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + sides[newline->sidenum[0]].rowoffset) * grTex->scaleY; - wallVerts[0].t = wallVerts[1].t = (h - l + (*rover->topheight - h + sides[newline->sidenum[0]].rowoffset)) * grTex->scaleY; + texturevpeg = sides[newline->sidenum[0]].rowoffset; + if (newline->flags & ML_DONTPEGBOTTOM) + texturevpeg -= *rover->topheight - *rover->bottomheight; } else { - wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + sides[rover->master->sidenum[0]].rowoffset) * grTex->scaleY; - wallVerts[0].t = wallVerts[1].t = (h - l + (*rover->topheight - h + sides[rover->master->sidenum[0]].rowoffset)) * grTex->scaleY; + texturevpeg = sides[rover->master->sidenum[0]].rowoffset; + if (gr_linedef->flags & ML_DONTPEGBOTTOM) + texturevpeg -= *rover->topheight - *rover->bottomheight; } + + grTex = HWR_GetTexture(texnum); + +#ifdef ESLOPE + wallVerts[3].t = (*rover->topheight - h + texturevpeg) * grTex->scaleY; + wallVerts[2].t = (*rover->topheight - hS + texturevpeg) * grTex->scaleY; + wallVerts[0].t = (*rover->topheight - l + texturevpeg) * grTex->scaleY; + wallVerts[1].t = (*rover->topheight - lS + texturevpeg) * grTex->scaleY; +#else + wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + texturevpeg) * grTex->scaleY; + wallVerts[0].t = wallVerts[1].t = (*rover->topheight - l + texturevpeg) * grTex->scaleY; #endif wallVerts[0].s = wallVerts[3].s = cliplow * grTex->scaleX; @@ -3032,8 +3039,8 @@ static boolean HWR_CheckBBox(fixed_t *bspcoord) return gld_clipper_SafeCheckRange(angle2, angle1); #else // check clip list for an open space - angle1 = R_PointToAngle(px1, py1) - dup_viewangle; - angle2 = R_PointToAngle(px2, py2) - dup_viewangle; + angle1 = R_PointToAngle2(dup_viewx>>1, dup_viewy>>1, px1>>1, py1>>1) - dup_viewangle; + angle2 = R_PointToAngle2(dup_viewx>>1, dup_viewy>>1, px2>>1, py2>>1) - dup_viewangle; span = angle1 - angle2; @@ -4072,7 +4079,7 @@ static void HWR_DrawSpriteShadow(gr_vissprite_t *spr, GLPatch_t *gpatch, float t angle_t shadowdir; // Set direction - if (splitscreen && stplyr != &players[displayplayer]) + if (splitscreen && stplyr == &players[secondarydisplayplayer]) shadowdir = localangle2 + FixedAngle(cv_cam2_rotate.value); else shadowdir = localangle + FixedAngle(cv_cam_rotate.value); @@ -4359,6 +4366,9 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) i = 0; temp = FLOAT_TO_FIXED(realtop); + if (spr->mobj->frame & FF_FULLBRIGHT) + lightlevel = 255; + #ifdef ESLOPE for (i = 1; i < sector->numlights; i++) { @@ -4366,14 +4376,16 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) : sector->lightlist[i].height; if (h <= temp) { - lightlevel = *list[i-1].lightlevel; + if (!(spr->mobj->frame & FF_FULLBRIGHT)) + lightlevel = *list[i-1].lightlevel; colormap = list[i-1].extra_colormap; break; } } #else i = R_GetPlaneLight(sector, temp, false); - lightlevel = *list[i].lightlevel; + if (!(spr->mobj->frame & FF_FULLBRIGHT)) + lightlevel = *list[i].lightlevel; colormap = list[i].extra_colormap; #endif @@ -4388,7 +4400,8 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) // even if we aren't changing colormap or lightlevel, we still need to continue drawing down the sprite if (!(list[i].flags & FF_NOSHADE) && (list[i].flags & FF_CUTSPRITES)) { - lightlevel = *list[i].lightlevel; + if (!(spr->mobj->frame & FF_FULLBRIGHT)) + lightlevel = *list[i].lightlevel; colormap = list[i].extra_colormap; } @@ -5305,7 +5318,6 @@ static void HWR_ProjectSprite(mobj_t *thing) float tr_x, tr_y; float tz; float x1, x2; - float z1, z2; float rightsin, rightcos; float this_scale; float gz, gzt; @@ -5320,9 +5332,7 @@ static void HWR_ProjectSprite(mobj_t *thing) INT32 heightsec, phs; const boolean papersprite = (thing->frame & FF_PAPERSPRITE); angle_t mobjangle = (thing->player ? thing->player->drawangle : thing->angle); -// float offset; -// float ang_scale = 1.0f, ang_scalez = 0.0f; -// float z1, z2; + float z1, z2; if (!thing) return; @@ -5377,28 +5387,7 @@ static void HWR_ProjectSprite(mobj_t *thing) I_Error("sprframes NULL for sprite %d\n", thing->sprite); #endif - if (papersprite) - { - // Use the actual view angle, rather than the angle formed - // between the view point and the thing - // this makes sure paper sprites always appear at the right angle! - // Note: DO NOT do this in software mode version, it actually - // makes papersprites look WORSE there (I know, I've tried) - // Monster Iestyn - 13/05/17 - ang = dup_viewangle - mobjangle; -/* - ang_scale = FIXED_TO_FLOAT(FINESINE(ang>>ANGLETOFINESHIFT)); - ang_scalez = FIXED_TO_FLOAT(FINECOSINE(ang>>ANGLETOFINESHIFT)); - - if (ang_scale < 0) - { - ang_scale = -ang_scale; - ang_scalez = -ang_scalez; - } -*/ - } - else if (sprframe->rotate != SRF_SINGLE) - ang = R_PointToAngle (thing->x, thing->y) - mobjangle; + ang = R_PointToAngle (thing->x, thing->y) - mobjangle; if (sprframe->rotate == SRF_SINGLE) { @@ -5406,6 +5395,14 @@ static void HWR_ProjectSprite(mobj_t *thing) rot = 0; //Fab: for vis->patch below lumpoff = sprframe->lumpid[0]; //Fab: see note above flip = sprframe->flip; // Will only be 0x00 or 0xFF + + if (papersprite && ang < ANGLE_180) + { + if (flip) + flip = 0; + else + flip = 255; + } } else { @@ -5420,20 +5417,23 @@ static void HWR_ProjectSprite(mobj_t *thing) //Fab: lumpid is the index for spritewidth,spriteoffset... tables lumpoff = sprframe->lumpid[rot]; flip = sprframe->flip & (1<skin && ((skin_t *)thing->skin)->flags & SF_HIRES) this_scale = this_scale * FIXED_TO_FLOAT(((skin_t *)thing->skin)->highresscale); - if (papersprite) // replaces the ang_scale and scalez thing above + if (papersprite) { - rightsin = FIXED_TO_FLOAT(FINESINE(mobjangle>>ANGLETOFINESHIFT)); - rightcos = FIXED_TO_FLOAT(FINECOSINE(mobjangle>>ANGLETOFINESHIFT)); - if (flip) // flip the signs of the above values so you don't end up displaying the sprite backwards - { - rightsin *= -1.0; - rightcos *= -1.0; - } + rightsin = FIXED_TO_FLOAT(FINESINE((mobjangle)>>ANGLETOFINESHIFT)); + rightcos = FIXED_TO_FLOAT(FINECOSINE((mobjangle)>>ANGLETOFINESHIFT)); } else { @@ -5487,7 +5487,10 @@ static void HWR_ProjectSprite(mobj_t *thing) } heightsec = thing->subsector->sector->heightsec; - phs = players[displayplayer].mo->subsector->sector->heightsec; + if (viewplayer->mo && viewplayer->mo->subsector) + phs = viewplayer->mo->subsector->sector->heightsec; + else + phs = -1; if (heightsec != -1 && phs != -1) // only clip things which are in special sectors { @@ -5505,13 +5508,13 @@ static void HWR_ProjectSprite(mobj_t *thing) vis = HWR_NewVisSprite(); vis->x1 = x1; vis->x2 = x2; - vis->z1 = z1; - vis->z2 = z2; vis->tz = tz; // Keep tz for the simple sprite sorting that happens vis->dispoffset = thing->info->dispoffset; // Monster Iestyn: 23/11/15: HARDWARE SUPPORT AT LAST vis->patchlumpnum = sprframe->lumppat[rot]; vis->flip = flip; vis->mobj = thing; + vis->z1 = z1; + vis->z2 = z2; //Hurdler: 25/04/2000: now support colormap in hardware mode if ((vis->mobj->flags & MF_BOSS) && (vis->mobj->flags2 & MF2_FRET) && (leveltime & 1)) // Bosses "flash" diff --git a/src/hu_stuff.c b/src/hu_stuff.c index a13801388..456f97bac 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -89,7 +89,7 @@ patch_t *tallinfin; // coop hud //------------------------------------------- -patch_t *emeraldpics[3][7]; // 0 = normal, 1 = tiny, 2 = coinbox +patch_t *emeraldpics[3][8]; // 0 = normal, 1 = tiny, 2 = coinbox static patch_t *emblemicon; patch_t *tokenicon; static patch_t *exiticon; @@ -256,6 +256,7 @@ void HU_LoadGraphics(void) emeraldpics[0][4] = W_CachePatchName("CHAOS5", PU_HUDGFX); emeraldpics[0][5] = W_CachePatchName("CHAOS6", PU_HUDGFX); emeraldpics[0][6] = W_CachePatchName("CHAOS7", PU_HUDGFX); + emeraldpics[0][7] = W_CachePatchName("CHAOS8", PU_HUDGFX); emeraldpics[1][0] = W_CachePatchName("TEMER1", PU_HUDGFX); emeraldpics[1][1] = W_CachePatchName("TEMER2", PU_HUDGFX); @@ -264,6 +265,7 @@ void HU_LoadGraphics(void) emeraldpics[1][4] = W_CachePatchName("TEMER5", PU_HUDGFX); emeraldpics[1][5] = W_CachePatchName("TEMER6", PU_HUDGFX); emeraldpics[1][6] = W_CachePatchName("TEMER7", PU_HUDGFX); + //emeraldpics[1][7] = W_CachePatchName("TEMER8", PU_HUDGFX); -- unused emeraldpics[2][0] = W_CachePatchName("EMBOX1", PU_HUDGFX); emeraldpics[2][1] = W_CachePatchName("EMBOX2", PU_HUDGFX); @@ -272,6 +274,7 @@ void HU_LoadGraphics(void) emeraldpics[2][4] = W_CachePatchName("EMBOX5", PU_HUDGFX); emeraldpics[2][5] = W_CachePatchName("EMBOX6", PU_HUDGFX); emeraldpics[2][6] = W_CachePatchName("EMBOX7", PU_HUDGFX); + //emeraldpics[2][7] = W_CachePatchName("EMBOX8", PU_HUDGFX); -- unused } // Initialise Heads up diff --git a/src/hu_stuff.h b/src/hu_stuff.h index fb1fa1817..46c47b59e 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -63,7 +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 *emeraldpics[3][7]; +extern patch_t *emeraldpics[3][8]; extern patch_t *rflagico; extern patch_t *bflagico; extern patch_t *rmatcico; diff --git a/src/info.c b/src/info.c index 5f46744bd..1489917b7 100644 --- a/src/info.c +++ b/src/info.c @@ -16,6 +16,7 @@ #include "doomstat.h" #include "sounds.h" #include "p_mobj.h" +#include "p_local.h" // DMG_ constants #include "m_misc.h" #include "z_zone.h" #include "d_player.h" @@ -35,33 +36,33 @@ char sprnames[NUMSPRITES + 1][5] = "PLAY", // Enemies - "POSS", - "SPOS", - "FISH", // Greenflower Fish + "POSS", // Crawla (Blue) + "SPOS", // Crawla (Red) + "FISH", // SDURF "BUZZ", // Buzz (Gold) "RBUZ", // Buzz (Red) "JETB", // Jetty-Syn Bomber - "JETW", // Jetty-Syn Water Bomber "JETG", // Jetty-Syn Gunner "CCOM", // Crawla Commander "DETN", // Deton "SKIM", // Skim mine dropper - "TRET", + "TRET", // Industrial Turret "TURR", // Pop-Up Turret "SHRP", // Sharp + "CRAB", // Crushstacean "JJAW", // Jet Jaw "SNLR", // Snailer - "VLTR", // Vulture + "VLTR", // BASH "PNTY", // Pointy "ARCH", // Robo-Hood - "CBFS", // CastleBot FaceStabber (Egg Knight?) + "CBFS", // Castlebot Facestabber + "STAB", // Castlebot Facestabber spear aura "SPSH", // Egg Guard - "ESHI", // Egg Shield for Egg Guard + "ESHI", // Egg Guard's shield "GSNP", // Green Snapper "MNUS", // Minus "SSHL", // Spring Shell "UNID", // Unidus - "BBUZ", // AquaBuzz, for Azure Temple // Generic Boss Items "JETF", // Boss jet fumes @@ -117,16 +118,16 @@ char sprnames[NUMSPRITES + 1][5] = "TOKE", // Special Stage Token "RFLG", // Red CTF Flag "BFLG", // Blue CTF Flag - "NWNG", // NiGHTS Wing collectable item. + "SPHR", // Sphere + "NCHP", // NiGHTS chip + "NSTR", // NiGHTS star "EMBM", // Emblem "CEMG", // Chaos Emeralds - "EMER", // Emerald Hunt + "SHRD", // Emerald hunt shards // Interactive Objects - "FANS", "BBLS", // water bubble source "SIGN", // Level end sign - "STEM", // Steam riser "SPIK", // Spike Ball "SFLM", // Spin fire "USPK", // Floor spike @@ -182,16 +183,20 @@ char sprnames[NUMSPRITES + 1][5] = "FWR4", "BUS1", // GFZ Bush w/ berries "BUS2", // GFZ Bush w/o berries + "BUS3", // GFZ Bush w/ BLUE berries // Trees (both GFZ and misc) "TRE1", // GFZ "TRE2", // Checker "TRE3", // Frozen Hillside "TRE4", // Polygon "TRE5", // Bush tree + "TRE6", // Spring tree // Techno Hill Scenery - "THZP", // Techno Hill Zone Plant - "FWR5", // Another one + "THZP", // THZ1 Steam Flower + "FWR5", // THZ1 Spin flower (red) + "FWR6", // THZ1 Spin flower (yellow) + "THZT", // Steam Whistle tree/bush "ALRM", // THZ2 Alarm // Deep Sea Scenery @@ -202,6 +207,9 @@ char sprnames[NUMSPRITES + 1][5] = "CRL2", // Coral 2 "CRL3", // Coral 3 "BCRY", // Blue Crystal + "KELP", // Kelp + "DSTG", // DSZ Stalagmites + "LIBE", // DSZ Light beam // Castle Eggman Scenery "CHAN", // CEZ Chain @@ -215,6 +223,15 @@ char sprnames[NUMSPRITES + 1][5] = "RSPB", // Red spring on a ball "SFBR", // Small Firebar "BFBR", // Big Firebar + "BANR", // Banner + "PINE", // Pine Tree + "CEZB", // Bush + "CNDL", // Candle/pricket + "FLMH", // Flame holder + "CTRC", // Fire torch + "CFLG", // Waving flag/segment + "CSTA", // Crawla statue + "CBBS", // Facestabber statue // Arid Canyon Scenery "BTBL", // Big tumbleweed @@ -235,12 +252,25 @@ char sprnames[NUMSPRITES + 1][5] = "XMS3", // Snowman "XMS4", // Lamppost "XMS5", // Hanging Star + "FHZI", // FHZ ice + + // Halloween Scenery + "PUMK", // Pumpkins + "HHPL", // Dr Seuss Trees + "SHRM", // Mushroom + "HHZM", // Misc // Botanic Serenity Scenery "BSZ1", // Tall flowers "BSZ2", // Medium flowers "BSZ3", // Small flowers - "BSZ4", // Tulip + //"BSZ4", -- Tulips + "BST1", // Red tulip + "BST2", // Purple tulip + "BST3", // Blue tulip + "BST4", // Cyan tulip + "BST5", // Yellow tulip + "BST6", // Orange tulip "BSZ5", // Cluster of Tulips "BSZ6", // Bush "BSZ7", // Vine @@ -286,13 +316,20 @@ char sprnames[NUMSPRITES + 1][5] = "FL14", // Dove "FL15", // Cat "FL16", // Canary + "FS01", // Spider + "FS02", // Bat // Springs - "SPRY", // yellow spring - "SPRR", // red spring - "SPRB", // Blue springs + "FANS", // Fan + "STEM", // Steam riser + "BUMP", // Bumpers + "BLON", // Balloons + "SPRY", // Yellow spring + "SPRR", // Red spring + "SPRB", // Blue spring "YSPR", // Yellow Diagonal Spring "RSPR", // Red Diagonal Spring + "BSPR", // Blue Diagonal Spring "SSWY", // Yellow Side Spring "SSWR", // Red Side Spring "SSWB", // Blue Side Spring @@ -366,13 +403,32 @@ char sprnames[NUMSPRITES + 1][5] = "NSCR", // NiGHTS score sprite "NPRU", // Nights Powerups "CAPS", // Capsule thingy for NiGHTS + "IDYA", // Ideya + "NTPN", // Nightopian + "SHLP", // Shleep + + // Secret badniks and hazards, shhhh + "PENG", + "POPH", + "HIVE", + "BUMB", + "BBUZ", + "FMCE", + "HMCE", + "CACO", + "BAL2", + "SBOB", + "SBFL", + "SBSK", + "HBAT", // Debris - "SPRK", // spark + "SPRK", // Sparkle "BOM1", // Robot Explosion "BOM2", // Boss Explosion 1 "BOM3", // Boss Explosion 2 "BOM4", // Underwater Explosion + "BMNB", // Mine Explosion // Crumbly rocks "ROIA", @@ -392,9 +448,6 @@ char sprnames[NUMSPRITES + 1][5] = "ROIO", "ROIP", - // Blue Spheres - "BBAL", - // Gravity Well Objects "GWLG", "GWLR", @@ -670,12 +723,12 @@ state_t states[NUMSTATES] = {SPR_PLAY, SPR2_MLEL, 35, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_MELEE_LANDING // SF_SUPER - {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS2}, // S_PLAY_SUPER_TRANS1 + {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_ANIMATE, 7, {NULL}, 0, 4, S_PLAY_SUPER_TRANS2}, // S_PLAY_SUPER_TRANS1 {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS3}, // S_PLAY_SUPER_TRANS2 {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_PLAY_SUPER_TRANS4}, // S_PLAY_SUPER_TRANS3 {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_PLAY_SUPER_TRANS5}, // S_PLAY_SUPER_TRANS4 {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_PLAY_SUPER_TRANS6}, // S_PLAY_SUPER_TRANS5 - {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT, 20, {A_FadeOverlay}, 0, 0, S_PLAY_FALL}, // S_PLAY_SUPER_TRANS6 + {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT, 19, {A_FadeOverlay}, 0, 0, S_PLAY_FALL}, // S_PLAY_SUPER_TRANS6 {SPR_NULL, 0, -1, {NULL}, 0, 0, S_OBJPLACE_DUMMY}, //S_OBJPLACE_DUMMY @@ -690,12 +743,12 @@ state_t states[NUMSTATES] = {SPR_PLAY, SPR2_SIGN, 1, {NULL}, 0, 24, S_PLAY_SIGN}, // S_PLAY_SIGN // NiGHTS Player, transforming - {SPR_PLAY, SPR2_TRNS, 3, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS2}, // S_PLAY_NIGHTS_TRANS1 + {SPR_PLAY, SPR2_TRNS|FF_ANIMATE, 7, {NULL}, 0, 4, S_PLAY_NIGHTS_TRANS2}, // S_PLAY_NIGHTS_TRANS1 {SPR_PLAY, SPR2_TRNS, 3, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS3}, // S_PLAY_NIGHTS_TRANS2 {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS4}, // S_PLAY_NIGHTS_TRANS3 {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS5}, // S_PLAY_NIGHTS_TRANS4 {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS6}, // S_PLAY_NIGHTS_TRANS5 - {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 25, {A_FadeOverlay}, 4, 0, S_PLAY_NIGHTS_FLOAT}, // S_PLAY_NIGHTS_TRANS5 + {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 21, {A_FadeOverlay}, 2, 0, S_PLAY_NIGHTS_FLOAT}, // S_PLAY_NIGHTS_TRANS5 // NiGHTS Player, stand, float, pain, pull and attack {SPR_PLAY, SPR2_NSTD, 7, {NULL}, 0, 0, S_PLAY_NIGHTS_STAND}, // S_PLAY_NIGHTS_STAND @@ -782,10 +835,6 @@ state_t states[NUMSTATES] = {SPR_RBUZ, 0, 2, {A_BuzzFly}, sfx_buzz4, 0, S_RBUZZFLY2}, // S_RBUZZFLY1 {SPR_RBUZ, 1, 2, {A_BuzzFly}, 0, 0, S_RBUZZFLY1}, // S_RBUZZFLY2 - // AquaBuzz - {SPR_BBUZ, 0, 2, {NULL}, 0, 0, S_BBUZZFLY2}, // S_BBUZZFLY1 - {SPR_BBUZ, 1, 2, {NULL}, 0, 0, S_BBUZZFLY1}, // S_BBUZZFLY2 - // Jetty-Syn Bomber {SPR_JETB, 0, 4, {A_Look}, 0, 0, S_JETBLOOK2}, // S_JETBLOOK1 {SPR_JETB, 1, 4, {A_Look}, 0, 0, S_JETBLOOK1}, // S_JETBLOOK2 @@ -822,7 +871,6 @@ state_t states[NUMSTATES] = {SPR_DETN, 3, 1, {A_DetonChase}, 0, 0, S_DETON14}, // S_DETON13 {SPR_DETN, 2, 1, {A_DetonChase}, 0, 0, S_DETON15}, // S_DETON14 {SPR_DETN, 1, 1, {A_DetonChase}, 0, 0, S_DETON2}, // S_DETON15 - {SPR_DETN, 0, -1, {NULL}, 0, 0, S_DETON16}, // S_DETON16 // Skim Mine Dropper {SPR_SKIM, 0, 1, {A_SkimChase}, 0, 0, S_SKIM2}, // S_SKIM1 @@ -863,14 +911,40 @@ state_t states[NUMSTATES] = {SPR_TURR, 1, 2, {NULL}, 0, 0, S_TURRETPOPDOWN8}, // S_TURRETPOPDOWN7 {SPR_TURR, 0, 69,{A_SetTics}, 0, 1, S_TURRETLOOK}, // S_TURRETPOPDOWN8 - // Sharp - {SPR_SHRP, 0, 1, {A_Look}, 0, 0, S_SHARP_ROAM1}, // S_SHARP_ROAM1, - {SPR_SHRP, 0, 1, {A_SharpChase}, 0, 0, S_SHARP_ROAM2}, // S_SHARP_ROAM2, - {SPR_SHRP, 1, 2, {A_FaceTarget}, 0, 0, S_SHARP_AIM2}, // S_SHARP_AIM1, - {SPR_SHRP, 2, 2, {A_FaceTarget}, 0, 0, S_SHARP_AIM3}, // S_SHARP_AIM2, - {SPR_SHRP, 3, 2, {A_FaceTarget}, 0, 0, S_SHARP_AIM4}, // S_SHARP_AIM3, - {SPR_SHRP, 4, 7, {A_FaceTarget}, 0, 0, S_SHARP_SPIN}, // S_SHARP_AIM4, - {SPR_SHRP, 4, 1, {A_SharpSpin}, 0, 0, S_SHARP_SPIN}, // S_SHARP_SPIN, + // Spincushion + {SPR_SHRP, 0, 2, {A_Look}, 0, 0, S_SPINCUSHION_LOOK}, // S_SPINCUSHION_LOOK + {SPR_SHRP, 1, 2, {A_SharpChase}, 0, 0, S_SPINCUSHION_CHASE2}, // S_SPINCUSHION_CHASE1 + {SPR_SHRP, 2, 2, {A_SharpChase}, 0, 0, S_SPINCUSHION_CHASE3}, // S_SPINCUSHION_CHASE2 + {SPR_SHRP, 3, 2, {A_SharpChase}, 0, 0, S_SPINCUSHION_CHASE4}, // S_SPINCUSHION_CHASE3 + {SPR_SHRP, 0, 2, {A_SharpChase}, 0, 0, S_SPINCUSHION_CHASE1}, // S_SPINCUSHION_CHASE4 + {SPR_SHRP, 0, 2, {NULL}, 0, 0, S_SPINCUSHION_AIM2}, // S_SPINCUSHION_AIM1 + {SPR_SHRP, 4, 2, {NULL}, 0, 0, S_SPINCUSHION_AIM3}, // S_SPINCUSHION_AIM2 + {SPR_SHRP, 5, 2, {A_SetObjectFlags}, MF_PAIN, 2, S_SPINCUSHION_AIM4}, // S_SPINCUSHION_AIM3 + {SPR_SHRP, 6, 16, {A_MultiShotDist}, (MT_DUST<<16)|6, -32, S_SPINCUSHION_AIM5}, // S_SPINCUSHION_AIM4 + {SPR_SHRP, 6, 0, {A_PlaySound}, sfx_shrpgo, 1, S_SPINCUSHION_SPIN1}, // S_SPINCUSHION_AIM5 + {SPR_SHRP, 6, 1, {A_SharpSpin}, 0, 0, S_SPINCUSHION_SPIN2}, // S_SPINCUSHION_SPIN1 + {SPR_SHRP, 8, 1, {A_SharpSpin}, 0, 0, S_SPINCUSHION_SPIN3}, // S_SPINCUSHION_SPIN2 + {SPR_SHRP, 7, 1, {A_SharpSpin}, 0, 0, S_SPINCUSHION_SPIN4}, // S_SPINCUSHION_SPIN3 + {SPR_SHRP, 8, 1, {A_SharpSpin}, MT_SPINDUST, 0, S_SPINCUSHION_SPIN1}, // S_SPINCUSHION_SPIN4 + {SPR_SHRP, 6, 1, {A_PlaySound}, sfx_s3k69, 1, S_SPINCUSHION_STOP2}, // S_SPINCUSHION_STOP1 + {SPR_SHRP, 6, 4, {A_SharpDecel}, 0, 0, S_SPINCUSHION_STOP2}, // S_SPINCUSHION_STOP2 + {SPR_SHRP, 5, 4, {A_FaceTarget}, 0, 0, S_SPINCUSHION_STOP4}, // S_SPINCUSHION_STOP3 + {SPR_SHRP, 4, 4, {A_SetObjectFlags}, MF_PAIN, 1, S_SPINCUSHION_LOOK}, // S_SPINCUSHION_STOP4 + + // Crushstacean + {SPR_CRAB, 0, 3, {A_CrushstaceanWalk}, 0, S_CRUSHSTACEAN_ROAMPAUSE, S_CRUSHSTACEAN_ROAM2}, // S_CRUSHSTACEAN_ROAM1 + {SPR_CRAB, 1, 3, {A_CrushstaceanWalk}, 0, S_CRUSHSTACEAN_ROAMPAUSE, S_CRUSHSTACEAN_ROAM3}, // S_CRUSHSTACEAN_ROAM2 + {SPR_CRAB, 0, 3, {A_CrushstaceanWalk}, 0, S_CRUSHSTACEAN_ROAMPAUSE, S_CRUSHSTACEAN_ROAM4}, // S_CRUSHSTACEAN_ROAM3 + {SPR_CRAB, 2, 3, {A_CrushstaceanWalk}, 0, S_CRUSHSTACEAN_ROAMPAUSE, S_CRUSHSTACEAN_ROAM1}, // S_CRUSHSTACEAN_ROAM4 + {SPR_CRAB, 0, 40, {NULL}, 0, 0, S_CRUSHSTACEAN_ROAM1}, // S_CRUSHSTACEAN_ROAMPAUSE + {SPR_CRAB, 0, 10, {NULL}, 0, 0, S_CRUSHSTACEAN_PUNCH2}, // S_CRUSHSTACEAN_PUNCH1 + {SPR_CRAB, 0, -1, {A_CrushstaceanPunch}, 0, 0, S_CRUSHSTACEAN_ROAMPAUSE}, // S_CRUSHSTACEAN_PUNCH2 + {SPR_CRAB, 3, 1, {A_CrushclawAim}, 40, 20, S_CRUSHCLAW_AIM}, // S_CRUSHCLAW_AIM + {SPR_CRAB, 3, 1, {A_CrushclawLaunch}, 0, S_CRUSHCLAW_STAY, S_CRUSHCLAW_OUT}, // S_CRUSHCLAW_OUT + {SPR_CRAB, 3, 10, {NULL}, 0, 0, S_CRUSHCLAW_IN}, // S_CRUSHCLAW_STAY + {SPR_CRAB, 3, 1, {A_CrushclawLaunch}, 1, S_CRUSHCLAW_WAIT, S_CRUSHCLAW_IN}, // S_CRUSHCLAW_IN + {SPR_CRAB, 3, 37, {NULL}, 0, 0, S_CRUSHCLAW_AIM}, // S_CRUSHCLAW_WAIT + {SPR_CRAB, 4, -1, {NULL}, 0, 0, S_NULL}, // S_CRUSHCHAIN // Jet Jaw {SPR_JJAW, 0, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM2}, // S_JETJAW_ROAM1 @@ -920,24 +994,31 @@ state_t states[NUMSTATES] = {SPR_PNTY, 1, 1, {A_CheckBuddy}, 0, 0, S_POINTYBALL1}, // S_POINTYBALL1 // Robo-Hood - {SPR_ARCH, 0, 14, {A_Look}, (512<<16), 0, S_ROBOHOOD_LOOK}, // S_ROBOHOOD_LOOK - {SPR_ARCH, 2, 1, {A_HoodThink}, 0, 0, S_ROBOHOOD_STND}, // S_ROBOHOOD_STND - {SPR_ARCH, 0, 35, {A_FireShot}, MT_ARROW, -24, S_ROBOHOOD_STND}, // S_ROBOHOOD_SHOOT - {SPR_ARCH, 1, 1, {A_BunnyHop}, 8, 5, S_ROBOHOOD_JUMP2}, // S_ROBOHOOD_JUMP - {SPR_ARCH, 1, 1, {A_HoodThink}, 0, 0, S_ROBOHOOD_JUMP2}, // S_ROBOHOOD_JUMP2 - {SPR_ARCH, 0, 1, {A_HoodThink}, 0, 0, S_ROBOHOOD_FALL}, // S_ROBOHOOD_FALL + {SPR_ARCH, 0, 4, {A_Look}, 2048<hnext = luaL_checkudata(L, 3, META_MOBJ); + if (lua_isnil(L, 3)) + P_SetTarget(&mo->hnext, NULL); + else + { + mobj_t *hnext = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); + P_SetTarget(&mo->hnext, hnext); + } break; case mobj_hprev: - mo->hprev = luaL_checkudata(L, 3, META_MOBJ); + if (lua_isnil(L, 3)) + P_SetTarget(&mo->hprev, NULL); + else + { + mobj_t *hprev = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); + P_SetTarget(&mo->hprev, hprev); + } break; case mobj_type: // yeah sure, we'll let you change the mobj's type. { diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 9b5aa6fdc..4cf63e704 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -130,6 +130,8 @@ static int player_get(lua_State *L) lua_pushangle(L, plr->drawangle); else if (fastcmp(field,"rings")) lua_pushinteger(L, plr->rings); + else if (fastcmp(field,"spheres")) + lua_pushinteger(L, plr->spheres); else if (fastcmp(field,"pity")) lua_pushinteger(L, plr->pity); else if (fastcmp(field,"currentweapon")) @@ -298,8 +300,8 @@ 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,"finishedrings")) - lua_pushinteger(L, plr->finishedrings); + else if (fastcmp(field,"finishedspheres")) + lua_pushinteger(L, plr->finishedspheres); else if (fastcmp(field,"marescore")) lua_pushinteger(L, plr->marescore); else if (fastcmp(field,"lastmarescore")) @@ -400,6 +402,8 @@ static int player_set(lua_State *L) plr->drawangle = luaL_checkangle(L, 3); else if (fastcmp(field,"rings")) plr->rings = (INT32)luaL_checkinteger(L, 3); + else if (fastcmp(field,"spheres")) + plr->spheres = (INT32)luaL_checkinteger(L, 3); else if (fastcmp(field,"pity")) plr->pity = (SINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"currentweapon")) @@ -452,7 +456,7 @@ static int player_set(lua_State *L) else if (fastcmp(field,"followitem")) plr->followitem = luaL_checkinteger(L, 3); else if (fastcmp(field,"followmobj")) - plr->followmobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); + P_SetTarget(&plr->followmobj, *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ))); else if (fastcmp(field,"actionspd")) plr->actionspd = (INT32)luaL_checkinteger(L, 3); else if (fastcmp(field,"mindash")) @@ -578,8 +582,8 @@ 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,"finishedrings")) - plr->finishedrings = (INT16)luaL_checkinteger(L, 3); + else if (fastcmp(field,"finishedspheres")) + plr->finishedspheres = (INT16)luaL_checkinteger(L, 3); else if (fastcmp(field,"marescore")) plr->marescore = (UINT32)luaL_checkinteger(L, 3); else if (fastcmp(field,"lastmarescore")) diff --git a/src/m_cheat.c b/src/m_cheat.c index 174e2780d..5ac742270 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -499,56 +499,211 @@ void Command_Teleport_f(void) REQUIRE_INLEVEL; REQUIRE_SINGLEPLAYER; - if (COM_Argc() < 3 || COM_Argc() > 7) + if (COM_Argc() < 3 || COM_Argc() > 11) { - CONS_Printf(M_GetText("teleport -x -y -z : teleport to a location\n")); + CONS_Printf(M_GetText("teleport -x -y -z -ang -aim : teleport to a location\nteleport -sp : teleport to specified checkpoint\n")); return; } if (!p->mo) return; - i = COM_CheckParm("-x"); - if (i) - intx = atoi(COM_Argv(i + 1)); - else - { - CONS_Alert(CONS_NOTICE, M_GetText("%s value not specified\n"), "X"); - return; - } - - i = COM_CheckParm("-y"); - if (i) - inty = atoi(COM_Argv(i + 1)); - else - { - CONS_Alert(CONS_NOTICE, M_GetText("%s value not specified\n"), "Y"); - return; - } - - ss = R_PointInSubsector(intx*FRACUNIT, inty*FRACUNIT); - if (!ss || ss->sector->ceilingheight - ss->sector->floorheight < p->mo->height) - { - CONS_Alert(CONS_NOTICE, M_GetText("Not a valid location.\n")); - return; - } - i = COM_CheckParm("-z"); + i = COM_CheckParm("-sp"); if (i) { - intz = atoi(COM_Argv(i + 1)); - intz <<= FRACBITS; - if (intz < ss->sector->floorheight) - intz = ss->sector->floorheight; - if (intz > ss->sector->ceilingheight - p->mo->height) - intz = ss->sector->ceilingheight - p->mo->height; + INT32 starpostnum = atoi(COM_Argv(i + 1)); // starpost number + INT32 starpostpath = atoi(COM_Argv(i + 2)); // quick, dirty way to distinguish between paths + + if (starpostnum < 0 || starpostpath < 0) + { + CONS_Alert(CONS_NOTICE, M_GetText("Negative starpost indexing is not valid.\n")); + return; + } + + if (!starpostnum) // spawnpoints... + { + mapthing_t *mt; + + if (starpostpath >= numcoopstarts) + { + CONS_Alert(CONS_NOTICE, M_GetText("Player %d spawnpoint not found (%d max).\n"), starpostpath+1, numcoopstarts-1); + return; + } + + mt = playerstarts[starpostpath]; // Given above check, should never be NULL. + intx = mt->x<y<sector->ceilingheight - ss->sector->floorheight < p->mo->height) + { + CONS_Alert(CONS_NOTICE, M_GetText("Spawnpoint not in a valid location.\n")); + return; + } + + // Flagging a player's ambush will make them start on the ceiling + // Objectflip inverts + if (!!(mt->options & MTF_AMBUSH) ^ !!(mt->options & MTF_OBJECTFLIP)) + { + intz = ss->sector->ceilingheight - p->mo->height; + if (mt->options >> ZSHIFT) + intz -= ((mt->options >> ZSHIFT) << FRACBITS); + } + else + { + intz = ss->sector->floorheight; + if (mt->options >> ZSHIFT) + intz += ((mt->options >> ZSHIFT) << FRACBITS); + } + + if (mt->options & MTF_OBJECTFLIP) // flip the player! + { + p->mo->eflags |= MFE_VERTICALFLIP; + p->mo->flags2 |= MF2_OBJECTFLIP; + } + else + { + p->mo->eflags &= ~MFE_VERTICALFLIP; + p->mo->flags2 &= ~MF2_OBJECTFLIP; + } + + localangle = p->mo->angle = p->drawangle = FixedAngle(mt->angle<next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo2 = (mobj_t *)th; + + if (mo2->type != MT_STARPOST) + continue; + + if (mo2->health != starpostnum) + { + if (mo2->health > starpostmax) + starpostmax = mo2->health; + continue; + } + + if (intz--) + continue; + + break; + } + + if (th == &thinkercap) + { + if (intz == starpostpath) + CONS_Alert(CONS_NOTICE, M_GetText("No starpost of position %d found (%d max).\n"), starpostnum, starpostmax); + else + CONS_Alert(CONS_NOTICE, M_GetText("Starpost of position %d, %d not found (%d, %d max).\n"), starpostnum, starpostpath, starpostmax, (starpostpath-intz)-1); + return; + } + + ss = R_IsPointInSubsector(mo2->x, mo2->y); + if (!ss || ss->sector->ceilingheight - ss->sector->floorheight < p->mo->height) + { + CONS_Alert(CONS_NOTICE, M_GetText("Starpost not in a valid location.\n")); + return; + } + + intx = mo2->x; + inty = mo2->y; + intz = mo2->z; + + if (mo2->flags2 & MF2_OBJECTFLIP) // flip the player! + { + p->mo->eflags |= MFE_VERTICALFLIP; + p->mo->flags2 |= MF2_OBJECTFLIP; + } + else + { + p->mo->eflags &= ~MFE_VERTICALFLIP; + p->mo->flags2 &= ~MF2_OBJECTFLIP; + } + + localangle = p->mo->angle = p->drawangle = mo2->angle; + } + + CONS_Printf(M_GetText("Teleporting to checkpoint %d, %d...\n"), starpostnum, starpostpath); } else - intz = ss->sector->floorheight; + { + i = COM_CheckParm("-nop"); // undocumented stupid addition to allow pivoting on the spot with -ang and -aim + if (i) + { + intx = p->mo->x; + inty = p->mo->y; + } + else + { + i = COM_CheckParm("-x"); + if (i) + intx = atoi(COM_Argv(i + 1))<sector->ceilingheight - ss->sector->floorheight < p->mo->height) + { + CONS_Alert(CONS_NOTICE, M_GetText("Not a valid location.\n")); + return; + } + i = COM_CheckParm("-z"); + if (i) + { + intz = atoi(COM_Argv(i + 1))<sector->floorheight) + intz = ss->sector->floorheight; + if (intz > ss->sector->ceilingheight - p->mo->height) + intz = ss->sector->ceilingheight - p->mo->height; + } + else + intz = ((p->mo->eflags & MFE_VERTICALFLIP) ? ss->sector->ceilingheight : ss->sector->floorheight); + + i = COM_CheckParm("-ang"); + if (i) + localangle = p->drawangle = p->mo->angle = FixedAngle(atoi(COM_Argv(i + 1))<= ANGLE_90 && aim <= ANGLE_270) + { + CONS_Alert(CONS_NOTICE, M_GetText("Not a valid aiming angle (between +/-90).\n")); + return; + } + localaiming = p->aiming = aim; + } + + CONS_Printf(M_GetText("Teleporting to %d, %d, %d...\n"), FixedInt(intx), FixedInt(inty), FixedInt(intz)); + } P_MapStart(); - if (!P_TeleportMove(p->mo, intx*FRACUNIT, inty*FRACUNIT, intz)) + if (!P_TeleportMove(p->mo, intx, inty, intz)) CONS_Alert(CONS_WARNING, M_GetText("Unable to teleport to that spot!\n")); else S_StartSound(p->mo, sfx_mixup); @@ -728,7 +883,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) || !useNightsSS) + if (!G_IsSpecialStage(gamemap) || !(maptol & TOL_NIGHTS)) players[consoleplayer].totalring -= atoi(COM_Argv(1)); //undo totalring addition done in P_GivePlayerRings G_SetGameModified(multiplayer); @@ -744,9 +899,15 @@ void Command_Setlives_f(void) if (COM_Argc() > 1) { - // P_GivePlayerLives does value clamping - players[consoleplayer].lives = 0; - P_GivePlayerLives(&players[consoleplayer], atoi(COM_Argv(1))); + SINT8 lives = atoi(COM_Argv(1)); + if (lives == -1) + players[consoleplayer].lives = 0x7f; // infinity! + else + { + // P_GivePlayerLives does value clamping + players[consoleplayer].lives = 0; + P_GivePlayerLives(&players[consoleplayer], atoi(COM_Argv(1))); + } G_SetGameModified(multiplayer); } @@ -1010,7 +1171,7 @@ void OP_NightsObjectplace(player_t *player) mt->options = (UINT16)((player->mo->z - fheight)>>FRACBITS); mt->angle = (INT16)(mt->angle+(INT16)((FixedInt(FixedDiv(temp*FRACUNIT, 360*(FRACUNIT/256))))<<8)); - P_SpawnHoopsAndRings(mt); + P_SpawnHoopsAndRings(mt, false); } // This places a bumper! @@ -1024,26 +1185,26 @@ void OP_NightsObjectplace(player_t *player) P_SpawnMapThing(mt); } - // This places a ring! + // This places a sphere! if (cmd->buttons & BT_WEAPONNEXT) { player->pflags |= PF_ATTACKDOWN; if (!OP_HeightOkay(player, false)) return; - mt = OP_CreateNewMapThing(player, (UINT16)mobjinfo[MT_RING].doomednum, false); - P_SpawnHoopsAndRings(mt); + mt = OP_CreateNewMapThing(player, (UINT16)mobjinfo[MT_BLUESPHERE].doomednum, false); + P_SpawnHoopsAndRings(mt, false); } - // This places a wing item! + // This places a ring! if (cmd->buttons & BT_WEAPONPREV) { player->pflags |= PF_ATTACKDOWN; if (!OP_HeightOkay(player, false)) return; - mt = OP_CreateNewMapThing(player, (UINT16)mobjinfo[MT_NIGHTSWING].doomednum, false); - P_SpawnHoopsAndRings(mt); + mt = OP_CreateNewMapThing(player, (UINT16)mobjinfo[MT_RING].doomednum, false); + P_SpawnHoopsAndRings(mt, false); } // This places a custom object as defined in the console cv_mapthingnum. @@ -1077,12 +1238,12 @@ void OP_NightsObjectplace(player_t *player) if (mt->type == 300 // Ring || mt->type == 308 || mt->type == 309 // Team Rings - || mt->type == 1706 // Nights Wing + || mt->type == 1706 // Sphere || (mt->type >= 600 && mt->type <= 609) // Placement patterns || mt->type == 1705 || mt->type == 1713 // NiGHTS Hoops || mt->type == 1800) // Mario Coin { - P_SpawnHoopsAndRings(mt); + P_SpawnHoopsAndRings(mt, false); } else P_SpawnMapThing(mt); @@ -1227,7 +1388,7 @@ void OP_ObjectplaceMovement(player_t *player) || mt->type == 1705 || mt->type == 1713 // NiGHTS Hoops || mt->type == 1800) // Mario Coin { - P_SpawnHoopsAndRings(mt); + P_SpawnHoopsAndRings(mt, false); } else P_SpawnMapThing(mt); diff --git a/src/m_fixed.c b/src/m_fixed.c index ce7471a28..014457386 100644 --- a/src/m_fixed.c +++ b/src/m_fixed.c @@ -33,7 +33,9 @@ */ fixed_t FixedMul(fixed_t a, fixed_t b) { - return (fixed_t)((((INT64)a * b) ) / FRACUNIT); + // Need to cast to unsigned before shifting to avoid undefined behaviour + // for negative integers + return (fixed_t)(((UINT64)((INT64)a * b)) >> FRACBITS); } #endif //__USE_C_FIXEDMUL__ diff --git a/src/m_menu.c b/src/m_menu.c index f10c6b653..b11873adb 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -458,7 +458,7 @@ consvar_t cv_ghost_guest = {"ghost_guest", "Show", CV_SAVE, ghost2_cons_ static CV_PossibleValue_t dummyteam_cons_t[] = {{0, "Spectator"}, {1, "Red"}, {2, "Blue"}, {0, NULL}}; static CV_PossibleValue_t dummyscramble_cons_t[] = {{0, "Random"}, {1, "Points"}, {0, NULL}}; static CV_PossibleValue_t ringlimit_cons_t[] = {{0, "MIN"}, {9999, "MAX"}, {0, NULL}}; -static CV_PossibleValue_t liveslimit_cons_t[] = {{0, "MIN"}, {99, "MAX"}, {0, NULL}}; +static CV_PossibleValue_t liveslimit_cons_t[] = {{-1, "MIN"}, {99, "MAX"}, {0, NULL}}; static CV_PossibleValue_t dummymares_cons_t[] = { {-1, "END"}, {0,"Overall"}, {1,"Mare 1"}, {2,"Mare 2"}, {3,"Mare 3"}, {4,"Mare 4"}, {5,"Mare 5"}, {6,"Mare 6"}, {7,"Mare 7"}, {8,"Mare 8"}, {0,NULL} }; @@ -4938,6 +4938,7 @@ static void M_DrawAddons(void) { INT32 x, y; ssize_t i, max; + const char* topstr; // hack - need to refresh at end of frame to handle addfile... if (refreshdirmenu & M_AddonsRefresh()) @@ -4949,9 +4950,16 @@ static void M_DrawAddons(void) if (addonsresponselimit) addonsresponselimit--; - V_DrawCenteredString(BASEVIDWIDTH/2, 4+offs, 0, (Playing() - ? "\x85""Adding files mid-game may cause problems." - : LOCATIONSTRING)); + if (Playing()) + topstr = "\x85""Adding files mid-game may cause problems."; + else if (savemoddata) + topstr = "\x83""Add-on has its own data, saving enabled."; + else if (modifiedgame) + topstr = "\x87""Game is modified, saving is disabled."; + else + topstr = LOCATIONSTRING; + + V_DrawCenteredString(BASEVIDWIDTH/2, 4+offs, 0, topstr); if (numwadfiles <= mainwads+1) y = 0; @@ -5222,7 +5230,7 @@ static void M_HandleAddons(INT32 choice) case EXT_SOC: case EXT_WAD: case EXT_PK3: - COM_BufAddText(va("addfile %s%s", menupath, dirmenu[dir_on[menudepthleft]]+DIR_STRING)); + COM_BufAddText(va("addfile \"%s%s\"", menupath, dirmenu[dir_on[menudepthleft]]+DIR_STRING)); addonsresponselimit = 5; break; default: @@ -5266,7 +5274,10 @@ static void M_PandorasBox(INT32 choice) { (void)choice; CV_StealthSetValue(&cv_dummyrings, max(players[consoleplayer].rings, 0)); - CV_StealthSetValue(&cv_dummylives, players[consoleplayer].lives); + if (players[consoleplayer].lives == 0x7f) + CV_StealthSetValue(&cv_dummylives, -1); + else + CV_StealthSetValue(&cv_dummylives, players[consoleplayer].lives); CV_StealthSetValue(&cv_dummycontinues, players[consoleplayer].continues); SR_PandorasBox[6].status = ((players[consoleplayer].charflags & SF_SUPER) #ifndef DEVELOP @@ -8441,6 +8452,13 @@ Update the maxplayers label... static void M_ConnectIP(INT32 choice) { (void)choice; + + if (*setupm_ip == 0) + { + M_StartMessage("You must specify an IP address.\n", NULL, MM_NOTHING); + return; + } + COM_BufAddText(va("connect \"%s\"\n", setupm_ip)); // A little "please wait" message. @@ -8822,7 +8840,7 @@ static void M_HandleSetupMultiPlayer(INT32 choice) break; S_StartSound(NULL,sfx_menu1); // Tails l = strlen(setupm_name); - if (l < MAXPLAYERNAME-1) + if (l < MAXPLAYERNAME) { setupm_name[l] = (char)choice; setupm_name[l+1] = 0; diff --git a/src/m_misc.c b/src/m_misc.c index bf637f7c3..50b6d7a05 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -56,7 +56,9 @@ typedef off_t off64_t; #endif #endif -#if defined (_WIN32) +#if defined(__MINGW32__) && ((__GNUC__ > 7) || (__GNUC__ == 6 && __GNUC_MINOR__ >= 3)) +#define PRIdS "u" +#elif defined (_WIN32) #define PRIdS "Iu" #elif defined (DJGPP) #define PRIdS "u" diff --git a/src/p_enemy.c b/src/p_enemy.c index 331202cbe..9235a1d0f 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -1,10619 +1,11639 @@ -// 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_JetJawRoam(mobj_t *actor); -void A_JetJawChomp(mobj_t *actor); -void A_PointyThink(mobj_t *actor); -void A_CheckBuddy(mobj_t *actor); -void A_HoodThink(mobj_t *actor); -void A_ArrowCheck(mobj_t *actor); -void A_SnailerThink(mobj_t *actor); -void A_SharpChase(mobj_t *actor); -void A_SharpSpin(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_ParticleSpawn(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); -//for p_enemy.c -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_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); - -// -// 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); - 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: 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); -} - -// 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); -} - -// Function: A_HoodThink -// -// Description: Thinker for Robo-Hood -// -// var1 = unused -// var2 = unused -// -void A_HoodThink(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_HoodThink", actor)) - return; -#endif - // Currently in the air... - if (!(actor->eflags & MFE_VERTICALFLIP) && actor->z > actor->floorz) - { - if (actor->momz > 0) - P_SetMobjStateNF(actor, actor->info->xdeathstate); // Rising - else - P_SetMobjStateNF(actor, actor->info->raisestate); // Falling - - return; - } - else if ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height < actor->ceilingz) - { - if (actor->momz < 0) - P_SetMobjStateNF(actor, actor->info->xdeathstate); // Rising - else - P_SetMobjStateNF(actor, actor->info->raisestate); // Falling - - return; - } - - if (actor->state == &states[actor->info->xdeathstate] - || actor->state == &states[actor->info->raisestate]) - P_SetMobjStateNF(actor, actor->info->seestate); - - if (!actor->target) - { - P_SetMobjState(actor, actor->info->spawnstate); - return; - } - - A_FaceTarget(actor); // Aiming... aiming... - - if (--actor->reactiontime > 0) - return; - - // Shoot, if not too close (cheap shots are lame) - if ((P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) > FixedMul(192*FRACUNIT, actor->scale)) - || (actor->spawnpoint && (actor->spawnpoint->options & MTF_AMBUSH))) // If you can't jump, might as well shoot regardless of distance! - P_SetMobjState(actor, actor->info->missilestate); - else if (!(actor->spawnpoint && (actor->spawnpoint->options & MTF_AMBUSH)))// But we WILL jump! - P_SetMobjState(actor, actor->info->painstate); - - actor->reactiontime = actor->info->reactiontime; -} - -// Function: A_ArrowCheck -// -// Description: Checks arrow direction and adjusts sprite accordingly -// -// var1 = unused -// var2 = unused -// -void A_ArrowCheck(mobj_t *actor) -{ - fixed_t x,y,z; - angle_t angle; - fixed_t dist; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ArrowCheck", actor)) - return; -#endif - - // Movement vector - x = actor->momx; - y = actor->momy; - z = actor->momz; - - // Calculate the angle of movement. - /* - Z - / | - / | - / | - 0------dist(X,Y) - */ - - dist = P_AproxDistance(x, y); - - angle = R_PointToAngle2(0, 0, dist, z); - - if (angle > ANG20 && angle <= ANGLE_180) - P_SetMobjStateNF(actor, actor->info->raisestate); - else if (angle < ANG340 && angle > ANGLE_180) - P_SetMobjStateNF(actor, actor->info->xdeathstate); - else - P_SetMobjStateNF(actor, actor->info->spawnstate); -} - -// 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 Sharps -// -// var1 = unused -// var2 = unused -// -void A_SharpChase(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SharpChase", actor)) - return; -#endif - - if (!actor->health) - { - P_SetMobjState(actor, actor->info->deathstate); - return; - } - - 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 Sharps -// -// var1 = unused -// var2 = unused -// -void A_SharpSpin(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SharpSpin", actor)) - return; -#endif - - if (!actor->health) - { - P_SetMobjState(actor, actor->info->deathstate); - return; - } - - if (actor->threshold && actor->target) - { - actor->angle += ANGLE_22h; - P_Thrust(actor, R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y), FixedMul(actor->info->speed*FRACUNIT, actor->scale)); - actor->threshold--; - } - else - { - actor->reactiontime = actor->info->reactiontime; - P_SetMobjState(actor, actor->info->spawnstate); - - var1 = 1; - A_Look(actor); - if (actor->target) - actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); - } -} - -// 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); - actor->flags2 |= MF2_STANDONME; - 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->flags2 &= ~MF2_STANDONME; - 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 = unused -// var2 = unused -// -void A_Explode(mobj_t *actor) -{ -#ifdef HAVE_BLUA - if (LUA_CallAction("A_Explode", actor)) - return; -#endif - P_RadiusAttack(actor, actor->target, actor->info->damage); -} - -// 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_ParticleSpawn -// -// Description: Hyper-specialised function for spawning a particle for MT_PARTICLEGEN. -// -// var1 = unused -// var2 = unused -// -void A_ParticleSpawn(mobj_t *actor) -{ - INT32 i = 0; - mobj_t *spawn; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_ParticleSpawn", actor)) - return; -#endif - if (!actor->health) - return; - - if (!actor->lastlook) - return; - - if (!actor->threshold) - return; - - for (i = 0; i < actor->lastlook; i++) - { - spawn = P_SpawnMobj( - actor->x + FixedMul(FixedMul(actor->friction, actor->scale), FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), - actor->y + FixedMul(FixedMul(actor->friction, actor->scale), FINESINE(actor->angle>>ANGLETOFINESHIFT)), - actor->z, - (mobjtype_t)actor->threshold); - P_SetScale(spawn, actor->scale); - spawn->momz = FixedMul(actor->movefactor, spawn->scale); - spawn->destscale = spawn->scale/100; - spawn->scalespeed = spawn->scale/actor->health; - spawn->tics = (tic_t)actor->health; - spawn->flags2 |= (actor->flags2 & MF2_OBJECTFLIP); - spawn->angle += P_RandomKey(36)*ANG10; // irrelevant for default objects but might make sense for some custom ones - - actor->angle += actor->movedir; - } - - actor->angle += (angle_t)actor->movecount; - actor->tics = (tic_t)actor->reactiontime; -} - -// 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 (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, 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->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 (actor->type == MT_SPECIALSPIKEBALL) // don't remove this, these spikeballs share the same states as the rotating spikeballs - return; - - 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) -{ - boolean firsttime = true; - UINT8 mlength = 4; - mobj_t *spawnee; -#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->health = actor->angle>>ANGLETOFINESHIFT; - actor->threshold = 0; - actor->movefactor = actor->threshold; - actor->friction = 128; - - while (mlength > 0) - { - spawnee = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLMACECHAIN); - - P_SetTarget(&spawnee->target, actor); - - spawnee->threshold = 0; - spawnee->reactiontime = mlength; - - if (firsttime) - { - // This is the outermost link in the chain - spawnee->flags2 |= MF2_AMBUSH; - firsttime = false; - } - - 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; -#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 & 1) - actor->flags2 |= MF2_DONTDRAW; - else - actor->flags2 &= ~MF2_DONTDRAW; - - if (actor->reactiontime > 0) - actor->reactiontime--; - - if (actor->fuse < 2) - { - actor->fuse = 0; - actor->flags2 &= ~MF2_FRET; - } - - // Hover mode - if (actor->health > 1 || actor->fuse) - { - 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 || !(actor->target->flags & MF_SHOOTABLE)) - { - // 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 && actor->health > 1) - { - if (dist < FixedMul(128*FRACUNIT, actor->scale) - && ((actor->target->player->pflags & PF_JUMPED) || (actor->target->player->pflags & PF_SPINNING))) - { - // Auugh! He's trying to kill you! Strafe! STRAAAAFFEEE!! - if (actor->target->momx || actor->target->momy) - 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 (actor->health > 1) - P_InstaThrust(actor, actor->angle, FixedMul(10*FRACUNIT, actor->scale)); - } - else if (!actor->reactiontime) - { - if (actor->health > 1) // 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(60*FRACUNIT, actor->scale)); - actor->threshold = 1; - } - } - actor->reactiontime = 2*TICRATE + P_RandomByte()/2; - } - - if (actor->health == 1) - P_Thrust(actor, actor->angle, 1); - - // Pogo Mode - if (!actor->fuse && actor->health == 1 && actor->z <= actor->floorz) - { - 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->tracer || !actor->tracer->health) && actor->threshold != 42) - { - P_SetTarget(&actor->tracer, NULL); - actor->threshold = 42; - P_SetMobjState(actor, actor->info->painstate); - actor->flags |= MF_SPECIAL|MF_SHOOTABLE; - 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; - } - - 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 || - !(actor->target->player->powers[pw_carry] == CR_NIGHTSMODE) || !actor->target->player->nightstime - // 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) // 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); - } - 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); - } - } - -} - -// 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_SpawnMobj(actor->x, actor->y, actor->z, actor->type); - newObject->angle = actor->angle; - P_SetScale(newObject, actor->scale); - newObject->destscale = actor->destscale; - 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 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); - - 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 -// -void A_FlickyFly(mobj_t *actor) -{ - // We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyFly instead. - //INT32 locvar1 = var1; - //INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FlickyFly", actor)) - return; -#endif - P_InternalFlickyFly(actor, var1, var2, - 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 -// -void A_FlickySoar(mobj_t *actor) -{ - // We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyFly instead. - //INT32 locvar1 = var1; - //INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FlickySoar", actor)) - return; -#endif - P_InternalFlickyFly(actor, var1, var2, - 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) -{ - // We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyHop instead. - //INT32 locvar1 = var1; - //INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FlickyHop", actor)) - return; -#endif - P_InternalFlickyHop(actor, var1, var2, 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 (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 (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) -{ - // We're not setting up locvars here - it passes var1 and var2 through to A_FlickyCheck instead. - //INT32 locvar1 = var1; - //INT32 locvar2 = var2; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_FlickyFlutter", actor)) - return; -#endif - 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 = momz of particle. -// var2 = chance of particle spawn -// -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 (!P_RandomChance(locvar2)) - return; - - if (!type) - return; - - rad = 2*actor->radius>>FRACBITS; - hei = actor->height>>FRACBITS; - particle = P_SpawnMobjFromMobj(actor, - P_RandomRange(rad, -rad)<momx = actor->momy = actor->momz = 0; - - fade = P_SpawnGhostMobj(actor); - fade->frame = actor->frame; - - if (!(locvar1 & 2)) - { - fade->fuse = 15; - fade->flags2 |= MF2_BOSSNOTRAP; - } - else - fade->fuse = 20; - - if (!(locvar1 & 4)) - 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. - actor->momx = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINECOSINE(actor->angle >> ANGLETOFINESHIFT)); - actor->momy = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), 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 -} +// 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_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 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); + + 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 +// +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 +// +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 (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 (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; + INT8 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); +} \ No newline at end of file diff --git a/src/p_floor.c b/src/p_floor.c index c72de6b70..0e28b831f 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -2144,6 +2144,7 @@ void T_EachTimeThinker(levelspecthink_t *eachtime) boolean floortouch = false; fixed_t bottomheight, topheight; msecnode_t *node; + ffloor_t *rover; for (i = 0; i < MAXPLAYERS; i++) { @@ -2191,6 +2192,19 @@ void T_EachTimeThinker(levelspecthink_t *eachtime) { targetsec = §ors[targetsecnum]; + // Find the FOF corresponding to the control linedef + for (rover = targetsec->ffloors; rover; rover = rover->next) + { + if (rover->master == sec->lines[i]) + break; + } + + if (!rover) // This should be impossible, but don't complain if it is the case somehow + continue; + + if (!(rover->flags & FF_EXISTS)) // If the FOF does not "exist", we pretend that nobody's there + continue; + for (j = 0; j < MAXPLAYERS; j++) { if (!playeringame[j]) @@ -3304,7 +3318,7 @@ INT32 EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher) P_SetThingPosition(thing); if (thing->flags & MF_SHOOTABLE) P_DamageMobj(thing, puncher, puncher, 1, 0); - else if (thing->type == MT_RING || thing->type == MT_COIN) + else if (thing->type == MT_RING || thing->type == MT_COIN || thing->type == MT_TOKEN) { thing->momz = FixedMul(3*FRACUNIT, thing->scale); P_TouchSpecialThing(thing, puncher, false); diff --git a/src/p_inter.c b/src/p_inter.c index c57d348fc..5737e2c2a 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -23,6 +23,7 @@ #include "hu_stuff.h" #include "lua_hook.h" #include "m_cond.h" // unlockables, emblems, etc +#include "p_setup.h" #include "m_cheat.h" // objectplace #include "m_misc.h" #include "v_video.h" // video flags for CEchos @@ -103,8 +104,13 @@ void P_ClearStarPost(INT32 postnum) mo2 = (mobj_t *)th; - if (mo2->type == MT_STARPOST && mo2->health <= postnum) - P_SetMobjState(mo2, mo2->info->seestate); + if (mo2->type != MT_STARPOST) + continue; + + if (mo2->health > postnum) + continue; + + P_SetMobjState(mo2, mo2->info->seestate); } return; } @@ -349,8 +355,8 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (player->spectator) return; - // Ignore eggman in "ouchie" mode - if (special->flags & MF_BOSS && special->flags2 & MF2_FRET) + // Ignore multihits in "ouchie" mode + if (special->flags & (MF_ENEMY|MF_BOSS) && special->flags2 & MF2_FRET) return; #ifdef HAVE_BLUA @@ -363,14 +369,52 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) ? (((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) ? 1 : 2) : 0); - if (special->flags & MF_BOSS) + if ((special->flags & (MF_ENEMY|MF_BOSS)) && !(special->flags & MF_MISSILE)) { + //////////////////////////////////////////////////////// + /////ENEMIES & BOSSES!!///////////////////////////////// + //////////////////////////////////////////////////////// + if (special->type == MT_BLACKEGGMAN) { P_DamageMobj(toucher, special, special, 1, 0); // ouch return; } + if (special->type == MT_BIGMINE) + { + special->momx = toucher->momx/3; + special->momy = toucher->momy/3; + special->momz = toucher->momz/3; + toucher->momx /= -8; + toucher->momy /= -8; + toucher->momz /= -8; + special->flags &= ~MF_SPECIAL; + if (special->info->activesound) + S_StartSound(special, special->info->activesound); + P_SetTarget(&special->tracer, toucher); + player->homing = 0; + return; + } + + if (special->type == MT_GSNAPPER && !elementalpierce + && toucher->z < special->z + special->height && toucher->z + toucher->height > special->z + && P_DamageMobj(toucher, special, special, 1, DMG_SPIKE)) + return; // Can only hit snapper from above + + if (special->type == MT_SPINCUSHION + && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0)) + { + if (player->pflags & PF_BOUNCING) + { + toucher->momz = -toucher->momz; + P_DoAbilityBounce(player, false); + return; + } + else if (P_DamageMobj(toucher, special, special, 1, DMG_SPIKE)) + return; // Cannot hit sharp from above + } + if (((player->powers[pw_carry] == CR_NIGHTSMODE) && (player->pflags & PF_DRILLING)) || ((player->pflags & PF_JUMPED) && (!(player->pflags & PF_NOJUMPDAMAGE) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY))) || (player->pflags & (PF_SPINNING|PF_GLIDING)) @@ -388,15 +432,18 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } if (player->pflags & PF_BOUNCING) P_DoAbilityBounce(player, false); - toucher->momx = -toucher->momx; - toucher->momy = -toucher->momy; + if (special->info->spawnhealth > 1) // Multi-hit? Bounce back! + { + toucher->momx = -toucher->momx; + toucher->momy = -toucher->momy; + } P_DamageMobj(special, toucher, toucher, 1, 0); } else if (((toucher->z < special->z && !(toucher->eflags & MFE_VERTICALFLIP)) || (toucher->z + toucher->height > special->z + special->height && (toucher->eflags & MFE_VERTICALFLIP))) && player->charability == CA_FLY && (player->powers[pw_tailsfly] - || toucher->state-states == S_PLAY_FLY_TIRED)) // Tails can shred stuff with his propeller. + || toucher->state-states == S_PLAY_FLY_TIRED)) // Tails can shred stuff with her propeller. { toucher->momz = -toucher->momz/2; @@ -407,66 +454,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; } - else if ((special->flags & MF_ENEMY) && !(special->flags & MF_MISSILE)) - { - //////////////////////////////////////////////////////// - /////ENEMIES!!////////////////////////////////////////// - //////////////////////////////////////////////////////// - if (special->type == MT_GSNAPPER && !(((player->powers[pw_carry] == CR_NIGHTSMODE) && (player->pflags & PF_DRILLING)) - || player->powers[pw_invulnerability] || player->powers[pw_super] || elementalpierce) - && toucher->z < special->z + special->height && toucher->z + toucher->height > special->z - && !(player->powers[pw_shield] & SH_PROTECTSPIKE)) - { - // Can only hit snapper from above - P_DamageMobj(toucher, special, special, 1, DMG_SPIKE); - } - else if (special->type == MT_SHARP - && ((special->state == &states[special->info->xdeathstate]) || (toucher->z > special->z + special->height/2)) - && !(player->powers[pw_shield] & SH_PROTECTSPIKE)) - { - if (player->pflags & PF_BOUNCING) - { - toucher->momz = -toucher->momz; - P_DoAbilityBounce(player, false); - } - else // Cannot hit sharp from above or when red and angry - P_DamageMobj(toucher, special, special, 1, DMG_SPIKE); - } - else if (((player->powers[pw_carry] == CR_NIGHTSMODE) && (player->pflags & PF_DRILLING)) - || ((player->pflags & PF_JUMPED) && (!(player->pflags & PF_NOJUMPDAMAGE) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY))) - || (player->pflags & (PF_SPINNING|PF_GLIDING)) - || (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2) - || ((player->charflags & SF_STOMPDAMAGE || player->pflags & PF_BOUNCING) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0)) - || player->powers[pw_invulnerability] || player->powers[pw_super]) // Do you possess the ability to subdue the object? - { - if ((P_MobjFlip(toucher)*toucher->momz < 0) && (elementalpierce != 1)) - { - if (elementalpierce == 2) - P_DoBubbleBounce(player); - else if (!(player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)) - toucher->momz = -toucher->momz; - } - if (player->pflags & PF_BOUNCING) - P_DoAbilityBounce(player, false); - - P_DamageMobj(special, toucher, toucher, 1, 0); - } - else if (((toucher->z < special->z && !(toucher->eflags & MFE_VERTICALFLIP)) - || (toucher->z + toucher->height > special->z + special->height && (toucher->eflags & MFE_VERTICALFLIP))) // Flame is bad at logic - JTE - && player->charability == CA_FLY - && (player->powers[pw_tailsfly] - || toucher->state-states == S_PLAY_FLY_TIRED)) // Tails can shred stuff with his propeller. - { - if (P_MobjFlip(toucher)*toucher->momz < 0) - toucher->momz = -toucher->momz/2; - - P_DamageMobj(special, toucher, toucher, 1, 0); - } - else - P_DamageMobj(toucher, special, special, 1, 0); - - return; - } else if (special->flags & MF_FIRE) { P_DamageMobj(toucher, special, special, 1, DMG_FIRE); @@ -490,43 +477,45 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) /* FALLTHRU */ case MT_RING: case MT_FLINGRING: - if (!(P_CanPickupItem(player, false))) - return; - - special->momx = special->momy = special->momz = 0; - P_GivePlayerRings(player, 1); - - if ((maptol & TOL_NIGHTS) && special->type != MT_FLINGRING) - P_DoNightsScore(player); - break; - case MT_COIN: case MT_FLINGCOIN: - if (!(P_CanPickupItem(player, false))) + case MT_NIGHTSSTAR: + if (!(P_CanPickupItem(player, false)) && !(special->flags2 & MF2_NIGHTSPULL)) return; - special->momx = special->momy = 0; - P_GivePlayerRings(player, 1); - - if ((maptol & TOL_NIGHTS) && special->type != MT_FLINGCOIN) - P_DoNightsScore(player); - break; - case MT_BLUEBALL: - if (!(P_CanPickupItem(player, false))) - return; - - P_GivePlayerRings(player, 1); - special->momx = special->momy = special->momz = 0; - special->destscale = FixedMul(8*FRACUNIT, special->scale); - if (states[special->info->deathstate].tics > 0) - special->scalespeed = FixedDiv(FixedDiv(special->destscale, special->scale), states[special->info->deathstate].tics<scalespeed = 4*FRACUNIT/5; + P_GivePlayerRings(player, 1); + + if ((maptol & TOL_NIGHTS) && special->type != MT_FLINGRING && special->type != MT_FLINGCOIN) + P_DoNightsScore(player); + break; + case MT_BLUESPHERE: + case MT_NIGHTSCHIP: + if (!(P_CanPickupItem(player, false)) && !(special->flags2 & MF2_NIGHTSPULL)) + return; + + special->momx = special->momy = special->momz = 0; + P_GivePlayerSpheres(player, 1); + + if (special->type == MT_BLUESPHERE) + { + special->destscale = ((player->powers[pw_carry] == CR_NIGHTSMODE) ? 4 : 2)*special->scale; + if (states[special->info->deathstate].tics > 0) + special->scalespeed = FixedDiv(FixedDiv(special->destscale, special->scale), states[special->info->deathstate].tics<scalespeed = 4*FRACUNIT/5; + } if (maptol & TOL_NIGHTS) P_DoNightsScore(player); break; + case MT_BOMBSPHERE: + if (!(P_CanPickupItem(player, false)) && !(special->flags2 & MF2_NIGHTSPULL)) + return; + + special->momx = special->momy = special->momz = 0; + P_DamageMobj(toucher, special, special, 1, 0); + break; case MT_AUTOPICKUP: case MT_BOUNCEPICKUP: case MT_SCATTERPICKUP: @@ -581,7 +570,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_AddPlayerScore(player, 1000); if (gametype != GT_COOP || modeattacking) // score only? + { + S_StartSound(toucher, sfx_chchng); break; + } tokenlist += special->health; @@ -593,10 +585,17 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) players->gotcontinue = true; if (P_IsLocalPlayer(player)) S_StartSound(NULL, sfx_s3kac); + else + S_StartSound(toucher, sfx_chchng); } + else + S_StartSound(toucher, sfx_chchng); } else + { token++; + S_StartSound(toucher, sfx_token); + } break; @@ -621,7 +620,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) players[i].exiting = (14*TICRATE)/5 + 1; } - S_StartSound(NULL, sfx_lvpass); + //S_StartSound(NULL, sfx_lvpass); } break; @@ -752,45 +751,86 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // NiGHTS gameplay items and powerups // // ********************************** // case MT_NIGHTSDRONE: - if (player->bot) - return; - if (player->exiting) - return; - if (player->bonustime) { - if (G_IsSpecialStage(gamemap)) //After-mare bonus time/emerald reward in special stages. + boolean spec = G_IsSpecialStage(gamemap); + boolean cangiveemmy = false; + if (player->bot) + return; + if (player->exiting) + return; + if (player->bonustime) { - // only allow the player with the emerald in-hand to leave. - if (toucher->tracer - && toucher->tracer->type == MT_GOTEMERALD) + if (spec) //After-mare bonus time/emerald reward in special stages. { + // only allow the player with the emerald in-hand to leave. + if (toucher->tracer + && toucher->tracer->type == MT_GOTEMERALD) + {} + else // Make sure that SOMEONE has the emerald, at least! + { + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && players[i].playerstate == PST_LIVE + && players[i].mo->tracer + && players[i].mo->tracer->type == MT_GOTEMERALD) + return; + // Well no one has an emerald, so exit anyway! + } + cangiveemmy = true; + // Don't play Ideya sound in special stage mode } - else // Make sure that SOMEONE has the emerald, at least! - { - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && players[i].playerstate == PST_LIVE - && players[i].mo->tracer - && players[i].mo->tracer->type == MT_GOTEMERALD) - return; - // Well no one has an emerald, so exit anyway! - } - P_GiveEmerald(false); - // Don't play Ideya sound in special stage mode + else + S_StartSound(toucher, special->info->activesound); } - else - S_StartSound(toucher, special->info->activesound); - } - else //Initial transformation. Don't allow second chances in special stages! - { - if (player->powers[pw_carry] == CR_NIGHTSMODE) + else //Initial transformation. Don't allow second chances in special stages! + { + if (player->powers[pw_carry] == CR_NIGHTSMODE) + return; + + S_StartSound(toucher, sfx_supert); + } + P_SwitchSpheresBonusMode(false); + if (!(netgame || multiplayer) && !(player->powers[pw_carry] == CR_NIGHTSMODE)) + P_SetTarget(&special->tracer, toucher); + P_NightserizePlayer(player, special->health); // Transform! + if (!spec) + { + if (toucher->tracer) // Move the ideya over to the drone! + { + mobj_t *hnext = special->hnext; + P_SetTarget(&special->hnext, toucher->tracer); + P_SetTarget(&special->hnext->hnext, hnext); // Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo. + P_SetTarget(&special->hnext->target, special); + P_SetTarget(&toucher->tracer, NULL); + if (hnext) + { + special->hnext->extravalue1 = (angle_t)(hnext->extravalue1 - 72*ANG1); + if (special->hnext->extravalue1 > hnext->extravalue1) + special->hnext->extravalue1 -= (72*ANG1)/special->hnext->extravalue1; + } + } + if (player->exiting) // ...then move it back? + { + mobj_t *hnext = special; + while ((hnext = hnext->hnext)) + P_SetTarget(&hnext->target, toucher); + } + return; + } + + if (!cangiveemmy) return; - S_StartSound(toucher, sfx_supert); + if (player->exiting) + P_GiveEmerald(false); + else if (player->mo->tracer && player->mare) + { + P_KillMobj(toucher->tracer, NULL, NULL, 0); // No emerald for you just yet! + S_StartSound(NULL, sfx_ghosty); + special->flags2 |= MF2_DONTDRAW; + } + + return; } - if (!(netgame || multiplayer) && !(player->powers[pw_carry] == CR_NIGHTSMODE)) - P_SetTarget(&special->tracer, toucher); - P_NightserizePlayer(player, special->health); // Transform! - return; case MT_NIGHTSLOOPHELPER: // One second delay if (special->fuse < toucher->fuse - TICRATE) @@ -895,9 +935,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } } - if (!(mo2->type == MT_NIGHTSWING || mo2->type == MT_RING || mo2->type == MT_COIN - || mo2->type == MT_BLUEBALL - || ((mo2->type == MT_EMBLEM) && (mo2->reactiontime & GE_NIGHTSPULL)))) + if (!(mo2->type == MT_RING || mo2->type == MT_COIN + || mo2->type == MT_BLUESPHERE || mo2->type == MT_BOMBSPHERE + || mo2->type == MT_NIGHTSCHIP || mo2->type == MT_NIGHTSSTAR + || ((mo2->type == MT_EMBLEM) && (mo2->reactiontime & GE_NIGHTSPULL)))) continue; // Yay! The thing's in reach! Pull it in! @@ -915,6 +956,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (player->powers[pw_carry] == CR_NIGHTSMODE && !toucher->target) return; + if (toucher->tracer) + return; // Don't have multiple ideya + if (player->mare != special->threshold) // wrong mare return; @@ -922,16 +966,16 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; if (G_IsSpecialStage(gamemap) && !player->exiting) - { // In special stages, share rings. Everyone gives up theirs to the player who touched the capsule + { // In special stages, share spheres. Everyone gives up theirs to the player who touched the capsule for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && (&players[i] != player) && players[i].rings > 0) + if (playeringame[i] && (&players[i] != player) && players[i].spheres > 0) { - player->rings += players[i].rings; - players[i].rings = 0; + player->spheres += players[i].spheres; + players[i].spheres = 0; } } - if (player->rings <= 0 || player->exiting) + if (player->spheres <= 0 || player->exiting) return; // Mark the player as 'pull into the capsule' @@ -1139,17 +1183,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) HU_DoCEcho(M_GetText("\\\\\\\\\\\\\\\\Link Freeze")); } break; - case MT_NIGHTSWING: - if (G_IsSpecialStage(gamemap) && useNightsSS) - { // Pseudo-ring. - S_StartSound(toucher, special->info->painsound); - player->totalring++; - } - else - S_StartSound(toucher, special->info->activesound); - - P_DoNightsScore(player); - break; case MT_HOOPCOLLIDE: // This produces a kind of 'domino effect' with the hoop's pieces. for (; special->hprev != NULL; special = special->hprev); // Move to the first sprite in the hoop @@ -1336,7 +1369,8 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_ClearStarPost(special->health); - // Find all starposts in the level with this value. + // Find all starposts in the level with this value - INCLUDING this one! + if (!(netgame && circuitmap && player != &players[consoleplayer])) { thinker_t *th; mobj_t *mo2; @@ -1348,21 +1382,16 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) mo2 = (mobj_t *)th; - if (mo2 == special) + if (mo2->type != MT_STARPOST) + continue; + if (mo2->health != special->health) continue; - if (mo2->type == MT_STARPOST && mo2->health == special->health) - { - if (!(netgame && circuitmap && player != &players[consoleplayer])) - P_SetMobjState(mo2, mo2->info->painstate); - } + P_SetMobjState(mo2, mo2->info->painstate); } } S_StartSound(toucher, special->info->painsound); - - if (!(netgame && circuitmap && player != &players[consoleplayer])) - P_SetMobjState(special, special->info->painstate); return; case MT_FAKEMOBILE: @@ -1425,23 +1454,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; case MT_EGGSHIELD: { - fixed_t touchx, touchy, touchspeed; - angle_t angle; - - if (P_AproxDistance(toucher->x-special->x, toucher->y-special->y) > - P_AproxDistance((toucher->x-toucher->momx)-special->x, (toucher->y-toucher->momy)-special->y)) - { - touchx = toucher->x + toucher->momx; - touchy = toucher->y + toucher->momy; - } - else - { - touchx = toucher->x; - touchy = toucher->y; - } - - angle = R_PointToAngle2(special->x, special->y, touchx, touchy) - special->angle; - touchspeed = P_AproxDistance(toucher->momx, toucher->momy); + angle_t angle = R_PointToAngle2(special->x, special->y, toucher->x, toucher->y) - special->angle; + fixed_t touchspeed = P_AproxDistance(toucher->momx, toucher->momy); + if (touchspeed < special->scale) + touchspeed = special->scale; // Blocked by the shield? if (!(angle > ANGLE_90 && angle < ANGLE_270)) @@ -1458,13 +1474,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Play a bounce sound? S_StartSound(toucher, special->info->painsound); - return; + + // experimental bounce + if (special->target) + special->target->extravalue1 = -special->target->info->speed; } - else if (((player->powers[pw_carry] == CR_NIGHTSMODE) && (player->pflags & PF_DRILLING)) - || ((player->pflags & PF_JUMPED) && (!(player->pflags & PF_NOJUMPDAMAGE) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY))) - || ((player->charflags & SF_STOMPDAMAGE || player->pflags & PF_BOUNCING) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0)) - || (player->pflags & (PF_SPINNING|PF_GLIDING)) - || player->powers[pw_invulnerability] || player->powers[pw_super]) // Do you possess the ability to subdue the object? + else { // Shatter the shield! toucher->momx = -toucher->momx/2; @@ -1489,10 +1504,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_SetMobjState(special, special->info->seestate); } return; - case MT_SMALLMACECHAIN: - case MT_BIGMACECHAIN: - // Is this the last link in the chain? - if (toucher->momz > 0 || !(special->flags2 & MF2_AMBUSH) + case MT_SMALLGRABCHAIN: + case MT_BIGGRABCHAIN: + if (P_MobjFlip(toucher)*toucher->momz > 0 || (player->powers[pw_carry])) return; @@ -1505,10 +1519,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (player->powers[pw_flashing]) return; - if (special->movefactor && special->tracer && (angle_t)special->tracer->health != ANGLE_90 && (angle_t)special->tracer->health != ANGLE_270) + if (special->movefactor && special->tracer && special->tracer->angle != ANGLE_90 && special->tracer->angle != ANGLE_270) { // I don't expect you to understand this, Mr Bond... - angle_t ang = R_PointToAngle2(special->x, special->y, toucher->x, toucher->y) - special->tracer->threshold; - if ((special->movefactor > 0) == ((angle_t)special->tracer->health > ANGLE_90 && (angle_t)special->tracer->health < ANGLE_270)) + angle_t ang = R_PointToAngle2(special->x, special->y, toucher->x, toucher->y) - special->tracer->angle; + if ((special->movefactor > 0) == (special->tracer->angle > ANGLE_90 && special->tracer->angle < ANGLE_270)) ang += ANGLE_180; if (ang < ANGLE_180) return; // I expect you to die. @@ -1530,20 +1544,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) player->pflags |= PF_JUMPSTASIS; return; - case MT_BIGMINE: - case MT_BIGAIRMINE: - // Spawn explosion! - P_SpawnMobj(special->x, special->y, special->z, special->info->mass); - P_RadiusAttack(special, special, special->info->damage); - S_StartSound(special, special->info->deathsound); - P_SetMobjState(special, special->info->deathstate); - return; - case MT_SPECIALSPIKEBALL: - if (!useNightsSS && G_IsSpecialStage(gamemap)) // Only for old special stages - P_SpecialStageDamage(player, special, NULL); - else - P_DamageMobj(toucher, special, special, 1, 0); - return; case MT_EGGMOBILE2_POGO: // sanity checks if (!special->target || !special->target->health) @@ -2112,8 +2112,8 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget if (inflictor && (inflictor->type == MT_SHELL || inflictor->type == MT_FIREBALL)) P_SetTarget(&target->tracer, inflictor); - if (!useNightsSS && G_IsSpecialStage(gamemap) && target->player && sstimer > 6) - sstimer = 6; // Just let P_Ticker take care of the rest. + if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap) && target->player && target->player->nightstime > 6) + target->player->nightstime = 6; // Just let P_Ticker take care of the rest. if (target->flags & (MF_ENEMY|MF_BOSS)) target->momx = target->momy = target->momz = 0; @@ -2188,21 +2188,27 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget { if (target->flags & MF_BOSS) score = 1000; - else if ((target->flags & MF_ENEMY) && !(target->flags & MF_MISSILE)) + else if ((target->flags & MF_ENEMY) && !(target->flags & MF_MISSILE) && target->info->spawnhealth) { + UINT8 locscoreadd = source->player->scoreadd + target->info->spawnhealth; mobj_t *scoremobj; UINT32 scorestate = mobjinfo[MT_SCORE].spawnstate; scoremobj = P_SpawnMobj(target->x, target->y, target->z + (target->height / 2), MT_SCORE); - // On ground? No chain starts. - if (!source->player->powers[pw_invulnerability] && P_IsObjectOnGround(source)) + // More Sonic-like point system + if (!mariomode) switch (locscoreadd) { - source->player->scoreadd = 0; - score = 100; + case 1: score = 100; break; + case 2: score = 200; scorestate += 1; break; + case 3: score = 500; scorestate += 2; break; + case 4: case 5: case 6: case 7: case 8: case 9: + case 10: case 11: case 12: case 13: case 14: + score = 1000; scorestate += 3; break; + default: score = 10000; scorestate += 4; break; } // Mario Mode has Mario-like chain point values - else if (mariomode) switch (++source->player->scoreadd) + else switch (locscoreadd) { case 1: score = 100; break; case 2: score = 200; scorestate += 1; break; @@ -2222,19 +2228,12 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget scorestate += 10; break; } - // More Sonic-like point system - else switch (++source->player->scoreadd) - { - case 1: score = 100; break; - case 2: score = 200; scorestate += 1; break; - case 3: score = 500; scorestate += 2; break; - case 4: case 5: case 6: case 7: case 8: case 9: - case 10: case 11: case 12: case 13: case 14: - score = 1000; scorestate += 3; break; - default: score = 10000; scorestate += 4; break; - } P_SetMobjState(scoremobj, scorestate); + + // On ground? No chain starts. + if (source->player->powers[pw_invulnerability] || !P_IsObjectOnGround(source)) + source->player->scoreadd = locscoreadd; } } @@ -2349,78 +2348,6 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget if (source && target && target->player && source->player) P_PlayVictorySound(source); // Killer laughs at you. LAUGHS! BWAHAHAHA! -#ifdef OLDANIMALSPAWNING - // Drop stuff. - // This determines the kind of object spawned - // during the death frame of a thing. - if (!mariomode // Don't show birds, etc. in Mario Mode Tails 12-23-2001 - && target->flags & MF_ENEMY) - { - mobjtype_t item; - INT32 prandom; - - switch (target->type) - { - case MT_REDCRAWLA: - case MT_GOLDBUZZ: - case MT_SKIM: - case MT_UNIDUS: - item = MT_FLICKY_02/*MT_BUNNY*/; - break; - - case MT_BLUECRAWLA: - case MT_JETTBOMBER: - case MT_GFZFISH: - item = MT_FLICKY_01/*MT_BIRD*/; - break; - - case MT_JETTGUNNER: - case MT_CRAWLACOMMANDER: - case MT_REDBUZZ: - case MT_DETON: - item = MT_FLICKY_12/*MT_MOUSE*/; - break; - - case MT_GSNAPPER: - case MT_EGGGUARD: - case MT_SPRINGSHELL: - item = MT_FLICKY_11/*MT_COW*/; - break; - - case MT_MINUS: - case MT_VULTURE: - case MT_POINTY: - case MT_YELLOWSHELL: - item = MT_FLICKY_03/*MT_CHICKEN*/; - break; - - case MT_AQUABUZZ: - item = MT_FLICKY_01/*MT_REDBIRD*/; - break; - - default: - if (target->info->doomednum) - prandom = target->info->doomednum%5; // "Random" animal for new enemies. - else - prandom = P_RandomKey(5); // No placable object, just use a random number. - - switch(prandom) - { - default: item = MT_FLICKY_02/*MT_BUNNY*/; break; - case 1: item = MT_FLICKY_01/*MT_BIRD*/; break; - case 2: item = MT_FLICKY_12/*MT_MOUSE*/; break; - case 3: item = MT_FLICKY_11/*MT_COW*/; break; - case 4: item = MT_FLICKY_03/*MT_CHICKEN*/; break; - } - break; - } - - mo = P_SpawnMobj(target->x, target->y, target->z + (target->height / 2) - FixedMul(mobjinfo[item].height / 2, target->scale), item); - mo->destscale = target->scale; - P_SetScale(mo, mo->destscale); - } - else -#endif // Other death animation effects switch(target->type) { @@ -2434,6 +2361,96 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget target->fuse = target->info->damage; break; + case MT_BUBBLEBUZZ: + if (inflictor && inflictor->player // did a player kill you? Spawn relative to the player so they're bound to get it + && P_AproxDistance(inflictor->x - target->x, inflictor->y - target->y) <= inflictor->radius + target->radius + FixedMul(8*FRACUNIT, inflictor->scale) // close enough? + && inflictor->z <= target->z + target->height + FixedMul(8*FRACUNIT, inflictor->scale) + && inflictor->z + inflictor->height >= target->z - FixedMul(8*FRACUNIT, inflictor->scale)) + mo = P_SpawnMobj(inflictor->x + inflictor->momx, inflictor->y + inflictor->momy, inflictor->z + (inflictor->height / 2) + inflictor->momz, MT_EXTRALARGEBUBBLE); + else + mo = P_SpawnMobj(target->x, target->y, target->z, MT_EXTRALARGEBUBBLE); + mo->destscale = target->scale; + P_SetScale(mo, mo->destscale); + break; + + case MT_YELLOWSHELL: + P_SpawnMobjFromMobj(target, 0, 0, 0, MT_YELLOWSPRING); + break; + + case MT_CRAWLACOMMANDER: + target->momx = target->momy = target->momz = 0; + break; + + case MT_CRUSHSTACEAN: + if (target->tracer) + { + mobj_t *chain = target->tracer->target, *chainnext; + while (chain) + { + chainnext = chain->target; + P_RemoveMobj(chain); + chain = chainnext; + } + S_StopSound(target->tracer); + P_KillMobj(target->tracer, inflictor, source, damagetype); + } + break; + + case MT_EGGSHIELD: + P_SetObjectMomZ(target, 4*target->scale, false); + P_InstaThrust(target, target->angle, 3*target->scale); + target->flags = (target->flags|MF_NOCLIPHEIGHT) & ~MF_NOGRAVITY; + break; + + case MT_EGGMOBILE3: + { + thinker_t *th; + UINT32 i = 0; // to check how many clones we've removed + + // scan the thinkers to make sure all the old pinch dummies are gone on death + // this can happen if the boss was hurt earlier than expected + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo = (mobj_t *)th; + if (mo->type == (mobjtype_t)target->info->mass && mo->tracer == target) + { + P_RemoveMobj(mo); + i++; + } + if (i == 2) // we've already removed 2 of these, let's stop now + break; + } + } + break; + + case MT_BIGMINE: + if (inflictor) + { + fixed_t dx = target->x - inflictor->x, dy = target->y - inflictor->y, dz = target->z - inflictor->z; + fixed_t dm = FixedHypot(dz, FixedHypot(dy, dx)); + target->momx = FixedDiv(FixedDiv(dx, dm), dm)*512; + target->momy = FixedDiv(FixedDiv(dy, dm), dm)*512; + target->momz = FixedDiv(FixedDiv(dz, dm), dm)*512; + } + if (source) + P_SetTarget(&target->tracer, source); + break; + + case MT_BLASTEXECUTOR: + if (target->spawnpoint) + P_LinedefExecute(target->spawnpoint->angle, (source ? source : inflictor), target->subsector->sector); + break; + + case MT_SPINBOBERT: + if (target->hnext) + P_KillMobj(target->hnext, inflictor, source, damagetype); + if (target->hprev) + P_KillMobj(target->hprev, inflictor, source, damagetype); + break; + case MT_EGGTRAP: // Time for birdies! Yaaaaaaaay! target->fuse = TICRATE*2; @@ -2467,57 +2484,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget break; } - // Enemy drops that ALWAYS occur regardless of mode - if (target->type == MT_AQUABUZZ) // Additionally spawns breathable bubble for players to get - { - if (inflictor && inflictor->player // did a player kill you? Spawn relative to the player so he's bound to get it - && P_AproxDistance(inflictor->x - target->x, inflictor->y - target->y) <= inflictor->radius + target->radius + FixedMul(8*FRACUNIT, inflictor->scale) // close enough? - && inflictor->z <= target->z + target->height + FixedMul(8*FRACUNIT, inflictor->scale) - && inflictor->z + inflictor->height >= target->z - FixedMul(8*FRACUNIT, inflictor->scale)) - mo = P_SpawnMobj(inflictor->x + inflictor->momx, inflictor->y + inflictor->momy, inflictor->z + (inflictor->height / 2) + inflictor->momz, MT_EXTRALARGEBUBBLE); - else - mo = P_SpawnMobj(target->x, target->y, target->z, MT_EXTRALARGEBUBBLE); - mo->destscale = target->scale; - P_SetScale(mo, mo->destscale); - } - else if (target->type == MT_YELLOWSHELL) // Spawns a spring that falls to the ground - { - mobjtype_t spawnspring = MT_YELLOWSPRING; - fixed_t spawnheight = target->z; - if (!(target->eflags & MFE_VERTICALFLIP)) - spawnheight += target->height; - - mo = P_SpawnMobj(target->x, target->y, spawnheight, spawnspring); - mo->destscale = target->scale; - P_SetScale(mo, mo->destscale); - - if (target->flags2 & MF2_OBJECTFLIP) - mo->flags2 |= MF2_OBJECTFLIP; - } - - if (target->type == MT_EGGMOBILE3) - { - thinker_t *th; - UINT32 i = 0; // to check how many clones we've removed - - // scan the thinkers to make sure all the old pinch dummies are gone on death - // this can happen if the boss was hurt earlier than expected - for (th = thinkercap.next; th != &thinkercap; th = th->next) - { - if (th->function.acp1 != (actionf_p1)P_MobjThinker) - continue; - - mo = (mobj_t *)th; - if (mo->type == (mobjtype_t)target->info->mass && mo->tracer == target) - { - P_RemoveMobj(mo); - i++; - } - if (i == 2) // we've already removed 2 of these, let's stop now - break; - } - } - + // Final state setting - do something instead of P_SetMobjState; if (target->type == MT_SPIKE && target->info->deathstate != S_NULL) { const angle_t ang = ((inflictor) ? inflictor->angle : 0) + ANGLE_90; @@ -2696,6 +2663,8 @@ static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source) player_t *player = target->player; tic_t oldnightstime = player->nightstime; + (void)source; // unused + if (!player->powers[pw_flashing]) { angle_t fa; @@ -2709,20 +2678,10 @@ static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source) player->drillmeter -= 5*20; else { - if (source && source->player) - { - if (player->nightstime > 20*TICRATE) - player->nightstime -= 20*TICRATE; - else - player->nightstime = 1; - } + if (player->nightstime > 5*TICRATE) + player->nightstime -= 5*TICRATE; else - { - if (player->nightstime > 5*TICRATE) - player->nightstime -= 5*TICRATE; - else - player->nightstime = 1; - } + player->nightstime = 1; } if (player->pflags & PF_TRANSFERTOCLOSEST) @@ -2746,12 +2705,12 @@ static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source) && player->nightstime < 10*TICRATE) { //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("_drown",false); + S_ChangeMusicInternal((((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? "_ntime" : "_drown"), false); } } } -static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage) +static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) { player_t *player = target->player; (void)damage; //unused parm @@ -2761,7 +2720,7 @@ static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sou return false; // Ignore IT players shooting each other, unless friendlyfire is on. - if ((player->pflags & PF_TAGIT && !(cv_friendlyfire.value && + if ((player->pflags & PF_TAGIT && !((cv_friendlyfire.value || (damagetype & DMG_CANHURTSELF)) && source && source->player && source->player->pflags & PF_TAGIT))) return false; @@ -2771,7 +2730,7 @@ static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sou // Don't allow players on the same team to hurt one another, // unless cv_friendlyfire is on. - if (!cv_friendlyfire.value && (player->pflags & PF_TAGIT) == (source->player->pflags & PF_TAGIT)) + if (!(cv_friendlyfire.value || (damagetype & DMG_CANHURTSELF)) && (player->pflags & PF_TAGIT) == (source->player->pflags & PF_TAGIT)) { if (!(inflictor->flags & MF_FIRE)) P_GivePlayerRings(player, 1); @@ -2826,21 +2785,26 @@ static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sou return true; } -static inline boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage) +static inline boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) { player_t *player = target->player; - // You can't kill yourself, idiot... - if (source == target) - return false; + if (!(damagetype & DMG_CANHURTSELF)) + { + // You can't kill yourself, idiot... + if (source == target) + return false; - // In COOP/RACE/CHAOS, you can't hurt other players unless cv_friendlyfire is on - if (!cv_friendlyfire.value && (G_PlatformGametype())) - return false; + // In COOP/RACE, you can't hurt other players unless cv_friendlyfire is on + if (!cv_friendlyfire.value && (G_PlatformGametype())) + return false; + } // Tag handling if (G_TagGametype()) - return P_TagDamage(target, inflictor, source, damage); + return P_TagDamage(target, inflictor, source, damage, damagetype); + else if (damagetype & DMG_CANHURTSELF) + return true; else if (G_GametypeHasTeams()) // CTF + Team Match { // Don't allow players on the same team to hurt one another, @@ -3071,11 +3035,13 @@ static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, IN // P_SpecialStageDamage // // Do old special stage-style damaging -// Removes 10 rings from the player, or knocks off their shield if they have one. +// Removes 5 seconds from the player, or knocks off their shield if they have one. // If they don't have anything, just knock the player back anyway (this doesn't kill them). // void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source) { + tic_t oldnightstime = player->nightstime; + if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super]) return; @@ -3086,17 +3052,24 @@ void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source) } else { - P_PlayRinglossSound(player->mo); - if (player->rings >= 10) - player->rings -= 10; + S_StartSound(player->mo, sfx_nghurt); + if (player->nightstime > 5*TICRATE) + player->nightstime -= 5*TICRATE; else - player->rings = 0; + player->nightstime = 0; } P_DoPlayerPain(player, inflictor, source); if (gametype == GT_CTF && player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)) P_PlayerFlagBurst(player, false); + + if (oldnightstime > 10*TICRATE + && player->nightstime < 10*TICRATE) + { + //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); + } } /** Damages an object, which may or may not be a player. @@ -3181,36 +3154,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da return false; } - // Special case for Crawla Commander - if (target->type == MT_CRAWLACOMMANDER) - { - if (!force && target->fuse) // Invincible - return false; - -#ifdef HAVE_BLUA - if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target)) - return true; -#endif - - if (target->health > 1) - { - if (target->info->painsound) - S_StartSound(target, target->info->painsound); - - target->fuse = TICRATE/2; - target->flags2 |= MF2_FRET; - } - else - { - target->flags |= MF_NOGRAVITY; - target->fuse = 0; - } - - target->momx = target->momy = target->momz = 0; - - P_InstaThrust(target, target->angle-ANGLE_180, FixedMul(5*FRACUNIT, target->scale)); - } - else if (target->flags & MF_BOSS) + if (target->flags & (MF_ENEMY|MF_BOSS)) { if (!force && target->flags2 & MF2_FRET) // Currently flashing from being hit return false; @@ -3223,13 +3167,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (target->health > 1) target->flags2 |= MF2_FRET; } -#ifdef HAVE_BLUA - else if (target->flags & MF_ENEMY) - { - if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target)) - return true; - } -#endif player = target->player; @@ -3282,6 +3219,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da return true; } + if (G_IsSpecialStage(gamemap)) + { + P_SpecialStageDamage(player, inflictor, source); + return true; + } + if (!force && inflictor && inflictor->flags & MF_FIRE) { if (player->powers[pw_shield] & SH_PROTECTFIRE) @@ -3294,7 +3237,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da // Player hits another player if (!force && source && source->player) { - if (!P_PlayerHitsPlayer(target, inflictor, source, damage)) + if (!P_PlayerHitsPlayer(target, inflictor, source, damage, damagetype)) return false; } @@ -3395,20 +3338,14 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (player) P_ResetPlayer(target->player); + else if ((target->type == MT_EGGMOBILE2) // egg slimer + && (target->health < target->info->damage)) // in pinch phase + P_SetMobjState(target, target->info->meleestate); // go to pinch pain state else - switch (target->type) - { - case MT_EGGMOBILE2: // egg slimer - if (target->health < target->info->damage) // in pinch phase - { - P_SetMobjState(target, target->info->meleestate); // go to pinch pain state - break; - } - /* FALLTHRU */ - default: - P_SetMobjState(target, target->info->painstate); - break; - } + P_SetMobjState(target, target->info->painstate); + + if (target->type == MT_HIVEELEMENTAL) + target->extravalue1 += 3; target->reactiontime = 0; // we're awake now... diff --git a/src/p_local.h b/src/p_local.h index 54ae37ed2..682fb7b55 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -146,6 +146,7 @@ void P_SpawnShieldOrb(player_t *player); void P_SwitchShield(player_t *player, UINT16 shieldtype); mobj_t *P_SpawnGhostMobj(mobj_t *mobj); void P_GivePlayerRings(player_t *player, INT32 num_rings); +void P_GivePlayerSpheres(player_t *player, INT32 num_spheres); void P_GivePlayerLives(player_t *player, INT32 numlives); void P_GiveCoopLives(player_t *player, INT32 numlives, boolean sound); UINT8 P_GetNextEmerald(void); @@ -256,6 +257,7 @@ fixed_t P_CameraCeilingZ(camera_t *mobj, sector_t *sector, sector_t *boundsec, f boolean P_InsideANonSolidFFloor(mobj_t *mobj, ffloor_t *rover); boolean P_CheckDeathPitCollide(mobj_t *mo); boolean P_CheckSolidLava(mobj_t *mo, ffloor_t *rover); +void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motype); mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zofs, mobjtype_t type); @@ -280,6 +282,8 @@ mobj_t *P_GetClosestAxis(mobj_t *source); boolean P_CanRunOnWater(player_t *player, ffloor_t *rover); +void P_MaceRotate(mobj_t *center, INT32 baserot, INT32 baseprevrot); + void P_FlashPal(player_t *pl, UINT16 type, UINT16 duration); #define PAL_WHITE 1 #define PAL_MIXUP 2 @@ -360,7 +364,7 @@ void P_DelPrecipSeclist(mprecipsecnode_t *node); void P_CreateSecNodeList(mobj_t *thing, fixed_t x, fixed_t y); void P_Initsecnode(void); -void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist); +void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 damagetype); fixed_t P_FloorzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height); boolean PIT_PushableMoved(mobj_t *thing); @@ -410,6 +414,8 @@ typedef struct BasicFF_s #define DMG_DEATHPIT 0x80+3 #define DMG_CRUSHED 0x80+4 #define DMG_SPECTATOR 0x80+5 +// Masks +#define DMG_CANHURTSELF 0x40 // Flag - can hurt self/team indirectly, such as through mines #define DMG_DEATHMASK DMG_INSTAKILL // if bit 7 is set, this is a death type instead of a damage type void P_ForceFeed(const player_t *player, INT32 attack, INT32 fade, tic_t duration, INT32 period); diff --git a/src/p_map.c b/src/p_map.c index 565680e22..103f8fb7d 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -109,21 +109,148 @@ boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z) // MOVEMENT ITERATOR FUNCTIONS // ========================================================================= +// P_DoSpring +// +// MF_SPRING does some weird, mildly hacky stuff sometimes. +// mass = vertical speed +// damage = horizontal speed +// raisestate = state to change spring to on collision +// reactiontime = number of times it can give 10 points (0 is standard) +// painchance = spring mode: +// 0 = standard vanilla spring behaviour +// Positive spring modes are minor variants of vanilla spring behaviour. +// 1 = launch players in jump +// 2 = don't modify player at all, just add momentum +// Negative spring modes are mildly-related gimmicks with customisation. +// -1 = pinball bumper +// Any other spring mode defaults to standard vanilla spring behaviour, +// ****** but forward compatibility is not guaranteed for these. ****** +// + boolean P_DoSpring(mobj_t *spring, mobj_t *object) { - INT32 pflags; - fixed_t offx, offy; fixed_t vertispeed = spring->info->mass; fixed_t horizspeed = spring->info->damage; - UINT8 secondjump; + boolean final; - if (object->eflags & MFE_SPRUNG) // Object was already sprung this tic + // Object was already sprung this tic + if (object->eflags & MFE_SPRUNG) return false; // Spectators don't trigger springs. if (object->player && object->player->spectator) return false; + // "Even in Death" is a song from Volume 8, not a command. + if (!spring->health || !object->health) + return false; + + if (spring->info->painchance == -1) // Pinball bumper mode. + { + // The first of the entirely different spring modes! + // Some of the attributes mean different things here. + // mass = default strength (can be controlled by mapthing's spawnangle) + // damage = unused + angle_t horizangle, vertiangle; + + if (!vertispeed) + return false; + + if (object->player && object->player->homing) // Sonic Heroes and Shadow the Hedgehog are the only games to contain homing-attackable bumpers! + { + horizangle = 0; + vertiangle = ((object->eflags & MFE_VERTICALFLIP) ? ANGLE_270 : ANGLE_90) >> ANGLETOFINESHIFT; + object->player->pflags &= ~PF_THOKKED; + if (spring->eflags & MFE_VERTICALFLIP) + object->z = spring->z - object->height - 1; + else + object->z = spring->z + spring->height + 1; + } + else + { + horizangle = R_PointToAngle2(spring->x, spring->y, object->x, object->y); + vertiangle = (R_PointToAngle2( + 0, + spring->z + spring->height/2, + FixedHypot(object->x - spring->x, object->y - spring->y), + object->z + object->height/2) + >> ANGLETOFINESHIFT) & FINEMASK; + } + + if (spring->spawnpoint && spring->spawnpoint->angle > 0) + vertispeed = (spring->spawnpoint->angle<<(FRACBITS-1))/5; + vertispeed = FixedMul(vertispeed, FixedMul(object->scale, spring->scale)); + + if (object->player) + { + fixed_t playervelocity; + + if (!(object->player->pflags & PF_THOKKED) && !(object->player->homing) + && ((playervelocity = FixedDiv(9*FixedHypot(object->player->speed, object->momz), 10< vertispeed)) + vertispeed = playervelocity; + + if (object->player->powers[pw_carry] == CR_NIGHTSMODE) // THIS has NiGHTS support, at least... + { + angle_t nightsangle = 0; + + if (object->player->bumpertime >= TICRATE/4) + return false; + + if ((object->player->pflags & PF_TRANSFERTOCLOSEST) && object->player->axis1 && object->player->axis2) + { + nightsangle = R_PointToAngle2(object->player->axis1->x, object->player->axis1->y, object->player->axis2->x, object->player->axis2->y); + nightsangle += ANGLE_90; + } + else if (object->target) + { + if (object->target->flags2 & MF2_AMBUSH) + nightsangle = R_PointToAngle2(object->target->x, object->target->y, object->x, object->y); + else + nightsangle = R_PointToAngle2(object->x, object->y, object->target->x, object->target->y); + } + + object->player->flyangle = AngleFixed(R_PointToAngle2( + 0, + spring->z + spring->height/2, + FixedMul( + FINESINE(((nightsangle - horizangle) >> ANGLETOFINESHIFT) & FINEMASK), + FixedHypot(object->x - spring->x, object->y - spring->y)), + object->z + object->height/2))>>FRACBITS; + + object->player->bumpertime = TICRATE/2; + } + else + { + INT32 pflags = object->player->pflags & (PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_THOKKED|PF_BOUNCING); // Not identical to below... + UINT8 secondjump = object->player->secondjump; + if (object->player->pflags & PF_GLIDING) + P_SetPlayerMobjState(object, S_PLAY_FALL); + P_ResetPlayer(object->player); + object->player->pflags |= pflags; + object->player->secondjump = secondjump; + } + } + + if (!P_IsObjectOnGround(object)) // prevents uncurling when spinning due to "landing" + object->momz = FixedMul(vertispeed, FINESINE(vertiangle)); + P_InstaThrust(object, horizangle, FixedMul(vertispeed, FINECOSINE(vertiangle))); + + object->eflags |= MFE_SPRUNG; // apply this flag asap! + + goto springstate; + } + + // Does nothing? + if (!vertispeed && !horizspeed) + return false; + +#ifdef ESLOPE + object->standingslope = NULL; // Okay, now we know it's not going to be relevant - no launching off at silly angles for you. +#endif + + if (spring->eflags & MFE_VERTICALFLIP) + vertispeed *= -1; + if (object->player && (object->player->powers[pw_carry] == CR_NIGHTSMODE)) { /*Someone want to make these work like bumpers?*/ @@ -135,48 +262,51 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) || (object->player->charability2 == CA2_MELEE && object->player->panim == PA_ABILITY2))) { S_StartSound(object, sfx_s3k8b); - horizspeed = FixedMul(horizspeed, (4*FRACUNIT)/3); - vertispeed = FixedMul(vertispeed, (6*FRACUNIT)/5); // aprox square root of above + if (horizspeed) + horizspeed = FixedMul(horizspeed, (4*FRACUNIT)/3); + if (vertispeed) + vertispeed = FixedMul(vertispeed, (6*FRACUNIT)/5); // aprox square root of above } object->eflags |= MFE_SPRUNG; // apply this flag asap! - spring->flags &= ~(MF_SOLID|MF_SPECIAL); // De-solidify + spring->flags &= ~(MF_SPRING|MF_SPECIAL); // De-solidify - if ((horizspeed && vertispeed) || (object->player && object->player->homing)) // Mimic SA + if (spring->info->painchance != 2) { - object->momx = object->momy = 0; - P_TryMove(object, spring->x, spring->y, true); - } + if ((horizspeed && vertispeed) || (object->player && object->player->homing)) // Mimic SA + { + object->momx = object->momy = 0; + P_TryMove(object, spring->x, spring->y, true); + } - if (spring->eflags & MFE_VERTICALFLIP) - vertispeed *= -1; + if (vertispeed > 0) + object->z = spring->z + spring->height + 1; + else if (vertispeed < 0) + object->z = spring->z - object->height - 1; + else + { + fixed_t offx, offy; + // Horizontal springs teleport you in FRONT of them. + object->momx = object->momy = 0; - if (vertispeed > 0) - object->z = spring->z + spring->height + 1; - else if (vertispeed < 0) - object->z = spring->z - object->height - 1; - else - { - // Horizontal springs teleport you in FRONT of them. - object->momx = object->momy = 0; + // Overestimate the distance to position you at + offx = P_ReturnThrustX(spring, spring->angle, (spring->radius + object->radius + 1) * 2); + offy = P_ReturnThrustY(spring, spring->angle, (spring->radius + object->radius + 1) * 2); - // Overestimate the distance to position you at - offx = P_ReturnThrustX(spring, spring->angle, (spring->radius + object->radius + 1) * 2); - offy = P_ReturnThrustY(spring, spring->angle, (spring->radius + object->radius + 1) * 2); + // Make it square by clipping + if (offx > (spring->radius + object->radius + 1)) + offx = spring->radius + object->radius + 1; + else if (offx < -(spring->radius + object->radius + 1)) + offx = -(spring->radius + object->radius + 1); - // Make it square by clipping - if (offx > (spring->radius + object->radius + 1)) - offx = spring->radius + object->radius + 1; - else if (offx < -(spring->radius + object->radius + 1)) - offx = -(spring->radius + object->radius + 1); + if (offy > (spring->radius + object->radius + 1)) + offy = spring->radius + object->radius + 1; + else if (offy < -(spring->radius + object->radius + 1)) + offy = -(spring->radius + object->radius + 1); - if (offy > (spring->radius + object->radius + 1)) - offy = spring->radius + object->radius + 1; - else if (offy < -(spring->radius + object->radius + 1)) - offy = -(spring->radius + object->radius + 1); - - // Set position! - P_TryMove(object, spring->x + offx, spring->y + offy, true); + // Set position! + P_TryMove(object, spring->x + offx, spring->y + offy, true); + } } if (vertispeed) @@ -186,12 +316,14 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) P_InstaThrustEvenIn2D(object, spring->angle, FixedMul(horizspeed,FixedSqrt(FixedMul(object->scale, spring->scale)))); // Re-solidify - spring->flags |= (spring->info->flags & (MF_SPECIAL|MF_SOLID)); - - P_SetMobjState(spring, spring->info->raisestate); + spring->flags |= (spring->info->flags & (MF_SPRING|MF_SPECIAL)); if (object->player) { + INT32 pflags; + UINT8 secondjump; + boolean washoming; + if (spring->flags & MF_ENEMY) // Spring shells P_SetTarget(&spring->target, object); @@ -212,19 +344,28 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) } } - pflags = object->player->pflags & (PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_THOKKED|PF_SHIELDABILITY|PF_BOUNCING); // I still need these. + pflags = object->player->pflags & (PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_THOKKED|PF_BOUNCING); // I still need these. secondjump = object->player->secondjump; + washoming = object->player->homing; + if (object->player->pflags & PF_GLIDING) + P_SetPlayerMobjState(object, S_PLAY_FALL); P_ResetPlayer(object->player); - if (spring->info->painchance) + if (spring->info->painchance == 1) // For all those ancient, SOC'd abilities. { object->player->pflags |= P_GetJumpFlags(object->player); P_SetPlayerMobjState(object, S_PLAY_JUMP); } - else if (!vertispeed || (pflags & PF_BOUNCING)) // horizontal spring or bouncing + else if ((spring->info->painchance == 2) || (pflags & PF_BOUNCING)) // Adding momentum only. { - if ((pflags & PF_BOUNCING) - || (pflags & (PF_JUMPED|PF_SPINNING) && (object->player->panim == PA_ROLL || object->player->panim == PA_JUMP || object->player->panim == PA_FALL))) + object->player->pflags |= (pflags &~ PF_STARTJUMP); + object->player->secondjump = secondjump; + if (washoming) + object->player->pflags &= ~PF_THOKKED; + } + else if (!vertispeed) + { + if (pflags & (PF_JUMPED|PF_SPINNING)) { object->player->pflags |= pflags; object->player->secondjump = secondjump; @@ -239,10 +380,25 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) } #ifdef ESLOPE - object->standingslope = NULL; // Okay, now we know it's not going to be relevant - no launching off at silly angles for you. + object->standingslope = NULL; // And again. #endif - return true; + final = true; + +springstate: + if ((statenum_t)(spring->state-states) < spring->info->raisestate) + { + P_SetMobjState(spring, spring->info->raisestate); + if (object->player && spring->reactiontime && !(spring->info->flags & MF_ENEMY)) + { + if (object->player->powers[pw_carry] != CR_NIGHTSMODE) // don't make graphic in NiGHTS + P_SetMobjState(P_SpawnMobj(spring->x, spring->y, spring->z + (spring->height/2), MT_SCORE), mobjinfo[MT_SCORE].spawnstate+11); + P_AddPlayerScore(object->player, 10); + spring->reactiontime--; + } + } + + return final; } static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object) @@ -398,7 +554,6 @@ static void P_DoTailsCarry(player_t *sonic, player_t *tails) static boolean PIT_CheckThing(mobj_t *thing) { fixed_t blockdist; - boolean iwassprung = false; // don't clip against self if (thing == tmthing) @@ -514,7 +669,7 @@ static boolean PIT_CheckThing(mobj_t *thing) return true; } - if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE))) + if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING))) return true; // Don't collide with your buddies while NiGHTS-flying. @@ -622,6 +777,54 @@ static boolean PIT_CheckThing(mobj_t *thing) } #endif + // Billiards mines! + if (thing->type == MT_BIGMINE) + { + if (tmthing->type == MT_BIGMINE) + { + if (!tmthing->momx && !tmthing->momy) + return true; + if ((statenum_t)(thing->state-states) >= thing->info->meleestate) + return true; + if (thing->z > tmthing->z + tmthing->height) + return true; // overhead + if (thing->z + thing->height < tmthing->z) + return true; // underneath + + thing->momx = tmthing->momx/3; + thing->momy = tmthing->momy/3; + thing->momz = tmthing->momz/3; + tmthing->momx /= -8; + tmthing->momy /= -8; + tmthing->momz /= -8; + if (thing->info->activesound) + S_StartSound(thing, thing->info->activesound); + P_SetMobjState(thing, thing->info->meleestate); + P_SetTarget(&thing->tracer, tmthing->tracer); + return true; + } + else if (tmthing->type == MT_CRUSHCLAW) + { + if (tmthing->extravalue1 <= 0) + return true; + if ((statenum_t)(thing->state-states) >= thing->info->meleestate) + return true; + if (thing->z > tmthing->z + tmthing->height) + return true; // overhead + if (thing->z + thing->height < tmthing->z) + return true; // underneath + + thing->momx = P_ReturnThrustX(tmthing, tmthing->angle, 2*tmthing->extravalue1*tmthing->scale/3); + thing->momy = P_ReturnThrustY(tmthing, tmthing->angle, 2*tmthing->extravalue1*tmthing->scale/3); + if (thing->info->activesound) + S_StartSound(thing, thing->info->activesound); + P_SetMobjState(thing, thing->info->meleestate); + if (tmthing->tracer) + P_SetTarget(&thing->tracer, tmthing->tracer->target); + return false; + } + } + // When solid spikes move, assume they just popped up and teleport things on top of them to hurt. if (tmthing->type == MT_SPIKE && tmthing->flags & MF_SOLID) { @@ -639,35 +842,37 @@ static boolean PIT_CheckThing(mobj_t *thing) return true; } - if (thing->flags & MF_PAIN) + if (thing->flags & MF_PAIN && tmthing->player) { // Player touches painful thing sitting on the floor // see if it went over / under if (thing->z > tmthing->z + tmthing->height) return true; // overhead if (thing->z + thing->height < tmthing->z) return true; // underneath - if (tmthing->player && tmthing->flags & MF_SHOOTABLE && thing->health > 0) + if (tmthing->flags & MF_SHOOTABLE && thing->health > 0) { - UINT8 damagetype = 0; - if (thing->flags & MF_FIRE) // BURN! + UINT8 damagetype = (thing->info->mass & 0xFF); + if (!damagetype && thing->flags & MF_FIRE) // BURN! damagetype = DMG_FIRE; - P_DamageMobj(tmthing, thing, thing, 1, damagetype); + if (P_DamageMobj(tmthing, thing, thing, 1, damagetype) && (damagetype = (thing->info->mass>>8))) + S_StartSound(thing, damagetype); } return true; } - else if (tmthing->flags & MF_PAIN) + else if (tmthing->flags & MF_PAIN && thing->player) { // Painful thing splats player in the face // see if it went over / under if (tmthing->z > thing->z + thing->height) return true; // overhead if (tmthing->z + tmthing->height < thing->z) return true; // underneath - if (thing->player && thing->flags & MF_SHOOTABLE && tmthing->health > 0) + if (thing->flags & MF_SHOOTABLE && tmthing->health > 0) { - UINT8 damagetype = 0; - if (tmthing->flags & MF_FIRE) // BURN! + UINT8 damagetype = (tmthing->info->mass & 0xFF); + if (!damagetype && tmthing->flags & MF_FIRE) // BURN! damagetype = DMG_FIRE; - P_DamageMobj(thing, tmthing, tmthing, 1, damagetype); + if (P_DamageMobj(thing, tmthing, tmthing, 1, damagetype) && (damagetype = (tmthing->info->mass>>8))) + S_StartSound(tmthing, damagetype); } return true; } @@ -714,6 +919,7 @@ static boolean PIT_CheckThing(mobj_t *thing) else if (tmz > thzh - sprarea && tmz < thzh) // Don't damage people springing up / down return true; } + // missiles can hit other things if (tmthing->flags & MF_MISSILE || tmthing->type == MT_SHELL) { @@ -768,30 +974,11 @@ static boolean PIT_CheckThing(mobj_t *thing) if (thing->type == MT_EGGSHIELD) { - fixed_t touchx, touchy; - angle_t angle; + angle_t angle = (R_PointToAngle2(thing->x, thing->y, tmthing->x - tmthing->momx, tmthing->y - tmthing->momy) - thing->angle) - ANGLE_90; - if (P_AproxDistance(tmthing->x-thing->x, tmthing->y-thing->y) > - P_AproxDistance((tmthing->x-tmthing->momx)-thing->x, (tmthing->y-tmthing->momy)-thing->y)) - { - touchx = tmthing->x + tmthing->momx; - touchy = tmthing->y + tmthing->momy; - } - else - { - touchx = tmthing->x; - touchy = tmthing->y; - } - - angle = R_PointToAngle2(thing->x, thing->y, touchx, touchy) - thing->angle; - - if (!(angle > ANGLE_90 && angle < ANGLE_270)) // hit front of shield, didn't destroy it - return false; - else // hit shield from behind, shield is destroyed! - { + if (angle < ANGLE_180) // hit shield from behind, shield is destroyed! P_KillMobj(thing, tmthing, tmthing, 0); - return false; - } + return false; } // damage / explode @@ -835,7 +1022,12 @@ static boolean PIT_CheckThing(mobj_t *thing) P_SetThingPosition(tmthing); } else if (!(tmthing->type == MT_SHELL && thing->player)) // player collision handled in touchspecial - P_DamageMobj(thing, tmthing, tmthing->target, 1, 0); + { + UINT8 damagetype = tmthing->info->mass; + if (!damagetype && tmthing->flags & MF_FIRE) // BURN! + damagetype = DMG_FIRE; + P_DamageMobj(thing, tmthing, tmthing->target, 1, damagetype); + } // don't traverse any more @@ -1037,15 +1229,28 @@ static boolean PIT_CheckThing(mobj_t *thing) if (tmthing->flags & MF_PUSHABLE) { if (thing->type == MT_FAN || thing->type == MT_STEAM) + { P_DoFanAndGasJet(thing, tmthing); + return true; + } else if (thing->flags & MF_SPRING) { if ( thing->z <= tmthing->z + tmthing->height && tmthing->z <= thing->z + thing->height) - iwassprung = P_DoSpring(thing, tmthing); + if (P_DoSpring(thing, tmthing)) + return false; + return true; } } + // thanks to sal for solidenemies dot lua + if (thing->flags & (MF_ENEMY|MF_BOSS) && tmthing->flags & (MF_ENEMY|MF_BOSS)) + { + if ((thing->z + thing->height >= tmthing->z) + && (tmthing->z + tmthing->height >= thing->z)) + return false; + } + // Damage other players when invincible if (tmthing->player && thing->player // Make sure they aren't able to damage you ANYWHERE along the Z axis, you have to be TOUCHING the person. @@ -1110,7 +1315,7 @@ static boolean PIT_CheckThing(mobj_t *thing) { // Objects kill you if it falls from above. if (thing != tmthing->target) - P_DamageMobj(thing, tmthing, tmthing->target, 1, DMG_INSTAKILL); + P_DamageMobj(thing, tmthing, tmthing->target, 1, DMG_CRUSHED); tmthing->momz = -tmthing->momz/2; // Bounce, just for fun! // The tmthing->target allows the pusher of the object @@ -1133,75 +1338,62 @@ static boolean PIT_CheckThing(mobj_t *thing) { if ( thing->z <= tmthing->z + tmthing->height && tmthing->z <= thing->z + thing->height) - iwassprung = P_DoSpring(thing, tmthing); + if (P_DoSpring(thing, tmthing)) + return false; + return true; } - // Are you touching the side of the object you're interacting with? - else if (thing->z - FixedMul(FRACUNIT, thing->scale) <= tmthing->z + tmthing->height - && thing->z + thing->height + FixedMul(FRACUNIT, thing->scale) >= tmthing->z) + // Monitor? + else if (thing->flags & MF_MONITOR + && !((thing->type == MT_RING_REDBOX && tmthing->player->ctfteam != 1) || (thing->type == MT_RING_BLUEBOX && tmthing->player->ctfteam != 2))) { // 0 = none, 1 = elemental pierce, 2 = bubble bounce UINT8 elementalpierce = (((tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL || (tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) && (tmthing->player->pflags & PF_SHIELDABILITY) ? (((tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) ? 1 : 2) : 0); - if (thing->flags & MF_MONITOR - && (tmthing->player->pflags & (PF_SPINNING|PF_GLIDING) - || ((tmthing->player->pflags & PF_JUMPED) - && (!(tmthing->player->pflags & PF_NOJUMPDAMAGE) - || (tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY))) - || (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2) - || ((tmthing->player->charflags & SF_STOMPDAMAGE || tmthing->player->pflags & PF_BOUNCING) - && (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0)) - || elementalpierce)) + if (!(thing->flags & MF_SOLID) + || tmthing->player->pflags & (PF_SPINNING|PF_GLIDING) + || ((tmthing->player->pflags & PF_JUMPED) + && (!(tmthing->player->pflags & PF_NOJUMPDAMAGE) + || (tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY))) + || (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2) + || ((tmthing->player->charflags & SF_STOMPDAMAGE || tmthing->player->pflags & PF_BOUNCING) + && (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0)) + || elementalpierce) { - player_t *player = tmthing->player; - SINT8 flipval = P_MobjFlip(thing); // Save this value in case monitor gets removed. - fixed_t *momz = &tmthing->momz; // tmthing gets changed by P_DamageMobj, so we need a new pointer?! X_x;; - fixed_t *z = &tmthing->z; // aau. - P_DamageMobj(thing, tmthing, tmthing, 1, 0); // break the monitor - // Going down? Then bounce back up. - if ((P_MobjWasRemoved(thing) // Monitor was removed - || !thing->health) // or otherwise popped - && (flipval*(*momz) < 0) // monitor is on the floor and you're going down, or on the ceiling and you're going up - && (elementalpierce != 1)) // you're not piercing through the monitor... + if (thing->z - thing->scale <= tmthing->z + tmthing->height + && thing->z + thing->height + thing->scale >= tmthing->z) { - if (elementalpierce == 2) - P_DoBubbleBounce(player); - else if (!(player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)) - *momz = -*momz; // Therefore, you should be thrust in the opposite direction, vertically. + player_t *player = tmthing->player; + SINT8 flipval = P_MobjFlip(thing); // Save this value in case monitor gets removed. + fixed_t *momz = &tmthing->momz; // tmthing gets changed by P_DamageMobj, so we need a new pointer?! X_x;; + fixed_t *z = &tmthing->z; // aau. + // Going down? Then bounce back up. + if (P_DamageMobj(thing, tmthing, tmthing, 1, 0) // break the monitor + && (flipval*(*momz) < 0) // monitor is on the floor and you're going down, or on the ceiling and you're going up + && (elementalpierce != 1)) // you're not piercing through the monitor... + { + if (elementalpierce == 2) + P_DoBubbleBounce(player); + else if (!(player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)) + *momz = -*momz; // Therefore, you should be thrust in the opposite direction, vertically. + } + if (!(elementalpierce == 1 && thing->flags & MF_GRENADEBOUNCE)) // prevent gold monitor clipthrough. + { + if (player->pflags & PF_BOUNCING) + P_DoAbilityBounce(player, false); + return false; + } + else + *z -= *momz; // to ensure proper collision. } - if (!(elementalpierce == 1 && thing->flags & MF_GRENADEBOUNCE)) // prevent gold monitor clipthrough. - { - if (player->pflags & PF_BOUNCING) - P_DoAbilityBounce(player, false); - return false; - } - else - *z -= *momz; // to ensure proper collision. + + return true; } } } - if (!(tmthing->player) && (thing->player)) + if ((!tmthing->player) && (thing->player)) ; // no solid thing should ever be able to step up onto a player - else if (thing->flags & MF_SPRING && (tmthing->player || tmthing->flags & MF_PUSHABLE)) - { - if (iwassprung) // this spring caused you to gain MFE_SPRUNG just now... - return false; // "cancel" P_TryMove via blocking so you keep your current position - } - else if (tmthing->flags & MF_SPRING && (thing->flags & MF_PUSHABLE)) - ; // Fix a few nasty spring-jumping bugs that happen sometimes. - // Monitors are not treated as solid to players who are jumping, spinning or gliding, - // unless it's a CTF team monitor and you're on the wrong team - else if (thing->flags & MF_MONITOR && tmthing->player - && (tmthing->player->pflags & (PF_SPINNING|PF_GLIDING) - || ((tmthing->player->pflags & PF_JUMPED) - && (!(tmthing->player->pflags & PF_NOJUMPDAMAGE) - || (tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY))) - || (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2) - || ((tmthing->player->charflags & SF_STOMPDAMAGE || tmthing->player->pflags & PF_BOUNCING) - && (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0))) - && !((thing->type == MT_RING_REDBOX && tmthing->player->ctfteam != 1) || (thing->type == MT_RING_BLUEBOX && tmthing->player->ctfteam != 2))) - ; // z checking at last // Treat noclip things as non-solid! else if ((thing->flags & (MF_SOLID|MF_NOCLIP)) == MF_SOLID @@ -1228,16 +1420,14 @@ static boolean PIT_CheckThing(mobj_t *thing) topz = thing->z - thing->scale; // FixedMul(FRACUNIT, thing->scale), but thing->scale == FRACUNIT in base scale anyways - if (thing->flags & MF_SPRING) - ; // block only when jumping not high enough, // (dont climb max. 24units while already in air) // since return false doesn't handle momentum properly, // we lie to P_TryMove() so it's always too high - else if (tmthing->player && tmthing->z + tmthing->height > topz + if (tmthing->player && tmthing->z + tmthing->height > topz && tmthing->z + tmthing->height < tmthing->ceilingz) { - if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->flags2 & MF2_STANDONME)) // Gold monitor hack... + if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->info->flags & MF_MONITOR)) // Gold monitor hack... return false; tmfloorz = tmceilingz = topz; // block while in air @@ -1274,16 +1464,14 @@ static boolean PIT_CheckThing(mobj_t *thing) topz = thing->z + thing->height + thing->scale; // FixedMul(FRACUNIT, thing->scale), but thing->scale == FRACUNIT in base scale anyways - if (thing->flags & MF_SPRING) - ; // block only when jumping not high enough, // (dont climb max. 24units while already in air) // since return false doesn't handle momentum properly, // we lie to P_TryMove() so it's always too high - else if (tmthing->player && tmthing->z < topz + if (tmthing->player && tmthing->z < topz && tmthing->z > tmthing->floorz) { - if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->flags2 & MF2_STANDONME)) // Gold monitor hack... + if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->info->flags & MF_MONITOR)) // Gold monitor hack... return false; tmfloorz = tmceilingz = topz; // block while in air @@ -3505,6 +3693,7 @@ bounceback: static fixed_t bombdamage; static mobj_t *bombsource; static mobj_t *bombspot; +static UINT8 bombdamagetype; // // PIT_RadiusAttack @@ -3515,17 +3704,13 @@ static boolean PIT_RadiusAttack(mobj_t *thing) { fixed_t dx, dy, dz, dist; - if (thing == bombspot // ignore the bomb itself (Deton fix) - || (bombsource && thing->type == bombsource->type)) // ignore the type of guys who dropped the bomb (Jetty-Syn Bomber or Skim can bomb eachother, but not themselves.) + if (thing == bombspot) // ignore the bomb itself (Deton fix) return true; - if (!(thing->flags & MF_SHOOTABLE)) + if ((thing->flags & (MF_MONITOR|MF_SHOOTABLE)) != MF_SHOOTABLE) return true; - if (thing->flags & MF_BOSS) - return true; - - if (thing->flags & MF_MONITOR) + if (bombsource && thing->type == bombsource->type && !(bombdamagetype & DMG_CANHURTSELF)) // ignore the type of guys who dropped the bomb (Jetty-Syn Bomber or Skim can bomb eachother, but not themselves.) return true; dx = abs(thing->x - bombspot->x); @@ -3549,7 +3734,7 @@ static boolean PIT_RadiusAttack(mobj_t *thing) if (P_CheckSight(thing, bombspot)) { // must be in direct path - P_DamageMobj(thing, bombspot, bombsource, 1, 0); // Tails 01-11-2001 + P_DamageMobj(thing, bombspot, bombsource, 1, bombdamagetype); // Tails 01-11-2001 } return true; @@ -3559,7 +3744,7 @@ static boolean PIT_RadiusAttack(mobj_t *thing) // P_RadiusAttack // Source is the creature that caused the explosion at spot. // -void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist) +void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 damagetype) { INT32 x, y; INT32 xl, xh, yl, yh; @@ -3576,6 +3761,7 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist) bombspot = spot; bombsource = source; bombdamage = FixedMul(damagedist, spot->scale); + bombdamagetype = damagetype; for (y = yl; y <= yh; y++) for (x = xl; x <= xh; x++) diff --git a/src/p_mobj.c b/src/p_mobj.c index f6b237e29..be373fbf4 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -877,7 +877,7 @@ void P_ExplodeMissile(mobj_t *mo) if (mo->type == MT_DETON) { - P_RadiusAttack(mo, mo, 96*FRACUNIT); + P_RadiusAttack(mo, mo, 96*FRACUNIT, 0); explodemo = P_SpawnMobj(mo->x, mo->y, mo->z, MT_EXPLODE); P_SetScale(explodemo, mo->scale); @@ -2133,7 +2133,7 @@ void P_XYMovement(mobj_t *mo) if (mo->flags & MF_NOCLIPHEIGHT) return; // no frictions for objects that can pass through floors - if (mo->flags & MF_MISSILE || mo->flags2 & MF2_SKULLFLY || mo->type == MT_SHELL || mo->type == MT_VULTURE) + if (mo->flags & MF_MISSILE || mo->flags2 & MF2_SKULLFLY || mo->type == MT_SHELL || mo->type == MT_VULTURE || mo->type == MT_PENGUINATOR) return; // no friction for missiles ever if (player && player->homing) // no friction for homing @@ -2200,7 +2200,7 @@ static void P_SceneryXYMovement(mobj_t *mo) // 1 - forces false check for water (rings) // 2 - forces false check for water + different quicksand behaviour (scenery) // -static void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motype) +void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motype) { ffloor_t *rover; fixed_t delta1, delta2, thingtop; @@ -2404,6 +2404,7 @@ boolean P_CheckSolidLava(mobj_t *mo, ffloor_t *rover) static boolean P_ZMovement(mobj_t *mo) { fixed_t dist, delta; + boolean onground; I_Assert(mo != NULL); I_Assert(!P_MobjWasRemoved(mo)); @@ -2421,13 +2422,14 @@ static boolean P_ZMovement(mobj_t *mo) mo->eflags &= ~MFE_APPLYPMOMZ; } mo->z += mo->momz; + onground = P_IsObjectOnGround(mo); #ifdef ESLOPE if (mo->standingslope) { if (mo->flags & MF_NOCLIPHEIGHT) mo->standingslope = NULL; - else if (!P_IsObjectOnGround(mo)) + else if (!onground) P_SlopeLaunch(mo); } #endif @@ -2513,7 +2515,10 @@ static boolean P_ZMovement(mobj_t *mo) case MT_RING: // Ignore still rings case MT_COIN: - case MT_BLUEBALL: + case MT_BLUESPHERE: + case MT_BOMBSPHERE: + case MT_NIGHTSCHIP: + case MT_NIGHTSSTAR: case MT_REDTEAMRING: case MT_BLUETEAMRING: case MT_FLINGRING: @@ -2550,10 +2555,6 @@ static boolean P_ZMovement(mobj_t *mo) if (!(mo->momx || mo->momy || mo->momz)) return true; break; - case MT_NIGHTSWING: - if (!(mo->momx || mo->momy || mo->momz)) - return true; - break; case MT_FLAMEJET: case MT_VERTICALFLAMEJET: if (!(mo->flags & MF_BOUNCE)) @@ -2572,15 +2573,9 @@ static boolean P_ZMovement(mobj_t *mo) break; } - if (P_CheckDeathPitCollide(mo)) + if (!mo->player && P_CheckDeathPitCollide(mo)) { - if (mo->flags & MF_PUSHABLE) - { - // Remove other pushable items from death pits. - P_RemoveMobj(mo); - return false; - } - else if (mo->flags & MF_ENEMY || mo->flags & MF_BOSS) + if (mo->flags & MF_ENEMY || mo->flags & MF_BOSS) { // Kill enemies and bosses that fall into death pits. if (mo->health) @@ -2589,6 +2584,11 @@ static boolean P_ZMovement(mobj_t *mo) return false; } } + else + { + P_RemoveMobj(mo); + return false; + } } if (P_MobjFlip(mo)*mo->momz < 0 @@ -2701,9 +2701,7 @@ static boolean P_ZMovement(mobj_t *mo) if (P_MobjFlip(mo)*mom.z < 0) // falling { - if (!tmfloorthing || tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR) - || tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER) - mo->eflags |= MFE_JUSTHITFLOOR; + mo->eflags |= MFE_JUSTHITFLOOR; if (mo->flags2 & MF2_SKULLFLY) // the skull slammed into something mom.z = -mom.z; @@ -2764,7 +2762,7 @@ static boolean P_ZMovement(mobj_t *mo) else if (mo->type == MT_FALLINGROCK) { if (P_MobjFlip(mo)*mom.z > FixedMul(2*FRACUNIT, mo->scale)) - S_StartSound(mo, mo->info->activesound + P_RandomKey(mo->info->mass)); + S_StartSound(mo, mo->info->activesound + P_RandomKey(mo->info->reactiontime)); mom.z /= 2; // Rocks not so bouncy @@ -2783,14 +2781,11 @@ static boolean P_ZMovement(mobj_t *mo) mom.z = 0; } } - else if (tmfloorthing && (tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR) - || tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER)) - mom.z = tmfloorthing->momz; - else if (!tmfloorthing) - mom.z = 0; + else + mom.z = (tmfloorthing ? tmfloorthing->momz : 0); + } - else if (tmfloorthing && (tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR) - || tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER)) + else if (tmfloorthing) mom.z = tmfloorthing->momz; #ifdef ESLOPE @@ -2876,6 +2871,8 @@ static boolean P_ZMovement(mobj_t *mo) static void P_PlayerZMovement(mobj_t *mo) { + boolean onground; + I_Assert(mo != NULL); I_Assert(!P_MobjWasRemoved(mo)); @@ -2909,6 +2906,7 @@ static void P_PlayerZMovement(mobj_t *mo) } mo->z += mo->momz; + onground = P_IsObjectOnGround(mo); // Have player fall through floor? if (mo->player->playerstate == PST_DEAD @@ -2920,13 +2918,13 @@ static void P_PlayerZMovement(mobj_t *mo) { if (mo->flags & MF_NOCLIPHEIGHT) mo->standingslope = NULL; - else if (!P_IsObjectOnGround(mo)) + else if (!onground) P_SlopeLaunch(mo); } #endif // clip movement - if (P_IsObjectOnGround(mo) && !(mo->flags & MF_NOCLIPHEIGHT)) + if (onground && !(mo->flags & MF_NOCLIPHEIGHT)) { if (mo->eflags & MFE_VERTICALFLIP) mo->z = mo->ceilingz - mo->height; @@ -2968,100 +2966,89 @@ static void P_PlayerZMovement(mobj_t *mo) if (P_MobjFlip(mo)*mo->momz < -FixedMul(8*FRACUNIT, mo->scale)) mo->player->deltaviewheight = (P_MobjFlip(mo)*mo->momz)>>3; // make sure momz is negative - if (!tmfloorthing || tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR) - || tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER) // Spin Attack + mo->eflags |= MFE_JUSTHITFLOOR; // Spin Attack + { - mo->eflags |= MFE_JUSTHITFLOOR; // Spin Attack - - if (mo->eflags & MFE_JUSTHITFLOOR) - { #ifdef POLYOBJECTS - // Check if we're on a polyobject - // that triggers a linedef executor. - msecnode_t *node; - boolean stopmovecut = false; + // Check if we're on a polyobject + // that triggers a linedef executor. + msecnode_t *node; + boolean stopmovecut = false; - for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next) + for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next) + { + sector_t *sec = node->m_sector; + subsector_t *newsubsec; + size_t i; + + for (i = 0; i < numsubsectors; i++) { - sector_t *sec = node->m_sector; - subsector_t *newsubsec; - size_t i; + newsubsec = &subsectors[i]; - for (i = 0; i < numsubsectors; i++) + if (newsubsec->sector != sec) + continue; + + if (newsubsec->polyList) { - newsubsec = &subsectors[i]; + polyobj_t *po = newsubsec->polyList; + sector_t *polysec; - if (newsubsec->sector != sec) - continue; - - if (newsubsec->polyList) + while(po) { - polyobj_t *po = newsubsec->polyList; - sector_t *polysec; - - while(po) + if (!P_MobjInsidePolyobj(po, mo) || !(po->flags & POF_SOLID)) { - if (!P_MobjInsidePolyobj(po, mo) || !(po->flags & POF_SOLID)) - { - po = (polyobj_t *)(po->link.next); - continue; - } - - // We're inside it! Yess... - polysec = po->lines[0]->backsector; - - // Moving polyobjects should act like conveyors if the player lands on one. (I.E. none of the momentum cut thing below) -Red - if ((mo->z == polysec->ceilingheight || mo->z+mo->height == polysec->floorheight) && po->thinker) - stopmovecut = true; - - if (!(po->flags & POF_LDEXEC)) - { - po = (polyobj_t *)(po->link.next); - continue; - } - - if (mo->z == polysec->ceilingheight) - { - // We're landing on a PO, so check for - // a linedef executor. - // Trigger tags are 32000 + the PO's ID number. - P_LinedefExecute((INT16)(32000 + po->id), mo, NULL); - } - po = (polyobj_t *)(po->link.next); + continue; } + + // We're inside it! Yess... + polysec = po->lines[0]->backsector; + + // Moving polyobjects should act like conveyors if the player lands on one. (I.E. none of the momentum cut thing below) -Red + if ((mo->z == polysec->ceilingheight || mo->z+mo->height == polysec->floorheight) && po->thinker) + stopmovecut = true; + + if (!(po->flags & POF_LDEXEC)) + { + po = (polyobj_t *)(po->link.next); + continue; + } + + if (mo->z == polysec->ceilingheight) + { + // We're landing on a PO, so check for + // a linedef executor. + // Trigger tags are 32000 + the PO's ID number. + P_LinedefExecute((INT16)(32000 + po->id), mo, NULL); + } + + po = (polyobj_t *)(po->link.next); } } } - - if (!stopmovecut) -#endif - - // Cut momentum in half when you hit the ground and - // aren't pressing any controls. - if (!(mo->player->cmd.forwardmove || mo->player->cmd.sidemove) && !mo->player->cmomx && !mo->player->cmomy && !(mo->player->pflags & PF_SPINNING)) - { - mo->momx >>= 1; - mo->momy >>= 1; - } } - clipmomz = P_PlayerHitFloor(mo->player); + if (!stopmovecut) +#endif + + // Cut momentum in half when you hit the ground and + // aren't pressing any controls. + if (!(mo->player->cmd.forwardmove || mo->player->cmd.sidemove) && !mo->player->cmomx && !mo->player->cmomy && !(mo->player->pflags & PF_SPINNING)) + { + mo->momx >>= 1; + mo->momy >>= 1; + } } + + clipmomz = P_PlayerHitFloor(mo->player); + if (!(mo->player->pflags & PF_SPINNING) && mo->player->powers[pw_carry] != CR_NIGHTSMODE) mo->player->pflags &= ~PF_STARTDASH; if (clipmomz) - { - if (tmfloorthing && (tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR) - || tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER)) - mo->momz = tmfloorthing->momz; - else if (!tmfloorthing) - mo->momz = 0; - } + mo->momz = (tmfloorthing ? tmfloorthing->momz : 0); } - else if (tmfloorthing && (tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR) - || tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER)) + else if (tmfloorthing) mo->momz = tmfloorthing->momz; } else if (!(mo->flags & MF_NOGRAVITY)) // Gravity here! @@ -3239,19 +3226,14 @@ static boolean P_SceneryZMovement(mobj_t *mo) P_RemoveMobj(mo); return false; } - default: break; } - // Fix for any silly pushables like the egg statues that are also scenery for some reason -- Monster Iestyn if (P_CheckDeathPitCollide(mo)) { - if (mo->flags & MF_PUSHABLE) - { - P_RemoveMobj(mo); - return false; - } + P_RemoveMobj(mo); + return false; } // clip movement @@ -3266,12 +3248,9 @@ static boolean P_SceneryZMovement(mobj_t *mo) if (P_MobjFlip(mo)*mo->momz < 0) // falling { - if (!tmfloorthing || tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR) - || tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER) - mo->eflags |= MFE_JUSTHITFLOOR; // Spin Attack + mo->eflags |= MFE_JUSTHITFLOOR; // Spin Attack - if (tmfloorthing && (tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR) - || tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER)) + if (tmfloorthing) mo->momz = tmfloorthing->momz; else if (!tmfloorthing) mo->momz = 0; @@ -6176,124 +6155,217 @@ static void P_NightsItemChase(mobj_t *thing) // // P_MaceRotate -// Spins an object around its target, or, swings it from side to side. +// Spins a hnext-chain of objects around its centerpoint, side to side or periodically. // -static void P_MaceRotate(mobj_t *mobj) +void P_MaceRotate(mobj_t *center, INT32 baserot, INT32 baseprevrot) { - TVector v; + TVector unit_lengthways, unit_sideways, pos_lengthways, pos_sideways; TVector *res; - fixed_t radius, dist; + fixed_t radius, dist, zstore; angle_t fa; - INT32 prevswing; - boolean donetwice = false; + boolean dosound = false; + mobj_t *mobj = center->hnext, *hnext = NULL; - // Tracer was removed. - if (!mobj->health) - return; - else if (!mobj->tracer) + INT32 rot = (baserot &= FINEMASK); + INT32 prevrot = (baseprevrot &= FINEMASK); + + INT32 lastthreshold = FINEMASK; // needs to never be equal at start of loop + fixed_t lastfriction = INT32_MIN; // ditto; almost certainly never, but... + + dist = pos_sideways[0] = pos_sideways[1] = pos_sideways[2] = pos_sideways[3] = unit_sideways[3] = pos_lengthways[0] = pos_lengthways[1] = pos_lengthways[2] = pos_lengthways[3] = 0; + + while (mobj) { - P_KillMobj(mobj, NULL, NULL, 0); - return; - } + if (!mobj->health) + { + mobj = mobj->hnext; + continue; + } - mobj->momx = mobj->momy = mobj->momz = 0; + mobj->momx = mobj->momy = mobj->momz = 0; - prevswing = mobj->threshold; - mobj->threshold += mobj->tracer->lastlook; - mobj->threshold &= FINEMASK; + if (mobj->threshold != lastthreshold + || mobj->friction != lastfriction) + { + rot = (baserot + mobj->threshold) & FINEMASK; + prevrot = (baseprevrot + mobj->threshold) & FINEMASK; - dist = ((mobj->info->speed) ? mobj->info->speed : mobjinfo[MT_SMALLMACECHAIN].speed); + pos_lengthways[0] = pos_lengthways[1] = pos_lengthways[2] = pos_lengthways[3] = 0; - // Radius of the link's rotation. - radius = FixedMul(dist * mobj->movecount, mobj->tracer->scale) + mobj->tracer->extravalue1; + dist = ((mobj->info->speed) ? mobj->info->speed : mobjinfo[MT_SMALLMACECHAIN].speed); + dist = ((center->scale == FRACUNIT) ? dist : FixedMul(dist, center->scale)); -maceretry: + fa = (FixedAngle(center->movefactor*FRACUNIT) >> ANGLETOFINESHIFT); + radius = FixedMul(dist, FINECOSINE(fa)); + unit_lengthways[1] = -FixedMul(dist, FINESINE(fa)); + unit_lengthways[3] = FRACUNIT; - fa = (FixedAngle(mobj->tracer->movefactor*FRACUNIT) >> ANGLETOFINESHIFT); - radius = FixedMul(FINECOSINE(fa), radius); - v[1] = -FixedMul(FINESINE(fa), radius) - + FixedMul(dist * mobj->movefactor, mobj->tracer->scale); - v[3] = FRACUNIT; + // Swinging Chain. + if (center->flags2 & MF2_STRONGBOX) + { + fixed_t swingmag = FixedMul(FINECOSINE(rot), center->lastlook << FRACBITS); + fixed_t prevswingmag = FINECOSINE(prevrot); - // Swinging Chain. - if (mobj->tracer->flags2 & MF2_STRONGBOX) - { - fixed_t swingmagnitude = FixedMul(FINECOSINE(mobj->threshold), mobj->tracer->lastlook << FRACBITS); - prevswing = FINECOSINE(prevswing); + if ((prevswingmag > 0) != (swingmag > 0)) // just passed its lowest point + dosound = true; - if (!donetwice - && (mobj->flags2 & MF2_BOSSNOTRAP) // at the end of the chain and can play a sound - && ((prevswing > 0) != (swingmagnitude > 0))) // just passed its lowest point + fa = ((FixedAngle(swingmag) >> ANGLETOFINESHIFT) + mobj->friction) & FINEMASK; + + unit_lengthways[0] = FixedMul(FINESINE(fa), -radius); + unit_lengthways[2] = FixedMul(FINECOSINE(fa), -radius); + } + // Rotating Chain. + else + { + angle_t prevfa = (prevrot + mobj->friction) & FINEMASK; + fa = (rot + mobj->friction) & FINEMASK; + + if (!(prevfa > (FINEMASK/2)) && (fa > (FINEMASK/2))) // completed a full swing + dosound = true; + + unit_lengthways[0] = FixedMul(FINECOSINE(fa), radius); + unit_lengthways[2] = FixedMul(FINESINE(fa), radius); + } + + // Calculate the angle matrixes for the link. + res = VectorMatrixMultiply(unit_lengthways, *RotateXMatrix(center->threshold << ANGLETOFINESHIFT)); + M_Memcpy(&unit_lengthways, res, sizeof(unit_lengthways)); + res = VectorMatrixMultiply(unit_lengthways, *RotateZMatrix(center->angle)); + M_Memcpy(&unit_lengthways, res, sizeof(unit_lengthways)); + + lastthreshold = mobj->threshold; + lastfriction = mobj->friction; + } + + if (dosound && (mobj->flags2 & MF2_BOSSNOTRAP)) + { S_StartSound(mobj, mobj->info->activesound); + dosound = false; + } - fa = ((FixedAngle(swingmagnitude) >> ANGLETOFINESHIFT) + mobj->friction) & FINEMASK; + if (pos_sideways[3] != mobj->movefactor) + { + if (!unit_sideways[3]) + { + unit_sideways[1] = dist; + unit_sideways[0] = unit_sideways[2] = 0; + unit_sideways[3] = FRACUNIT; - v[0] = FixedMul(FINESINE(fa), -radius); - v[2] = FixedMul(FINECOSINE(fa), -radius); + res = VectorMatrixMultiply(unit_sideways, *RotateXMatrix(center->threshold << ANGLETOFINESHIFT)); + M_Memcpy(&unit_sideways, res, sizeof(unit_sideways)); + res = VectorMatrixMultiply(unit_sideways, *RotateZMatrix(center->angle)); + M_Memcpy(&unit_sideways, res, sizeof(unit_sideways)); + } + + if (pos_sideways[3] > mobj->movefactor) + { + do + { + pos_sideways[0] -= unit_sideways[0]; + pos_sideways[1] -= unit_sideways[1]; + pos_sideways[2] -= unit_sideways[2]; + } + while ((--pos_sideways[3]) != mobj->movefactor); + } + else + { + do + { + pos_sideways[0] += unit_sideways[0]; + pos_sideways[1] += unit_sideways[1]; + pos_sideways[2] += unit_sideways[2]; + } + while ((++pos_sideways[3]) != mobj->movefactor); + } + } + + hnext = mobj->hnext; // just in case the mobj is removed + + if (pos_lengthways[3] > mobj->movecount) + { + do + { + pos_lengthways[0] -= unit_lengthways[0]; + pos_lengthways[1] -= unit_lengthways[1]; + pos_lengthways[2] -= unit_lengthways[2]; + } + while ((--pos_lengthways[3]) != mobj->movecount); + } + else if (pos_lengthways[3] < mobj->movecount) + { + do + { + pos_lengthways[0] += unit_lengthways[0]; + pos_lengthways[1] += unit_lengthways[1]; + pos_lengthways[2] += unit_lengthways[2]; + } + while ((++pos_lengthways[3]) != mobj->movecount); + } + + P_UnsetThingPosition(mobj); + + mobj->x = center->x; + mobj->y = center->y; + mobj->z = center->z; + + // Add on the appropriate distances to the center's co-ordinates. + if (pos_lengthways[3]) + { + mobj->x += pos_lengthways[0]; + mobj->y += pos_lengthways[1]; + zstore = pos_lengthways[2] + pos_sideways[2]; + } + else + zstore = pos_sideways[2]; + + mobj->x += pos_sideways[0]; + mobj->y += pos_sideways[1]; + + // Cut the height to align the link with the axis. + if (mobj->type == MT_SMALLMACECHAIN || mobj->type == MT_BIGMACECHAIN || mobj->type == MT_SMALLGRABCHAIN || mobj->type == MT_BIGGRABCHAIN) + zstore -= P_MobjFlip(mobj)*mobj->height/4; + else + zstore -= P_MobjFlip(mobj)*mobj->height/2; + + mobj->z += zstore; + +#if 0 // toaster's testing flashie! + if (!(mobj->movecount & 1) && !(leveltime & TICRATE)) // I had a brainfart and the flashing isn't exactly what I expected it to be, but it's actually much more useful. + mobj->flags2 ^= MF2_DONTDRAW; +#endif + + P_SetThingPosition(mobj); + +#if 0 // toaster's height-clipping dealie! + if (!pos_lengthways[3] || P_MobjWasRemoved(mobj) || (mobj->flags & MF_NOCLIPHEIGHT)) + goto cont; + + if ((fa = ((center->threshold & (FINEMASK/2)) << ANGLETOFINESHIFT)) > ANGLE_45 && fa < ANGLE_135) // only move towards center when the motion is towards/away from the ground, rather than alongside it + goto cont; + + if (mobj->subsector->sector->ffloors) + P_AdjustMobjFloorZ_FFloors(mobj, mobj->subsector->sector, 2); + + if (mobj->floorz > mobj->z) + zstore = (mobj->floorz - zstore); + else if (mobj->ceilingz < mobj->z) + zstore = (mobj->ceilingz - mobj->height - zstore); + else + goto cont; + + zstore = FixedDiv(zstore, dist); // Still needs work... scaling factor is wrong! + + P_UnsetThingPosition(mobj); + + mobj->x -= FixedMul(unit_lengthways[0], zstore); + mobj->y -= FixedMul(unit_lengthways[1], zstore); + + P_SetThingPosition(mobj); + +cont: +#endif + mobj = hnext; } - // Rotating Chain. - else - { - prevswing = (prevswing + mobj->friction) & FINEMASK; - fa = (mobj->threshold + mobj->friction) & FINEMASK; - - if (!donetwice - && (mobj->flags2 & MF2_BOSSNOTRAP) // at the end of the chain and can play a sound - && (!(prevswing > (FINEMASK/2)) && (fa > (FINEMASK/2)))) // completed a full swing - S_StartSound(mobj, mobj->info->activesound); - - v[0] = FixedMul(FINECOSINE(fa), radius); - v[2] = FixedMul(FINESINE(fa), radius); - } - - // Calculate the angle matrixes for the link. - res = VectorMatrixMultiply(v, *RotateXMatrix(mobj->tracer->threshold << ANGLETOFINESHIFT)); - M_Memcpy(&v, res, sizeof(v)); - res = VectorMatrixMultiply(v, *RotateZMatrix(mobj->tracer->health << ANGLETOFINESHIFT)); - M_Memcpy(&v, res, sizeof(v)); - - // Cut the height to align the link with the axis. - if (mobj->type == MT_SMALLMACECHAIN || mobj->type == MT_BIGMACECHAIN) - v[2] -= P_MobjFlip(mobj)*mobj->height/4; - else - v[2] -= P_MobjFlip(mobj)*mobj->height/2; - - P_UnsetThingPosition(mobj); - - // Add on the appropriate distances to the center's co-ordinates. - mobj->x = mobj->tracer->x + v[0]; - mobj->y = mobj->tracer->y + v[1]; - mobj->z = mobj->tracer->z + v[2]; - - P_SetThingPosition(mobj); - - if (donetwice || P_MobjWasRemoved(mobj)) - return; - - if (mobj->flags & (MF_NOCLIP|MF_NOCLIPHEIGHT)) - return; - - if ((fa = ((mobj->tracer->threshold & (FINEMASK/2)) << ANGLETOFINESHIFT)) > ANGLE_45 && fa < ANGLE_135) // only move towards center when the motion is towards/away from the ground, rather than alongside it - return; - - if (mobj->subsector->sector->ffloors) - P_AdjustMobjFloorZ_FFloors(mobj, mobj->subsector->sector, 2); - - // Variable reuse - if (mobj->floorz > mobj->z) - dist = (mobj->floorz - mobj->tracer->z); - else if (mobj->ceilingz < mobj->z) - dist = (mobj->ceilingz - mobj->tracer->z); - else - return; - - if ((dist = FixedDiv(dist, v[2])) > FRACUNIT) - return; - - radius = FixedMul(radius, dist); - donetwice = true; - dist = ((mobj->info->speed) ? mobj->info->speed : mobjinfo[MT_SMALLMACECHAIN].speed); - goto maceretry; } static boolean P_ShieldLook(mobj_t *thing, shieldtype_t shield) @@ -6596,6 +6668,10 @@ void P_MobjThinker(mobj_t *mobj) P_SetTarget(&mobj->target, NULL); if (mobj->tracer && P_MobjWasRemoved(mobj->tracer)) P_SetTarget(&mobj->tracer, NULL); + if (mobj->hnext && P_MobjWasRemoved(mobj->hnext)) + P_SetTarget(&mobj->hnext, NULL); + if (mobj->hprev && P_MobjWasRemoved(mobj->hprev)) + P_SetTarget(&mobj->hprev, NULL); mobj->eflags &= ~(MFE_PUSHED|MFE_SPRUNG); @@ -6664,13 +6740,6 @@ void P_MobjThinker(mobj_t *mobj) } } - if (mobj->flags2 & MF2_MACEROTATE) - { - P_MaceRotate(mobj); - if (P_MobjWasRemoved(mobj)) - return; - } - // Special thinker for scenery objects if (mobj->flags & MF_SCENERY) { @@ -6687,6 +6756,18 @@ void P_MobjThinker(mobj_t *mobj) switch (mobj->type) { + case MT_MACEPOINT: + case MT_CHAINMACEPOINT: + case MT_SPRINGBALLPOINT: + case MT_CHAINPOINT: + case MT_FIREBARPOINT: + case MT_CUSTOMMACEPOINT: + case MT_HIDDEN_SLING: + // The following was pretty good, but liked breaking whenever mobj->lastlook changed. + //P_MaceRotate(mobj, ((leveltime + 1) * mobj->lastlook), (leveltime * mobj->lastlook)); + P_MaceRotate(mobj, mobj->movedir + mobj->lastlook, mobj->movedir); + mobj->movedir = (mobj->movedir + mobj->lastlook) & FINEMASK; + break; case MT_HOOP: if (mobj->fuse > 1) P_MoveHoop(mobj); @@ -7035,6 +7116,59 @@ void P_MobjThinker(mobj_t *mobj) return; } break; + case MT_PARTICLEGEN: + if (!mobj->lastlook) + return; + + if (!mobj->threshold) + return; + + if (--mobj->fuse <= 0) + { + INT32 i = 0; + mobj_t *spawn; + fixed_t bottomheight, topheight; + INT32 type = mobj->threshold, line = mobj->cvmem; + + mobj->fuse = (tic_t)mobj->reactiontime; + + bottomheight = lines[line].frontsector->floorheight; + topheight = lines[line].frontsector->ceilingheight - mobjinfo[(mobjtype_t)type].height; + + if (mobj->waterbottom != bottomheight || mobj->watertop != topheight) + { + if (mobj->movefactor && (topheight > bottomheight)) + mobj->health = (tic_t)(FixedDiv((topheight - bottomheight), abs(mobj->movefactor)) >> FRACBITS); + else + mobj->health = 0; + + mobj->z = ((mobj->flags2 & MF2_OBJECTFLIP) ? topheight : bottomheight); + } + + if (!mobj->health) + return; + + for (i = 0; i < mobj->lastlook; i++) + { + spawn = P_SpawnMobj( + mobj->x + FixedMul(FixedMul(mobj->friction, mobj->scale), FINECOSINE(mobj->angle>>ANGLETOFINESHIFT)), + mobj->y + FixedMul(FixedMul(mobj->friction, mobj->scale), FINESINE(mobj->angle>>ANGLETOFINESHIFT)), + mobj->z, + (mobjtype_t)mobj->threshold); + P_SetScale(spawn, mobj->scale); + spawn->momz = FixedMul(mobj->movefactor, spawn->scale); + spawn->destscale = spawn->scale/100; + spawn->scalespeed = spawn->scale/mobj->health; + spawn->tics = (tic_t)mobj->health; + spawn->flags2 |= (mobj->flags2 & MF2_OBJECTFLIP); + spawn->angle += P_RandomKey(36)*ANG10; // irrelevant for default objects but might make sense for some custom ones + + mobj->angle += mobj->movedir; + } + + mobj->angle += (angle_t)mobj->movecount; + } + break; default: if (mobj->fuse) { // Scenery object fuse! Very basic! @@ -7142,7 +7276,7 @@ void P_MobjThinker(mobj_t *mobj) else if (mobj->health <= 0) // Dead things think differently than the living. switch (mobj->type) { - case MT_BLUEBALL: + case MT_BLUESPHERE: if ((mobj->tics>>2)+1 > 0 && (mobj->tics>>2)+1 <= tr_trans60) // tr_trans50 through tr_trans90, shifting once every second frame mobj->frame = (NUMTRANSMAPS-((mobj->tics>>2)+1))<flags2 ^= MF2_DONTDRAW; + break; case MT_EGGTRAP: // Egg Capsule animal release if (mobj->fuse > 0 && mobj->fuse < 2*TICRATE-(TICRATE/7) && (mobj->fuse & 3)) @@ -7233,348 +7370,496 @@ void P_MobjThinker(mobj_t *mobj) default: break; } - else switch (mobj->type) + else { - case MT_WALLSPIKEBASE: - if (!mobj->target) { - P_RemoveMobj(mobj); - return; - } - mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|(mobj->target->frame & FF_FRAMEMASK); + if ((mobj->flags & MF_ENEMY) && (mobj->state->nextstate == mobj->info->spawnstate && mobj->tics == 1)) + mobj->flags2 &= ~MF2_FRET; + + switch (mobj->type) + { + case MT_WALLSPIKEBASE: + if (!mobj->target) { + P_RemoveMobj(mobj); + return; + } + mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|(mobj->target->frame & FF_FRAMEMASK); #if 0 - if (mobj->angle != mobj->target->angle + ANGLE_90) // reposition if not the correct angle - { - mobj_t *target = mobj->target; // shortcut - const fixed_t baseradius = target->radius - (target->scale/2); //FixedMul(FRACUNIT/2, target->scale); - P_UnsetThingPosition(mobj); - mobj->x = target->x - P_ReturnThrustX(target, target->angle, baseradius); - mobj->y = target->y - P_ReturnThrustY(target, target->angle, baseradius); - P_SetThingPosition(mobj); - mobj->angle = target->angle + ANGLE_90; - } + if (mobj->angle != mobj->target->angle + ANGLE_90) // reposition if not the correct angle + { + mobj_t *target = mobj->target; // shortcut + const fixed_t baseradius = target->radius - (target->scale/2); //FixedMul(FRACUNIT/2, target->scale); + P_UnsetThingPosition(mobj); + mobj->x = target->x - P_ReturnThrustX(target, target->angle, baseradius); + mobj->y = target->y - P_ReturnThrustY(target, target->angle, baseradius); + P_SetThingPosition(mobj); + mobj->angle = target->angle + ANGLE_90; + } #endif - break; - case MT_FALLINGROCK: - // Despawn rocks here in case zmovement code can't do so (blame slopes) - if (!mobj->momx && !mobj->momy && !mobj->momz - && ((mobj->eflags & MFE_VERTICALFLIP) ? - mobj->z + mobj->height >= mobj->ceilingz - : mobj->z <= mobj->floorz)) - { - P_RemoveMobj(mobj); - return; - } - P_MobjCheckWater(mobj); - break; - case MT_EMERALDSPAWN: - if (mobj->threshold) - { - mobj->threshold--; - - if (!mobj->threshold && !mobj->target && mobj->reactiontime) + break; + case MT_FALLINGROCK: + // Despawn rocks here in case zmovement code can't do so (blame slopes) + if (!mobj->momx && !mobj->momy && !mobj->momz + && ((mobj->eflags & MFE_VERTICALFLIP) ? + mobj->z + mobj->height >= mobj->ceilingz + : mobj->z <= mobj->floorz)) { - mobj_t *emerald = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->reactiontime); - emerald->threshold = 42; - P_SetTarget(&mobj->target, emerald); - P_SetTarget(&emerald->target, mobj); + P_RemoveMobj(mobj); + return; } - } - break; - case MT_AQUABUZZ: - P_MobjCheckWater(mobj); // solely for MFE_UNDERWATER for A_FlickySpawn - /* FALLTHRU */ - case MT_BIGAIRMINE: - { - if (mobj->tracer && mobj->tracer->player && mobj->tracer->health > 0 - && P_AproxDistance(P_AproxDistance(mobj->tracer->x - mobj->x, mobj->tracer->y - mobj->y), mobj->tracer->z - mobj->z) <= mobj->radius * 16) + P_MobjCheckWater(mobj); + break; + case MT_ARROW: + if (mobj->flags & MF_MISSILE) { - // Home in on the target. - P_HomingAttack(mobj, mobj->tracer); + // Calculate the angle of movement. + /* + momz + / | + / | + / | + 0------dist(momx,momy) + */ - if (mobj->z < mobj->floorz) - mobj->z = mobj->floorz; + fixed_t dist = P_AproxDistance(mobj->momx, mobj->momy); + angle_t angle = R_PointToAngle2(0, 0, dist, mobj->momz); - if (leveltime % mobj->info->painchance == 0) - S_StartSound(mobj, mobj->info->activesound); - } - else - { - // Try to find a player - P_LookForPlayers(mobj, true, true, mobj->radius * 16); - mobj->momx >>= 1; - mobj->momy >>= 1; - mobj->momz >>= 1; - } - } - break; - case MT_BIGMINE: - { - if (mobj->tracer && mobj->tracer->player && mobj->tracer->health > 0 - && P_AproxDistance(P_AproxDistance(mobj->tracer->x - mobj->x, mobj->tracer->y - mobj->y), mobj->tracer->z - mobj->z) <= mobj->radius * 16) - { - P_MobjCheckWater(mobj); + if (angle > ANG20 && angle <= ANGLE_180) + mobj->frame = 2; + else if (angle < ANG340 && angle > ANGLE_180) + mobj->frame = 0; + else + mobj->frame = 1; - // Home in on the target. - P_HomingAttack(mobj, mobj->tracer); - - // Don't let it go out of water - if (mobj->z + mobj->height > mobj->watertop) - mobj->z = mobj->watertop - mobj->height; - - if (mobj->z < mobj->floorz) - mobj->z = mobj->floorz; - - if (leveltime % mobj->info->painchance == 0) - S_StartSound(mobj, mobj->info->activesound); - } - else - { - // Try to find a player - P_LookForPlayers(mobj, true, true, mobj->radius * 16); - mobj->momx >>= 1; - mobj->momy >>= 1; - mobj->momz >>= 1; - } - } - break; - case MT_CHAINPOINT: - case MT_CHAINMACEPOINT: - if (leveltime & 1) - { - if (mobj->lastlook > mobj->movecount) - mobj->lastlook--; -/* - if (mobj->threshold > mobj->movefactor) - mobj->threshold -= FRACUNIT; - else if (mobj->threshold < mobj->movefactor) - mobj->threshold += FRACUNIT;*/ - } - break; - case MT_EGGCAPSULE: - if (!mobj->reactiontime) - { - // Target nearest player on your mare. - // (You can make it float up/down by adding MF_FLOAT, - // but beware level design pitfalls.) - fixed_t shortest = 1024*FRACUNIT; - INT32 i; - P_SetTarget(&mobj->target, NULL); - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && players[i].mo - && players[i].mare == mobj->threshold && players[i].rings > 0) + if (!(mobj->extravalue1) && (mobj->momz < 0)) { - fixed_t dist = P_AproxDistance(players[i].mo->x - mobj->x, players[i].mo->y - mobj->y); - if (dist < shortest) + mobj->extravalue1 = 1; + S_StartSound(mobj, mobj->info->activesound); + } + if (leveltime & 1) + { + mobj_t *dust = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_PARTICLE); + dust->tics = 18; + dust->scalespeed = 4096; + dust->destscale = FRACUNIT/32; + } + } + else + mobj->flags2 ^= MF2_DONTDRAW; + break; + case MT_EMERALDSPAWN: + if (mobj->threshold) + { + mobj->threshold--; + + if (!mobj->threshold && !mobj->target && mobj->reactiontime) + { + mobj_t *emerald = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->reactiontime); + emerald->threshold = 42; + P_SetTarget(&mobj->target, emerald); + P_SetTarget(&emerald->target, mobj); + } + } + break; + case MT_BUBBLEBUZZ: + mobj->eflags |= MFE_UNDERWATER; //P_MobjCheckWater(mobj); // solely for MFE_UNDERWATER for A_FlickySpawn + { + if (mobj->tracer && mobj->tracer->player && mobj->tracer->health > 0 + && P_AproxDistance(P_AproxDistance(mobj->tracer->x - mobj->x, mobj->tracer->y - mobj->y), mobj->tracer->z - mobj->z) <= mobj->radius * 16) + { + // Home in on the target. + P_HomingAttack(mobj, mobj->tracer); + + if (mobj->z < mobj->floorz) + mobj->z = mobj->floorz; + + if (leveltime % mobj->info->painchance == 0) + S_StartSound(mobj, mobj->info->activesound); + } + else + { + // Try to find a player + P_LookForPlayers(mobj, true, true, mobj->radius * 16); + mobj->momx >>= 1; + mobj->momy >>= 1; + mobj->momz >>= 1; + } + } + break; + case MT_BUMBLEBORE: + { + statenum_t st = mobj->state-states; + if (st == S_BUMBLEBORE_FLY1 || st == S_BUMBLEBORE_FLY2) + { + if (!mobj->target) + P_SetMobjState(mobj, mobj->info->spawnstate); + else if (P_MobjFlip(mobj)*((mobj->z + (mobj->height>>1)) - (mobj->target->z + (mobj->target->height>>1))) > 0 + && R_PointToDist2(mobj->x, mobj->y, mobj->target->x, mobj->target->y) <= 32*FRACUNIT) { - P_SetTarget(&mobj->target, players[i].mo); - shortest = dist; + mobj->momx >>= 1; + mobj->momy >>= 1; + if (++mobj->movefactor == 4) + { + S_StartSound(mobj, mobj->info->seesound); + mobj->momx = mobj->momy = mobj->momz = 0; + mobj->flags = (mobj->flags|MF_PAIN) & ~MF_NOGRAVITY; + P_SetMobjState(mobj, mobj->info->meleestate); + } + } + else + mobj->movefactor = 0; + } + else if (st == S_BUMBLEBORE_RAISE || st == S_BUMBLEBORE_FALL2) // no _FALL1 because it's an 0-tic + { + if (P_IsObjectOnGround(mobj)) + { + S_StopSound(mobj); + S_StartSound(mobj, mobj->info->attacksound); + mobj->flags = (mobj->flags|MF_NOGRAVITY) & ~MF_PAIN; + mobj->momx = mobj->momy = mobj->momz = 0; + P_SetMobjState(mobj, mobj->info->painstate); + } + else + { + mobj->angle += ANGLE_22h; + mobj->frame = mobj->state->frame + ((mobj->tics & 2)>>1); } } - } - break; - case MT_EGGMOBILE2_POGO: - if (!mobj->target - || !mobj->target->health - || mobj->target->state == &states[mobj->target->info->spawnstate] - || mobj->target->state == &states[mobj->target->info->raisestate]) - { - P_RemoveMobj(mobj); - return; - } - P_TeleportMove(mobj, mobj->target->x, mobj->target->y, mobj->target->z - mobj->height); - break; - case MT_HAMMER: - if (mobj->z <= mobj->floorz) - { - P_RemoveMobj(mobj); - return; - } - break; - case MT_KOOPA: - P_KoopaThinker(mobj); - break; - case MT_REDRING: - if (((mobj->z < mobj->floorz) || (mobj->z + mobj->height > mobj->ceilingz)) - && mobj->flags & MF_MISSILE) - { - P_ExplodeMissile(mobj); - return; - } - break; - case MT_BOSSFLYPOINT: - return; - case MT_NIGHTSCORE: - mobj->color = (UINT8)(leveltime % SKINCOLOR_WHITE); - break; - case MT_JETFUME1: - { - fixed_t jetx, jety; + else if (st == S_BUMBLEBORE_STUCK2 && mobj->tics < TICRATE) + mobj->frame = mobj->state->frame + ((mobj->tics & 2)>>1); + } + break; + case MT_BIGMINE: + mobj->extravalue1 += 3; + mobj->extravalue1 %= 360; + P_UnsetThingPosition(mobj); + mobj->z += FINESINE(mobj->extravalue1*(FINEMASK+1)/360); + P_SetThingPosition(mobj); + break; + case MT_FLAME: + if (mobj->flags2 & MF2_BOSSNOTRAP) + { + if (!mobj->target || P_MobjWasRemoved(mobj->target)) + { + if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer)) + P_RemoveMobj(mobj->tracer); + P_RemoveMobj(mobj); + return; + } + mobj->z = mobj->target->z + mobj->target->momz; + if (!(mobj->eflags & MFE_VERTICALFLIP)) + mobj->z += mobj->target->height; + } + if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer)) + { + mobj->tracer->z = mobj->z + P_MobjFlip(mobj)*20*mobj->scale; + if (mobj->eflags & MFE_VERTICALFLIP) + mobj->tracer->z += mobj->height; + } + break; + case MT_WAVINGFLAG: + { + fixed_t base = (leveltime<<(FRACBITS+1)); + mobj_t *seg = mobj->tracer, *prev = mobj; + mobj->movedir = mobj->angle + + ((((FINESINE((FixedAngle(base<<1)>>ANGLETOFINESHIFT) & FINEMASK) + + FINESINE((FixedAngle(base<<4)>>ANGLETOFINESHIFT) & FINEMASK))>>1) + + FINESINE((FixedAngle(base*9)>>ANGLETOFINESHIFT) & FINEMASK) + + FINECOSINE(((FixedAngle(base*9))>>ANGLETOFINESHIFT) & FINEMASK))<<12); //*2^12 + while (seg) + { + seg->movedir = seg->angle; + seg->angle = prev->movedir; + P_UnsetThingPosition(seg); + seg->x = prev->x + P_ReturnThrustX(prev, prev->angle, prev->radius); + seg->y = prev->y + P_ReturnThrustY(prev, prev->angle, prev->radius); + seg->z = prev->z + prev->height - (seg->scale>>1); + P_SetThingPosition(seg); + prev = seg; + seg = seg->tracer; + } + } + break; + case MT_SPINCUSHION: + if (mobj->target && mobj->state-states >= S_SPINCUSHION_AIM1 && mobj->state-states <= S_SPINCUSHION_AIM5) + mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y); + break; + case MT_CRUSHCLAW: + if (mobj->state-states == S_CRUSHCLAW_STAY && mobj->target) + { + mobj_t *chain = mobj->target->target; + SINT8 sign = ((mobj->tics & 1) ? mobj->tics : -(SINT8)(mobj->tics)); + while (chain) + { + chain->z = chain->watertop + sign*mobj->scale; + sign = -sign; + chain = chain->target; + } + } + break; + case MT_SMASHINGSPIKEBALL: + mobj->momx = mobj->momy = 0; + if (mobj->state-states == S_SMASHSPIKE_FALL && P_IsObjectOnGround(mobj)) + { + P_SetMobjState(mobj, S_SMASHSPIKE_STOMP1); + S_StartSound(mobj, sfx_spsmsh); + } + else if (mobj->state-states == S_SMASHSPIKE_RISE2 && P_MobjFlip(mobj)*(mobj->z - mobj->movecount) >= 0) + { + mobj->momz = 0; + P_SetMobjState(mobj, S_SMASHSPIKE_FLOAT); + } + break; + case MT_HANGSTER: + { + statenum_t st = mobj->state-states; + //ghost image trail when flying down + if (st == S_HANGSTER_SWOOP1 || st == S_HANGSTER_SWOOP2) + { + P_SpawnGhostMobj(mobj); + //curve when in line with target, otherwise curve to avoid crashing into floor + if ((mobj->z - mobj->floorz <= 80*FRACUNIT) || (mobj->target && (mobj->z - mobj->target->z <= 80*FRACUNIT))) + P_SetMobjState(mobj, (st = S_HANGSTER_ARC1)); + } - if (!mobj->target // if you have no target - || (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now - { // then remove yourself as well! + //swoop arc movement stuff + if (st == S_HANGSTER_ARC1) + { + A_FaceTarget(mobj); + P_Thrust(mobj, mobj->angle, 1*FRACUNIT); + } + else if (st == S_HANGSTER_ARC2) + P_Thrust(mobj, mobj->angle, 2*FRACUNIT); + else if (st == S_HANGSTER_ARC3) + P_Thrust(mobj, mobj->angle, 4*FRACUNIT); + //if movement has stopped while flying (like hitting a wall), fly up immediately + else if (st == S_HANGSTER_FLY1 && !mobj->momx && !mobj->momy) + { + mobj->extravalue1 = 0; + P_SetMobjState(mobj, S_HANGSTER_ARCUP1); + } + //after swooping back up, check for ceiling + else if ((st == S_HANGSTER_RETURN1 || st == S_HANGSTER_RETURN2) && mobj->momz == 0 && mobj->ceilingz == (mobj->z + mobj->height)) + P_SetMobjState(mobj, (st = S_HANGSTER_RETURN3)); + + //should you roost on a ceiling with F_SKY1 as its flat, disappear forever + if (st == S_HANGSTER_RETURN3 && mobj->momz == 0 && mobj->ceilingz == (mobj->z + mobj->height) + && mobj->subsector->sector->ceilingpic == skyflatnum + && mobj->subsector->sector->ceilingheight == mobj->ceilingz) + { + P_RemoveMobj(mobj); + return; + } + } + break; + case MT_EGGCAPSULE: + if (!mobj->reactiontime) + { + // Target nearest player on your mare. + // (You can make it float up/down by adding MF_FLOAT, + // but beware level design pitfalls.) + fixed_t shortest = 1024*FRACUNIT; + INT32 i; + P_SetTarget(&mobj->target, NULL); + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && players[i].mo + && players[i].mare == mobj->threshold && players[i].spheres > 0) + { + fixed_t dist = P_AproxDistance(players[i].mo->x - mobj->x, players[i].mo->y - mobj->y); + if (dist < shortest) + { + P_SetTarget(&mobj->target, players[i].mo); + shortest = dist; + } + } + } + break; + case MT_EGGMOBILE2_POGO: + if (!mobj->target + || !mobj->target->health + || mobj->target->state == &states[mobj->target->info->spawnstate] + || mobj->target->state == &states[mobj->target->info->raisestate]) + { P_RemoveMobj(mobj); return; } - - jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, FixedMul(-64*FRACUNIT, mobj->target->scale)); - jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, FixedMul(-64*FRACUNIT, mobj->target->scale)); - - if (mobj->fuse == 56) // First one + P_TeleportMove(mobj, mobj->target->x, mobj->target->y, mobj->target->z - mobj->height); + break; + case MT_HAMMER: + if (mobj->z <= mobj->floorz) { + P_RemoveMobj(mobj); + return; + } + break; + case MT_KOOPA: + P_KoopaThinker(mobj); + break; + case MT_REDRING: + if (((mobj->z < mobj->floorz) || (mobj->z + mobj->height > mobj->ceilingz)) + && mobj->flags & MF_MISSILE) + { + P_ExplodeMissile(mobj); + return; + } + break; + case MT_BOSSFLYPOINT: + return; + case MT_NIGHTSCORE: + mobj->color = (UINT8)(leveltime % SKINCOLOR_WHITE); + break; + case MT_JETFUME1: + { + fixed_t jetx, jety; + + if (!mobj->target // if you have no target + || (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now + { // then remove yourself as well! + P_RemoveMobj(mobj); + return; + } + + jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, FixedMul(-64*FRACUNIT, mobj->target->scale)); + jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, FixedMul(-64*FRACUNIT, mobj->target->scale)); + + if (mobj->fuse == 56) // First one + { + P_UnsetThingPosition(mobj); + mobj->x = jetx; + mobj->y = jety; + if (mobj->target->eflags & MFE_VERTICALFLIP) + mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(38*FRACUNIT, mobj->target->scale); + else + mobj->z = mobj->target->z + FixedMul(38*FRACUNIT, mobj->target->scale); + mobj->floorz = mobj->z; + mobj->ceilingz = mobj->z+mobj->height; + P_SetThingPosition(mobj); + } + else if (mobj->fuse == 57) + { + P_UnsetThingPosition(mobj); + mobj->x = jetx + P_ReturnThrustX(mobj->target, mobj->target->angle-ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale)); + mobj->y = jety + P_ReturnThrustY(mobj->target, mobj->target->angle-ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale)); + if (mobj->target->eflags & MFE_VERTICALFLIP) + mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(12*FRACUNIT, mobj->target->scale); + else + mobj->z = mobj->target->z + FixedMul(12*FRACUNIT, mobj->target->scale); + mobj->floorz = mobj->z; + mobj->ceilingz = mobj->z+mobj->height; + P_SetThingPosition(mobj); + } + else if (mobj->fuse == 58) + { + P_UnsetThingPosition(mobj); + mobj->x = jetx + P_ReturnThrustX(mobj->target, mobj->target->angle+ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale)); + mobj->y = jety + P_ReturnThrustY(mobj->target, mobj->target->angle+ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale)); + if (mobj->target->eflags & MFE_VERTICALFLIP) + mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(12*FRACUNIT, mobj->target->scale); + else + mobj->z = mobj->target->z + FixedMul(12*FRACUNIT, mobj->target->scale); + mobj->floorz = mobj->z; + mobj->ceilingz = mobj->z+mobj->height; + P_SetThingPosition(mobj); + } + else if (mobj->fuse == 59) + { + jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, -mobj->target->radius); + jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, -mobj->target->radius); + P_UnsetThingPosition(mobj); + mobj->x = jetx; + mobj->y = jety; + if (mobj->target->eflags & MFE_VERTICALFLIP) + mobj->z = mobj->target->z + mobj->target->height/2 + mobj->height/2; + else + mobj->z = mobj->target->z + mobj->target->height/2 - mobj->height/2; + mobj->floorz = mobj->z; + mobj->ceilingz = mobj->z+mobj->height; + P_SetThingPosition(mobj); + } + mobj->fuse++; + } + break; + case MT_PROPELLER: + { + fixed_t jetx, jety; + + if (!mobj->target // if you have no target + || (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now + { // then remove yourself as well! + P_RemoveMobj(mobj); + return; + } + + jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, FixedMul(-60*FRACUNIT, mobj->target->scale)); + jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, FixedMul(-60*FRACUNIT, mobj->target->scale)); + P_UnsetThingPosition(mobj); mobj->x = jetx; mobj->y = jety; - if (mobj->target->eflags & MFE_VERTICALFLIP) - mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(38*FRACUNIT, mobj->target->scale); - else - mobj->z = mobj->target->z + FixedMul(38*FRACUNIT, mobj->target->scale); + mobj->z = mobj->target->z + FixedMul(17*FRACUNIT, mobj->target->scale); + mobj->angle = mobj->target->angle - ANGLE_180; mobj->floorz = mobj->z; mobj->ceilingz = mobj->z+mobj->height; P_SetThingPosition(mobj); } - else if (mobj->fuse == 57) + break; + case MT_JETFLAME: { + if (!mobj->target // if you have no target + || (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now + { // then remove yourself as well! + P_RemoveMobj(mobj); + return; + } + P_UnsetThingPosition(mobj); - mobj->x = jetx + P_ReturnThrustX(mobj->target, mobj->target->angle-ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale)); - mobj->y = jety + P_ReturnThrustY(mobj->target, mobj->target->angle-ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale)); - if (mobj->target->eflags & MFE_VERTICALFLIP) - mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(12*FRACUNIT, mobj->target->scale); - else - mobj->z = mobj->target->z + FixedMul(12*FRACUNIT, mobj->target->scale); + mobj->x = mobj->target->x; + mobj->y = mobj->target->y; + mobj->z = mobj->target->z - FixedMul(50*FRACUNIT, mobj->target->scale); mobj->floorz = mobj->z; mobj->ceilingz = mobj->z+mobj->height; P_SetThingPosition(mobj); } - else if (mobj->fuse == 58) + break; + case MT_NIGHTSDRONE: + // GOAL mode? + if (mobj->state >= &states[S_NIGHTSDRONE_SPARKLING1] && mobj->state <= &states[S_NIGHTSDRONE_SPARKLING16]) { - P_UnsetThingPosition(mobj); - mobj->x = jetx + P_ReturnThrustX(mobj->target, mobj->target->angle+ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale)); - mobj->y = jety + P_ReturnThrustY(mobj->target, mobj->target->angle+ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale)); - if (mobj->target->eflags & MFE_VERTICALFLIP) - mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(12*FRACUNIT, mobj->target->scale); - else - mobj->z = mobj->target->z + FixedMul(12*FRACUNIT, mobj->target->scale); - mobj->floorz = mobj->z; - mobj->ceilingz = mobj->z+mobj->height; - P_SetThingPosition(mobj); - } - else if (mobj->fuse == 59) - { - jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, -mobj->target->radius); - jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, -mobj->target->radius); - P_UnsetThingPosition(mobj); - mobj->x = jetx; - mobj->y = jety; - if (mobj->target->eflags & MFE_VERTICALFLIP) - mobj->z = mobj->target->z + mobj->target->height/2 + mobj->height/2; - else - mobj->z = mobj->target->z + mobj->target->height/2 - mobj->height/2; - mobj->floorz = mobj->z; - mobj->ceilingz = mobj->z+mobj->height; - P_SetThingPosition(mobj); - } - mobj->fuse++; - } - break; - case MT_PROPELLER: - { - fixed_t jetx, jety; - - if (!mobj->target // if you have no target - || (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now - { // then remove yourself as well! - P_RemoveMobj(mobj); - return; - } - - jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, FixedMul(-60*FRACUNIT, mobj->target->scale)); - jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, FixedMul(-60*FRACUNIT, mobj->target->scale)); - - P_UnsetThingPosition(mobj); - mobj->x = jetx; - mobj->y = jety; - mobj->z = mobj->target->z + FixedMul(17*FRACUNIT, mobj->target->scale); - mobj->angle = mobj->target->angle - ANGLE_180; - mobj->floorz = mobj->z; - mobj->ceilingz = mobj->z+mobj->height; - P_SetThingPosition(mobj); - } - break; - case MT_JETFLAME: - { - if (!mobj->target // if you have no target - || (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now - { // then remove yourself as well! - P_RemoveMobj(mobj); - return; - } - - P_UnsetThingPosition(mobj); - mobj->x = mobj->target->x; - mobj->y = mobj->target->y; - mobj->z = mobj->target->z - FixedMul(50*FRACUNIT, mobj->target->scale); - mobj->floorz = mobj->z; - mobj->ceilingz = mobj->z+mobj->height; - P_SetThingPosition(mobj); - } - break; - case MT_NIGHTSDRONE: - if (mobj->state >= &states[S_NIGHTSDRONE_SPARKLING1] && mobj->state <= &states[S_NIGHTSDRONE_SPARKLING16]) - { - mobj->flags2 &= ~MF2_DONTDRAW; - mobj->z = mobj->floorz + mobj->height + (mobj->spawnpoint->options >> ZSHIFT) * FRACUNIT; - mobj->angle = 0; - - if (!mobj->target) - { - mobj_t *goalpost = P_SpawnMobj(mobj->x, mobj->y, mobj->z + FRACUNIT, MT_NIGHTSGOAL); - CONS_Debug(DBG_NIGHTSBASIC, "Adding goal post\n"); - goalpost->angle = mobj->angle; - P_SetTarget(&mobj->target, goalpost); - } - - if (G_IsSpecialStage(gamemap)) - { // Never show the NiGHTS drone in special stages. Check ANYONE for bonustime. INT32 i; boolean bonustime = false; + for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && players[i].bonustime) + if (playeringame[i] && players[i].bonustime && players[i].powers[pw_carry] == CR_NIGHTSMODE) { bonustime = true; break; } + if (!bonustime) { - mobj->flags &= ~MF_NOGRAVITY; - P_SetMobjState(mobj, S_NIGHTSDRONE1); - mobj->flags2 |= MF2_DONTDRAW; - } - } - else if (mobj->tracer && mobj->tracer->player) - { - if (!(mobj->tracer->player->powers[pw_carry] == CR_NIGHTSMODE)) - { - mobj->flags &= ~MF_NOGRAVITY; - mobj->flags2 &= ~MF2_DONTDRAW; - P_SetMobjState(mobj, S_NIGHTSDRONE1); - } - else if (!mobj->tracer->player->bonustime) - { - mobj->flags &= ~MF_NOGRAVITY; - P_SetMobjState(mobj, S_NIGHTSDRONE1); - } - } - } - else - { - if (G_IsSpecialStage(gamemap)) - { // Never show the NiGHTS drone in special stages. Check ANYONE for bonustime. - INT32 i; + CONS_Debug(DBG_NIGHTSBASIC, "Removing goal post\n"); + P_RemoveMobj(mobj->target); + P_SetTarget(&mobj->target, NULL); + mobj->flags &= ~MF_NOGRAVITY; + mobj->flags2 |= MF2_DONTDRAW; + P_SetMobjState(mobj, S_NIGHTSDRONE1); + } + } + // Invisible/bouncing mode. + else + { + INT32 i; boolean bonustime = false; + + // Bouncy bouncy! + mobj->angle += ANG10; + if (mobj->flags2 & MF2_DONTDRAW) + mobj->momz = 0; + else if (mobj->z <= mobj->floorz) + mobj->momz = 5*FRACUNIT; + for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && players[i].bonustime) + if (playeringame[i] && players[i].bonustime && players[i].powers[pw_carry] == CR_NIGHTSMODE) { bonustime = true; break; @@ -7582,203 +7867,191 @@ void P_MobjThinker(mobj_t *mobj) if (bonustime) { - P_SetMobjState(mobj, S_NIGHTSDRONE_SPARKLING1); - mobj->flags |= MF_NOGRAVITY; - } - else - { - if (mobj->target) - { - CONS_Debug(DBG_NIGHTSBASIC, "Removing goal post\n"); - P_RemoveMobj(mobj->target); - P_SetTarget(&mobj->target, NULL); - } - mobj->flags2 |= MF2_DONTDRAW; - } - } - else if (mobj->tracer && mobj->tracer->player) - { - if (mobj->target) - { - CONS_Debug(DBG_NIGHTSBASIC, "Removing goal post\n"); - P_RemoveMobj(mobj->target); - P_SetTarget(&mobj->target, NULL); - } + mobj->z = mobj->floorz + mobj->height; + mobj->angle = mobj->momz = 0; - if (mobj->tracer->player->powers[pw_carry] == CR_NIGHTSMODE) + if (mobj->spawnpoint) + mobj->z += (mobj->spawnpoint->options >> ZSHIFT)<target, P_SpawnMobjFromMobj(mobj, 0, 0, FRACUNIT, MT_NIGHTSGOAL)); + + mobj->flags2 &= ~MF2_DONTDRAW; + mobj->flags |= MF_NOGRAVITY; + P_SetMobjState(mobj, S_NIGHTSDRONE_SPARKLING1); + } + else if (!G_IsSpecialStage(gamemap)) { - if (mobj->tracer->player->bonustime) - { - P_SetMobjState(mobj, S_NIGHTSDRONE_SPARKLING1); - mobj->flags |= MF_NOGRAVITY; - } + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && players[i].powers[pw_carry] != CR_NIGHTSMODE) + { + bonustime = true; // variable reuse + break; + } + + if (bonustime) + mobj->flags2 &= ~MF2_DONTDRAW; else mobj->flags2 |= MF2_DONTDRAW; } - else // Not NiGHTS - mobj->flags2 &= ~MF2_DONTDRAW; } - mobj->angle += ANG10; - if (mobj->z <= mobj->floorz) - mobj->momz = 5*FRACUNIT; - } - break; - case MT_PLAYER: - if (mobj->player) - P_PlayerMobjThinker(mobj); - return; - case MT_SKIM: - // check mobj against possible water content, before movement code - P_MobjCheckWater(mobj); - - // Keep Skim at water surface - if (mobj->z <= mobj->watertop) - { - mobj->flags |= MF_NOGRAVITY; - if (mobj->z < mobj->watertop) - { - if (mobj->watertop - mobj->z <= FixedMul(mobj->info->speed*FRACUNIT, mobj->scale)) - mobj->z = mobj->watertop; - else - mobj->momz = FixedMul(mobj->info->speed*FRACUNIT, mobj->scale); - } - } - else - { - mobj->flags &= ~MF_NOGRAVITY; - if (mobj->z > mobj->watertop && mobj->z - mobj->watertop < FixedMul(MAXSTEPMOVE, mobj->scale)) - mobj->z = mobj->watertop; - } - break; - case MT_RING: - case MT_COIN: - case MT_BLUEBALL: - case MT_REDTEAMRING: - case MT_BLUETEAMRING: - // No need to check water. Who cares? - P_RingThinker(mobj); - if (mobj->flags2 & MF2_NIGHTSPULL) - P_NightsItemChase(mobj); - else - A_AttractChase(mobj); - return; - // Flung items - case MT_FLINGRING: - case MT_FLINGCOIN: - if (mobj->flags2 & MF2_NIGHTSPULL) - P_NightsItemChase(mobj); - else - A_AttractChase(mobj); - break; - case MT_NIGHTSWING: - if (mobj->flags2 & MF2_NIGHTSPULL) - P_NightsItemChase(mobj); - break; - case MT_EMBLEM: - if (mobj->flags2 & MF2_NIGHTSPULL) - P_NightsItemChase(mobj); - break; - case MT_SHELL: - if (mobj->threshold && mobj->threshold != TICRATE) - mobj->threshold--; - - if (mobj->threshold >= TICRATE) - { - mobj->angle += ((mobj->movedir == 1) ? ANGLE_22h : ANGLE_337h); - P_InstaThrust(mobj, R_PointToAngle2(0, 0, mobj->momx, mobj->momy), (mobj->info->speed*mobj->scale)); - } - break; - case MT_TURRET: - P_MobjCheckWater(mobj); - P_CheckPosition(mobj, mobj->x, mobj->y); - if (P_MobjWasRemoved(mobj)) - return; - mobj->floorz = tmfloorz; - mobj->ceilingz = tmceilingz; - - if ((mobj->eflags & MFE_UNDERWATER) && mobj->health > 0) - { - P_SetMobjState(mobj, mobj->info->deathstate); - mobj->health = 0; - mobj->flags2 &= ~MF2_FIRING; - } - else if (mobj->health > 0 && mobj->z + mobj->height > mobj->ceilingz) // Crushed - { - INT32 i,j; - fixed_t ns; - fixed_t x,y,z; - mobj_t *mo2; - - z = mobj->subsector->sector->floorheight + FixedMul(64*FRACUNIT, mobj->scale); - for (j = 0; j < 2; j++) - { - for (i = 0; i < 32; i++) - { - const angle_t fa = (i*FINEANGLES/16) & FINEMASK; - ns = FixedMul(64 * FRACUNIT, mobj->scale); - x = mobj->x + FixedMul(FINESINE(fa),ns); - y = mobj->y + FixedMul(FINECOSINE(fa),ns); - - mo2 = P_SpawnMobj(x, y, z, MT_EXPLODE); - ns = FixedMul(16 * FRACUNIT, mobj->scale); - mo2->momx = FixedMul(FINESINE(fa),ns); - mo2->momy = FixedMul(FINECOSINE(fa),ns); - } - z -= FixedMul(32*FRACUNIT, mobj->scale); - } - P_SetMobjState(mobj, mobj->info->deathstate); - mobj->health = 0; - mobj->flags2 &= ~MF2_FIRING; - } - break; - case MT_BLUEFLAG: - case MT_REDFLAG: - { - sector_t *sec2; - sec2 = P_ThingOnSpecial3DFloor(mobj); - if ((sec2 && GETSECSPECIAL(sec2->special, 4) == 2) || (GETSECSPECIAL(mobj->subsector->sector->special, 4) == 2)) - mobj->fuse = 1; // Return to base. break; - } - case MT_CANNONBALL: -#ifdef FLOORSPLATS - R_AddFloorSplat(mobj->tracer->subsector, mobj->tracer, "TARGET", mobj->tracer->x, - mobj->tracer->y, mobj->tracer->floorz, SPLATDRAWMODE_SHADE); -#endif - break; - case MT_SPINDUST: // Spindash dust - mobj->momx = FixedMul(mobj->momx, (3*FRACUNIT)/4); // originally 50000 - mobj->momy = FixedMul(mobj->momy, (3*FRACUNIT)/4); // same - //mobj->momz = mobj->momz+P_MobjFlip(mobj)/3; // no meaningful change in value to be frank - if (mobj->state >= &states[S_SPINDUST_BUBBLE1] && mobj->state <= &states[S_SPINDUST_BUBBLE4]) // bubble dust! - { - P_MobjCheckWater(mobj); - if (mobj->watertop != mobj->subsector->sector->floorheight - 1000*FRACUNIT - && mobj->z+mobj->height >= mobj->watertop - 5*FRACUNIT) - mobj->flags2 |= MF2_DONTDRAW; - } - break; - case MT_SPINFIRE: - if (mobj->flags & MF_NOGRAVITY) - { - if (mobj->eflags & MFE_VERTICALFLIP) - mobj->z = mobj->ceilingz - mobj->height; - else - mobj->z = mobj->floorz; - } - /* FALLTHRU */ - default: - // check mobj against possible water content, before movement code - P_MobjCheckWater(mobj); - - // Extinguish fire objects in water - if (mobj->flags & MF_FIRE && mobj->type != MT_PUMA && mobj->type != MT_FIREBALL - && (mobj->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER))) - { - P_KillMobj(mobj, NULL, NULL, 0); + case MT_PLAYER: + if (mobj->player) + P_PlayerMobjThinker(mobj); return; - } - break; + case MT_SKIM: + // check mobj against possible water content, before movement code + P_MobjCheckWater(mobj); + + // Keep Skim at water surface + if (mobj->z <= mobj->watertop) + { + mobj->flags |= MF_NOGRAVITY; + if (mobj->z < mobj->watertop) + { + if (mobj->watertop - mobj->z <= FixedMul(mobj->info->speed*FRACUNIT, mobj->scale)) + mobj->z = mobj->watertop; + else + mobj->momz = FixedMul(mobj->info->speed*FRACUNIT, mobj->scale); + } + } + else + { + mobj->flags &= ~MF_NOGRAVITY; + if (mobj->z > mobj->watertop && mobj->z - mobj->watertop < FixedMul(MAXSTEPMOVE, mobj->scale)) + mobj->z = mobj->watertop; + } + break; + case MT_RING: + case MT_COIN: + case MT_BLUESPHERE: + case MT_BOMBSPHERE: + case MT_NIGHTSCHIP: + case MT_NIGHTSSTAR: + case MT_REDTEAMRING: + case MT_BLUETEAMRING: + // No need to check water. Who cares? + P_RingThinker(mobj); + if (mobj->flags2 & MF2_NIGHTSPULL) + P_NightsItemChase(mobj); + else + A_AttractChase(mobj); + return; + // Flung items + case MT_FLINGRING: + case MT_FLINGCOIN: + if (mobj->flags2 & MF2_NIGHTSPULL) + P_NightsItemChase(mobj); + else + A_AttractChase(mobj); + break; + case MT_EMBLEM: + if (mobj->flags2 & MF2_NIGHTSPULL) + P_NightsItemChase(mobj); + break; + case MT_SHELL: + if (mobj->threshold && mobj->threshold != TICRATE) + mobj->threshold--; + + if (mobj->threshold >= TICRATE) + { + mobj->angle += ((mobj->movedir == 1) ? ANGLE_22h : ANGLE_337h); + P_InstaThrust(mobj, R_PointToAngle2(0, 0, mobj->momx, mobj->momy), (mobj->info->speed*mobj->scale)); + } + break; + case MT_TURRET: + P_MobjCheckWater(mobj); + P_CheckPosition(mobj, mobj->x, mobj->y); + if (P_MobjWasRemoved(mobj)) + return; + mobj->floorz = tmfloorz; + mobj->ceilingz = tmceilingz; + + if ((mobj->eflags & MFE_UNDERWATER) && mobj->health > 0) + { + P_SetMobjState(mobj, mobj->info->deathstate); + mobj->health = 0; + mobj->flags2 &= ~MF2_FIRING; + } + else if (mobj->health > 0 && mobj->z + mobj->height > mobj->ceilingz) // Crushed + { + INT32 i,j; + fixed_t ns; + fixed_t x,y,z; + mobj_t *mo2; + + z = mobj->subsector->sector->floorheight + FixedMul(64*FRACUNIT, mobj->scale); + for (j = 0; j < 2; j++) + { + for (i = 0; i < 32; i++) + { + const angle_t fa = (i*FINEANGLES/16) & FINEMASK; + ns = FixedMul(64 * FRACUNIT, mobj->scale); + x = mobj->x + FixedMul(FINESINE(fa),ns); + y = mobj->y + FixedMul(FINECOSINE(fa),ns); + + mo2 = P_SpawnMobj(x, y, z, MT_EXPLODE); + ns = FixedMul(16 * FRACUNIT, mobj->scale); + mo2->momx = FixedMul(FINESINE(fa),ns); + mo2->momy = FixedMul(FINECOSINE(fa),ns); + } + z -= FixedMul(32*FRACUNIT, mobj->scale); + } + P_SetMobjState(mobj, mobj->info->deathstate); + mobj->health = 0; + mobj->flags2 &= ~MF2_FIRING; + } + break; + case MT_BLUEFLAG: + case MT_REDFLAG: + { + sector_t *sec2; + sec2 = P_ThingOnSpecial3DFloor(mobj); + if ((sec2 && GETSECSPECIAL(sec2->special, 4) == 2) || (GETSECSPECIAL(mobj->subsector->sector->special, 4) == 2)) + mobj->fuse = 1; // Return to base. + break; + } + case MT_CANNONBALL: +#ifdef FLOORSPLATS + R_AddFloorSplat(mobj->tracer->subsector, mobj->tracer, "TARGET", mobj->tracer->x, + mobj->tracer->y, mobj->tracer->floorz, SPLATDRAWMODE_SHADE); +#endif + break; + case MT_SPINDUST: // Spindash dust + mobj->momx = FixedMul(mobj->momx, (3*FRACUNIT)/4); // originally 50000 + mobj->momy = FixedMul(mobj->momy, (3*FRACUNIT)/4); // same + //mobj->momz = mobj->momz+P_MobjFlip(mobj)/3; // no meaningful change in value to be frank + if (mobj->state >= &states[S_SPINDUST_BUBBLE1] && mobj->state <= &states[S_SPINDUST_BUBBLE4]) // bubble dust! + { + P_MobjCheckWater(mobj); + if (mobj->watertop != mobj->subsector->sector->floorheight - 1000*FRACUNIT + && mobj->z+mobj->height >= mobj->watertop - 5*FRACUNIT) + mobj->flags2 |= MF2_DONTDRAW; + } + break; + case MT_SPINFIRE: + if (mobj->flags & MF_NOGRAVITY) + { + if (mobj->eflags & MFE_VERTICALFLIP) + mobj->z = mobj->ceilingz - mobj->height; + else + mobj->z = mobj->floorz; + } + /* FALLTHRU */ + default: + // check mobj against possible water content, before movement code + P_MobjCheckWater(mobj); + + // Extinguish fire objects in water + if (mobj->flags & MF_FIRE && mobj->type != MT_PUMA && mobj->type != MT_FIREBALL + && (mobj->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER))) + { + P_KillMobj(mobj, NULL, NULL, 0); + return; + } + break; + } } if (P_MobjWasRemoved(mobj)) return; @@ -7795,7 +8068,7 @@ void P_MobjThinker(mobj_t *mobj) { mobj_t *missile; - if (mobj->target->player && mobj->target->player->nightstime) + if (mobj->target->player && mobj->target->player->powers[pw_carry] == CR_NIGHTSMODE) { fixed_t oldval = mobjinfo[mobj->extravalue1].speed; @@ -7955,6 +8228,8 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s case MT_WALLSPIKE: P_SetMobjState(mobj, mobj->state->nextstate); mobj->fuse = mobj->info->speed; + if (mobj->spawnpoint) + mobj->fuse += (mobj->spawnpoint->angle/360); break; case MT_NIGHTSCORE: P_RemoveMobj(mobj); @@ -8243,7 +8518,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->height = info->height; mobj->flags = info->flags; - mobj->health = info->spawnhealth; + mobj->health = (info->spawnhealth ? info->spawnhealth : 1); mobj->reactiontime = info->reactiontime; @@ -8336,7 +8611,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) if (titlemapinaction) mobj->flags &= ~MF_NOTHINK; break; case MT_CYBRAKDEMON_NAPALM_BOMB_LARGE: - mobj->fuse = mobj->info->mass; + mobj->fuse = mobj->info->painchance; break; case MT_BLACKEGGMAN: { @@ -8346,20 +8621,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) P_SetTarget(&spawn->target, mobj); } break; - case MT_BLACKEGGMAN_HELPER: - // Collision helper can be stood on but not pushed - mobj->flags2 |= MF2_STANDONME; - break; - case MT_WALLSPIKE: - case MT_SPIKE: - mobj->flags2 |= MF2_STANDONME; - break; - case MT_GFZTREE: - case MT_GFZBERRYTREE: - case MT_GFZCHERRYTREE: - case MT_LAMPPOST1: - case MT_LAMPPOST2: - mobj->flags2 |= MF2_STANDONME; + case MT_FAKEMOBILE: + case MT_EGGSHIELD: + mobj->flags2 |= MF2_INVERTAIMABLE; break; case MT_DETON: mobj->movedir = 0; @@ -8374,41 +8638,68 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) } break; case MT_UNIDUS: - { - INT32 i; - mobj_t *ball; - // Spawn "damage" number of "painchance" spikeball mobjs - // threshold is the distance they should keep from the MT_UNIDUS (touching radius + ball painchance) - for (i = 0; i < mobj->info->damage; i++) { - ball = P_SpawnMobj(x, y, z, mobj->info->painchance); - ball->destscale = mobj->scale; - P_SetScale(ball, mobj->scale); - P_SetTarget(&ball->target, mobj); - ball->movedir = FixedAngle(FixedMul(FixedDiv(i<info->damage<threshold = ball->radius + mobj->radius + FixedMul(ball->info->painchance, ball->scale); + INT32 i; + mobj_t *ball; + // Spawn "damage" number of "painchance" spikeball mobjs + // threshold is the distance they should keep from the MT_UNIDUS (touching radius + ball painchance) + for (i = 0; i < mobj->info->damage; i++) + { + ball = P_SpawnMobj(x, y, z, mobj->info->painchance); + ball->destscale = mobj->scale; + P_SetScale(ball, mobj->scale); + P_SetTarget(&ball->target, mobj); + ball->movedir = FixedAngle(FixedMul(FixedDiv(i<info->damage<threshold = ball->radius + mobj->radius + FixedMul(ball->info->painchance, ball->scale); - var1 = ball->state->var1, var2 = ball->state->var2; - ball->state->action.acp1(ball); + var1 = ball->state->var1, var2 = ball->state->var2; + ball->state->action.acp1(ball); + } } break; - } case MT_POINTY: - { - INT32 q; - mobj_t *ball, *lastball = mobj; - - for (q = 0; q < mobj->info->painchance; q++) { - ball = P_SpawnMobj(x, y, z, mobj->info->mass); - ball->destscale = mobj->scale; - P_SetScale(ball, mobj->scale); - P_SetTarget(&lastball->tracer, ball); - P_SetTarget(&ball->target, mobj); - lastball = ball; + INT32 q; + mobj_t *ball, *lastball = mobj; + + for (q = 0; q < mobj->info->painchance; q++) + { + ball = P_SpawnMobj(x, y, z, mobj->info->mass); + ball->destscale = mobj->scale; + P_SetScale(ball, mobj->scale); + P_SetTarget(&lastball->tracer, ball); + P_SetTarget(&ball->target, mobj); + lastball = ball; + } + } + break; + case MT_CRUSHSTACEAN: + { + mobj_t *bigmeatyclaw = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_CRUSHCLAW); + bigmeatyclaw->angle = mobj->angle + ((mobj->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270);; + P_SetTarget(&mobj->tracer, bigmeatyclaw); + P_SetTarget(&bigmeatyclaw->tracer, mobj); + mobj->reactiontime >>= 1; + } + break; + case MT_BIGMINE: + mobj->extravalue1 = FixedHypot(mobj->x, mobj->y)>>FRACBITS; + break; + case MT_WAVINGFLAG: + { + mobj_t *prev = mobj, *cur; + UINT8 i; + mobj->destscale <<= 2; + P_SetScale(mobj, mobj->destscale); + for (i = 0; i <= 16; i++) // probably should be < but staying authentic to the Lua version + { + cur = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_WAVINGFLAGSEG); + P_SetTarget(&prev->tracer, cur); + cur->extravalue1 = i; + prev = cur; + } } break; - } case MT_EGGMOBILE2: // Special condition for the 2nd boss. mobj->watertop = mobj->info->speed; @@ -8416,6 +8707,26 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_FLICKY_08: mobj->color = (P_RandomChance(FRACUNIT/2) ? SKINCOLOR_RED : SKINCOLOR_AQUA); break; + case MT_BALLOON: + mobj->color = SKINCOLOR_RED; + break; + case MT_HIVEELEMENTAL: + mobj->extravalue1 = 5; + break; + case MT_SMASHINGSPIKEBALL: + mobj->movecount = mobj->z; + break; + case MT_SPINBOBERT: + { + mobj_t *fire; + fire = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_SPINBOBERT_FIRE1); + P_SetTarget(&fire->target, mobj); + P_SetTarget(&mobj->hnext, fire); + fire = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_SPINBOBERT_FIRE2); + P_SetTarget(&fire->target, mobj); + P_SetTarget(&mobj->hprev, fire); + } + break; case MT_REDRING: // Make MT_REDRING red by default mobj->color = skincolor_redring; break; @@ -8424,6 +8735,11 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_EXTRALARGEBUBBLE: mobj->fuse += 30 * TICRATE; break; + case MT_NIGHTSDRONE: + if (G_IsSpecialStage(gamemap)) + mobj->flags2 |= MF2_DONTDRAW; + 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 break; @@ -8435,8 +8751,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) break; case MT_RING: case MT_COIN: - case MT_BLUEBALL: - nummaprings++; + case MT_NIGHTSSTAR: + if (nummaprings >= 0) + nummaprings++; default: break; } @@ -8570,7 +8887,7 @@ void P_RemoveMobj(mobj_t *mobj) if (mobj->spawnpoint && (mobj->type == MT_RING || mobj->type == MT_COIN - || mobj->type == MT_BLUEBALL + || mobj->type == MT_NIGHTSSTAR || mobj->type == MT_REDTEAMRING || mobj->type == MT_BLUETEAMRING || P_WeaponOrPanel(mobj->type)) @@ -8590,7 +8907,7 @@ void P_RemoveMobj(mobj_t *mobj) if (mobj->player && mobj->player->followmobj) { P_RemoveMobj(mobj->player->followmobj); - mobj->player->followmobj = NULL; + P_SetTarget(&mobj->player->followmobj, NULL); } mobj->health = 0; // Just because @@ -8615,6 +8932,13 @@ void P_RemoveMobj(mobj_t *mobj) // Remove any references to other mobjs. P_SetTarget(&mobj->target, P_SetTarget(&mobj->tracer, NULL)); + if (mobj->hnext && !P_MobjWasRemoved(mobj->hnext)) + P_SetTarget(&mobj->hnext->hprev, mobj->hprev); + if (mobj->hprev && !P_MobjWasRemoved(mobj->hprev)) + P_SetTarget(&mobj->hprev->hnext, mobj->hnext); + + P_SetTarget(&mobj->hnext, P_SetTarget(&mobj->hprev, NULL)); + // free block // DBG: set everything in mobj_t to 0xFF instead of leaving it. debug memory error. if (mobj->flags & MF_NOTHINK && !mobj->thinker.next) @@ -8967,7 +9291,7 @@ void P_RespawnSpecials(void) #endif ss->sector->ceilingheight) - (mthing->options >> ZSHIFT) * FRACUNIT; if (mthing->options & MTF_AMBUSH - && (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || P_WeaponOrPanel(i))) + && (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || i == MT_NIGHTSSTAR || P_WeaponOrPanel(i))) z -= 24*FRACUNIT; z -= mobjinfo[i].height; // Don't forget the height! } @@ -8979,7 +9303,7 @@ void P_RespawnSpecials(void) #endif ss->sector->floorheight) + (mthing->options >> ZSHIFT) * FRACUNIT; if (mthing->options & MTF_AMBUSH - && (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || P_WeaponOrPanel(i))) + && (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || i == MT_NIGHTSSTAR || P_WeaponOrPanel(i))) z += 24*FRACUNIT; } @@ -9016,7 +9340,7 @@ void P_SpawnPlayer(INT32 playernum) p->spectator = p->outofcoop = (((multiplayer || netgame) && gametype == GT_COOP) // only question status in coop && ((leveltime > 0 - && ((G_IsSpecialStage(gamemap) && useNightsSS) // late join special stage + && ((G_IsSpecialStage(gamemap) && (maptol & TOL_NIGHTS)) // late join special stage || (cv_coopstarposts.value == 2 && (p->jointime < 1 || p->outofcoop)))) // late join or die in new coop || (((cv_cooplives.value == 1) || !P_GetLives(p)) && p->lives <= 0))); // game over and can't redistribute lives } @@ -9085,7 +9409,7 @@ void P_SpawnPlayer(INT32 playernum) P_SetupStateAnimation(mobj, mobj->state); mobj->health = 1; - p->rings = 0; + p->rings = p->spheres = 0; p->playerstate = PST_LIVE; p->bonustime = false; @@ -9104,6 +9428,22 @@ void P_SpawnPlayer(INT32 playernum) mobj->radius = FixedMul(skins[p->skin].radius, mobj->scale); mobj->height = P_GetPlayerHeight(p); + if (!leveltime && ((maptol & TOL_NIGHTS) == TOL_NIGHTS) != (G_IsSpecialStage(gamemap))) // non-special NiGHTS stage or special non-NiGHTS stage + { + if (maptol & TOL_NIGHTS) + { + if (p == players) // this is totally the wrong place to do this aaargh. + { + mobj_t *idya = P_SpawnMobjFromMobj(mobj, 0, 0, mobj->height, MT_GOTEMERALD); + P_SetTarget(&idya->target, mobj); + P_SetMobjState(idya, mobjinfo[MT_GOTEMERALD].missilestate); + P_SetTarget(&mobj->tracer, idya); + } + } + else if (sstimer) + p->nightstime = sstimer; + } + // Spawn with a pity shield if necessary. P_DoPityCheck(p); } @@ -9301,6 +9641,7 @@ void P_SpawnMapThing(mapthing_t *mthing) mobj_t *mobj; fixed_t x, y, z; subsector_t *ss; + boolean doangle = true; if (!mthing->type) return; // Ignore type-0 things as NOPs @@ -9361,19 +9702,21 @@ void P_SpawnMapThing(mapthing_t *mthing) else if (mthing->type == 750) // Slope vertex point (formerly chaos spawn) return; - else if (mthing->type == 300 // Ring - || mthing->type == 308 || mthing->type == 309 // Team Rings - || mthing->type == 1706 // Nights Wing - || (mthing->type >= 600 && mthing->type <= 609) // Placement patterns - || mthing->type == 1705 || mthing->type == 1713 // NiGHTS Hoops - || mthing->type == 1800) // Mario Coin + else if (mthing->type == mobjinfo[MT_RING].doomednum || mthing->type == mobjinfo[MT_COIN].doomednum + || mthing->type == mobjinfo[MT_REDTEAMRING].doomednum || mthing->type == mobjinfo[MT_BLUETEAMRING].doomednum + || mthing->type == mobjinfo[MT_BLUESPHERE].doomednum || mthing->type == mobjinfo[MT_BOMBSPHERE].doomednum + || (mthing->type >= 600 && mthing->type <= 609) // circles and diagonals + || mthing->type == 1705 || mthing->type == 1713 || mthing->type == 1800) // hoops { // Don't spawn hoops, wings, or rings yet! return; } // check for players specially - if (mthing->type > 0 && mthing->type <= 32) +#if MAXPLAYERS > 32 +You should think about modifying the deathmatch starts to take full advantage of this! +#endif + if (mthing->type > 0 && mthing->type <= MAXPLAYERS) { // save spots for respawning in network games if (!metalrecording) @@ -9416,10 +9759,6 @@ void P_SpawnMapThing(mapthing_t *mthing) return; } - if (!G_RingSlingerGametype() || !cv_specialrings.value) - if (P_WeaponOrPanel(i)) - return; // Don't place weapons/panels in non-ringslinger modes - if (i == MT_EMERHUNT) { // Emerald Hunt is Coop only. @@ -9453,6 +9792,10 @@ void P_SpawnMapThing(mapthing_t *mthing) if ((mobjinfo[i].flags & MF_ENEMY) || (mobjinfo[i].flags & MF_BOSS)) return; + if (!G_RingSlingerGametype() || !cv_specialrings.value) + if (P_WeaponOrPanel(i)) + return; // Don't place weapons/panels in non-ringslinger modes + // Altering monitor spawns via cvars // If MF_GRENADEBOUNCE is set in the monitor's info, // skip this step. (Used for gold monitors) @@ -9489,9 +9832,7 @@ void P_SpawnMapThing(mapthing_t *mthing) if (gametype != GT_CTF) // CTF specific things { - if (i == MT_BLUETEAMRING || i == MT_REDTEAMRING) - i = MT_RING; - else if (i == MT_RING_BLUEBOX || i == MT_RING_REDBOX) + if (i == MT_RING_BLUEBOX || i == MT_RING_REDBOX) i = MT_RING_BOX; else if (i == MT_BLUEFLAG || i == MT_REDFLAG) return; // No flags in non-CTF modes! @@ -9529,11 +9870,9 @@ void P_SpawnMapThing(mapthing_t *mthing) { if (i == MT_PITY_BOX || i == MT_ELEMENTAL_BOX || i == MT_ATTRACT_BOX || i == MT_FORCE_BOX || i == MT_ARMAGEDDON_BOX || i == MT_WHIRLWIND_BOX - || i == MT_FLAMEAURA_BOX || i == MT_BUBBLEWRAP_BOX || i == MT_THUNDERCOIN_BOX) - return; // No shields in Ultimate mode - - if (i == MT_RING_BOX && !G_IsSpecialStage(gamemap)) - return; // No rings in Ultimate mode (except special stages) + || i == MT_FLAMEAURA_BOX || i == MT_BUBBLEWRAP_BOX || i == MT_THUNDERCOIN_BOX + || i == MT_RING_BOX) + return; // No rings or shields in Ultimate mode // Don't include the gold repeating boxes here please. // They're likely facets of the level's design and therefore required to progress. @@ -9558,7 +9897,7 @@ void P_SpawnMapThing(mapthing_t *mthing) ss->sector->floorheight) + ((mthing->options >> ZSHIFT) << FRACBITS); else if (i == MT_AXIS || i == MT_AXISTRANSFER || i == MT_AXISTRANSFERLINE) z = ONFLOORZ; - else if (i == MT_SPECIALSPIKEBALL || P_WeaponOrPanel(i) || i == MT_EMERALDSPAWN || i == MT_TOKEN) + else if (i == MT_SPIKEBALL || P_WeaponOrPanel(i) || i == MT_EMERALDSPAWN || i == MT_TOKEN) { if (mthing->options & MTF_OBJECTFLIP) { @@ -9683,6 +10022,58 @@ void P_SpawnMapThing(mapthing_t *mthing) else mobj->health = FixedMul(ss->sector->ceilingheight-ss->sector->floorheight, 3*(FRACUNIT/4))>>FRACBITS; break; + case MT_BALLOON: + if (mthing->angle > 0) + mobj->color = ((mthing->angle-1) % (MAXSKINCOLORS-1))+1; + break; +#define makesoftwarecorona(mo, h) \ + corona = P_SpawnMobjFromMobj(mo, 0, 0, h<sprite = SPR_FLAM;\ + corona->frame = (FF_FULLBRIGHT|FF_TRANS90|12);\ + corona->tics = -1 + case MT_FLAME: + if (mthing->options & MTF_EXTRA) + { + mobj_t *corona; + makesoftwarecorona(mobj, 20); + P_SetScale(corona, (corona->destscale = mobj->scale*3)); + P_SetTarget(&mobj->tracer, corona); + } + break; + case MT_FLAMEHOLDER: + if (!(mthing->options & MTF_OBJECTSPECIAL)) // Spawn the fire + { + mobj_t *flame = P_SpawnMobjFromMobj(mobj, 0, 0, mobj->height, MT_FLAME); + P_SetTarget(&flame->target, mobj); + flame->flags2 |= MF2_BOSSNOTRAP; + if (mthing->options & MTF_EXTRA) + { + mobj_t *corona; + makesoftwarecorona(flame, 20); + P_SetScale(corona, (corona->destscale = flame->scale*3)); + P_SetTarget(&flame->tracer, corona); + } + } + break; + case MT_CANDLE: + case MT_CANDLEPRICKET: + if (mthing->options & MTF_EXTRA) + { + mobj_t *corona; + makesoftwarecorona(mobj, ((mobj->type == MT_CANDLE) ? 42 : 176)); + } + break; +#undef makesoftwarecorona + case MT_JACKO1: + case MT_JACKO2: + case MT_JACKO3: + if (!(mthing->options & MTF_EXTRA)) // take the torch out of the crafting recipe + { + mobj_t *overlay = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_OVERLAY); + P_SetTarget(&overlay->target, mobj); + P_SetMobjState(overlay, mobj->info->raisestate); + } + break; case MT_WATERDRIP: if (mthing->angle) mobj->tics = 3*TICRATE + mthing->angle; @@ -9706,11 +10097,11 @@ void P_SpawnMapThing(mapthing_t *mthing) case MT_FIREBARPOINT: case MT_CUSTOMMACEPOINT: { - fixed_t mlength, mlengthset, mspeed, mphase, myaw, mpitch, mmaxspeed, mnumspokes, mnumspokesset, mpinch, mroll, mnumnospokes, mwidth, mmin, msound, radiusfactor; + fixed_t mlength, mmaxlength, mlengthset, mspeed, mphase, myaw, mpitch, mminlength, mnumspokes, mpinch, mroll, mnumnospokes, mwidth, mwidthset, mmin, msound, radiusfactor, widthfactor; angle_t mspokeangle; mobjtype_t chainlink, macetype, firsttype, linktype; - boolean mdoall = true; - mobj_t *spawnee; + boolean mdosound, mdocenter, mchainlike = false; + mobj_t *spawnee = NULL, *hprev = mobj; mobjflag_t mflagsapply; mobjflag2_t mflags2apply; mobjeflag_t meflagsapply; @@ -9747,8 +10138,10 @@ ML_EFFECT4 : Don't clip inside the ground mlength = abs(lines[line].dx >> FRACBITS); mspeed = abs(lines[line].dy >> (FRACBITS - 4)); mphase = (sides[lines[line].sidenum[0]].textureoffset >> FRACBITS) % 360; - if ((mmaxspeed = sides[lines[line].sidenum[0]].rowoffset >> (FRACBITS - 4)) < mspeed) - mmaxspeed = mspeed << 1; + if ((mminlength = -sides[lines[line].sidenum[0]].rowoffset>>FRACBITS) < 0) + mminlength = 0; + else if (mminlength > mlength-1) + mminlength = mlength-1; mpitch = (lines[line].frontsector->floorheight >> FRACBITS) % 360; myaw = (lines[line].frontsector->ceilingheight >> FRACBITS) % 360; @@ -9767,30 +10160,29 @@ ML_EFFECT4 : Don't clip inside the ground mpinch = mroll = mnumnospokes = mwidth = 0; CONS_Debug(DBG_GAMELOGIC, "Mace/Chain (mapthing #%s):\n" - "Length is %d\n" + "Length is %d (minus %d)\n" "Speed is %d\n" "Phase is %d\n" "Yaw is %d\n" "Pitch is %d\n" - "Max. speed is %d\n" - "No. of spokes is %d\n" + "No. of spokes is %d (%d antispokes)\n" "Pinch is %d\n" "Roll is %d\n" - "No. of antispokes is %d\n" "Width is %d\n", - sizeu1(mthingi), mlength, mspeed, mphase, myaw, mpitch, mmaxspeed, mnumspokes, mpinch, mroll, mnumnospokes, mwidth); + sizeu1(mthingi), mlength, mminlength, mspeed, mphase, myaw, mpitch, mnumspokes, mnumnospokes, mpinch, mroll, mwidth); if (mnumnospokes > 0 && (mnumnospokes < mnumspokes)) mnumnospokes = mnumspokes/mnumnospokes; else - mnumnospokes = ((mobj->type == MT_CHAINMACEPOINT) ? (mnumspokes - 1) : 0); + mnumnospokes = ((mobj->type == MT_CHAINMACEPOINT) ? (mnumspokes) : 0); mobj->lastlook = mspeed; mobj->movecount = mobj->lastlook; - mobj->health = (FixedAngle(myaw*FRACUNIT)>>ANGLETOFINESHIFT); + mobj->angle = FixedAngle(myaw*FRACUNIT); + doangle = false; mobj->threshold = (FixedAngle(mpitch*FRACUNIT)>>ANGLETOFINESHIFT); - mobj->friction = mmaxspeed; mobj->movefactor = mpinch; + mobj->movedir = 0; // Mobjtype selection switch(mobj->type) @@ -9814,6 +10206,19 @@ ML_EFFECT4 : Don't clip inside the ground else chainlink = MT_NULL; break; + case MT_CHAINPOINT: + if (mthing->options & MTF_AMBUSH) + { + macetype = MT_BIGGRABCHAIN; + chainlink = MT_BIGMACECHAIN; + } + else + { + macetype = MT_SMALLGRABCHAIN; + chainlink = MT_SMALLMACECHAIN; + } + mchainlike = true; + break; default: if (mthing->options & MTF_AMBUSH) { @@ -9828,20 +10233,18 @@ ML_EFFECT4 : Don't clip inside the ground break; } - if (!macetype) + if (!macetype && !chainlink) break; - if (mobj->type != MT_CHAINPOINT) - { - firsttype = macetype; - mlength++; - } - else + if (mobj->type == MT_CHAINPOINT) { if (!mlength) break; - firsttype = chainlink; } + else + mlength++; + + firsttype = macetype; // Adjustable direction if (lines[line].flags & ML_NOCLIMB) @@ -9857,7 +10260,7 @@ ML_EFFECT4 : Don't clip inside the ground mmin = mnumspokes; // Make the links the same type as the end - repeated below - if ((mobj->type != MT_CHAINPOINT) && ((lines[line].flags & ML_EFFECT2) != (mobj->type == MT_FIREBARPOINT))) // exclusive or + if ((mobj->type != MT_CHAINPOINT) && (((lines[line].flags & ML_EFFECT2) == ML_EFFECT2) != (mobj->type == MT_FIREBARPOINT))) // exclusive or { linktype = macetype; radiusfactor = 2; // Double the radius. @@ -9865,35 +10268,41 @@ ML_EFFECT4 : Don't clip inside the ground else radiusfactor = (((linktype = chainlink) == MT_NULL) ? 2 : 1); + if (!mchainlike) + mchainlike = (firsttype == chainlink); + widthfactor = (mchainlike ? 1 : 2); + mflagsapply = ((lines[line].flags & ML_EFFECT4) ? 0 : (MF_NOCLIP|MF_NOCLIPHEIGHT)); - mflags2apply = (MF2_MACEROTATE|((mthing->options & MTF_OBJECTFLIP) ? MF2_OBJECTFLIP : 0)); + mflags2apply = ((mthing->options & MTF_OBJECTFLIP) ? MF2_OBJECTFLIP : 0); meflagsapply = ((mthing->options & MTF_OBJECTFLIP) ? MFE_VERTICALFLIP : 0); - msound = ((firsttype == chainlink) ? 0 : (mwidth & 1)); + msound = (mchainlike ? 0 : (mwidth & 1)); // Quick and easy preparatory variable setting mphase = (FixedAngle(mphase*FRACUNIT)>>ANGLETOFINESHIFT); mroll = (FixedAngle(mroll*FRACUNIT)>>ANGLETOFINESHIFT); -#define makemace(mobjtype, dist, moreflags2) P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobjtype);\ - P_SetTarget(&spawnee->tracer, mobj);\ - spawnee->threshold = mphase;\ - spawnee->friction = mroll;\ - spawnee->movefactor = mwidth;\ - spawnee->movecount = dist;\ - spawnee->angle = myaw;\ - spawnee->flags |= (MF_NOGRAVITY|mflagsapply);\ - spawnee->flags2 |= (mflags2apply|moreflags2);\ - spawnee->eflags |= meflagsapply +#define makemace(mobjtype, dist, moreflags2) {\ + spawnee = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobjtype);\ + P_SetTarget(&spawnee->tracer, mobj);\ + spawnee->threshold = mphase;\ + spawnee->friction = mroll;\ + spawnee->movefactor = mwidthset;\ + spawnee->movecount = dist;\ + spawnee->angle = myaw;\ + spawnee->flags |= (MF_NOGRAVITY|mflagsapply);\ + spawnee->flags2 |= (mflags2apply|moreflags2);\ + spawnee->eflags |= meflagsapply;\ + P_SetTarget(&hprev->hnext, spawnee);\ + P_SetTarget(&spawnee->hprev, hprev);\ + hprev = spawnee;\ +} -domaceagain: - mnumspokesset = mnumspokes; - - if (mdoall && lines[line].flags & ML_EFFECT3) // Innermost mace/link - { spawnee = makemace(macetype, 0, MF2_AMBUSH); } + mdosound = (mspeed && !(mthing->options & MTF_OBJECTSPECIAL)); + mdocenter = (macetype && (lines[line].flags & ML_EFFECT3)); // The actual spawning of spokes - while (mnumspokesset-- > 0) + while (mnumspokes-- > 0) { // Offsets if (lines[line].flags & ML_EFFECT1) // Swinging @@ -9901,14 +10310,15 @@ domaceagain: else // Spinning mphase = (mphase - mspokeangle) & FINEMASK; - if (mnumnospokes && !(mnumspokesset % mnumnospokes)) // Skipping a "missing" spoke + if (mnumnospokes && !(mnumspokes % mnumnospokes)) // Skipping a "missing" spoke { if (mobj->type != MT_CHAINMACEPOINT) continue; - firsttype = linktype = chainlink; - mlengthset = 1 + (mlength - 1)*radiusfactor; - radiusfactor = 1; + linktype = chainlink; + firsttype = ((mthing->options & MTF_AMBUSH) ? MT_BIGGRABCHAIN : MT_SMALLGRABCHAIN); + mmaxlength = 1 + (mlength - 1)*radiusfactor; + radiusfactor = widthfactor = 1; } else { @@ -9921,42 +10331,76 @@ domaceagain: radiusfactor = 2; } else - { - linktype = chainlink; radiusfactor = (((linktype = chainlink) == MT_NULL) ? 2 : 1); - } firsttype = macetype; + widthfactor = 2; } - mlengthset = mlength; + mmaxlength = mlength; } + mwidthset = mwidth; + mlengthset = mminlength; + + if (mdocenter) // Innermost link + makemace(linktype, 0, 0); + + // Out from the center... + if (linktype) + { + while ((++mlengthset) < mmaxlength) + makemace(linktype, radiusfactor*mlengthset, 0); + } + else + mlengthset = mmaxlength; + // Outermost mace/link - spawnee = makemace(firsttype, radiusfactor*(mlengthset--), MF2_AMBUSH); + if (firsttype) + makemace(firsttype, radiusfactor*mlengthset, MF2_AMBUSH); - if (mspeed && (mwidth == msound) && !(mthing->options & MTF_OBJECTSPECIAL) && mnumspokesset <= mmin) // Can it make a sound? - spawnee->flags2 |= MF2_BOSSNOTRAP; + if (!mwidth) + { + if (mdosound && mnumspokes <= mmin) // Can it make a sound? + spawnee->flags2 |= MF2_BOSSNOTRAP; + } + else + { + // Across the bar! + if (!firsttype) + mwidthset = -mwidth; + else if (mwidth > 0) + { + while ((mwidthset -= widthfactor) > -mwidth) + { + makemace(firsttype, radiusfactor*mlengthset, MF2_AMBUSH); + if (mdosound && (mwidthset == msound) && mnumspokes <= mmin) // Can it make a sound? + spawnee->flags2 |= MF2_BOSSNOTRAP; + } + } + else + { + while ((mwidthset += widthfactor) < -mwidth) + { + makemace(firsttype, radiusfactor*mlengthset, MF2_AMBUSH); + if (mdosound && (mwidthset == msound) && mnumspokes <= mmin) // Can it make a sound? + spawnee->flags2 |= MF2_BOSSNOTRAP; + } + } + mwidth = -mwidth; - if (!mdoall || !linktype) - continue; + // Outermost mace/link again! + if (firsttype) + makemace(firsttype, radiusfactor*(mlengthset--), MF2_AMBUSH); - // The rest of the links - while (mlengthset > 0) - { spawnee = makemace(linktype, radiusfactor*(mlengthset--), 0); } - } + // ...and then back into the center! + if (linktype) + while (mlengthset > mminlength) + makemace(linktype, radiusfactor*(mlengthset--), 0); - if (mwidth > 0) - { - mwidth *= -1; - goto domaceagain; - } - else if (mwidth != 0) - { - if ((mwidth = -(mwidth + ((firsttype == chainlink) ? 1 : 2))) < 0) - break; - mdoall = false; - goto domaceagain; + if (mdocenter) // Innermost link + makemace(linktype, 0, 0); + } } #undef makemace @@ -9965,8 +10409,8 @@ domaceagain: } case MT_PARTICLEGEN: { - fixed_t radius, speed, bottomheight, topheight; - INT32 type, numdivisions, time, anglespeed, ticcount; + fixed_t radius, speed; + INT32 type, numdivisions, anglespeed, ticcount; angle_t angledivision; INT32 line; const size_t mthingi = (size_t)(mthing - mapthings); @@ -9985,13 +10429,9 @@ domaceagain: else type = (INT32)MT_PARTICLE; - speed = abs(sides[lines[line].sidenum[0]].textureoffset); - bottomheight = lines[line].frontsector->floorheight; - topheight = lines[line].frontsector->ceilingheight - mobjinfo[(mobjtype_t)type].height; - if (!lines[line].backsector || (ticcount = (sides[lines[line].sidenum[1]].textureoffset >> FRACBITS)) < 1) - ticcount = states[S_PARTICLEGEN].tics; + ticcount = 3; numdivisions = (mthing->options >> ZSHIFT); @@ -10009,18 +10449,9 @@ domaceagain: angledivision = 0; } - if ((speed) && (topheight > bottomheight)) - time = (INT32)(FixedDiv((topheight - bottomheight), speed) >> FRACBITS); - else - time = 1; // There's no reasonable way to set it, so just show the object for one tic and move on. - + speed = abs(sides[lines[line].sidenum[0]].textureoffset); if (mthing->options & MTF_OBJECTFLIP) - { - mobj->z = topheight; speed *= -1; - } - else - mobj->z = bottomheight; CONS_Debug(DBG_GAMELOGIC, "Particle Generator (mapthing #%s):\n" "Radius is %d\n" @@ -10028,20 +10459,20 @@ domaceagain: "Anglespeed is %d\n" "Numdivisions is %d\n" "Angledivision is %d\n" - "Time is %d\n" "Type is %d\n" "Tic seperation is %d\n", - sizeu1(mthingi), radius, speed, anglespeed, numdivisions, angledivision, time, type, ticcount); + sizeu1(mthingi), radius, speed, anglespeed, numdivisions, angledivision, type, ticcount); mobj->angle = 0; mobj->movefactor = speed; mobj->lastlook = numdivisions; mobj->movedir = angledivision*ANG1; mobj->movecount = anglespeed*ANG1; - mobj->health = time; mobj->friction = radius; mobj->threshold = type; mobj->reactiontime = ticcount; + mobj->cvmem = line; + mobj->watertop = mobj->waterbottom = 0; break; } @@ -10076,12 +10507,62 @@ domaceagain: if (mthing->angle > 0) mobj->health = mthing->angle; break; + case MT_HIVEELEMENTAL: + if (mthing->extrainfo) + mobj->extravalue1 = mthing->extrainfo; + break; case MT_TRAPGOYLE: case MT_TRAPGOYLEUP: case MT_TRAPGOYLEDOWN: case MT_TRAPGOYLELONG: if (mthing->angle >= 360) mobj->tics += 7*(mthing->angle / 360) + 1; // starting delay + break; + case MT_DSZSTALAGMITE: + case MT_DSZ2STALAGMITE: + case MT_KELP: + if (mthing->options & MTF_OBJECTSPECIAL) { // make mobj twice as big as normal + P_SetScale(mobj, 2*mobj->scale); // not 2*FRACUNIT in case of something like the old ERZ3 mode + mobj->destscale = mobj->scale; + } + break; + case MT_THZTREE: + { // Spawn the branches + angle_t mobjangle = FixedAngle((mthing->angle % 113)<angle = mobjangle + ANGLE_22h; + P_SpawnMobjFromMobj(mobj, 0, 1*FRACUNIT, 0, MT_THZTREEBRANCH)->angle = mobjangle + ANGLE_157h; + P_SpawnMobjFromMobj(mobj, -1*FRACUNIT, 0, 0, MT_THZTREEBRANCH)->angle = mobjangle + ANGLE_270; + } + break; + case MT_CEZPOLE: + { // Spawn the banner + angle_t mobjangle = FixedAngle(mthing->angle<angle = mobjangle + ANGLE_90; + } + break; + case MT_HHZTREE_TOP: + { // Spawn the branches + angle_t mobjangle = FixedAngle(mthing->angle<angle = mobjangle;\ + P_SetMobjState(leaf, leaf->info->seestate);\ + mobjangle += ANGLE_90 + doleaf(1*FRACUNIT, 0); + doleaf(0, 1*FRACUNIT); + doleaf(-1*FRACUNIT, 0); + doleaf(0, -1*FRACUNIT); +#undef doleaf + } + break; + case MT_SMASHINGSPIKEBALL: + if (mthing->angle > 0) + mobj->tics += mthing->angle; + break; default: break; } @@ -10114,9 +10595,6 @@ domaceagain: } else if (i == MT_TOKEN) { - if (mthing->options & MTF_OBJECTSPECIAL) // Mario Block version - mobj->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); - // We advanced tokenbits earlier due to the return check. // Subtract 1 here for the correct value. mobj->health = 1 << (tokenbits - 1); @@ -10126,7 +10604,7 @@ domaceagain: mobj_t *elecmobj; elecmobj = P_SpawnMobj(x, y, z, MT_CYBRAKDEMON_ELECTRIC_BARRIER); P_SetTarget(&elecmobj->target, mobj); - elecmobj->angle = FixedAngle(mthing->angle*FRACUNIT);; + elecmobj->angle = FixedAngle(mthing->angle<destscale = mobj->scale*2; P_SetScale(elecmobj, elecmobj->destscale); } @@ -10164,7 +10642,9 @@ domaceagain: if (mthing->options & MTF_OBJECTSPECIAL) { mobj->flags &= ~MF_SCENERY; - mobj->fuse = mthing->angle + mobj->info->speed; + mobj->fuse = (16 - mthing->extrainfo) * (mthing->angle + mobj->info->speed) / 16; + if (mthing->options & MTF_EXTRA) + P_SetMobjState(mobj, mobj->info->meleestate); } // Use per-thing collision for spikes if the deaf flag isn't checked. if (!(mthing->options & MTF_AMBUSH) && !metalrecording) @@ -10181,7 +10661,9 @@ domaceagain: if (mthing->options & MTF_OBJECTSPECIAL) { mobj->flags &= ~MF_SCENERY; - mobj->fuse = mobj->info->speed; + mobj->fuse = (16 - mthing->extrainfo) * ((mthing->angle/360) + mobj->info->speed) / 16; + if (mthing->options & MTF_EXTRA) + P_SetMobjState(mobj, mobj->info->meleestate); } // Use per-thing collision for spikes if the deaf flag isn't checked. if (!(mthing->options & MTF_AMBUSH) && !metalrecording) @@ -10194,7 +10676,7 @@ domaceagain: // spawn base { - const angle_t mobjangle = FixedAngle(mthing->angle*FRACUNIT); // the mobj's own angle hasn't been set quite yet so... + const angle_t mobjangle = FixedAngle(mthing->angle<radius - mobj->scale; mobj_t *base = P_SpawnMobj( mobj->x - P_ReturnThrustX(mobj, mobjangle, baseradius), @@ -10209,7 +10691,7 @@ domaceagain: } //count 10 ring boxes into the number of rings equation too. - if (i == MT_RING_BOX) + if (i == MT_RING_BOX && nummaprings >= 0) nummaprings += 10; if (i == MT_BIGTUMBLEWEED || i == MT_LITTLETUMBLEWEED) @@ -10261,16 +10743,16 @@ domaceagain: } } + if (doangle) + mobj->angle = FixedAngle(mthing->angle<angle = FixedAngle(mthing->angle*FRACUNIT); mthing->mobj = mobj; return; } - mobj->angle = FixedAngle(mthing->angle*FRACUNIT); - if ((mthing->options & MTF_AMBUSH) && (mthing->options & MTF_OBJECTSPECIAL) && (mobj->flags & MF_PUSHABLE)) @@ -10290,10 +10772,7 @@ domaceagain: } if (mobj->flags & MF_PUSHABLE) - { mobj->flags &= ~MF_PUSHABLE; - mobj->flags2 |= MF2_STANDONME; - } if ((mobj->flags & MF_MONITOR) && mobj->info->speed != 0) { @@ -10346,14 +10825,16 @@ domaceagain: mthing->mobj = mobj; } -void P_SpawnHoopsAndRings(mapthing_t *mthing) +void P_SpawnHoopsAndRings(mapthing_t *mthing, boolean bonustime) { + mobjtype_t ringthing = MT_RING; mobj_t *mobj = NULL; INT32 r, i; fixed_t x, y, z, finalx, finaly, finalz; sector_t *sec; TVector v, *res; angle_t closestangle, fa; + boolean nightsreplace = ((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)); x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; @@ -10626,9 +11107,132 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing) return; } - // Wing logo item. - else if (mthing->type == mobjinfo[MT_NIGHTSWING].doomednum) + // *** + // Special placement patterns + // *** + + // Vertical Rings - Stack of 5 (handles both red and yellow) + else if (mthing->type == 600 || mthing->type == 601) { + INT32 dist = 64*FRACUNIT; + if (mthing->type == 601) + dist = 128*FRACUNIT; + + if (ultimatemode) + return; // No rings in Ultimate! + + if (nightsreplace) + ringthing = MT_NIGHTSSTAR; + + for (r = 1; r <= 5; r++) + { + if (mthing->options & MTF_OBJECTFLIP) + { + z = ( +#ifdef ESLOPE + sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : +#endif + sec->ceilingheight) - mobjinfo[ringthing].height - dist*r; + if (mthing->options >> ZSHIFT) + z -= ((mthing->options >> ZSHIFT) << FRACBITS); + } + else + { + z = ( +#ifdef ESLOPE + sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : +#endif + sec->floorheight) + dist*r; + if (mthing->options >> ZSHIFT) + z += ((mthing->options >> ZSHIFT) << FRACBITS); + } + + mobj = P_SpawnMobj(x, y, z, ringthing); + + if (mthing->options & MTF_OBJECTFLIP) + { + mobj->eflags |= MFE_VERTICALFLIP; + mobj->flags2 |= MF2_OBJECTFLIP; + } + + mobj->angle = FixedAngle(mthing->angle*FRACUNIT); + if (mthing->options & MTF_AMBUSH) + mobj->flags2 |= MF2_AMBUSH; + + if ((maptol & TOL_XMAS) && (ringthing == MT_NIGHTSSTAR)) + P_SetMobjState(mobj, mobj->info->seestate); + } + } + // Diagonal rings (handles both types) + else if (mthing->type == 602 || mthing->type == 603) // Diagonal rings (5) + { + INT32 iterations = 5; + if (mthing->type == 603) + iterations = 10; + + if (ultimatemode) + return; // No rings in Ultimate! + + if (nightsreplace) + ringthing = MT_NIGHTSSTAR; + + closestangle = FixedAngle(mthing->angle*FRACUNIT); + fa = (closestangle >> ANGLETOFINESHIFT); + + for (r = 1; r <= iterations; r++) + { + x += FixedMul(64*FRACUNIT, FINECOSINE(fa)); + y += FixedMul(64*FRACUNIT, FINESINE(fa)); + + if (mthing->options & MTF_OBJECTFLIP) + { + z = ( +#ifdef ESLOPE + sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : +#endif + sec->ceilingheight) - mobjinfo[ringthing].height - 64*FRACUNIT*r; + if (mthing->options >> ZSHIFT) + z -= ((mthing->options >> ZSHIFT) << FRACBITS); + } + else + { + z = ( +#ifdef ESLOPE + sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : +#endif + sec->floorheight) + 64*FRACUNIT*r; + if (mthing->options >> ZSHIFT) + z += ((mthing->options >> ZSHIFT) << FRACBITS); + } + + mobj = P_SpawnMobj(x, y, z, ringthing); + + if (mthing->options & MTF_OBJECTFLIP) + { + mobj->eflags |= MFE_VERTICALFLIP; + mobj->flags2 |= MF2_OBJECTFLIP; + } + + mobj->angle = closestangle; + if (mthing->options & MTF_AMBUSH) + mobj->flags2 |= MF2_AMBUSH; + + if ((maptol & TOL_XMAS) && (ringthing == MT_NIGHTSSTAR)) + P_SetMobjState(mobj, mobj->info->seestate); + } + } + // Rings of items (all six of them) + else if (mthing->type >= 604 && mthing->type <= 609) + { + INT32 numitems = 8; + INT32 size = 96*FRACUNIT; + + if (mthing->type & 1) + { + numitems = 16; + size = 192*FRACUNIT; + } + z = #ifdef ESLOPE sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : @@ -10637,49 +11241,100 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing) if (mthing->options >> ZSHIFT) z += ((mthing->options >> ZSHIFT) << FRACBITS); - mthing->z = (INT16)(z>>FRACBITS); + closestangle = FixedAngle(mthing->angle*FRACUNIT); - mobj = P_SpawnMobj(x, y, z, MT_NIGHTSWING); - mobj->spawnpoint = mthing; - - if (G_IsSpecialStage(gamemap) && useNightsSS) - P_SetMobjState(mobj, mobj->info->meleestate); - else if (maptol & TOL_XMAS) - P_SetMobjState(mobj, mobj->info->seestate); - - mobj->angle = FixedAngle(mthing->angle*FRACUNIT); - mobj->flags2 |= MF2_AMBUSH; - mthing->mobj = mobj; - } - // All manners of rings and coins - else if (mthing->type == mobjinfo[MT_RING].doomednum || mthing->type == mobjinfo[MT_COIN].doomednum || - mthing->type == mobjinfo[MT_REDTEAMRING].doomednum || mthing->type == mobjinfo[MT_BLUETEAMRING].doomednum) - { - mobjtype_t ringthing = MT_RING; - - // No rings in Ultimate! - if (ultimatemode && !(G_IsSpecialStage(gamemap) || maptol & TOL_NIGHTS)) - return; - - // Which ringthing to use switch (mthing->type) { - case 1800: - ringthing = MT_COIN; + case 604: + case 605: + if (ultimatemode) + return; // No rings in Ultimate! + if (nightsreplace) + ringthing = MT_NIGHTSSTAR; break; - case 308: // No team rings in non-CTF - ringthing = (gametype == GT_CTF) ? MT_REDTEAMRING : MT_RING; - break; - case 309: // No team rings in non-CTF - ringthing = (gametype == GT_CTF) ? MT_BLUETEAMRING : MT_RING; + case 608: + case 609: + /*ringthing = (i & 1) ? MT_RING : MT_BLUESPHERE; -- i == 0 is bluesphere + break;*/ + case 606: + case 607: + ringthing = (nightsreplace) ? MT_NIGHTSCHIP : MT_BLUESPHERE; break; default: - // Spawn rings as blue spheres in special stages, ala S3+K. - if (G_IsSpecialStage(gamemap) && useNightsSS) - ringthing = MT_BLUEBALL; break; } + // Create the hoop! + for (i = 0; i < numitems; i++) + { + if (mthing->type == 608 || mthing->type == 609) + { + if (i & 1) + { + if (ultimatemode) + continue; // No rings in Ultimate! + ringthing = (nightsreplace) ? MT_NIGHTSSTAR : MT_RING; + } + else + ringthing = (nightsreplace) ? MT_NIGHTSCHIP : MT_BLUESPHERE; + } + + fa = i*FINEANGLES/numitems; + v[0] = FixedMul(FINECOSINE(fa),size); + v[1] = 0; + v[2] = FixedMul(FINESINE(fa),size); + v[3] = FRACUNIT; + + res = VectorMatrixMultiply(v, *RotateZMatrix(closestangle)); + M_Memcpy(&v, res, sizeof (v)); + + finalx = x + v[0]; + finaly = y + v[1]; + finalz = z + v[2]; + + mobj = P_SpawnMobj(finalx, finaly, finalz, ringthing); + mobj->z -= mobj->height/2; + + if (mthing->options & MTF_OBJECTFLIP) + { + mobj->eflags |= MFE_VERTICALFLIP; + mobj->flags2 |= MF2_OBJECTFLIP; + } + + mobj->angle = closestangle; + if (mthing->options & MTF_AMBUSH) + mobj->flags2 |= MF2_AMBUSH; + + if (bonustime && (ringthing == MT_BLUESPHERE || ringthing == MT_NIGHTSCHIP)) + P_SetMobjState(mobj, mobj->info->raisestate); + else if ((maptol & TOL_XMAS) && (ringthing == MT_NIGHTSSTAR)) + P_SetMobjState(mobj, mobj->info->seestate); + } + } + // All manners of rings and coins + else + { + + // Which ringthing to use + if (mthing->type == mobjinfo[MT_BLUESPHERE].doomednum) + ringthing = (nightsreplace) ? MT_NIGHTSCHIP : MT_BLUESPHERE; + else if (mthing->type == mobjinfo[MT_BOMBSPHERE].doomednum) + ringthing = MT_BOMBSPHERE; + else + { + if (ultimatemode) + return; // No rings in Ultimate! + + if (nightsreplace) + ringthing = MT_NIGHTSSTAR; + else if (mthing->type == mobjinfo[MT_COIN].doomednum) + ringthing = MT_COIN; + else if (mthing->type == mobjinfo[MT_REDTEAMRING].doomednum) // No team rings in non-CTF + ringthing = (gametype == GT_CTF) ? MT_REDTEAMRING : MT_RING; + else if (mthing->type == mobjinfo[MT_BLUETEAMRING].doomednum) // Ditto + ringthing = (gametype == GT_CTF) ? MT_BLUETEAMRING : MT_RING; + } + // Set proper height if (mthing->options & MTF_OBJECTFLIP) { @@ -10722,203 +11377,14 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing) } mobj->angle = FixedAngle(mthing->angle*FRACUNIT); - mobj->flags2 |= MF2_AMBUSH; mthing->mobj = mobj; - } - // *** - // Special placement patterns - // *** + if (mthing->options & MTF_AMBUSH) + mobj->flags2 |= MF2_AMBUSH; - // Vertical Rings - Stack of 5 (handles both red and yellow) - else if (mthing->type == 600 || mthing->type == 601) - { - INT32 dist = 64*FRACUNIT; - mobjtype_t ringthing = MT_RING; - if (mthing->type == 601) - dist = 128*FRACUNIT; - - // No rings in Ultimate! - if (ultimatemode && !(G_IsSpecialStage(gamemap) || maptol & TOL_NIGHTS)) - return; - - // Spawn rings as blue spheres in special stages, ala S3+K. - if (G_IsSpecialStage(gamemap) && useNightsSS) - ringthing = MT_BLUEBALL; - - for (r = 1; r <= 5; r++) - { - if (mthing->options & MTF_OBJECTFLIP) - { - z = ( -#ifdef ESLOPE - sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : -#endif - sec->ceilingheight) - mobjinfo[ringthing].height - dist*r; - if (mthing->options >> ZSHIFT) - z -= ((mthing->options >> ZSHIFT) << FRACBITS); - } - else - { - z = ( -#ifdef ESLOPE - sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : -#endif - sec->floorheight) + dist*r; - if (mthing->options >> ZSHIFT) - z += ((mthing->options >> ZSHIFT) << FRACBITS); - } - - mobj = P_SpawnMobj(x, y, z, ringthing); - - if (mthing->options & MTF_OBJECTFLIP) - { - mobj->eflags |= MFE_VERTICALFLIP; - mobj->flags2 |= MF2_OBJECTFLIP; - } - - mobj->angle = FixedAngle(mthing->angle*FRACUNIT); - if (mthing->options & MTF_AMBUSH) - mobj->flags2 |= MF2_AMBUSH; - } - } - // Diagonal rings (handles both types) - else if (mthing->type == 602 || mthing->type == 603) // Diagonal rings (5) - { - angle_t angle = FixedAngle(mthing->angle*FRACUNIT); - mobjtype_t ringthing = MT_RING; - INT32 iterations = 5; - if (mthing->type == 603) - iterations = 10; - - // No rings in Ultimate! - if (ultimatemode && !(G_IsSpecialStage(gamemap) || maptol & TOL_NIGHTS)) - return; - - // Spawn rings as blue spheres in special stages, ala S3+K. - if (G_IsSpecialStage(gamemap) && useNightsSS) - ringthing = MT_BLUEBALL; - - angle >>= ANGLETOFINESHIFT; - - for (r = 1; r <= iterations; r++) - { - x += FixedMul(64*FRACUNIT, FINECOSINE(angle)); - y += FixedMul(64*FRACUNIT, FINESINE(angle)); - - if (mthing->options & MTF_OBJECTFLIP) - { - z = ( -#ifdef ESLOPE - sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : -#endif - sec->ceilingheight) - mobjinfo[ringthing].height - 64*FRACUNIT*r; - if (mthing->options >> ZSHIFT) - z -= ((mthing->options >> ZSHIFT) << FRACBITS); - } - else - { - z = ( -#ifdef ESLOPE - sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : -#endif - sec->floorheight) + 64*FRACUNIT*r; - if (mthing->options >> ZSHIFT) - z += ((mthing->options >> ZSHIFT) << FRACBITS); - } - - mobj = P_SpawnMobj(x, y, z, ringthing); - - if (mthing->options & MTF_OBJECTFLIP) - { - mobj->eflags |= MFE_VERTICALFLIP; - mobj->flags2 |= MF2_OBJECTFLIP; - } - - mobj->angle = FixedAngle(mthing->angle*FRACUNIT); - if (mthing->options & MTF_AMBUSH) - mobj->flags2 |= MF2_AMBUSH; - } - } - // Rings of items (all six of them) - else if (mthing->type >= 604 && mthing->type <= 609) - { - INT32 numitems = 8; - INT32 size = 96*FRACUNIT; - mobjtype_t itemToSpawn = MT_NIGHTSWING; - - if (mthing->type & 1) - { - numitems = 16; - size = 192*FRACUNIT; - } - - z = -#ifdef ESLOPE - sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : -#endif - sec->floorheight; - if (mthing->options >> ZSHIFT) - z += ((mthing->options >> ZSHIFT) << FRACBITS); - - closestangle = FixedAngle(mthing->angle*FRACUNIT); - - // Create the hoop! - for (i = 0; i < numitems; i++) - { - switch (mthing->type) - { - case 604: - case 605: - itemToSpawn = MT_RING; - break; - case 608: - case 609: - itemToSpawn = (i & 1) ? MT_NIGHTSWING : MT_RING; - break; - case 606: - case 607: - itemToSpawn = MT_NIGHTSWING; - break; - default: - break; - } - - // No rings in Ultimate! - if (itemToSpawn == MT_RING) - { - if (ultimatemode && !(G_IsSpecialStage(gamemap) || (maptol & TOL_NIGHTS))) - continue; - - // Spawn rings as blue spheres in special stages, ala S3+K. - if (G_IsSpecialStage(gamemap) && useNightsSS) - itemToSpawn = MT_BLUEBALL; - } - - fa = i*FINEANGLES/numitems; - v[0] = FixedMul(FINECOSINE(fa),size); - v[1] = 0; - v[2] = FixedMul(FINESINE(fa),size); - v[3] = FRACUNIT; - - res = VectorMatrixMultiply(v, *RotateZMatrix(closestangle)); - M_Memcpy(&v, res, sizeof (v)); - - finalx = x + v[0]; - finaly = y + v[1]; - finalz = z + v[2]; - - mobj = P_SpawnMobj(finalx, finaly, finalz, itemToSpawn); - mobj->z -= mobj->height/2; - - if (itemToSpawn == MT_NIGHTSWING) - { - if (G_IsSpecialStage(gamemap) && useNightsSS) - P_SetMobjState(mobj, mobj->info->meleestate); - else if ((maptol & TOL_XMAS)) - P_SetMobjState(mobj, mobj->info->seestate); - } - } - return; + if (bonustime && (ringthing == MT_BLUESPHERE || ringthing == MT_NIGHTSCHIP)) + P_SetMobjState(mobj, mobj->info->raisestate); + else if ((maptol & TOL_XMAS) && (ringthing == MT_NIGHTSSTAR)) + P_SetMobjState(mobj, mobj->info->seestate); } } diff --git a/src/p_mobj.h b/src/p_mobj.h index c6e1bfbf2..afab6fda6 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -175,8 +175,8 @@ typedef enum MF2_SCATTER = 1<<8, // Thrown ring has scatter properties MF2_BEYONDTHEGRAVE = 1<<9, // Source of this missile has died and has since respawned. MF2_SLIDEPUSH = 1<<10, // MF_PUSHABLE that pushes continuously. - MF2_CLASSICPUSH = 1<<11, // Drops straight down when object has negative Z. - MF2_STANDONME = 1<<12, // While not pushable, stand on me anyway. + MF2_CLASSICPUSH = 1<<11, // Drops straight down when object has negative momz. + MF2_INVERTAIMABLE = 1<<12, // Flips whether it's targetable by A_LookForEnemies (enemies no, decoys yes) MF2_INFLOAT = 1<<13, // Floating to a height for a move, don't auto float to target's height. MF2_DEBRIS = 1<<14, // Splash ring from explosion ring MF2_NIGHTSPULL = 1<<15, // Attracted from a paraloop @@ -194,7 +194,6 @@ typedef enum MF2_AMBUSH = 1<<27, // Alternate behaviour typically set by MTF_AMBUSH MF2_LINKDRAW = 1<<28, // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position) MF2_SHIELD = 1<<29, // Thinker calls P_AddShield/P_ShieldLook (must be partnered with MF_SCENERY to use) - MF2_MACEROTATE = 1<<30, // Thinker calls P_MaceRotate around tracer // free: to and including 1<<31 } mobjflag2_t; @@ -315,7 +314,7 @@ typedef struct mobj_s mobjtype_t type; const mobjinfo_t *info; // &mobjinfo[mobj->type] - INT32 health; // for player this is rings + 1 + INT32 health; // for player this is rings + 1 -- no it isn't, not any more!! // Movement direction, movement generation (zig-zagging). angle_t movedir; // dirtype_t 0-7; also used by Deton for up/down angle @@ -389,6 +388,7 @@ typedef struct precipmobj_s angle_t angle; // orientation spritenum_t sprite; // used to find patch_t and flip value UINT32 frame; // frame number, plus bits see p_pspr.h + UINT8 sprite2; // player sprites UINT16 anim_duration; // for FF_ANIMATE states struct mprecipsecnode_s *touching_sectorlist; // a linked list of sectors where this object appears @@ -436,7 +436,7 @@ void P_MovePlayerToStarpost(INT32 playernum); void P_AfterPlayerSpawn(INT32 playernum); void P_SpawnMapThing(mapthing_t *mthing); -void P_SpawnHoopsAndRings(mapthing_t *mthing); +void P_SpawnHoopsAndRings(mapthing_t *mthing, boolean bonustime); void P_SpawnHoopOfSomething(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 number, mobjtype_t type, angle_t rotangle); void P_SpawnPrecipitation(void); void P_SpawnParaloop(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 number, mobjtype_t type, statenum_t nstate, angle_t rotangle, boolean spawncenter); diff --git a/src/p_saveg.c b/src/p_saveg.c index 59d1775a8..fb2356e8f 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -116,7 +116,8 @@ static void P_NetArchivePlayers(void) WRITEANGLE(save_p, players[i].drawangle); WRITEANGLE(save_p, players[i].awayviewaiming); WRITEINT32(save_p, players[i].awayviewtics); - WRITEINT32(save_p, players[i].rings); + WRITEINT16(save_p, players[i].rings); + WRITEINT16(save_p, players[i].spheres); WRITESINT8(save_p, players[i].pity); WRITEINT32(save_p, players[i].currentweapon); @@ -203,7 +204,7 @@ static void P_NetArchivePlayers(void) WRITEUINT32(save_p, players[i].marebegunat); WRITEUINT32(save_p, players[i].startedtime); WRITEUINT32(save_p, players[i].finishedtime); - WRITEINT16(save_p, players[i].finishedrings); + WRITEINT16(save_p, players[i].finishedspheres); WRITEUINT32(save_p, players[i].marescore); WRITEUINT32(save_p, players[i].lastmarescore); WRITEUINT8(save_p, players[i].lastmare); @@ -305,7 +306,8 @@ static void P_NetUnArchivePlayers(void) players[i].drawangle = READANGLE(save_p); players[i].awayviewaiming = READANGLE(save_p); players[i].awayviewtics = READINT32(save_p); - players[i].rings = READINT32(save_p); + players[i].rings = READINT16(save_p); + players[i].spheres = READINT16(save_p); players[i].pity = READSINT8(save_p); players[i].currentweapon = READINT32(save_p); @@ -392,7 +394,7 @@ 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].finishedrings = READINT16(save_p); + players[i].finishedspheres = READINT16(save_p); players[i].marescore = READUINT32(save_p); players[i].lastmarescore = READUINT32(save_p); players[i].lastmare = READUINT8(save_p); @@ -1990,7 +1992,7 @@ static void LoadMobjThinker(actionf_p1 thinker) if (mapthings[spawnpointnum].type == 1705 || mapthings[spawnpointnum].type == 1713) // NiGHTS Hoop special case { - P_SpawnHoopsAndRings(&mapthings[spawnpointnum]); + P_SpawnHoopsAndRings(&mapthings[spawnpointnum], false); return; } @@ -3265,7 +3267,7 @@ static void P_NetArchiveMisc(void) WRITEUINT32(save_p, tokenlist); WRITEUINT32(save_p, leveltime); - WRITEUINT32(save_p, totalrings); + WRITEUINT32(save_p, ssspheres); WRITEINT16(save_p, lastmap); WRITEUINT16(save_p, emeralds); @@ -3342,7 +3344,7 @@ static inline boolean P_NetUnArchiveMisc(void) // get the time leveltime = READUINT32(save_p); - totalrings = READUINT32(save_p); + ssspheres = READUINT32(save_p); lastmap = READINT16(save_p); emeralds = READUINT16(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index f3e92abce..a8e710469 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -808,7 +808,7 @@ void P_ReloadRings(void) mapthing_t *hoopsToRespawn[4096]; mapthing_t *mt = mapthings; - // scan the thinkers to find rings/wings/hoops to unset + // scan the thinkers to find rings/spheres/hoops to unset for (th = thinkercap.next; th != &thinkercap; th = th->next) { if (th->function.acp1 != (actionf_p1)P_MobjThinker) @@ -826,7 +826,9 @@ void P_ReloadRings(void) } continue; } - if (!(mo->type == MT_RING || mo->type == MT_NIGHTSWING || mo->type == MT_COIN || mo->type == MT_BLUEBALL)) + if (!(mo->type == MT_RING || mo->type == MT_COIN + || mo->type == MT_BLUESPHERE || mo->type == MT_BOMBSPHERE + || mo->type == MT_NIGHTSCHIP || mo->type == MT_NIGHTSSTAR)) continue; // Don't auto-disintegrate things being pulled to us @@ -840,9 +842,10 @@ void P_ReloadRings(void) for (i = 0; i < nummapthings; i++, mt++) { // Notice an omission? We handle hoops differently. - if (mt->type == 300 || mt->type == 308 || mt->type == 309 - || mt->type == 1706 || (mt->type >= 600 && mt->type <= 609) - || mt->type == 1800) + if (mt->type == mobjinfo[MT_RING].doomednum || mt->type == mobjinfo[MT_COIN].doomednum + || mt->type == mobjinfo[MT_REDTEAMRING].doomednum || mt->type == mobjinfo[MT_BLUETEAMRING].doomednum + || mt->type == mobjinfo[MT_BLUESPHERE].doomednum || mt->type == mobjinfo[MT_BOMBSPHERE].doomednum + || (mt->type >= 600 && mt->type <= 609)) // circles and diagonals { mt->mobj = NULL; @@ -850,12 +853,45 @@ void P_ReloadRings(void) mt->z = (INT16)(R_PointInSubsector(mt->x << FRACBITS, mt->y << FRACBITS) ->sector->floorheight>>FRACBITS); - P_SpawnHoopsAndRings (mt); + P_SpawnHoopsAndRings(mt, +#ifdef MANIASPHERES + true); +#else + !G_IsSpecialStage(gamemap)); // prevent flashing spheres in special stages +#endif } } for (i = 0; i < numHoops; i++) { - P_SpawnHoopsAndRings(hoopsToRespawn[i]); + P_SpawnHoopsAndRings(hoopsToRespawn[i], false); + } +} + +void P_SwitchSpheresBonusMode(boolean bonustime) +{ + mobj_t *mo; + thinker_t *th; + +#ifndef MANIASPHERES + if (G_IsSpecialStage(gamemap)) // prevent flashing spheres in special stages + return; +#endif + + // scan the thinkers to find spheres to switch + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo = (mobj_t *)th; + + if (mo->type != MT_BLUESPHERE && mo->type != MT_NIGHTSCHIP) + continue; + + if (!mo->health) + continue; + + P_SetMobjState(mo, ((bonustime) ? mo->info->raisestate : mo->info->spawnstate)); } } @@ -1025,20 +1061,22 @@ static void P_LoadThings(void) } //decrement spawn values to the actual number because zero is valid. - if (emer1) - P_SpawnMobj(huntemeralds[emer1 - 1]->x<y<z<x<y<z<x<y<z<x<y<z<x<y<z<x<y<z<type == 300 || mt->type == 308 || mt->type == 309 - || mt->type == 1706 || (mt->type >= 600 && mt->type <= 609) - || mt->type == 1705 || mt->type == 1713 || mt->type == 1800) + if (mt->type == mobjinfo[MT_RING].doomednum || mt->type == mobjinfo[MT_COIN].doomednum + || mt->type == mobjinfo[MT_REDTEAMRING].doomednum || mt->type == mobjinfo[MT_BLUETEAMRING].doomednum + || mt->type == mobjinfo[MT_BLUESPHERE].doomednum || mt->type == mobjinfo[MT_BOMBSPHERE].doomednum + || (mt->type >= 600 && mt->type <= 609) // circles and diagonals + || mt->type == 1705 || mt->type == 1713 || mt->type == 1800) // hoops { mt->mobj = NULL; @@ -1058,7 +1098,7 @@ static void P_LoadThings(void) mt->z = (INT16)(R_PointInSubsector(mt->x << FRACBITS, mt->y << FRACBITS) ->sector->floorheight>>FRACBITS); - P_SpawnHoopsAndRings (mt); + P_SpawnHoopsAndRings(mt, false); } } } @@ -2298,7 +2338,7 @@ static void P_LevelInitStuff(void) // circuit, race and competition stuff circuitmap = false; numstarposts = 0; - totalrings = timeinmap = 0; + ssspheres = timeinmap = 0; // special stage stagefailed = false; @@ -2320,6 +2360,8 @@ static void P_LevelInitStuff(void) } } + countdown = countdown2 = 0; + for (i = 0; i < MAXPLAYERS; i++) { if (canresetlives && (netgame || multiplayer) && playeringame[i] && (gametype == GT_COMPETITION || players[i].lives <= 0)) @@ -2328,42 +2370,37 @@ static void P_LevelInitStuff(void) players[i].lives = cv_startinglives.value; } - players[i].realtime = countdown = countdown2 = 0; + // obliteration station... + players[i].rings = players[i].spheres =\ + players[i].xtralife = players[i].deadtimer =\ + players[i].numboxes = players[i].totalring =\ + players[i].laps = players[i].aiming =\ + players[i].losstime = players[i].timeshit =\ + players[i].marescore = players[i].lastmarescore =\ + players[i].maxlink = players[i].startedtime =\ + players[i].finishedtime = players[i].finishedspheres =\ + players[i].lastmare = players[i].marebegunat =\ + players[i].textvar = players[i].texttimer =\ + players[i].linkcount = 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].exiting = 0; + // i guess this could be part of the above but i feel mildly uncomfortable implicitly casting players[i].gotcontinue = false; - players[i].xtralife = players[i].deadtimer = players[i].numboxes = players[i].totalring = players[i].laps = 0; - players[i].rings = 0; - players[i].aiming = 0; - players[i].pflags &= ~PF_GAMETYPEOVER; - - players[i].losstime = 0; - players[i].timeshit = 0; - - players[i].marescore = players[i].lastmarescore = players[i].maxlink = 0; - players[i].startedtime = players[i].finishedtime = players[i].finishedrings = 0; - players[i].lastmare = players[i].marebegunat = 0; - - // Don't show anything - players[i].textvar = players[i].texttimer = 0; - - 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); + // aha, the first evidence this shouldn't be a memset! players[i].drillmeter = 40*20; - players[i].exiting = 0; P_ResetPlayer(&players[i]); + // hit these too + players[i].pflags &= ~(PF_GAMETYPEOVER|PF_TRANSFERTOCLOSEST); - players[i].mo = NULL; - - // we must unset axis details too - players[i].axis1 = players[i].axis2 = NULL; - - // and this stupid flag as a result - players[i].pflags &= ~PF_TRANSFERTOCLOSEST; + // unset ALL the pointers. P_SetTarget isn't needed here because if this + // function is being called we're just going to clobber the data anyways + players[i].mo = players[i].followmobj = players[i].awayviewmobj =\ + players[i].capsule = players[i].axis1 = players[i].axis2 = NULL; } } @@ -2403,7 +2440,17 @@ void P_LoadThingsOnly(void) P_LevelInitStuff(); - P_PrepareThings(lastloadedmaplumpnum + ML_THINGS); + if (W_IsLumpWad(lastloadedmaplumpnum)) // welp it's a map wad in a pk3 + { // HACK: Open wad file rather quickly so we can use the things lump + UINT8 *wadData = W_CacheLumpNum(lastloadedmaplumpnum, PU_STATIC); + filelump_t *fileinfo = (filelump_t *)(wadData + ((wadinfo_t *)wadData)->infotableofs); + fileinfo += ML_THINGS; // we only need the THINGS lump + P_PrepareRawThings(wadData + fileinfo->filepos, fileinfo->size); + Z_Free(wadData); // we're done with this now + } + else // phew it's just a WAD + P_PrepareThings(lastloadedmaplumpnum + ML_THINGS); + P_LoadThings(); @@ -2676,11 +2723,6 @@ boolean P_SetupLevel(boolean skipprecip) // Reset the palette -#ifdef HWRENDER - if (rendermode == render_opengl) - HWR_SetPaletteColor(0); - else -#endif if (rendermode != render_none) V_SetPaletteLump("PLAYPAL"); @@ -2738,6 +2780,7 @@ boolean P_SetupLevel(boolean skipprecip) { tic_t starttime = I_GetTime(); tic_t endtime = starttime + (3*TICRATE)/2; + tic_t nowtime; S_StartSound(NULL, sfx_s3kaf); @@ -2747,9 +2790,17 @@ boolean P_SetupLevel(boolean skipprecip) F_WipeEndScreen(); F_RunWipe(wipedefs[wipe_speclevel_towhite], false); + nowtime = lastwipetic; // Hold on white for extra effect. - while (I_GetTime() < endtime) - I_Sleep(); + while (nowtime < endtime) + { + // wait loop + while (!((nowtime = I_GetTime()) - lastwipetic)) + I_Sleep(); + lastwipetic = nowtime; + if (moviemode) // make sure we save frames for the white hold too + M_SaveFrame(); + } ranspecialwipe = 1; } @@ -3337,7 +3388,7 @@ boolean P_AddWadFile(const char *wadfilename) if ((numlumps = W_InitFile(wadfilename)) == INT16_MAX) { refreshdirmenu |= REFRESHDIR_NOTLOADED; - CONS_Printf(M_GetText("Errors occured while loading %s; not added.\n"), wadfilename); + CONS_Printf(M_GetText("Errors occurred while loading %s; not added.\n"), wadfilename); return false; } else @@ -3351,7 +3402,7 @@ boolean P_AddWadFile(const char *wadfilename) for (i = 0; i < numlumps; i++, lumpinfo++) { // lumpinfo = FindFolder("Lua/", &luaPos, &luaNum, lumpinfo, &numlumps, &i); -// lumpinfo = FindFolder("SOCs/", &socPos, &socNum, lumpinfo, &numlumps, &i); +// lumpinfo = FindFolder("SOC/", &socPos, &socNum, lumpinfo, &numlumps, &i); lumpinfo = FindFolder("Sounds/", &sfxPos, &sfxNum, lumpinfo, &numlumps, &i); lumpinfo = FindFolder("Music/", &musPos, &musNum, lumpinfo, &numlumps, &i); // lumpinfo = FindFolder("Sprites/", &sprPos, &sprNum, lumpinfo, &numlumps, &i); diff --git a/src/p_setup.h b/src/p_setup.h index a42ac5b76..569501531 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -72,6 +72,7 @@ void P_DeleteFlickies(INT16 i); // Needed for NiGHTS void P_ReloadRings(void); +void P_SwitchSpheresBonusMode(boolean bonustime); void P_DeleteGrades(INT16 i); void P_AddGradesForMare(INT16 i, UINT8 mare, char *gtext); UINT8 P_GetGrade(UINT32 pscore, INT16 map, UINT8 mare); diff --git a/src/p_spec.c b/src/p_spec.c index 595473a5c..103312b52 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2868,7 +2868,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) // Unlocked something? if (M_UpdateUnlockablesAndExtraEmblems()) { - S_StartSound(NULL, sfx_ncitem); + S_StartSound(NULL, sfx_s3k68); G_SaveGameData(); // only save if unlocked something } } @@ -3527,7 +3527,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers if (player->exiting || player->bot) // Don't do anything for bots or players who have just finished break; - if (!(player->powers[pw_shield] || player->rings > 0)) // Don't do anything if no shield or rings anyway + if (!(player->powers[pw_shield] || player->spheres > 0)) // Don't do anything if no shield or spheres anyway break; P_SpecialStageDamage(player, NULL, NULL); @@ -3775,8 +3775,8 @@ DoneSection2: case 2: // Special stage GOAL sector / Exit Sector / CTF Flag Return if (player->bot) break; - if (!useNightsSS && G_IsSpecialStage(gamemap) && sstimer > 6) - sstimer = 6; // Just let P_Ticker take care of the rest. + if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap) && player->nightstime > 6) + player->nightstime = 6; // Just let P_Ticker take care of the rest. // Exit (for FOF exits; others are handled in P_PlayerThink in p_user.c) { @@ -4643,7 +4643,7 @@ static void P_RunSpecialSectorCheck(player_t *player, sector_t *sector) switch(GETSECSPECIAL(sector->special, 4)) { case 2: // Level Exit / GOAL Sector / Flag Return - if (!useNightsSS && G_IsSpecialStage(gamemap)) + if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap)) { // Special stage GOAL sector // requires touching floor. @@ -5502,7 +5502,7 @@ void P_InitSpecials(void) // Defaults in case levels don't have them set. sstimer = 90*TICRATE + 6; - totalrings = 1; + ssspheres = 1; CheckForBustableBlocks = CheckForBouncySector = CheckForQuicksand = CheckForMarioBlocks = CheckForFloatBob = CheckForReverseGravity = false; @@ -5527,6 +5527,30 @@ void P_InitSpecials(void) P_InitTagLists(); // Create xref tables for tags } +static void P_ApplyFlatAlignment(line_t *master, sector_t *sector, angle_t flatangle, fixed_t xoffs, fixed_t yoffs) +{ + if (!(master->flags & ML_NOSONIC)) // Modify floor flat alignment unless NOSONIC flag is set + { + sector->spawn_flrpic_angle = sector->floorpic_angle = flatangle; + sector->floor_xoffs += xoffs; + sector->floor_yoffs += yoffs; + // saved for netgames + sector->spawn_flr_xoffs = sector->floor_xoffs; + sector->spawn_flr_yoffs = sector->floor_yoffs; + } + + if (!(master->flags & ML_NOTAILS)) // Modify ceiling flat alignment unless NOTAILS flag is set + { + sector->spawn_ceilpic_angle = sector->ceilingpic_angle = flatangle; + sector->ceiling_xoffs += xoffs; + sector->ceiling_yoffs += yoffs; + // saved for netgames + sector->spawn_ceil_xoffs = sector->ceiling_xoffs; + sector->spawn_ceil_yoffs = sector->ceiling_yoffs; + } + +} + /** After the map has loaded, scans for specials that spawn 3Dfloors and * thinkers. * @@ -5572,7 +5596,7 @@ void P_SpawnSpecials(INT32 fromnetsave) { case 10: // Time for special stage sstimer = (sector->floorheight>>FRACBITS) * TICRATE + 6; // Time to finish - totalrings = sector->ceilingheight>>FRACBITS; // Ring count for special stage + ssspheres = sector->ceilingheight>>FRACBITS; // Ring count for special stage break; case 11: // Custom global gravity! @@ -5730,27 +5754,13 @@ void P_SpawnSpecials(INT32 fromnetsave) yoffs = lines[i].v1->y; } - for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;) + //If no tag is given, apply to front sector + if (lines[i].tag == 0) + P_ApplyFlatAlignment(lines + i, lines[i].frontsector, flatangle, xoffs, yoffs); + else { - if (!(lines[i].flags & ML_NOSONIC)) // Modify floor flat alignment unless NOSONIC flag is set - { - sectors[s].spawn_flrpic_angle = sectors[s].floorpic_angle = flatangle; - sectors[s].floor_xoffs += xoffs; - sectors[s].floor_yoffs += yoffs; - // saved for netgames - sectors[s].spawn_flr_xoffs = sectors[s].floor_xoffs; - sectors[s].spawn_flr_yoffs = sectors[s].floor_yoffs; - } - - if (!(lines[i].flags & ML_NOTAILS)) // Modify ceiling flat alignment unless NOTAILS flag is set - { - sectors[s].spawn_ceilpic_angle = sectors[s].ceilingpic_angle = flatangle; - sectors[s].ceiling_xoffs += xoffs; - sectors[s].ceiling_yoffs += yoffs; - // saved for netgames - sectors[s].spawn_ceil_xoffs = sectors[s].ceiling_xoffs; - sectors[s].spawn_ceil_yoffs = sectors[s].ceiling_yoffs; - } + for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0;) + P_ApplyFlatAlignment(lines + i, sectors + s, flatangle, xoffs, yoffs); } } else // Otherwise, print a helpful warning. Can I do no less? @@ -6689,6 +6699,7 @@ void T_Scroll(scroll_t *s) line_t *line; size_t i; INT32 sect; + ffloor_t *rover; case sc_side: // scroll wall texture side = sides + s->affectee; @@ -6730,6 +6741,19 @@ void T_Scroll(scroll_t *s) sector_t *psec; psec = sectors + sect; + // Find the FOF corresponding to the control linedef + for (rover = psec->ffloors; rover; rover = rover->next) + { + if (rover->master == sec->lines[i]) + break; + } + + if (!rover) // This should be impossible, but don't complain if it is the case somehow + continue; + + if (!(rover->flags & FF_EXISTS)) // If the FOF does not "exist", we pretend that nobody's there + continue; + for (node = psec->touching_thinglist; node; node = node->m_thinglist_next) { thing = node->m_thing; @@ -6793,6 +6817,19 @@ void T_Scroll(scroll_t *s) sector_t *psec; psec = sectors + sect; + // Find the FOF corresponding to the control linedef + for (rover = psec->ffloors; rover; rover = rover->next) + { + if (rover->master == sec->lines[i]) + break; + } + + if (!rover) // This should be impossible, but don't complain if it is the case somehow + continue; + + if (!(rover->flags & FF_EXISTS)) // If the FOF does not "exist", we pretend that nobody's there + continue; + for (node = psec->touching_thinglist; node; node = node->m_thinglist_next) { thing = node->m_thing; @@ -7646,7 +7683,6 @@ void T_Pusher(pusher_t *p) thing->player->pflags |= jumped; thing->player->pflags |= PF_SLIDING; - P_SetPlayerMobjState (thing, thing->info->painstate); // Whee! thing->angle = R_PointToAngle2 (0, 0, xspeed<<(FRACBITS-PUSH_FACTOR), yspeed<<(FRACBITS-PUSH_FACTOR)); if (!demoplayback || P_AnalogMove(thing->player)) diff --git a/src/p_tick.c b/src/p_tick.c index 658b1e4ea..a51ce2eb6 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -424,7 +424,7 @@ void P_DoTeamscrambling(void) static inline void P_DoSpecialStageStuff(void) { - boolean inwater = false; + boolean stillalive = false; INT32 i; // Can't drown in a special stage @@ -436,68 +436,60 @@ static inline void P_DoSpecialStageStuff(void) players[i].powers[pw_underwater] = players[i].powers[pw_spacetime] = 0; } - if (sstimer < 15*TICRATE+6 && sstimer > 7 && (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC)) - S_SpeedMusic(1.4f); + //if (sstimer < 15*TICRATE+6 && sstimer > 7 && (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC)) + //S_SpeedMusic(1.4f); - if (sstimer < 7 && sstimer > 0) // The special stage time is up! + if (sstimer && !objectplacing) { - sstimer = 0; - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i]) - { - players[i].exiting = (14*TICRATE)/5 + 1; - players[i].pflags &= ~PF_GLIDING; - } - - if (i == consoleplayer) - S_StartSound(NULL, sfx_lose); - } - - if (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC) - S_SpeedMusic(1.0f); - - stagefailed = true; - } - - if (sstimer > 1) // As long as time isn't up... - { - UINT32 ssrings = 0; + UINT16 countspheres = 0; // Count up the rings of all the players and see if // they've collected the required amount. for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) { - ssrings += players[i].rings; + tic_t oldnightstime = players[i].nightstime; + countspheres += players[i].spheres; // If in water, deplete timer 6x as fast. - if ((players[i].mo->eflags & MFE_TOUCHWATER) - || (players[i].mo->eflags & MFE_UNDERWATER)) - inwater = true; + if (players[i].mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER)) + players[i].nightstime -= 5; + if (--players[i].nightstime > 6) + { + if (P_IsLocalPlayer(&players[i]) && oldnightstime > 10*TICRATE && players[i].nightstime <= 10*TICRATE) + S_ChangeMusicInternal("_drown", false); + stillalive = true; + } + else if (!players[i].exiting) + { + players[i].exiting = (14*TICRATE)/5 + 1; + players[i].pflags &= ~(PF_GLIDING|PF_BOUNCING); + players[i].nightstime = 0; + if (P_IsLocalPlayer(&players[i])) + S_StartSound(NULL, sfx_s3k66); + } } - if (ssrings >= totalrings && totalrings > 0) + if (stillalive) { - // Halt all the players - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i]) - { - players[i].mo->momx = players[i].mo->momy = 0; - players[i].exiting = (14*TICRATE)/5 + 1; - } + if (countspheres >= ssspheres) + { + // Halt all the players + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + { + players[i].mo->momx = players[i].mo->momy = 0; + players[i].exiting = (14*TICRATE)/5 + 1; + } - sstimer = 0; - - P_GiveEmerald(true); + sstimer = 0; + P_GiveEmerald(true); + P_RestoreMusic(&players[consoleplayer]); + } } - - // Decrement the timer - if (!objectplacing) + else { - if (inwater) - sstimer -= 6; - else - sstimer--; + sstimer = 0; + stagefailed = true; } } } @@ -606,9 +598,10 @@ void P_Ticker(boolean run) } // Keep track of how long they've been playing! - totalplaytime++; + if (!demoplayback) // Don't increment if a demo is playing. + totalplaytime++; - if (!useNightsSS && G_IsSpecialStage(gamemap)) + if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap)) P_DoSpecialStageStuff(); if (runemeraldmanager) diff --git a/src/p_user.c b/src/p_user.c index e805a6d2e..8ddd418b4 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -283,22 +283,11 @@ boolean P_PlayerMoving(INT32 pnum) // UINT8 P_GetNextEmerald(void) { - if (!useNightsSS) // In order - { - if (!(emeralds & EMERALD1)) return 0; - if (!(emeralds & EMERALD2)) return 1; - if (!(emeralds & EMERALD3)) return 2; - if (!(emeralds & EMERALD4)) return 3; - if (!(emeralds & EMERALD5)) return 4; - if (!(emeralds & EMERALD6)) return 5; - return 6; - } - else // Depends on stage - { - if (gamemap < sstage_start || gamemap > sstage_end) - return 0; + if (gamemap >= sstage_start && gamemap <= sstage_end) return (UINT8)(gamemap - sstage_start); - } + if (gamemap >= smpstage_start || gamemap <= smpstage_end) + return (UINT8)(gamemap - smpstage_start); + return 0; } // @@ -309,20 +298,19 @@ UINT8 P_GetNextEmerald(void) // void P_GiveEmerald(boolean spawnObj) { - INT32 i; - UINT8 em; + UINT8 em = P_GetNextEmerald(); S_StartSound(NULL, sfx_cgot); // Got the emerald! - em = P_GetNextEmerald(); emeralds |= (1 << em); - if (spawnObj) + if (spawnObj && playeringame[consoleplayer]) { - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i]) - P_SetMobjState(P_SpawnMobj(players[i].mo->x, players[i].mo->y, players[i].mo->z + players[i].mo->info->height, MT_GOTEMERALD), - mobjinfo[MT_GOTEMERALD].spawnstate + em); - + // The Chaos Emerald begins to orbit us! + // Only give it to ONE person! + mobj_t *emmo = P_SpawnMobjFromMobj(players[consoleplayer].mo, 0, 0, players[consoleplayer].mo->height, MT_GOTEMERALD); + P_SetTarget(&emmo->target, players[consoleplayer].mo); + P_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em); + P_SetTarget(&players[consoleplayer].mo->tracer, emmo); } } @@ -696,10 +684,10 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) oldmare = player->mare; - if (P_TransferToNextMare(player) == false) + if (!P_TransferToNextMare(player)) { INT32 i; - INT32 total_rings = 0; + INT32 total_spheres = 0; P_SetTarget(&player->mo->target, NULL); @@ -707,7 +695,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) { for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]/* && players[i].powers[pw_carry] == CR_NIGHTSMODE*/) - total_rings += players[i].rings; + total_spheres += players[i].spheres; } for (i = 0; i < MAXPLAYERS; i++) @@ -720,13 +708,13 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) players[i].lastmare = players[i].mare; if (G_IsSpecialStage(gamemap)) { - players[i].finishedrings = (INT16)total_rings; - P_AddPlayerScore(player, total_rings * 50); + players[i].finishedspheres = (INT16)total_spheres; + P_AddPlayerScore(player, total_spheres * 50); } else { - players[i].finishedrings = (INT16)(players[i].rings); - P_AddPlayerScore(&players[i], (players[i].rings) * 50); + players[i].finishedspheres = (INT16)(players[i].spheres); + P_AddPlayerScore(&players[i], (players[i].spheres) * 50); } // Add score to leaderboards now @@ -737,20 +725,20 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) players[i].lastmarescore = players[i].marescore; players[i].marescore = 0; - players[i].rings = 0; + players[i].spheres = 0; P_DoPlayerExit(&players[i]); } } else if (oldmare != player->mare) { /// \todo Handle multi-mare special stages. - // Ring bonus - P_AddPlayerScore(player, (player->rings) * 50); + // Spheres bonus + P_AddPlayerScore(player, (player->spheres) * 50); player->lastmare = (UINT8)oldmare; player->texttimer = 4*TICRATE; player->textvar = 4; // Score and grades - player->finishedrings = (INT16)(player->rings); + player->finishedspheres = (INT16)(player->spheres); // Add score to temp leaderboards if (!(netgame||multiplayer) && P_IsLocalPlayer(player)) @@ -761,7 +749,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->marescore = 0; player->marebegunat = leveltime; - player->rings = 0; + player->spheres = 0; } else { @@ -861,7 +849,7 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor) } else { - ang = R_PointToAngle2(player->mo->momx, player->mo->momy, 0, 0); + ang = ((player->mo->momx || player->mo->momy) ? R_PointToAngle2(player->mo->momx, player->mo->momy, 0, 0) : player->drawangle); fallbackspeed = FixedMul(4*FRACUNIT, player->mo->scale); } @@ -927,8 +915,7 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings) player->rings += num_rings; - if (!G_IsSpecialStage(gamemap) || !useNightsSS) - player->totalring += num_rings; + player->totalring += num_rings; // Can only get up to 9999 rings, sorry! if (player->rings > 9999) @@ -960,6 +947,26 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings) } } +void P_GivePlayerSpheres(player_t *player, INT32 num_spheres) +{ + if (!player) + return; + + if (player->bot) + player = &players[consoleplayer]; + + if (!player->mo) + return; + + player->spheres += num_spheres; + + // Can only get up to 9999 spheres, sorry! + if (player->spheres > 9999) + player->spheres = 9999; + else if (player->spheres < 0) + player->spheres = 0; +} + // // P_GivePlayerLives // @@ -1039,12 +1046,11 @@ void P_DoSuperTransformation(player_t *player, boolean giverings) S_StartSound(NULL, sfx_supert); //let all players hear it -mattw_cfi + player->mo->momx = player->mo->momy = player->mo->momz = player->cmomx = player->cmomy = player->rmomx = player->rmomy = 0; + // Transformation animation P_SetPlayerMobjState(player->mo, S_PLAY_SUPER_TRANS1); - player->mo->momx = player->mo->momy = player->mo->momz = 0; - player->pflags |= PF_NOJUMPDAMAGE; // just to avoid recurling but still allow thok - if (giverings) player->rings = 50; @@ -1800,6 +1806,9 @@ boolean P_InSpaceSector(mobj_t *mo) // Returns true if you are in space for (rover = sector->ffloors; rover; rover = rover->next) { + if (!(rover->flags & FF_EXISTS)) + continue; + if (GETSECSPECIAL(rover->master->frontsector->special, 1) != SPACESPECIAL) continue; #ifdef ESLOPE @@ -2160,6 +2169,12 @@ static void P_CheckBouncySectors(player_t *player) for (rover = node->m_sector->ffloors; rover; rover = rover->next) { + if (!(rover->flags & FF_EXISTS)) + continue; // FOFs should not be bouncy if they don't even "exist" + + if (GETSECSPECIAL(rover->master->frontsector->special, 1) != 15) + continue; // this sector type is required for FOFs to be bouncy + topheight = P_GetFOFTopZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL); bottomheight = P_GetFOFBottomZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL); @@ -2173,7 +2188,6 @@ static void P_CheckBouncySectors(player_t *player) && oldz + player->mo->height > P_GetFOFBottomZ(player->mo, node->m_sector, rover, oldx, oldy, NULL)) top = false; - if (GETSECSPECIAL(rover->master->frontsector->special, 1) == 15) { fixed_t linedist; @@ -3488,194 +3502,198 @@ static void P_SetWeaponDelay(player_t *player, INT32 delay) static void P_DoFiring(player_t *player, ticcmd_t *cmd) { INT32 i; + mobj_t *mo = NULL; I_Assert(player != NULL); I_Assert(!P_MobjWasRemoved(player->mo)); - if (cmd->buttons & BT_ATTACK || cmd->buttons & BT_FIRENORMAL) + if (!(cmd->buttons & (BT_ATTACK|BT_FIRENORMAL))) { - if (!(player->pflags & PF_ATTACKDOWN) && (player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER && !player->climbing) - { - player->pflags |= PF_ATTACKDOWN; - P_SpawnPlayerMissile(player->mo, MT_FIREBALL, 0); - S_StartSound(player->mo, sfx_mario7); - } - else if (G_RingSlingerGametype() && (!G_TagGametype() || player->pflags & PF_TAGIT) - && !player->weapondelay && !player->climbing - && !(player->pflags & PF_ATTACKDOWN)) - { - mobj_t *mo = NULL; - player->pflags |= PF_ATTACKDOWN; - - #define TAKE_AMMO(player, power) \ - player->powers[power]--; \ - if (player->rings < 1) \ - { \ - if (player->powers[power] > 0) \ - player->powers[power]--; \ - } \ - else \ - player->rings--; - - if (cmd->buttons & BT_FIRENORMAL) // No powers, just a regular ring. - goto firenormal; //code repetition sucks. - // Bounce ring - else if (player->currentweapon == WEP_BOUNCE && player->powers[pw_bouncering]) - { - TAKE_AMMO(player, pw_bouncering); - P_SetWeaponDelay(player, TICRATE/4); - - mo = P_SpawnPlayerMissile(player->mo, MT_THROWNBOUNCE, MF2_BOUNCERING); - - if (mo) - mo->fuse = 3*TICRATE; // Bounce Ring time - } - // Rail ring - else if (player->currentweapon == WEP_RAIL && player->powers[pw_railring]) - { - TAKE_AMMO(player, pw_railring); - P_SetWeaponDelay(player, (3*TICRATE)/2); - - mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, MF2_RAILRING|MF2_DONTDRAW); - - // Rail has no unique thrown object, therefore its sound plays here. - S_StartSound(player->mo, sfx_rail1); - } - // Automatic - else if (player->currentweapon == WEP_AUTO && player->powers[pw_automaticring]) - { - TAKE_AMMO(player, pw_automaticring); - player->pflags &= ~PF_ATTACKDOWN; - P_SetWeaponDelay(player, 2); - - mo = P_SpawnPlayerMissile(player->mo, MT_THROWNAUTOMATIC, MF2_AUTOMATIC); - } - // Explosion - else if (player->currentweapon == WEP_EXPLODE && player->powers[pw_explosionring]) - { - TAKE_AMMO(player, pw_explosionring); - P_SetWeaponDelay(player, (3*TICRATE)/2); - - mo = P_SpawnPlayerMissile(player->mo, MT_THROWNEXPLOSION, MF2_EXPLOSION); - } - // Grenade - else if (player->currentweapon == WEP_GRENADE && player->powers[pw_grenadering]) - { - TAKE_AMMO(player, pw_grenadering); - P_SetWeaponDelay(player, TICRATE/3); - - mo = P_SpawnPlayerMissile(player->mo, MT_THROWNGRENADE, MF2_EXPLOSION); - - if (mo) - { - //P_InstaThrust(mo, player->mo->angle, FixedMul(mo->info->speed, player->mo->scale)); - mo->fuse = mo->info->mass; - } - } - // Scatter - // Note: Ignores MF2_RAILRING - else if (player->currentweapon == WEP_SCATTER && player->powers[pw_scatterring]) - { - fixed_t oldz = player->mo->z; - angle_t shotangle = player->mo->angle; - angle_t oldaiming = player->aiming; - - TAKE_AMMO(player, pw_scatterring); - P_SetWeaponDelay(player, (2*TICRATE)/3); - - // Center - mo = P_SpawnPlayerMissile(player->mo, MT_THROWNSCATTER, MF2_SCATTER); - if (mo) - shotangle = R_PointToAngle2(player->mo->x, player->mo->y, mo->x, mo->y); - - // Left - mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle-ANG2, true, MF2_SCATTER); - - // Right - mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle+ANG2, true, MF2_SCATTER); - - // Down - player->mo->z += FixedMul(12*FRACUNIT, player->mo->scale); - player->aiming += ANG1; - mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle, true, MF2_SCATTER); - - // Up - player->mo->z -= FixedMul(24*FRACUNIT, player->mo->scale); - player->aiming -= ANG2; - mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle, true, MF2_SCATTER); - - player->mo->z = oldz; - player->aiming = oldaiming; - return; - } - // No powers, just a regular ring. - else - { -firenormal: - // Infinity ring was selected. - // Mystic wants this ONLY to happen specifically if it's selected, - // and to not be able to get around it EITHER WAY with firenormal. - - // Infinity Ring - if (player->currentweapon == 0 - && player->powers[pw_infinityring]) - { - P_SetWeaponDelay(player, TICRATE/4); - - mo = P_SpawnPlayerMissile(player->mo, MT_THROWNINFINITY, 0); - - player->powers[pw_infinityring]--; - } - // Red Ring - else - { - if (player->rings <= 0) - return; - P_SetWeaponDelay(player, TICRATE/4); - - mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, 0); - - if (mo) - P_ColorTeamMissile(mo, player); - - player->rings--; - } - } - - #undef TAKE_AMMO - - if (mo) - { - if (mo->flags & MF_MISSILE && mo->flags2 & MF2_RAILRING) - { - const boolean nblockmap = !(mo->flags & MF_NOBLOCKMAP); - for (i = 0; i < 256; i++) - { - if (nblockmap) - { - P_UnsetThingPosition(mo); - mo->flags |= MF_NOBLOCKMAP; - P_SetThingPosition(mo); - } - - if (i&1) - P_SpawnMobj(mo->x, mo->y, mo->z, MT_SPARK); - - if (P_RailThinker(mo)) - break; // mobj was removed (missile hit a wall) or couldn't move - } - - // Other rail sound plays at contact point. - S_StartSound(mo, sfx_rail2); - } - } - } + // Not holding any firing buttons anymore. + // Release the grenade / whatever. + player->pflags &= ~PF_ATTACKDOWN; return; } - // Not holding any firing buttons anymore. - // Release the grenade / whatever. - player->pflags &= ~PF_ATTACKDOWN; + if (player->pflags & PF_ATTACKDOWN || player->climbing || (G_TagGametype() && !(player->pflags & PF_TAGIT))) + return; + + if ((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER) + { + player->pflags |= PF_ATTACKDOWN; + mo = P_SpawnPlayerMissile(player->mo, MT_FIREBALL, 0); + P_InstaThrust(mo, player->mo->angle, ((mo->info->speed>>FRACBITS)*player->mo->scale) + player->speed); + S_StartSound(player->mo, sfx_mario7); + return; + } + + if (!G_RingSlingerGametype() || player->weapondelay) + return; + + player->pflags |= PF_ATTACKDOWN; + +#define TAKE_AMMO(player, power) \ + player->powers[power]--; \ + if (player->rings < 1) \ + { \ + if (player->powers[power] > 0) \ + player->powers[power]--; \ + } \ + else \ + player->rings--; + + if (cmd->buttons & BT_FIRENORMAL) // No powers, just a regular ring. + goto firenormal; //code repetition sucks. + // Bounce ring + else if (player->currentweapon == WEP_BOUNCE && player->powers[pw_bouncering]) + { + TAKE_AMMO(player, pw_bouncering); + P_SetWeaponDelay(player, TICRATE/4); + + mo = P_SpawnPlayerMissile(player->mo, MT_THROWNBOUNCE, MF2_BOUNCERING); + + if (mo) + mo->fuse = 3*TICRATE; // Bounce Ring time + } + // Rail ring + else if (player->currentweapon == WEP_RAIL && player->powers[pw_railring]) + { + TAKE_AMMO(player, pw_railring); + P_SetWeaponDelay(player, (3*TICRATE)/2); + + mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, MF2_RAILRING|MF2_DONTDRAW); + + // Rail has no unique thrown object, therefore its sound plays here. + S_StartSound(player->mo, sfx_rail1); + } + // Automatic + else if (player->currentweapon == WEP_AUTO && player->powers[pw_automaticring]) + { + TAKE_AMMO(player, pw_automaticring); + player->pflags &= ~PF_ATTACKDOWN; + P_SetWeaponDelay(player, 2); + + mo = P_SpawnPlayerMissile(player->mo, MT_THROWNAUTOMATIC, MF2_AUTOMATIC); + } + // Explosion + else if (player->currentweapon == WEP_EXPLODE && player->powers[pw_explosionring]) + { + TAKE_AMMO(player, pw_explosionring); + P_SetWeaponDelay(player, (3*TICRATE)/2); + + mo = P_SpawnPlayerMissile(player->mo, MT_THROWNEXPLOSION, MF2_EXPLOSION); + } + // Grenade + else if (player->currentweapon == WEP_GRENADE && player->powers[pw_grenadering]) + { + TAKE_AMMO(player, pw_grenadering); + P_SetWeaponDelay(player, TICRATE/3); + + mo = P_SpawnPlayerMissile(player->mo, MT_THROWNGRENADE, MF2_EXPLOSION); + + if (mo) + { + //P_InstaThrust(mo, player->mo->angle, FixedMul(mo->info->speed, player->mo->scale)); + mo->fuse = mo->info->reactiontime; + } + } + // Scatter + // Note: Ignores MF2_RAILRING + else if (player->currentweapon == WEP_SCATTER && player->powers[pw_scatterring]) + { + fixed_t oldz = player->mo->z; + angle_t shotangle = player->mo->angle; + angle_t oldaiming = player->aiming; + + TAKE_AMMO(player, pw_scatterring); + P_SetWeaponDelay(player, (2*TICRATE)/3); + + // Center + mo = P_SpawnPlayerMissile(player->mo, MT_THROWNSCATTER, MF2_SCATTER); + if (mo) + shotangle = R_PointToAngle2(player->mo->x, player->mo->y, mo->x, mo->y); + + // Left + mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle-ANG2, true, MF2_SCATTER); + + // Right + mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle+ANG2, true, MF2_SCATTER); + + // Down + player->mo->z += FixedMul(12*FRACUNIT, player->mo->scale); + player->aiming += ANG1; + mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle, true, MF2_SCATTER); + + // Up + player->mo->z -= FixedMul(24*FRACUNIT, player->mo->scale); + player->aiming -= ANG2; + mo = P_SPMAngle(player->mo, MT_THROWNSCATTER, shotangle, true, MF2_SCATTER); + + player->mo->z = oldz; + player->aiming = oldaiming; + return; + } + // No powers, just a regular ring. + else + { +firenormal: + // Infinity ring was selected. + // Mystic wants this ONLY to happen specifically if it's selected, + // and to not be able to get around it EITHER WAY with firenormal. + + // Infinity Ring + if (player->currentweapon == 0 + && player->powers[pw_infinityring]) + { + P_SetWeaponDelay(player, TICRATE/4); + + mo = P_SpawnPlayerMissile(player->mo, MT_THROWNINFINITY, 0); + + player->powers[pw_infinityring]--; + } + // Red Ring + else + { + if (player->rings <= 0) + return; + P_SetWeaponDelay(player, TICRATE/4); + + mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, 0); + + if (mo) + P_ColorTeamMissile(mo, player); + + player->rings--; + } + } + + #undef TAKE_AMMO + + if (mo) + { + if (mo->flags & MF_MISSILE && mo->flags2 & MF2_RAILRING) + { + const boolean nblockmap = !(mo->flags & MF_NOBLOCKMAP); + for (i = 0; i < 256; i++) + { + if (nblockmap) + { + P_UnsetThingPosition(mo); + mo->flags |= MF_NOBLOCKMAP; + P_SetThingPosition(mo); + } + + if (i&1) + P_SpawnMobj(mo->x, mo->y, mo->z, MT_SPARK); + + if (P_RailThinker(mo)) + break; // mobj was removed (missile hit a wall) or couldn't move + } + + // Other rail sound plays at contact point. + S_StartSound(mo, sfx_rail2); + } + } } // @@ -3797,12 +3815,15 @@ static void P_DoSuperStuff(player_t *player) // boolean P_SuperReady(player_t *player) { - if ((ALL7EMERALDS(emeralds) && player->rings >= 50) && !player->powers[pw_super] && !player->powers[pw_tailsfly] - && !(player->powers[pw_shield] & SH_NOSTACK) + if (!player->powers[pw_super] && !player->powers[pw_invulnerability] - && !(maptol & TOL_NIGHTS || (player->powers[pw_carry] == CR_NIGHTSMODE)) // don't turn 'regular super' in nights levels - && player->pflags & PF_JUMPED - && player->charflags & SF_SUPER) + && !player->powers[pw_tailsfly] + && (player->charflags & SF_SUPER) + && (player->pflags & PF_JUMPED) + && !(player->powers[pw_shield] & SH_NOSTACK) + && !(maptol & TOL_NIGHTS) + && ALL7EMERALDS(emeralds) + && (player->rings >= 50)) return true; return false; @@ -4488,12 +4509,12 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) } else if (player->pflags & PF_SLIDING || (gametype == GT_CTF && player->gotflag)) ; - else if (P_SuperReady(player)) + /*else if (P_SuperReady(player)) { // If you can turn super and aren't already, // and you don't have a shield, do it! P_DoSuperTransformation(player, false); - } + }*/ else if (player->pflags & PF_JUMPED) { #ifdef HAVE_BLUA @@ -5957,10 +5978,10 @@ static void P_DoNiGHTSCapsule(player_t *player) if (G_IsSpecialStage(gamemap)) { // In special stages, share rings. Everyone gives up theirs to the capsule player always, because we can't have any individualism here! for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && (&players[i] != player) && players[i].rings > 0) + if (playeringame[i] && (&players[i] != player) && players[i].spheres > 0) { - player->rings += players[i].rings; - players[i].rings = 0; + player->spheres += players[i].spheres; + players[i].spheres = 0; } } @@ -5969,9 +5990,9 @@ static void P_DoNiGHTSCapsule(player_t *player) && player->mo->y == player->capsule->y && player->mo->z == player->capsule->z+(player->capsule->height/3)) { - if (player->rings > 0) + if (player->spheres > 0) { - player->rings--; + player->spheres--; player->capsule->health--; player->capsule->extravalue1++; @@ -6003,9 +6024,6 @@ static void P_DoNiGHTSCapsule(player_t *player) if (G_IsSpecialStage(gamemap)) { - // The Chaos Emerald begins to orbit us! - mobj_t *emmo; - UINT8 em = P_GetNextEmerald(); tic_t lowest_time; /*for (i = 0; i < MAXPLAYERS; i++) @@ -6020,8 +6038,10 @@ static void P_DoNiGHTSCapsule(player_t *player) if (player->powers[pw_carry] == CR_NIGHTSMODE) { + // The Chaos Emerald begins to orbit us! + UINT8 em = P_GetNextEmerald(); // Only give it to ONE person, and THAT player has to get to the goal! - emmo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->info->height, MT_GOTEMERALD); + mobj_t *emmo = P_SpawnMobjFromMobj(player->mo, 0, 0, player->mo->height, MT_GOTEMERALD); P_SetTarget(&emmo->target, player->mo); P_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em); P_SetTarget(&player->mo->tracer, emmo); @@ -6039,18 +6059,24 @@ static void P_DoNiGHTSCapsule(player_t *player) } else { - for (i = 0; i < 16; i++) + /*for (i = 0; i < 16; i++) { mobj_t *flicky = P_InternalFlickySpawn(player->capsule, 0, ((i%4) + 1)*2*FRACUNIT, true); flicky->z += player->capsule->height/2; flicky->angle = (i*(ANGLE_MAX/16)); P_InstaThrust(flicky, flicky->angle, 8*FRACUNIT); - } + }*/ + mobj_t *idya = P_SpawnMobjFromMobj(player->mo, 0, 0, player->mo->height, MT_GOTEMERALD); + idya->extravalue2 = player->mare/5; + P_SetTarget(&idya->target, player->mo); + P_SetMobjState(idya, mobjinfo[MT_GOTEMERALD].missilestate + ((player->mare + 1) % 5)); + P_SetTarget(&player->mo->tracer, idya); } for (i = 0; i < MAXPLAYERS; i++) 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_SwitchSpheresBonusMode(true); } } else @@ -6143,7 +6169,7 @@ static void P_NiGHTSMovement(player_t *player) } else if (P_IsLocalPlayer(player) && player->nightstime == 10*TICRATE) // 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("_drown",false); + S_ChangeMusicInternal((((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? "_ntime" : "_drown"), false); if (player->mo->z < player->mo->floorz) @@ -6942,12 +6968,17 @@ static void P_MovePlayer(player_t *player) if ((player->powers[pw_carry] == CR_NIGHTSMODE) && (player->exiting || !(player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1] - && player->mo->state < &states[S_PLAY_NIGHTS_TRANS6]))) + && player->mo->state < &states[S_PLAY_NIGHTS_TRANS6]))) // Note the < instead of <= { skin_t *skin = ((skin_t *)(player->mo->skin)); - if (skin->flags & SF_SUPER && player->mo->color < MAXSKINCOLORS) + if (skin->flags & SF_SUPER) + { + player->mo->color = skin->supercolor + + ((player->nightstime == player->startedtime) + ? 4 + : abs((((signed)leveltime >> 1) % 9) - 4)); // This is where super flashing is handled. G_GhostAddColor(GHC_SUPER); - player->mo->color = (skin->flags & SF_SUPER) ? skin->supercolor + abs((((signed)(player->startedtime - player->nightstime) >> 1) % 9) - 4) : player->mo->color; // This is where super flashing is handled. + } } if (!player->capsule && !player->bonustime) @@ -7481,7 +7512,7 @@ static void P_MovePlayer(player_t *player) #endif { if (!(player->pflags & (PF_USEDOWN|PF_GLIDING|PF_SLIDING|PF_SHIELDABILITY)) // If the player is not holding down BT_USE, or having used an ability previously - && (!(player->pflags & PF_THOKKED) || ((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP && player->secondjump == UINT8_MAX))) // thokked is optional if you're bubblewrapped + && (!(player->powers[pw_shield] & SH_NOSTACK) || !(player->pflags & PF_THOKKED) || ((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP && player->secondjump == UINT8_MAX))) // thokked is optional if you're bubblewrapped/turning super { // Force shield activation if ((player->powers[pw_shield] & ~(SH_FORCEHP|SH_STACK)) == SH_FORCE) @@ -7494,6 +7525,11 @@ static void P_MovePlayer(player_t *player) { switch (player->powers[pw_shield] & SH_NOSTACK) { + // Super! + case SH_NONE: + if (P_SuperReady(player)) + P_DoSuperTransformation(player, false); + break; // Whirlwind/Thundercoin shield activation case SH_WHIRLWIND: case SH_THUNDERCOIN: @@ -7945,17 +7981,18 @@ static void P_DoRopeHang(player_t *player) if (player->cmd.buttons & BT_USE && !(player->pflags & PF_STASIS)) // Drop off of the rope { - P_SetTarget(&player->mo->tracer, NULL); - player->pflags |= P_GetJumpFlags(player); + P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); + + P_SetTarget(&player->mo->tracer, NULL); player->powers[pw_carry] = CR_NONE; - if (!(player->pflags & PF_SLIDING) && (player->pflags & PF_JUMPED) - && !(player->panim == PA_JUMP)) - P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); return; } + if (player->mo->state-states != S_PLAY_RIDE) + P_SetPlayerMobjState(player->mo, S_PLAY_RIDE); + // If not allowed to move, we're done here. if (!speed) return; @@ -8046,10 +8083,7 @@ static void P_DoRopeHang(player_t *player) if (player->mo->tracer->flags & MF_SLIDEME) { player->pflags |= P_GetJumpFlags(player); - - if (!(player->pflags & PF_SLIDING) && (player->pflags & PF_JUMPED) - && !(player->panim == PA_JUMP)) - P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); + P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); } P_SetTarget(&player->mo->tracer, NULL); @@ -8167,7 +8201,6 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet) mobj_t *mo; thinker_t *think; mobj_t *closestmo = NULL; - const UINT32 targetmask = (MF_ENEMY|MF_BOSS|(nonenemies ? (MF_MONITOR|MF_SPRING) : 0)); const fixed_t maxdist = FixedMul((bullet ? RING_DIST*2 : RING_DIST), player->mo->scale); const angle_t span = (bullet ? ANG30 : ANGLE_90); fixed_t dist, closestdist = 0; @@ -8178,9 +8211,7 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet) continue; // not a mobj thinker mo = (mobj_t *)think; - if (!(mo->flags & targetmask - || mo->type == MT_FAKEMOBILE // hehehehe - || mo->type == MT_EGGSHIELD)) + if (!(mo->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR|MF_SPRING)) == !(mo->flags2 & MF2_INVERTAIMABLE)) // allows if it has the flags desired XOR it has the invert aimable flag continue; // not a valid target if (mo->health <= 0) // dead @@ -8195,6 +8226,9 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet) if ((mo->flags & (MF_ENEMY|MF_BOSS)) && !(mo->flags & MF_SHOOTABLE)) // don't aim at something you can't shoot at anyway (see Egg Guard or Minus) continue; + if (!nonenemies && mo->flags & (MF_MONITOR|MF_SPRING)) + continue; + if (!bullet && mo->type == MT_DETON) // Don't be STUPID, Sonic! continue; @@ -8232,7 +8266,7 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet) if (closestmo && dist > closestdist) continue; - if ((R_PointToAngle2(player->mo->x, player->mo->y, mo->x, mo->y) - player->mo->angle + span) > span*2) + if ((R_PointToAngle2(player->mo->x + P_ReturnThrustX(player->mo, player->mo->angle, player->mo->radius), player->mo->y + P_ReturnThrustY(player->mo, player->mo->angle, player->mo->radius), mo->x, mo->y) - player->mo->angle + span) > span*2) continue; // behind back if (!P_CheckSight(player->mo, mo)) @@ -9567,11 +9601,8 @@ void P_PlayerThink(player_t *player) // If 11 seconds are left on the timer, // begin the drown music for countdown! - if (countdown == 11*TICRATE - 1) - { - if (P_IsLocalPlayer(player)) - S_ChangeMusicInternal("_drown", false); - } + if (countdown == 11*TICRATE - 1 && P_IsLocalPlayer(player)) + S_ChangeMusicInternal("_drown", false); // If you've hit the countdown and you haven't made // it to the exit, you're a goner! @@ -9671,7 +9702,7 @@ void P_PlayerThink(player_t *player) if (gametype != GT_COOP) player->score = 0; player->mo->health = 1; - player->rings = 0; + player->rings = player->spheres = 0; } else if ((netgame || multiplayer) && player->lives <= 0 && gametype != GT_COOP) { @@ -9724,8 +9755,9 @@ void P_PlayerThink(player_t *player) mo2 = (mobj_t *)th; - if (!(mo2->type == MT_NIGHTSWING || mo2->type == MT_RING || mo2->type == MT_COIN - || mo2->type == MT_BLUEBALL)) + if (!(mo2->type == MT_RING || mo2->type == MT_COIN + || mo2->type == MT_BLUESPHERE || mo2->type == MT_BOMBSPHERE + || mo2->type == MT_NIGHTSCHIP || mo2->type == MT_NIGHTSSTAR)) continue; if (P_AproxDistance(P_AproxDistance(mo2->x - x, mo2->y - y), mo2->z - z) > FixedMul(128*FRACUNIT, player->mo->scale)) @@ -9761,8 +9793,6 @@ void P_PlayerThink(player_t *player) ticmiss++; P_DoRopeHang(player); - if (player->mo->state-states != S_PLAY_RIDE) - P_SetPlayerMobjState(player->mo, S_PLAY_RIDE); P_DoJumpStuff(player, &player->cmd); } else //if (player->powers[pw_carry] == CR_ZOOMTUBE) @@ -9878,7 +9908,8 @@ void P_PlayerThink(player_t *player) if (!player->powers[pw_carry] && ((player->pflags & (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE)) == (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE)) && !(cmd->forwardmove || cmd->sidemove) - && (player->rmomx || player->rmomy)) + && (player->rmomx || player->rmomy) + && (!player->capsule || (player->capsule->reactiontime != (player-players)+1))) { fixed_t acceleration = (player->accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * player->acceleration) * player->thrustfactor * 20; angle_t moveAngle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy); @@ -9900,7 +9931,7 @@ void P_PlayerThink(player_t *player) || player->climbing || player->pflags & (PF_SPINNING|PF_SLIDING)) player->pflags &= ~PF_APPLYAUTOBRAKE; - else if (currentlyonground) + else if (currentlyonground || player->powers[pw_tailsfly]) player->pflags |= PF_APPLYAUTOBRAKE; } } @@ -10214,7 +10245,7 @@ void P_PlayerAfterThink(player_t *player) if (player->followmobj) { P_RemoveMobj(player->followmobj); - player->followmobj = NULL; + P_SetTarget(&player->followmobj, NULL); } return; } @@ -10332,7 +10363,7 @@ void P_PlayerAfterThink(player_t *player) if (P_IsLocalPlayer(player) && (player->pflags & PF_WPNDOWN) && player->currentweapon != oldweapon) S_StartSound(NULL, sfx_wepchg); - if (player->pflags & PF_SLIDING) + if ((player->pflags & PF_SLIDING) && ((player->pflags & (PF_JUMPED|PF_NOJUMPDAMAGE)) != PF_JUMPED)) P_SetPlayerMobjState(player->mo, player->mo->info->painstate); /* if (player->powers[pw_carry] == CR_NONE && player->mo->tracer && !player->homing) @@ -10448,21 +10479,10 @@ void P_PlayerAfterThink(player_t *player) player->secondjump = 0; player->pflags &= ~PF_THOKKED; - if (cmd->forwardmove > 0) - { - if ((player->mo->tracer->tracer->lastlook += 2) > player->mo->tracer->tracer->friction) - player->mo->tracer->tracer->lastlook = player->mo->tracer->tracer->friction; - } - else if (cmd->forwardmove < 0) - { - if ((player->mo->tracer->tracer->lastlook -= 2) < player->mo->tracer->tracer->movecount) - player->mo->tracer->tracer->lastlook = player->mo->tracer->tracer->movecount; - } - if ((player->mo->tracer->tracer->flags & MF_SLIDEME) // Noclimb on chain parameters gives this && !(twodlevel || player->mo->flags2 & MF2_TWOD)) // why on earth would you want to turn them in 2D mode? { - player->mo->tracer->tracer->health += cmd->sidemove; + player->mo->tracer->tracer->angle += cmd->sidemove<mo->angle += cmd->sidemove< ANGLE_MAX if (!demoplayback || P_AnalogMove(player)) @@ -10510,14 +10530,14 @@ void P_PlayerAfterThink(player_t *player) if (player->followmobj && (player->spectator || player->mo->health <= 0 || player->followmobj->type != player->followitem)) { P_RemoveMobj(player->followmobj); - player->followmobj = NULL; + P_SetTarget(&player->followmobj, NULL); } if (!player->spectator && player->mo->health && player->followitem) { if (!player->followmobj || P_MobjWasRemoved(player->followmobj)) { - player->followmobj = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, player->followitem); + P_SetTarget(&player->followmobj, P_SpawnMobjFromMobj(player->mo, 0, 0, 0, player->followitem)); P_SetTarget(&player->followmobj->tracer, player->mo); player->followmobj->flags2 |= MF2_LINKDRAW; } diff --git a/src/r_draw.c b/src/r_draw.c index e06d43f67..40945f4e0 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -257,7 +257,7 @@ const UINT8 Color_Index[MAXTRANSLATIONS-1][16] = { {0x00, 0x50, 0x50, 0x51, 0x51, 0x52, 0x52, 0x52, 0x54, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5}, // SKINCOLOR_SUPERTAN2 {0x50, 0x51, 0x51, 0x52, 0x52, 0x52, 0x54, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9}, // SKINCOLOR_SUPERTAN3 {0x51, 0x52, 0x52, 0x52, 0x52, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9, 0xfb, 0xed}, // SKINCOLOR_SUPERTAN4 - {0x52, 0x52, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9, 0xfb, 0xed, 0xee, 0xef, 0xef} // SKINCOLOR_SUPERTAN5 + {0x52, 0x52, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9, 0xfb, 0xed, 0xee, 0xef, 0xef} // SKINCOLOR_SUPERTAN5 }; // See also the enum skincolors_t diff --git a/src/r_things.c b/src/r_things.c index fad47d26b..9e858aba9 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -717,7 +717,7 @@ static void R_DrawVisSprite(vissprite_t *vis) colfunc = basecolfunc; // hack: this isn't resetting properly somewhere. dc_colormap = vis->colormap; - if (!(vis->cut & SC_PRECIP) && (vis->mobj->flags & MF_BOSS) && (vis->mobj->flags2 & MF2_FRET) && (leveltime & 1)) // Bosses "flash" + if (!(vis->cut & SC_PRECIP) && (vis->mobj->flags & (MF_ENEMY|MF_BOSS)) && (vis->mobj->flags2 & MF2_FRET) && (leveltime & 1)) // Bosses "flash" { // translate certain pixels to white colfunc = transcolfunc; @@ -2673,7 +2673,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) if (player->followmobj) { P_RemoveMobj(player->followmobj); - player->followmobj = NULL; + P_SetTarget(&player->followmobj, NULL); } if (player->mo) diff --git a/src/screen.c b/src/screen.c index cd97b62fa..9bbcb995c 100644 --- a/src/screen.c +++ b/src/screen.c @@ -446,10 +446,11 @@ void SCR_ClosedCaptions(void) { if (splitscreen) basey -= 8; - else if (((maptol & TOL_NIGHTS) && (modeattacking == ATTACKING_NIGHTS)) - || (cv_powerupdisplay.value == 2) + else if ((modeattacking == ATTACKING_NIGHTS) + || (!(maptol & TOL_NIGHTS) + && ((cv_powerupdisplay.value == 2) || (cv_powerupdisplay.value == 1 && ((stplyr == &players[displayplayer] && !camera.chase) - || ((splitscreen && stplyr == &players[secondarydisplayplayer]) && !camera2.chase)))) + || ((splitscreen && stplyr == &players[secondarydisplayplayer]) && !camera2.chase)))))) basey -= 16; } diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index d12e70eaf..a1031209d 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -658,6 +658,14 @@ static void Impl_HandleMouseButtonEvent(SDL_MouseButtonEvent evt, Uint32 type) SDL_memset(&event, 0, sizeof(event_t)); + // Ignore the event if the mouse is not actually focused on the window. + // This can happen if you used the mouse to restore keyboard focus; + // this apparently makes a mouse button down event but not a mouse button up event, + // resulting in whatever key was pressed down getting "stuck" if we don't ignore it. + // -- Monster Iestyn (28/05/18) + if (SDL_GetMouseFocus() != window) + return; + /// \todo inputEvent.button.which if (USE_MOUSEINPUT) { diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c index 718324591..a82335f6c 100644 --- a/src/sdl/mixer_sound.c +++ b/src/sdl/mixer_sound.c @@ -66,6 +66,7 @@ static boolean midimode; static Mix_Music *music; static UINT8 music_volume, midi_volume, sfx_volume; static float loop_point; +static boolean songpaused; #ifdef HAVE_LIBGME static Music_Emu *gme; @@ -102,6 +103,7 @@ void I_StartupSound(void) } sound_started = true; + songpaused = false; Mix_AllocateChannels(256); } @@ -450,7 +452,7 @@ static void mix_gme(void *udata, Uint8 *stream, int len) (void)udata; // no gme? no music. - if (!gme || gme_track_ended(gme)) + if (!gme || gme_track_ended(gme) || songpaused) return; // play gme into stream @@ -458,7 +460,7 @@ static void mix_gme(void *udata, Uint8 *stream, int len) // apply volume to stream for (i = 0, p = (short *)stream; i < len/2; i++, p++) - *p = ((INT32)*p) * music_volume / 31; + *p = ((INT32)*p) * music_volume*2 / 42; } #endif @@ -476,12 +478,14 @@ void I_PauseSong(INT32 handle) { (void)handle; Mix_PauseMusic(); + songpaused = true; } void I_ResumeSong(INT32 handle) { (void)handle; Mix_ResumeMusic(); + songpaused = false; } // diff --git a/src/sdl/sdl_sound.c b/src/sdl/sdl_sound.c index 6ca11c9ef..63b51c625 100644 --- a/src/sdl/sdl_sound.c +++ b/src/sdl/sdl_sound.c @@ -1180,12 +1180,6 @@ void I_StartupSound(void) audio.callback = I_UpdateStream; audio.userdata = &localdata; - if (dedicated) - { - nosound = nomidimusic = nodigimusic = true; - return; - } - // Configure sound device CONS_Printf("I_StartupSound:\n"); @@ -1481,9 +1475,6 @@ void I_InitMusic(void) I_AddExitFunc(I_ShutdownGMEMusic); #endif - if ((nomidimusic && nodigimusic) || dedicated) - return; - #ifdef HAVE_MIXER MIX_VERSION(&MIXcompiled) MIXlinked = Mix_Linked_Version(); diff --git a/src/sounds.c b/src/sounds.c index 293ce381d..35b21e590 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -65,7 +65,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"buzz1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Electric zap"}, {"buzz2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Electric zap"}, {"buzz3", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Wacky worksurface"}, - {"buzz4", false, 8, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Buzz"}, + {"buzz4", true, 8, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Buzz"}, {"crumbl", true, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Crumbling"}, // Platform Crumble Tails 03-16-2001 {"fire", false, 8, 32, -1, NULL, 0, -1, -1, LUMPERROR, "Flamethrower"}, {"grind", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Metallic grinding"}, @@ -76,6 +76,8 @@ sfxinfo_t S_sfx[NUMSFX] = {"steam1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Steam jet"}, // Tails 06-19-2001 {"steam2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Steam jet"}, // Tails 06-19-2001 {"wbreak", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Wood breaking"}, + {"ambmac", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Machinery"}, + {"spsmsh", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Heavy impact"}, {"rainin", true, 24, 4, -1, NULL, 0, -1, -1, LUMPERROR, "Rain"}, {"litng1", false, 16, 2, -1, NULL, 0, -1, -1, LUMPERROR, "Lightning"}, @@ -139,14 +141,14 @@ sfxinfo_t S_sfx[NUMSFX] = {"bnce1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bounce"}, // Boing! {"bnce2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Scatter"}, // Boing! {"cannon", false, 64, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Powerful shot"}, - {"cgot" , true, 120, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Got Chaos Emerald"}, // Got Emerald! Tails 09-02-2001 + {"cgot" , true, 120, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Got Emerald"}, // Got Emerald! Tails 09-02-2001 {"cybdth", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Explosion"}, - {"deton", true, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Ominous beeping"}, + {"deton", true, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Threatening beeping"}, {"ding", false, 127, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Ding"}, {"dmpain", false, 96, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Machine damage"}, {"drown", false, 192, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Drowning"}, {"fizzle", false, 127, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Electric fizzle"}, - {"gbeep", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Ominous beeping"}, // Grenade beep + {"gbeep", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Threatening beeping"}, // Grenade beep {"wepfir", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Firing weapon"}, // defaults to thok {"ghit" , false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Goop splash"}, {"gloop", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Splash"}, @@ -177,15 +179,17 @@ sfxinfo_t S_sfx[NUMSFX] = {"spring", false, 112, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spring"}, {"statu1", true, 64, 2, -1, NULL, 0, -1, -1, LUMPERROR, "Pushing a statue"}, {"statu2", true, 64, 2, -1, NULL, 0, -1, -1, LUMPERROR, "Pushing a statue"}, - {"strpst", true, 192, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Starpost"}, // Starpost Sound Tails 07-04-2002 + {"strpst", true, 192, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Starpost"}, {"supert", true, 127, 2, -1, NULL, 0, -1, -1, LUMPERROR, "Transformation"}, {"telept", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Dash"}, {"tink" , false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Tink"}, - {"token" , true, 224, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Got Token"}, // SS token + {"token" , true, 224, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Got Token"}, {"trfire", true, 60, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Laser fired"}, {"trpowr", true, 127, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Powering up"}, {"turhit", false, 40, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Laser hit"}, {"wdjump", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Whirlwind jump"}, + {"shrpsp", true, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spincushion"}, + {"shrpgo", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Launch"}, {"mswarp", false, 60, 16, -1, NULL, 0, -1, -1, LUMPERROR, "Spinning out"}, {"mspogo", false, 60, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Breaking through"}, {"boingf", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bouncing"}, @@ -210,11 +214,12 @@ sfxinfo_t S_sfx[NUMSFX] = {"xideya", false, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Success"}, // Xmas {"nbmper", false, 96, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bumper"}, {"nxbump", false, 96, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bumper"}, // Xmas + {"ncchip", false, 204, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Got chip"}, {"ncitem", false, 204, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Got special"}, {"nxitem", false, 204, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Got special"}, // Xmas {"ngdone", true, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bonus time start"}, {"nxdone", true, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bonus time start"}, // Xmas - {"drill1", false, 48, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Drill start"}, + {"drill1", false, 48, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Drill"}, {"drill2", false, 48, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Drill"}, {"ncspec", false, 204, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Power-up"}, // Tails 12-15-2003 {"nghurt", false, 96, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Hurt"}, @@ -224,19 +229,26 @@ sfxinfo_t S_sfx[NUMSFX] = {"hoop3", false, 192, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Hoop++"}, {"hidden", false, 204, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Discovery"}, {"prloop", false, 104, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Gust of wind"}, - {"timeup", true, 256, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Ominous Countdown"}, + {"ngjump", false, 96, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Jump"}, + {"peww", false, 96, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Pew"}, + + // Halloween + {"lntsit", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Cacolantern awake"}, + {"lntdie", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Cacolantern death"}, + {"pumpkn", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Pumpkin smash"}, // idspispopd + {"ghosty", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Laughter"}, // Mario {"koopfr" , true, 127, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Fire"}, - {"mario1", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Hitting a ceiling"}, - {"mario2", false, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Koopa shell"}, + {"mario1", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Hit"}, + {"mario2", false, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bonk"}, {"mario3", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Power-up"}, {"mario4", true, 78, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Got coin"}, - {"mario5", false, 78, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Boot"}, + {"mario5", false, 78, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Boot-stomp"}, {"mario6", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Jump"}, {"mario7", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Fire"}, {"mario8", false, 48, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Hurt"}, - {"mario9", true, 120, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Emerging"}, + {"mario9", true, 120, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Emerging power-up"}, {"marioa", true, 192, 0, -1, NULL, 0, -1, -1, LUMPERROR, "One-up"}, {"thwomp", true, 127, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Thwomp"}, @@ -287,7 +299,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"s3k3d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Pop"}, {"s3k3e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Flame Shield"}, {"s3k3f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bubble Shield"}, - {"s3k40", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Attraction shot"}, + {"s3k40", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Attraction blast"}, {"s3k41", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Lightning Shield"}, {"s3k42", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Twinspin"}, {"s3k43", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Flame burst"}, @@ -295,22 +307,22 @@ sfxinfo_t S_sfx[NUMSFX] = {"s3k45", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Lightning zap"}, {"s3k46", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Transformation"}, {"s3k47", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Rising dust"}, - {"s3k48", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Metallic clink"}, - {"s3k49", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Falling rock"}, + {"s3k48", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Pulse"}, + {"s3k49", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Impact"}, {"s3k4a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Grab"}, {"s3k4b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Water splash"}, {"s3k4c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Heavy hit"}, {"s3k4d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Firing bullet"}, - {"s3k4e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bomb explosion"}, + {"s3k4e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Big explosion"}, {"s3k4f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Flamethrower"}, {"s3k50", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Siren"}, - {"s3k51", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Falling bomb"}, + {"s3k51", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Falling hazard"}, {"s3k52", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spike"}, {"s3k53", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Powering up"}, {"s3k54", false, 64, 64, -1, NULL, 0, -1, -1, LUMPERROR, "Firing"}, // MetalSonic shot fire {"s3k55", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Mechanical movement"}, {"s3k56", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Heavy landing"}, - {"s3k57", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Splash"}, + {"s3k57", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Burst"}, {"s3k58", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Mechanical movement"}, {"s3k59", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Crumbling"}, {"s3k5a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Aiming"}, @@ -324,13 +336,13 @@ sfxinfo_t S_sfx[NUMSFX] = {"s3k62", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Jump"}, {"s3k63", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Starpost"}, {"s3k64", false, 64, 2, -1, NULL, 0, -1, -1, LUMPERROR, "Clatter"}, - {"s3k65", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Got blue sphere"}, // Blue Spheres - {"s3k66", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Special stage clear"}, + {"s3k65", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Got sphere"}, // Blue Spheres + {"s3k66", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Special stage end"}, {"s3k67", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Firing missile"}, - {"s3k68", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Unknown possibilities"}, + {"s3k68", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Discovery"}, {"s3k69", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Switch click"}, {"s3k6a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Special stage clear"}, - {"s3k6b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s3k6b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Punch"}, {"s3k6c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Burst"}, {"s3k6d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"s3k6e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Mechanical damage"}, @@ -363,16 +375,16 @@ sfxinfo_t S_sfx[NUMSFX] = {"s3k89", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Advanced technology"}, {"s3k8a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Boing"}, {"s3k8b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Powerful hit"}, - {"s3k8c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s3k8c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Humming power"}, {"s3k8d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, - {"s3k8e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"s3k8e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Accelerating"}, {"s3k8f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Opening"}, {"s3k90", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Impact"}, {"s3k91", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Closed"}, {"s3k92", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Ghost"}, {"s3k93", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Rebuilding"}, {"s3k94", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spike"}, - {"s3k95", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Rising from lava"}, + {"s3k95", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Lava burst"}, {"s3k96", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Falling object"}, {"s3k97", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Wind"}, {"s3k98", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Falling spike"}, @@ -429,8 +441,8 @@ sfxinfo_t S_sfx[NUMSFX] = {"s3kc3l", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Levitation"}, // ditto {"s3kc4s", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Firing laser"}, {"s3kc4l", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Firing laser"}, // ditto - {"s3kc5s", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, - {"s3kc5l", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // ditto + {"s3kc5s", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Revving up"}, + {"s3kc5l", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Revving up"}, // ditto {"s3kc6s", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Orbiting"}, {"s3kc6l", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Orbiting"}, // ditto {"s3kc7", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Aiming"}, @@ -455,7 +467,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"s3kd1s", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"s3kd1l", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // ditto {"s3kd2s", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Turning"}, - {"s3kd2l", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Turning"}, // ditto + {"s3kd2l", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Moving chain"}, // ditto {"s3kd3s", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"s3kd3l", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // ditto {"s3kd4s", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Engine"}, diff --git a/src/sounds.h b/src/sounds.h index 60451b6ea..5fffc7b2e 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -142,6 +142,8 @@ typedef enum sfx_steam1, sfx_steam2, sfx_wbreak, + sfx_ambmac, + sfx_spsmsh, sfx_rainin, sfx_litng1, @@ -252,6 +254,8 @@ typedef enum sfx_trpowr, sfx_turhit, sfx_wdjump, + sfx_shrpsp, + sfx_shrpgo, sfx_mswarp, sfx_mspogo, sfx_boingf, @@ -276,6 +280,7 @@ typedef enum sfx_xideya, // Xmas sfx_nbmper, sfx_nxbump, // Xmas + sfx_ncchip, sfx_ncitem, sfx_nxitem, // Xmas sfx_ngdone, @@ -290,7 +295,14 @@ typedef enum sfx_hoop3, sfx_hidden, sfx_prloop, - sfx_timeup, // Was gonna be played when less than ten seconds are on the clock; uncomment uses of this to see it in-context + sfx_ngjump, + sfx_peww, + + // Halloween + sfx_lntsit, + sfx_lntdie, + sfx_pumpkn, + sfx_ghosty, // Mario sfx_koopfr, diff --git a/src/st_stuff.c b/src/st_stuff.c index 3b07d375e..3ecd0acdf 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -27,6 +27,8 @@ #include "i_system.h" #include "m_menu.h" #include "m_cheat.h" +#include "m_misc.h" // moviemode +#include "m_anigif.h" // cv_gif_downscale #include "p_setup.h" // NiGHTS grading //random index @@ -110,6 +112,8 @@ static patch_t *yelstat; static patch_t *nbracket; static patch_t *nhud[12]; static patch_t *nsshud; +static patch_t *nbon[12]; +static patch_t *nssbon; static patch_t *narrow[9]; static patch_t *nredar[8]; // Red arrow static patch_t *drillbar; @@ -200,17 +204,17 @@ void ST_doPaletteStuff(void) else palette = 0; +#ifdef HWRENDER + if (rendermode == render_opengl) + palette = 0; // No flashpals here in OpenGL +#endif + palette = min(max(palette, 0), 13); if (palette != st_palette) { st_palette = palette; -#ifdef HWRENDER - if (rendermode == render_opengl) - HWR_SetPaletteColor(0); - else -#endif if (rendermode != render_none) { V_SetPaletteLump(GetPalette()); // Reset the palette @@ -307,8 +311,12 @@ void ST_LoadGraphics(void) yelstat = W_CachePatchName("YELSTAT", PU_HUDGFX); nbracket = W_CachePatchName("NBRACKET", PU_HUDGFX); for (i = 0; i < 12; ++i) + { nhud[i] = W_CachePatchName(va("NHUD%d", i+1), PU_HUDGFX); + nbon[i] = W_CachePatchName(va("NBON%d", i+1), PU_HUDGFX); + } nsshud = W_CachePatchName("NSSHUD", PU_HUDGFX); + nssbon = W_CachePatchName("NSSBON", PU_HUDGFX); minicaps = W_CachePatchName("MINICAPS", PU_HUDGFX); for (i = 0; i < 8; ++i) @@ -460,7 +468,7 @@ static void ST_drawDebugInfo(void) #define VFLAGS V_MONOSPACE|V_SNAPTOTOP|V_SNAPTORIGHT - if (vid.dupx == 1) + if ((moviemode == MM_GIF && cv_gif_downscale.value) || vid.dupx == 1) { if (cv_debug & DBG_BASIC) { @@ -657,9 +665,9 @@ static void ST_drawTime(void) else { // Counting down the hidetime? - if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (leveltime <= (hidetime*TICRATE))) + if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (stplyr->realtime <= (hidetime*TICRATE))) { - tics = (hidetime*TICRATE - leveltime); + tics = (hidetime*TICRATE - stplyr->realtime); if (tics < 3*TICRATE) ST_drawRaceNum(tics); downwards = true; @@ -667,15 +675,15 @@ static void ST_drawTime(void) else { // Hidetime finish! - if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (leveltime < ((hidetime+1)*TICRATE))) - ST_drawRaceNum(hidetime*TICRATE - leveltime); + if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (stplyr->realtime < ((hidetime+1)*TICRATE))) + ST_drawRaceNum(hidetime*TICRATE - stplyr->realtime); // Time limit? - if (gametype != GT_RACE && gametype != GT_COMPETITION && gametype != GT_COOP && cv_timelimit.value && timelimitintics > 0) + if (gametype != GT_COOP && gametype != GT_RACE && gametype != GT_COMPETITION && cv_timelimit.value && timelimitintics > 0) { - if (timelimitintics >= leveltime) + if (timelimitintics >= stplyr->realtime) { - tics = (timelimitintics - leveltime); + tics = (timelimitintics - stplyr->realtime); if (tics < 3*TICRATE) ST_drawRaceNum(tics); } @@ -733,18 +741,7 @@ static inline void ST_drawRings(void) ST_DrawPatchFromHud(HUD_RINGS, ((!stplyr->spectator && stplyr->rings <= 0 && leveltime/5 & 1) ? sboredrings : sborings), ((stplyr->spectator) ? V_HUDTRANSHALF : V_HUDTRANS)); - if (objectplacing) - ringnum = op_currentdoomednum; - else if (!useNightsSS && G_IsSpecialStage(gamemap)) - { - INT32 i; - ringnum = 0; - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && players[i].mo && players[i].rings > 0) - ringnum += players[i].rings; - } - else - ringnum = max(stplyr->rings, 0); + ringnum = ((objectplacing) ? op_currentdoomednum : max(stplyr->rings, 0)); if (cv_timetic.value == 2) // Yes, even in modeattacking ST_DrawNumFromHud(HUD_RINGSNUMTICS, ringnum, V_PERPLAYER|((stplyr->spectator) ? V_HUDTRANSHALF : V_HUDTRANS)); @@ -1096,7 +1093,8 @@ static void ST_drawInput(void) ((!stplyr->powers[pw_carry] && (stplyr->pflags & PF_APPLYAUTOBRAKE) && !(stplyr->cmd.sidemove || stplyr->cmd.forwardmove) - && (stplyr->rmomx || stplyr->rmomy)) + && (stplyr->rmomx || stplyr->rmomy) + && (!stplyr->capsule || (stplyr->capsule->reactiontime != (stplyr-players)+1))) ? 0 : V_GRAYMAP), "AUTOBRAKE"); y -= 8; @@ -1392,17 +1390,17 @@ static void ST_drawNightsRecords(void) 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" : "RING", + (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:" : "RINGS:"); + 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->finishedrings)); - V_DrawRightAlignedString(BASEVIDWIDTH/2 + 56, 140, V_ORANGEMAP|aflag, va("%d", stplyr->finishedrings * 50)); + 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! @@ -1460,15 +1458,15 @@ static void ST_drawNiGHTSHUD(void) { INT32 origamount; INT32 minlink = 1; - INT32 total_ringcount; - - // When debugging, show "0 Link". - if (cv_debug & DBG_NIGHTSBASIC) - minlink = 0; + 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 (stplyr->texttimer && stplyr->textvar == 4) + 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 ( @@ -1574,25 +1572,37 @@ static void ST_drawNiGHTSHUD(void) // Begin drawing brackets/chip display #ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_nightsrings)) + if (LUA_HudEnabled(hud_nightsspheres)) { #endif ST_DrawTopLeftOverlayPatch(16, 8, nbracket); if (G_IsSpecialStage(gamemap)) - ST_DrawTopLeftOverlayPatch(24, 16, nsshud); + ST_DrawTopLeftOverlayPatch(24, 16, ( +#ifdef MANIASPHERES + (stplyr->bonustime && (leveltime & 4)) ? nssbon : +#endif + nsshud)); else - ST_DrawTopLeftOverlayPatch(24, 16, nhud[(leveltime/2)%12]); + ST_DrawTopLeftOverlayPatch(24, 16, *(((stplyr->bonustime) ? nbon : nhud)+((leveltime/2)%12))); if (G_IsSpecialStage(gamemap)) { INT32 i; - total_ringcount = 0; + total_spherecount = 0; for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] /*&& players[i].powers[pw_carry] == CR_NIGHTSMODE*/ && players[i].rings) - total_ringcount += players[i].rings; + if (playeringame[i] /*&& players[i].powers[pw_carry] == CR_NIGHTSMODE*/ && players[i].spheres) + total_spherecount += players[i].spheres; } else - total_ringcount = stplyr->rings; + total_spherecount = stplyr->spheres; + + /*if (oldspecialstage) + { + if (total_spherecount < ssspheres) + total_spherecount = ssspheres - total_spherecount; + else + total_spherecount = 0; + }*/ if (stplyr->capsule) { @@ -1648,28 +1658,48 @@ static void ST_drawNiGHTSHUD(void) amount = (origamount - stplyr->capsule->health); amount = (amount * length)/origamount; - for (cfill = 0; cfill < amount && cfill < 88; ++cfill) + for (cfill = 0; cfill < amount && cfill < length; ++cfill) V_DrawScaledPatch(15 + cfill + 1, 8 + 35, V_PERPLAYER|V_SNAPTOLEFT|V_SNAPTOTOP|V_HUDTRANS, capsulefill); } - if (total_ringcount >= stplyr->capsule->health) - ST_DrawTopLeftOverlayPatch(40, 8 + 5, nredar[leveltime%8]); + if (total_spherecount >= stplyr->capsule->health) + ST_DrawTopLeftOverlayPatch(40, 8 + 5, nredar[leveltime&7]); else - ST_DrawTopLeftOverlayPatch(40, 8 + 5, narrow[(leveltime/2)%8]); + ST_DrawTopLeftOverlayPatch(40, 8 + 5, narrow[(leveltime/2)&7]); + } + else if (oldspecialstage && total_spherecount < (INT32)ssspheres) + { + INT32 cfill, amount; + const INT32 length = 88; + UINT8 em = P_GetNextEmerald(); + ST_DrawTopLeftOverlayPatch(72, 8, nbracket); + + if (em <= 7) + ST_DrawTopLeftOverlayPatch(80, 8 + 8, emeraldpics[0][em]); + + ST_DrawTopLeftOverlayPatch(40, 8 + 5, narrow[(leveltime/2)&7]); + + // Lil' white box! + V_DrawScaledPatch(15, 8 + 34, V_PERPLAYER|V_SNAPTOLEFT|V_SNAPTOTOP|V_HUDTRANS, capsulebar); + + amount = (total_spherecount * length)/ssspheres; + + for (cfill = 0; cfill < amount && cfill < length; ++cfill) + V_DrawScaledPatch(15 + cfill + 1, 8 + 35, V_PERPLAYER|V_SNAPTOLEFT|V_SNAPTOTOP|V_HUDTRANS, capsulefill); } else ST_DrawTopLeftOverlayPatch(40, 8 + 5, narrow[8]); - if (total_ringcount >= 100) - V_DrawTallNum((total_ringcount >= 1000) ? 76 : 72, 8 + 11, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTOLEFT|V_HUDTRANS, total_ringcount); + if (total_spherecount >= 100) + V_DrawTallNum((total_spherecount >= 1000) ? 76 : 72, 8 + 11, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTOLEFT|V_HUDTRANS, total_spherecount); else - V_DrawTallNum(68, 8 + 11, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTOLEFT|V_HUDTRANS, total_ringcount); + V_DrawTallNum(68, 8 + 11, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTOLEFT|V_HUDTRANS, total_spherecount); #ifdef HAVE_BLUA } #endif // Score - if (!stplyr->exiting + if (!stplyr->exiting && !oldspecialstage #ifdef HAVE_BLUA && LUA_HudEnabled(hud_nightsscore) #endif @@ -1712,6 +1742,7 @@ static void ST_drawNiGHTSHUD(void) { INT32 realnightstime = stplyr->nightstime/TICRATE; INT32 numbersize; + UINT8 col = ((realnightstime < 10) ? SKINCOLOR_RED : SKINCOLOR_SUPERGOLD4); if (G_IsSpecialStage(gamemap)) { @@ -1742,46 +1773,66 @@ static void ST_drawNiGHTSHUD(void) else numbersize = 48/2; - ST_DrawNightsOverlayNum((160 + numbersize)<mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER))) + col = SKINCOLOR_ORANGE; + + ST_DrawNightsOverlayNum((160 + numbersize)<nightstime))); } - // Show pickup durations - if (cv_debug & DBG_NIGHTSBASIC) + if (oldspecialstage) { - UINT16 pwr; - - if (stplyr->powers[pw_nights_superloop]) + if (leveltime < 5*TICRATE) { - pwr = stplyr->powers[pw_nights_superloop]; - V_DrawSmallScaledPatch(110, 44, 0, W_CachePatchName("NPRUA0",PU_CACHE)); - V_DrawThinString(106, 52, V_MONOSPACE, va("%2d.%02d", pwr/TICRATE, G_TicsToCentiseconds(pwr))); - } - - if (stplyr->powers[pw_nights_helper]) - { - pwr = stplyr->powers[pw_nights_helper]; - V_DrawSmallScaledPatch(150, 44, 0, W_CachePatchName("NPRUC0",PU_CACHE)); - V_DrawThinString(146, 52, V_MONOSPACE, va("%2d.%02d", pwr/TICRATE, G_TicsToCentiseconds(pwr))); - } - - if (stplyr->powers[pw_nights_linkfreeze]) - { - pwr = stplyr->powers[pw_nights_linkfreeze]; - V_DrawSmallScaledPatch(190, 44, 0, W_CachePatchName("NPRUE0",PU_CACHE)); - V_DrawThinString(186, 52, V_MONOSPACE, va("%2d.%02d", pwr/TICRATE, G_TicsToCentiseconds(pwr))); + INT32 aflag = V_PERPLAYER; + tic_t drawtime = (5*TICRATE) - leveltime; + if (drawtime < TICRATE/2) + aflag |= (9 - 9*drawtime/(TICRATE/2)) << V_ALPHASHIFT; + // This one, not quite as much so. + V_DrawCenteredString(BASEVIDWIDTH/2, 60, aflag, + va(M_GetText("\x80GET\x82 %d\x80 SPHERE%s!"), ssspheres, + (ssspheres > 1) ? "S" : "")); } } + else + { + // Show pickup durations + if (cv_debug & DBG_NIGHTSBASIC) + { + UINT16 pwr; - // Records/extra text + if (stplyr->powers[pw_nights_superloop]) + { + pwr = stplyr->powers[pw_nights_superloop]; + V_DrawSmallScaledPatch(110, 44, 0, W_CachePatchName("NPRUA0",PU_CACHE)); + V_DrawThinString(106, 52, V_MONOSPACE, va("%2d.%02d", pwr/TICRATE, G_TicsToCentiseconds(pwr))); + } + + if (stplyr->powers[pw_nights_helper]) + { + pwr = stplyr->powers[pw_nights_helper]; + V_DrawSmallScaledPatch(150, 44, 0, W_CachePatchName("NPRUC0",PU_CACHE)); + V_DrawThinString(146, 52, V_MONOSPACE, va("%2d.%02d", pwr/TICRATE, G_TicsToCentiseconds(pwr))); + } + + if (stplyr->powers[pw_nights_linkfreeze]) + { + pwr = stplyr->powers[pw_nights_linkfreeze]; + V_DrawSmallScaledPatch(190, 44, 0, W_CachePatchName("NPRUE0",PU_CACHE)); + V_DrawThinString(186, 52, V_MONOSPACE, va("%2d.%02d", pwr/TICRATE, G_TicsToCentiseconds(pwr))); + } + } + + // Records/extra text #ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_nightsrecords)) + if (LUA_HudEnabled(hud_nightsrecords)) #endif - ST_drawNightsRecords(); + ST_drawNightsRecords(); + } } static inline void ST_drawWeaponSelect(INT32 xoffs, INT32 y) @@ -1950,7 +2001,7 @@ static void ST_drawTextHUD(void) } else if (stplyr->spectator && (gametype != GT_COOP || stplyr->playerstate == PST_LIVE)) { - if (G_IsSpecialStage(gamemap) && useNightsSS) + if (G_IsSpecialStage(gamemap) && (maptol & TOL_NIGHTS)) textHUDdraw(M_GetText("\x82""Wait for the stage to end...")) else if (gametype == GT_COOP) { @@ -2068,22 +2119,22 @@ num: #undef SEP } -static void ST_drawSpecialStageHUD(void) +/*static void ST_drawSpecialStageHUD(void) { - if (totalrings > 0) + if (ssspheres > 0) { if (hudinfo[HUD_SS_TOTALRINGS].x) - ST_DrawNumFromHud(HUD_SS_TOTALRINGS, totalrings, V_HUDTRANS); + ST_DrawNumFromHud(HUD_SS_TOTALRINGS, ssspheres, V_HUDTRANS); else if (cv_timetic.value == 2) - V_DrawTallNum(hudinfo[HUD_RINGSNUMTICS].x, hudinfo[HUD_SS_TOTALRINGS].y, hudinfo[HUD_RINGSNUMTICS].f|V_PERPLAYER|V_HUDTRANS, totalrings); + V_DrawTallNum(hudinfo[HUD_RINGSNUMTICS].x, hudinfo[HUD_SS_TOTALRINGS].y, hudinfo[HUD_RINGSNUMTICS].f|V_PERPLAYER|V_HUDTRANS, ssspheres); else - V_DrawTallNum(hudinfo[HUD_RINGSNUM].x, hudinfo[HUD_SS_TOTALRINGS].y, hudinfo[HUD_RINGSNUM].f|V_PERPLAYER|V_HUDTRANS, totalrings); + V_DrawTallNum(hudinfo[HUD_RINGSNUM].x, hudinfo[HUD_SS_TOTALRINGS].y, hudinfo[HUD_RINGSNUM].f|V_PERPLAYER|V_HUDTRANS, ssspheres); } - if (leveltime < 5*TICRATE && totalrings > 0) + if (leveltime < 5*TICRATE && ssspheres > 0) { ST_DrawPatchFromHud(HUD_GETRINGS, getall, V_HUDTRANS); - ST_DrawNumFromHud(HUD_GETRINGSNUM, totalrings, V_HUDTRANS); + ST_DrawNumFromHud(HUD_GETRINGSNUM, ssspheres, V_HUDTRANS); } if (sstimer) @@ -2093,7 +2144,7 @@ static void ST_drawSpecialStageHUD(void) } else ST_DrawPatchFromHud(HUD_TIMEUP, timeup, V_HUDTRANS); -} +}*/ static INT32 ST_drawEmeraldHuntIcon(mobj_t *hunt, patch_t **patches, INT32 offset) { @@ -2229,7 +2280,7 @@ static void ST_overlayDrawer(void) //hu_showscores = auto hide score/time/rings when tab rankings are shown if (!(hu_showscores && (netgame || multiplayer))) { - if (maptol & TOL_NIGHTS) + if (maptol & TOL_NIGHTS || G_IsSpecialStage(gamemap)) ST_drawNiGHTSHUD(); else { @@ -2338,10 +2389,6 @@ static void ST_overlayDrawer(void) if (gametype == GT_RACE || gametype == GT_COMPETITION) ST_drawRaceHUD(); - // Special Stage HUD - if (!useNightsSS && G_IsSpecialStage(gamemap) && stplyr == &players[displayplayer]) - ST_drawSpecialStageHUD(); - // Emerald Hunt Indicators if (cv_itemfinder.value && M_SecretUnlocked(SECRET_ITEMFINDER)) ST_doItemFinderIconsAndSound(); @@ -2360,15 +2407,18 @@ static void ST_overlayDrawer(void) } // This is where we draw all the fun cheese if you have the chasecam off! - if ((stplyr == &players[displayplayer] && !camera.chase) - || ((splitscreen && stplyr == &players[secondarydisplayplayer]) && !camera2.chase)) + if (!(maptol & TOL_NIGHTS)) { - ST_drawFirstPersonHUD(); - if (cv_powerupdisplay.value) + if ((stplyr == &players[displayplayer] && !camera.chase) + || ((splitscreen && stplyr == &players[secondarydisplayplayer]) && !camera2.chase)) + { + ST_drawFirstPersonHUD(); + if (cv_powerupdisplay.value) + ST_drawPowerupHUD(); + } + else if (cv_powerupdisplay.value == 2) ST_drawPowerupHUD(); } - else if (cv_powerupdisplay.value == 2) - ST_drawPowerupHUD(); } #ifdef HAVE_BLUA diff --git a/src/v_video.c b/src/v_video.c index 6ad6c08aa..d2c645d55 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1624,7 +1624,7 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string) { INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, center = 0, left = 0; const char *ch = string; - INT32 charflags = 0; + INT32 charflags = (option & V_CHARCOLORMASK); const UINT8 *colormap = NULL; INT32 spacewidth = 4, charwidth = 0; @@ -1644,8 +1644,6 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string) left = (scrwidth - BASEVIDWIDTH)/2; } - charflags = (option & V_CHARCOLORMASK); - switch (option & V_SPACINGMASK) { case V_MONOSPACE: @@ -2154,8 +2152,10 @@ 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; + INT32 charflags = (option & V_CHARCOLORMASK); + const UINT8 *colormap = NULL; if (option & V_NOSCALESTART) { @@ -2164,21 +2164,31 @@ void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string) scrwidth = vid.width; } else - dupx = dupy = 1; - - for (;;) { - c = *ch++; - if (!c) + dupx = dupy = 1; + scrwidth = vid.width/vid.dupx; + left = (scrwidth - BASEVIDWIDTH)/2; + } + + for (;;ch++) + { + if (!*ch) break; - if (c == '\n') + if (*ch & 0x80) //color parsing -x 2.16.09 + { + // manually set flags override color codes + if (!(option & V_CHARCOLORMASK)) + charflags = ((*ch & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK; + continue; + } + if (*ch == '\n') { cx = x; cy += 12*dupy; continue; } - c = toupper(c) - LT_FONTSTART; + c = toupper(*ch) - LT_FONTSTART; if (c < 0 || c >= LT_FONTSIZE || !lt_font[c]) { cx += 16*dupx; @@ -2186,17 +2196,19 @@ void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string) } w = SHORT(lt_font[c]->width) * dupx; - if (cx + w > scrwidth) - break; + if (cx+left > scrwidth) + break; //left boundary check - if (cx < 0) + if (cx+left + w < 0) { cx += w; continue; } - V_DrawScaledPatch(cx, cy, option, lt_font[c]); + colormap = V_GetStringColormap(charflags); + V_DrawFixedPatch(cx<= LT_FONTSIZE || !lt_font[c]) w += 16; @@ -2265,11 +2279,9 @@ INT32 V_StringWidth(const char *string, INT32 option) for (i = 0; i < strlen(string); i++) { - c = string[i]; - if ((UINT8)c >= 0x80 && (UINT8)c <= 0x89) //color parsing! -Inuyasha 2.16.09 + if (string[i] & 0x80) continue; - - c = toupper(c) - HU_FONTSTART; + c = toupper(string[i]) - HU_FONTSTART; if (c < 0 || c >= HU_FONTSIZE || !hu_font[c]) w += spacewidth; else @@ -2307,11 +2319,9 @@ INT32 V_SmallStringWidth(const char *string, INT32 option) for (i = 0; i < strlen(string); i++) { - c = string[i]; - if ((UINT8)c >= 0x80 && (UINT8)c <= 0x89) //color parsing! -Inuyasha 2.16.09 + if (string[i] & 0x80) continue; - - c = toupper(c) - HU_FONTSTART; + c = toupper(string[i]) - HU_FONTSTART; if (c < 0 || c >= HU_FONTSIZE || !hu_font[c]) w += spacewidth; else @@ -2346,11 +2356,9 @@ INT32 V_ThinStringWidth(const char *string, INT32 option) for (i = 0; i < strlen(string); i++) { - c = string[i]; - if ((UINT8)c >= 0x80 && (UINT8)c <= 0x89) //color parsing! -Inuyasha 2.16.09 + if (string[i] & 0x80) continue; - - c = toupper(c) - HU_FONTSTART; + c = toupper(string[i]) - HU_FONTSTART; if (c < 0 || c >= HU_FONTSIZE || !tny_font[c]) w += spacewidth; else diff --git a/src/w_wad.c b/src/w_wad.c index 1b0e501a6..7babd22ef 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -194,11 +194,11 @@ static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum) for (; posStart < posEnd; posStart++) LUA_LoadLump(wadnum, posStart); } - posStart = W_CheckNumForFolderStartPK3("SOCs/", wadnum, 0); + posStart = W_CheckNumForFolderStartPK3("SOC/", wadnum, 0); if (posStart != INT16_MAX) { - posEnd = W_CheckNumForFolderEndPK3("SOCs/", wadnum, posStart); - posStart++; // first "lump" will be "SOCs/" folder itself, so ignore it + posEnd = W_CheckNumForFolderEndPK3("SOC/", wadnum, posStart); + posStart++; // first "lump" will be "SOC/" folder itself, so ignore it for(; posStart < posEnd; posStart++) { lumpinfo_t *lump_p = &wadfiles[wadnum]->lumpinfo[posStart]; diff --git a/src/y_inter.c b/src/y_inter.c index ed9cc4185..68dda198c 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -77,8 +77,8 @@ typedef union struct { - char passed1[29]; // KNUCKLES GOT / CRAWLA HONCHO - char passed2[17]; // A CHAOS EMERALD / GOT THEM ALL! + char passed1[29]; // KNUCKLES GOT / CRAWLA HONCHO + char passed2[17]; // A CHAOS EMERALD? / GOT THEM ALL! char passed3[15]; // CAN NOW BECOME char passed4[SKINNAMESIZE+7]; // SUPER CRAWLA HONCHO INT32 passedx1; @@ -315,6 +315,9 @@ void Y_IntermissionDrawer(void) INT32 xoffset1 = 0; // Line 1 x offset INT32 xoffset2 = 0; // Line 2 x offset INT32 xoffset3 = 0; // Line 3 x offset + INT32 xoffset4 = 0; // Line 4 x offset + INT32 xoffset5 = 0; // Line 5 x offset + INT32 xoffset6 = 0; // Line 6 x offset UINT8 drawsection = 0; if (gottoken) // first to be behind everything else @@ -324,71 +327,130 @@ void Y_IntermissionDrawer(void) if (intertic <= 2*TICRATE) animatetic = 0; else if (!animatetic && data.spec.bonus.points == 0 && data.spec.passed3[0] != '\0') - animatetic = intertic; + animatetic = intertic + TICRATE; - if (animatetic) + if (animatetic && (tic_t)intertic >= animatetic) { + const INT32 scradjust = (vid.width/vid.dupx)>>3; // 40 for BASEVIDWIDTH INT32 animatetimer = (intertic - animatetic); - if (animatetimer <= 8) + if (animatetimer <= 14) { - xoffset1 = -(animatetimer * 40); - xoffset2 = -((animatetimer-2) * 40); + xoffset1 = -(animatetimer * scradjust); + xoffset2 = -((animatetimer-2) * scradjust); + xoffset3 = -((animatetimer-4) * scradjust); + xoffset4 = -((animatetimer-6) * scradjust); + xoffset5 = -((animatetimer-8) * scradjust); if (xoffset2 > 0) xoffset2 = 0; + if (xoffset3 > 0) xoffset3 = 0; + if (xoffset4 > 0) xoffset4 = 0; + if (xoffset5 > 0) xoffset5 = 0; } - else if (animatetimer <= 19) + else if (animatetimer < 32) { drawsection = 1; - xoffset1 = (16-animatetimer) * 40; - xoffset2 = (18-animatetimer) * 40; - xoffset3 = (20-animatetimer) * 40; + xoffset1 = (22-animatetimer) * scradjust; + xoffset2 = (24-animatetimer) * scradjust; + xoffset3 = (26-animatetimer) * scradjust; + xoffset4 = (28-animatetimer) * scradjust; + xoffset5 = (30-animatetimer) * scradjust; + xoffset6 = (32-animatetimer) * scradjust; if (xoffset1 < 0) xoffset1 = 0; if (xoffset2 < 0) xoffset2 = 0; + if (xoffset3 < 0) xoffset3 = 0; + if (xoffset4 < 0) xoffset4 = 0; + if (xoffset5 < 0) xoffset5 = 0; } else + { drawsection = 1; + if (animatetimer == 32) + S_StartSound(NULL, sfx_s3k68); + } } if (drawsection == 1) { + const char *ringtext = "\x86" "50 RINGS, NO SHIELD"; + const char *tut1text = "\x86" "PRESS " "\x82" "SPIN"; + const char *tut2text = "\x86" "MID-" "\x82" "JUMP"; ttheight = 16; V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1); ttheight += V_LevelNameHeight(data.spec.passed3) + 2; V_DrawLevelTitle(data.spec.passedx3 + xoffset2, ttheight, 0, data.spec.passed3); ttheight += V_LevelNameHeight(data.spec.passed4) + 2; V_DrawLevelTitle(data.spec.passedx4 + xoffset3, ttheight, 0, data.spec.passed4); - } - else if (data.spec.passed1[0] != '\0') - { - ttheight = 24; - V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1); - ttheight += V_LevelNameHeight(data.spec.passed2) + 2; - V_DrawLevelTitle(data.spec.passedx2 + xoffset2, ttheight, 0, data.spec.passed2); + + ttheight = 108; + V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset4 - (V_LevelNameWidth(ringtext)/2), ttheight, 0, ringtext); + ttheight += V_LevelNameHeight(ringtext) + 2; + V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset5 - (V_LevelNameWidth(tut1text)/2), ttheight, 0, tut1text); + ttheight += V_LevelNameHeight(tut1text) + 2; + V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset6 - (V_LevelNameWidth(tut2text)/2), ttheight, 0, tut2text); } else { - ttheight = 24 + (V_LevelNameHeight(data.spec.passed2)/2) + 2; - V_DrawLevelTitle(data.spec.passedx2 + xoffset1, ttheight, 0, data.spec.passed2); + if (data.spec.passed1[0] != '\0') + { + ttheight = 24; + V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1); + ttheight += V_LevelNameHeight(data.spec.passed2) + 2; + V_DrawLevelTitle(data.spec.passedx2 + xoffset2, ttheight, 0, data.spec.passed2); + } + else + { + ttheight = 24 + (V_LevelNameHeight(data.spec.passed2)/2) + 2; + V_DrawLevelTitle(data.spec.passedx2 + xoffset1, ttheight, 0, data.spec.passed2); + } + + V_DrawScaledPatch(152 + xoffset3, 108, 0, data.spec.bonuspatch); + V_DrawTallNum(BASEVIDWIDTH + xoffset3 - 68, 109, 0, data.spec.bonus.points); + V_DrawScaledPatch(152 + xoffset4, 124, 0, data.spec.pscore); + V_DrawTallNum(BASEVIDWIDTH + xoffset4 - 68, 125, 0, data.spec.score); + + // Draw continues! + if (!multiplayer /* && (data.spec.continues & 0x80) */) // Always draw outside of netplay + { + UINT8 continues = data.spec.continues & 0x7F; + + V_DrawScaledPatch(152 + xoffset5, 150, 0, data.spec.pcontinues); + for (i = 0; i < continues; ++i) + { + if ((data.spec.continues & 0x80) && i == continues-1 && (endtic < 0 || intertic%20 < 10)) + break; + V_DrawContinueIcon(246 + xoffset5 - (i*12), 162, 0, *data.spec.playerchar, *data.spec.playercolor); + } + } } // draw the emeralds //if (intertic & 1) { + boolean drawthistic = !(ALL7EMERALDS(emeralds) && (intertic & 1)); INT32 emeraldx = 152 - 3*28; - INT32 em = (gamemap - sstage_start); + INT32 em = P_GetNextEmerald(); - for (i = 0; i < 7; ++i) + if (em == 7) { - if ((i != em) && !(intertic & 1) && (emeralds & (1 << i))) - V_DrawScaledPatch(emeraldx, 74, 0, emeraldpics[0][i]); - emeraldx += 28; + if (!stagefailed) + { + fixed_t adjust = 2*(FINESINE(FixedAngle((intertic + 1)<<(FRACBITS-4)) & FINEMASK)); + V_DrawFixedPatch(152< (3*TICRATE)/2) { endtic = intertic + 4*TICRATE; // 4 second pause after end of tally - S_StartSound(NULL, sfx_s3kac); // bingly-bingly-bing! + if (data.spec.continues & 0x80) + S_StartSound(NULL, sfx_s3kac); // bingly-bingly-bing! + } return; } @@ -887,7 +938,7 @@ void Y_Ticker(void) if (!data.spec.bonus.points) { tallydonetic = intertic; - if (!(data.spec.continues & 0x80)) // don't set endtic yet! + if (!((data.spec.continues & 0x80) || (super && ALL7EMERALDS(emeralds)))) // don't set endtic yet! endtic = intertic + 4*TICRATE; // 4 second pause after end of tally S_StartSound(NULL, (gottoken ? sfx_token : sfx_chchng)); // cha-ching! @@ -896,7 +947,7 @@ void Y_Ticker(void) if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && !demoplayback) { if (M_UpdateUnlockablesAndExtraEmblems()) - S_StartSound(NULL, sfx_ncitem); + S_StartSound(NULL, sfx_s3k68); G_SaveGameData(); } @@ -1286,7 +1337,7 @@ void Y_StartIntermission(void) data.spec.passed1[sizeof data.spec.passed1 - 1] = '\0'; strcpy(data.spec.passed2, "GOT THEM ALL!"); - if (skins[players[consoleplayer].skin].flags & SF_SUPER) + if (players[consoleplayer].charflags & SF_SUPER) { strcpy(data.spec.passed3, "CAN NOW BECOME"); snprintf(data.spec.passed4, @@ -1307,6 +1358,11 @@ void Y_StartIntermission(void) else strcpy(data.spec.passed1, "YOU GOT"); strcpy(data.spec.passed2, "A CHAOS EMERALD"); + if (P_GetNextEmerald() > 6) + { + data.spec.passed2[15] = '?'; + data.spec.passed2[16] = '\0'; + } } data.spec.passedx1 = (BASEVIDWIDTH - V_LevelNameWidth(data.spec.passed1))/2; data.spec.passedx2 = (BASEVIDWIDTH - V_LevelNameWidth(data.spec.passed2))/2; @@ -1745,7 +1801,7 @@ static void Y_SetPerfectBonus(player_t *player, y_bonus_t *bstruct) if (!playeringame[i]) continue; sharedringtotal += players[i].rings; } - if (!sharedringtotal || sharedringtotal < nummaprings) + if (!sharedringtotal || nummaprings == -1 || sharedringtotal < nummaprings) data.coop.gotperfbonus = 0; else data.coop.gotperfbonus = 1; @@ -1857,7 +1913,7 @@ static void Y_AwardSpecialStageBonus(void) if (!playeringame[i] || players[i].lives < 1) // not active or game over Y_SetNullBonus(&players[i], &localbonus); - else if (useNightsSS) // Link instead of Score + else if (maptol & TOL_NIGHTS) // Link instead of Rings Y_SetLinkBonus(&players[i], &localbonus); else Y_SetRingBonus(&players[i], &localbonus);