From 420a6ccccb01fb94a246bf428d39c9ddb70cc353 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 28 Feb 2021 22:39:47 -0500 Subject: [PATCH] Fix Dehacked loading --- src/console.c | 5 - src/deh_soc.c | 495 ++------ src/deh_tables.c | 32 +- src/g_demo.c | 3 +- src/g_game.c | 3 +- src/hu_stuff.c | 5 - src/info.h | 64 +- src/p_enemy.c | 2817 +++++++++++++++++++++++----------------------- src/p_mobj.c | 5 +- src/r_defs.h | 36 +- src/r_things.c | 5 + src/r_things.h | 1 + src/y_inter.c | 1 - 13 files changed, 1599 insertions(+), 1873 deletions(-) diff --git a/src/console.c b/src/console.c index c3557c4ee..90fc34ad1 100644 --- a/src/console.c +++ b/src/console.c @@ -400,10 +400,6 @@ static void CON_SetupColormaps(void) brownmap[0] = (UINT8)224; tanmap[0] = (UINT8)217; // no longer nice :( - // Yeah just straight up invert it like a normal person - for (i = 0x00; i <= 0x1F; i++) - invertmap[0x1F - i] = i; - // Init back colormap CON_SetupBackColormap(); } @@ -1761,7 +1757,6 @@ static void CON_DrawBackpic(void) patch_t *con_backpic; lumpnum_t piclump; int x, w, h; - int scale; // Get the lumpnum for CONSBACK, STARTUP (Only during game startup) or fallback into MISSING. if (con_startup) diff --git a/src/deh_soc.c b/src/deh_soc.c index 21aaf6c2a..c6ad81b7b 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -48,6 +48,8 @@ #include "deh_tables.h" // SRB2Kart +#include "filesrch.h" // refreshdirmenu + int freeslotusage[2][2] = {{0, 0}, {0, 0}}; // [S_, MT_][max, previous .wad's max] void DEH_UpdateMaxFreeslots(void) { @@ -1112,15 +1114,11 @@ void readgametype(MYFILE *f, char *gtname) UINT32 newgttol = 0; INT32 newgtpointlimit = 0; INT32 newgttimelimit = 0; - UINT8 newgtleftcolor = 0; - UINT8 newgtrightcolor = 0; INT16 newgtrankingstype = -1; int newgtinttype = 0; - char gtdescription[441]; char gtconst[MAXLINELEN]; // Empty strings. - gtdescription[0] = '\0'; gtconst[0] = '\0'; do @@ -1136,44 +1134,6 @@ void readgametype(MYFILE *f, char *gtname) else break; - if (fastcmp(word, "DESCRIPTION")) - { - char *descr = NULL; - - for (i = 0; i < MAXLINELEN-3; i++) - { - if (s[i] == '=') - { - descr = &s[i+2]; - break; - } - } - if (descr) - { - strcpy(gtdescription, descr); - strcat(gtdescription, myhashfgets(descr, sizeof (gtdescription), f)); - } - else - strcpy(gtdescription, ""); - - // For some reason, cutting the string did not work above. Most likely due to strcpy or strcat... - // It works down here, though. - { - INT32 numline = 0; - for (i = 0; (size_t)i < sizeof(gtdescription)-1; i++) - { - if (numline < 20 && gtdescription[i] == '\n') - numline++; - - if (numline >= 20 || gtdescription[i] == '\0' || gtdescription[i] == '#') - break; - } - } - gtdescription[strlen(gtdescription)-1] = '\0'; - gtdescription[i] = '\0'; - continue; - } - word2 = strtok(NULL, " = "); if (word2) { @@ -1206,13 +1166,6 @@ void readgametype(MYFILE *f, char *gtname) newgtpointlimit = (INT32)i; else if (fastcmp(word, "DEFAULTTIMELIMIT")) newgttimelimit = (INT32)i; - // Level platter - else if (fastcmp(word, "HEADERCOLOR") || fastcmp(word, "HEADERCOLOUR")) - newgtleftcolor = newgtrightcolor = (UINT8)get_number(word2); - else if (fastcmp(word, "HEADERLEFTCOLOR") || fastcmp(word, "HEADERLEFTCOLOUR")) - newgtleftcolor = (UINT8)get_number(word2); - else if (fastcmp(word, "HEADERRIGHTCOLOR") || fastcmp(word, "HEADERRIGHTCOLOUR")) - newgtrightcolor = (UINT8)get_number(word2); // Rankings type else if (fastcmp(word, "RANKINGTYPE")) { @@ -1280,7 +1233,6 @@ void readgametype(MYFILE *f, char *gtname) // Add the new gametype newgtidx = G_AddGametype(newgtrules); G_AddGametypeTOL(newgtidx, newgttol); - G_SetGametypeDescription(newgtidx, gtdescription, newgtleftcolor, newgtrightcolor); // Not covered by G_AddGametype alone. if (newgtrankingstype == -1) @@ -1481,25 +1433,11 @@ void readlevelheader(MYFILE *f, INT32 num) } } - // NiGHTS grades - else if (fastncmp(word, "GRADES", 6)) - { - UINT8 mare = (UINT8)atoi(word + 6); - - if (mare <= 0 || mare > 8) - { - deh_warning("Level header %d: unknown word '%s'", num, word); - continue; - } - - P_AddGradesForMare((INT16)(num-1), mare-1, word2); - } - // Strings that can be truncated - else if (fastcmp(word, "SELECTHEADING")) + else if (fastcmp(word, "ZONETITLE")) { - deh_strlcpy(mapheaderinfo[num-1]->selectheading, word2, - sizeof(mapheaderinfo[num-1]->selectheading), va("Level header %d: selectheading", num)); + deh_strlcpy(mapheaderinfo[num-1]->zonttl, word2, + sizeof(mapheaderinfo[num-1]->zonttl), va("Level header %d: zonetitle", num)); } else if (fastcmp(word, "SCRIPTNAME")) { @@ -1601,34 +1539,6 @@ void readlevelheader(MYFILE *f, INT32 num) mapheaderinfo[num-1]->mustrack = ((UINT16)i - 1); else if (fastcmp(word, "MUSICPOS")) mapheaderinfo[num-1]->muspos = (UINT32)get_number(word2); - else if (fastcmp(word, "MUSICINTERFADEOUT")) - mapheaderinfo[num-1]->musinterfadeout = (UINT32)get_number(word2); - else if (fastcmp(word, "MUSICINTER")) - deh_strlcpy(mapheaderinfo[num-1]->musintername, word2, - sizeof(mapheaderinfo[num-1]->musintername), va("Level header %d: intermission music", num)); - else if (fastcmp(word, "MUSICPOSTBOSS")) - deh_strlcpy(mapheaderinfo[num-1]->muspostbossname, word2, - sizeof(mapheaderinfo[num-1]->muspostbossname), va("Level header %d: post-boss music", num)); - else if (fastcmp(word, "MUSICPOSTBOSSTRACK")) - mapheaderinfo[num-1]->muspostbosstrack = ((UINT16)i - 1); - else if (fastcmp(word, "MUSICPOSTBOSSPOS")) - mapheaderinfo[num-1]->muspostbosspos = (UINT32)get_number(word2); - else if (fastcmp(word, "MUSICPOSTBOSSFADEIN")) - mapheaderinfo[num-1]->muspostbossfadein = (UINT32)get_number(word2); - else if (fastcmp(word, "FORCERESETMUSIC")) - { - // This is a weird one because "FALSE"/"NO" could either apply to "leave to default preference" (cv_resetmusic) - // or "force off". Let's assume it means "force off", and let an unspecified value mean "default preference" - if (fastcmp(word2, "OFF") || word2[0] == 'F' || word2[0] == 'N') i = 0; - else if (fastcmp(word2, "ON") || word2[0] == 'T' || word2[0] == 'Y') i = 1; - else i = -1; // (fastcmp(word2, "DEFAULT")) - - if (i >= -1 && i <= 1) // -1 to force off, 1 to force on, 0 to honor default. - // This behavior can be disabled with cv_resetmusicbyheader - mapheaderinfo[num-1]->musforcereset = (SINT8)i; - else - deh_warning("Level header %d: invalid forceresetmusic option %d", num, i); - } else if (fastcmp(word, "FORCECHARACTER")) { strlcpy(mapheaderinfo[num-1]->forcecharacter, word2, SKINNAMESIZE+1); @@ -1636,18 +1546,17 @@ void readlevelheader(MYFILE *f, INT32 num) } else if (fastcmp(word, "WEATHER")) mapheaderinfo[num-1]->weather = (UINT8)get_number(word2); - else if (fastcmp(word, "SKYNUM")) - mapheaderinfo[num-1]->skynum = (INT16)i; - else if (fastcmp(word, "INTERSCREEN")) - strncpy(mapheaderinfo[num-1]->interscreen, word2, 8); + else if (fastcmp(word, "SKYTEXTURE")) + deh_strlcpy(mapheaderinfo[num-1]->skytexture, word2, + sizeof(mapheaderinfo[num-1]->skytexture), va("Level header %d: sky texture", num)); else if (fastcmp(word, "PRECUTSCENENUM")) mapheaderinfo[num-1]->precutscenenum = (UINT8)i; else if (fastcmp(word, "CUTSCENENUM")) mapheaderinfo[num-1]->cutscenenum = (UINT8)i; - else if (fastcmp(word, "COUNTDOWN")) - mapheaderinfo[num-1]->countdown = (INT16)i; else if (fastcmp(word, "PALETTE")) mapheaderinfo[num-1]->palette = (UINT16)i; + else if (fastcmp(word, "ENCOREPAL")) + mapheaderinfo[num-1]->encorepal = (UINT16)i; else if (fastcmp(word, "NUMLAPS")) mapheaderinfo[num-1]->numlaps = (UINT8)i; else if (fastcmp(word, "UNLOCKABLE")) @@ -1657,8 +1566,6 @@ void readlevelheader(MYFILE *f, INT32 num) else deh_warning("Level header %d: invalid unlockable number %d", num, i); } - else if (fastcmp(word, "LEVELSELECT")) - mapheaderinfo[num-1]->levelselect = (UINT8)i; else if (fastcmp(word, "SKYBOXSCALE")) mapheaderinfo[num-1]->skybox_scalex = mapheaderinfo[num-1]->skybox_scaley = mapheaderinfo[num-1]->skybox_scalez = (INT16)i; else if (fastcmp(word, "SKYBOXSCALEX")) @@ -1667,46 +1574,15 @@ void readlevelheader(MYFILE *f, INT32 num) mapheaderinfo[num-1]->skybox_scaley = (INT16)i; else if (fastcmp(word, "SKYBOXSCALEZ")) mapheaderinfo[num-1]->skybox_scalez = (INT16)i; - - else if (fastcmp(word, "BONUSTYPE")) - { - if (fastcmp(word2, "NONE")) i = -1; - else if (fastcmp(word2, "NORMAL")) i = 0; - else if (fastcmp(word2, "BOSS")) i = 1; - else if (fastcmp(word2, "ERZ3")) i = 2; - else if (fastcmp(word2, "NIGHTS")) i = 3; - else if (fastcmp(word2, "NIGHTSLINK")) i = 4; - - if (i >= -1 && i <= 4) // -1 for no bonus. Max is 4. - mapheaderinfo[num-1]->bonustype = (SINT8)i; - else - deh_warning("Level header %d: invalid bonus type number %d", num, i); - } - - // Title card - else if (fastcmp(word, "TITLECARDZIGZAG")) - { - deh_strlcpy(mapheaderinfo[num-1]->ltzzpatch, word2, - sizeof(mapheaderinfo[num-1]->ltzzpatch), va("Level header %d: title card zigzag patch name", num)); - } - else if (fastcmp(word, "TITLECARDZIGZAGTEXT")) - { - deh_strlcpy(mapheaderinfo[num-1]->ltzztext, word2, - sizeof(mapheaderinfo[num-1]->ltzztext), va("Level header %d: title card zigzag text patch name", num)); - } - else if (fastcmp(word, "TITLECARDACTDIAMOND")) - { - deh_strlcpy(mapheaderinfo[num-1]->ltactdiamond, word2, - sizeof(mapheaderinfo[num-1]->ltactdiamond), va("Level header %d: title card act diamond patch name", num)); - } - - else if (fastcmp(word, "MAXBONUSLIVES")) - mapheaderinfo[num-1]->maxbonuslives = (SINT8)i; else if (fastcmp(word, "LEVELFLAGS")) - mapheaderinfo[num-1]->levelflags = (UINT16)i; + mapheaderinfo[num-1]->levelflags = get_number(word2); else if (fastcmp(word, "MENUFLAGS")) - mapheaderinfo[num-1]->menuflags = (UINT8)i; - + mapheaderinfo[num-1]->menuflags = get_number(word2); + // SRB2Kart + else if (fastcmp(word, "MOBJSCALE")) + mapheaderinfo[num-1]->mobj_scale = get_number(word2); + else if (fastcmp(word, "DEFAULTWAYPOINTRADIUS")) + mapheaderinfo[num-1]->default_waypoint_radius = get_number(word2); // Individual triggers for level flags, for ease of use (and 2.0 compatibility) else if (fastcmp(word, "SCRIPTISFILE")) { @@ -1715,27 +1591,6 @@ void readlevelheader(MYFILE *f, INT32 num) else mapheaderinfo[num-1]->levelflags &= ~LF_SCRIPTISFILE; } - else if (fastcmp(word, "SPEEDMUSIC")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_SPEEDMUSIC; - else - mapheaderinfo[num-1]->levelflags &= ~LF_SPEEDMUSIC; - } - else if (fastcmp(word, "NOSSMUSIC")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_NOSSMUSIC; - else - mapheaderinfo[num-1]->levelflags &= ~LF_NOSSMUSIC; - } - else if (fastcmp(word, "NORELOAD")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_NORELOAD; - else - mapheaderinfo[num-1]->levelflags &= ~LF_NORELOAD; - } else if (fastcmp(word, "NOZONE")) { if (i || word2[0] == 'T' || word2[0] == 'Y') @@ -1743,51 +1598,19 @@ void readlevelheader(MYFILE *f, INT32 num) else mapheaderinfo[num-1]->levelflags &= ~LF_NOZONE; } - else if (fastcmp(word, "SAVEGAME")) + else if (fastcmp(word, "SECTIONRACE")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_SAVEGAME; + mapheaderinfo[num-1]->levelflags |= LF_SECTIONRACE; else - mapheaderinfo[num-1]->levelflags &= ~LF_SAVEGAME; + mapheaderinfo[num-1]->levelflags &= ~LF_SECTIONRACE; } - else if (fastcmp(word, "MIXNIGHTSCOUNTDOWN")) + else if (fastcmp(word, "SUBTRACTNUM")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_MIXNIGHTSCOUNTDOWN; + mapheaderinfo[num-1]->levelflags |= LF_SUBTRACTNUM; else - mapheaderinfo[num-1]->levelflags &= ~LF_MIXNIGHTSCOUNTDOWN; - } - else if (fastcmp(word, "WARNINGTITLE")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_WARNINGTITLE; - else - mapheaderinfo[num-1]->levelflags &= ~LF_WARNINGTITLE; - } - else if (fastcmp(word, "NOTITLECARD")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_NOTITLECARD; - else - mapheaderinfo[num-1]->levelflags &= ~LF_NOTITLECARD; - } - else if (fastcmp(word, "SHOWTITLECARDFOR")) - { - mapheaderinfo[num-1]->levelflags |= LF_NOTITLECARD; - tmp = strtok(word2,","); - do { - if (fastcmp(tmp, "FIRST")) - mapheaderinfo[num-1]->levelflags &= ~LF_NOTITLECARDFIRST; - else if (fastcmp(tmp, "RESPAWN")) - mapheaderinfo[num-1]->levelflags &= ~LF_NOTITLECARDRESPAWN; - else if (fastcmp(tmp, "RECORDATTACK")) - mapheaderinfo[num-1]->levelflags &= ~LF_NOTITLECARDRECORDATTACK; - else if (fastcmp(tmp, "ALL")) - mapheaderinfo[num-1]->levelflags &= ~LF_NOTITLECARD; - else if (!fastcmp(tmp, "NONE")) - deh_warning("Level header %d: unknown titlecard show option %s\n", num, tmp); - - } while((tmp = strtok(NULL,",")) != NULL); + mapheaderinfo[num-1]->levelflags &= ~LF_SUBTRACTNUM; } // Individual triggers for menu flags @@ -1805,40 +1628,20 @@ void readlevelheader(MYFILE *f, INT32 num) else mapheaderinfo[num-1]->menuflags &= ~LF2_HIDEINSTATS; } - else if (fastcmp(word, "RECORDATTACK") || fastcmp(word, "TIMEATTACK")) - { // TIMEATTACK is an accepted alias + else if (fastcmp(word, "TIMEATTACK") || fastcmp(word, "RECORDATTACK")) + { // RECORDATTACK is an accepted alias if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags |= LF2_RECORDATTACK; + mapheaderinfo[num-1]->menuflags |= LF2_TIMEATTACK; else - mapheaderinfo[num-1]->menuflags &= ~LF2_RECORDATTACK; + mapheaderinfo[num-1]->menuflags &= ~LF2_TIMEATTACK; } - else if (fastcmp(word, "NIGHTSATTACK")) + else if (fastcmp(word, "VISITNEEDED")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags |= LF2_NIGHTSATTACK; + mapheaderinfo[num-1]->menuflags |= LF2_VISITNEEDED; else - mapheaderinfo[num-1]->menuflags &= LF2_NIGHTSATTACK; + mapheaderinfo[num-1]->menuflags &= ~LF2_VISITNEEDED; } - else if (fastcmp(word, "NOVISITNEEDED")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags |= LF2_NOVISITNEEDED; - else - mapheaderinfo[num-1]->menuflags &= ~LF2_NOVISITNEEDED; - } - else if (fastcmp(word, "WIDEICON")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags |= LF2_WIDEICON; - else - mapheaderinfo[num-1]->menuflags &= ~LF2_WIDEICON; - } - else if (fastcmp(word, "STARTRINGS")) - mapheaderinfo[num-1]->startrings = (UINT16)i; - else if (fastcmp(word, "SPECIALSTAGETIME")) - mapheaderinfo[num-1]->sstimer = i; - else if (fastcmp(word, "SPECIALSTAGESPHERES")) - mapheaderinfo[num-1]->ssspheres = i; else if (fastcmp(word, "GRAVITY")) mapheaderinfo[num-1]->gravity = FLOAT_TO_FIXED(atof(word2)); else @@ -2527,8 +2330,6 @@ void readmenu(MYFILE *f, INT32 num) { if (fastcmp(word2, "USER")) menupres[num].ttmode = TTMODE_USER; - else if (fastcmp(word2, "ALACROIX")) - menupres[num].ttmode = TTMODE_ALACROIX; else if (fastcmp(word2, "HIDE") || fastcmp(word2, "HIDDEN") || fastcmp(word2, "NONE")) { menupres[num].ttmode = TTMODE_USER; @@ -2982,20 +2783,10 @@ void reademblemdata(MYFILE *f, INT32 num) { if (fastcmp(word2, "GLOBAL")) emblemlocations[num-1].type = ET_GLOBAL; - else if (fastcmp(word2, "SKIN")) - emblemlocations[num-1].type = ET_SKIN; - else if (fastcmp(word2, "SCORE")) - emblemlocations[num-1].type = ET_SCORE; - else if (fastcmp(word2, "TIME")) - emblemlocations[num-1].type = ET_TIME; - else if (fastcmp(word2, "RINGS")) - emblemlocations[num-1].type = ET_RINGS; else if (fastcmp(word2, "MAP")) emblemlocations[num-1].type = ET_MAP; - else if (fastcmp(word2, "NGRADE")) - emblemlocations[num-1].type = ET_NGRADE; - else if (fastcmp(word2, "NTIME")) - emblemlocations[num-1].type = ET_NTIME; + else if (fastcmp(word2, "TIME")) + emblemlocations[num-1].type = ET_TIME; else emblemlocations[num-1].type = (UINT8)value; } @@ -3036,27 +2827,17 @@ void reademblemdata(MYFILE *f, INT32 num) // Default sprite and color definitions for lazy people like me if (!emblemlocations[num-1].sprite) switch (emblemlocations[num-1].type) { - case ET_RINGS: - emblemlocations[num-1].sprite = 'R'; break; - case ET_SCORE: case ET_NGRADE: - emblemlocations[num-1].sprite = 'S'; break; - case ET_TIME: case ET_NTIME: - emblemlocations[num-1].sprite = 'T'; break; + case ET_TIME: + emblemlocations[num-1].sprite = 'B'; break; default: emblemlocations[num-1].sprite = 'A'; break; } if (!emblemlocations[num-1].color) switch (emblemlocations[num-1].type) { - case ET_RINGS: - emblemlocations[num-1].color = SKINCOLOR_GOLD; break; - case ET_SCORE: - emblemlocations[num-1].color = SKINCOLOR_BROWN; break; - case ET_NGRADE: - emblemlocations[num-1].color = SKINCOLOR_TEAL; break; - case ET_TIME: case ET_NTIME: + case ET_TIME: //case ET_NTIME: emblemlocations[num-1].color = SKINCOLOR_GREY; break; default: - emblemlocations[num-1].color = SKINCOLOR_BLUE; break; + emblemlocations[num-1].color = SKINCOLOR_GOLD; break; } Z_Free(s); @@ -3137,9 +2918,9 @@ void readextraemblemdata(MYFILE *f, INT32 num) } while (!myfeof(f)); if (!extraemblems[num-1].sprite) - extraemblems[num-1].sprite = 'X'; + extraemblems[num-1].sprite = 'C'; if (!extraemblems[num-1].color) - extraemblems[num-1].color = SKINCOLOR_BLUE; + extraemblems[num-1].color = SKINCOLOR_RED; Z_Free(s); } @@ -3195,9 +2976,8 @@ void readunlockable(MYFILE *f, INT32 num) else { strupr(word2); - if (fastcmp(word, "HEIGHT")) - unlockables[num].height = (UINT16)i; - else if (fastcmp(word, "CONDITIONSET")) + + if (fastcmp(word, "CONDITIONSET")) unlockables[num].conditionset = (UINT8)i; else if (fastcmp(word, "SHOWCONDITIONSET")) unlockables[num].showconditionset = (UINT8)i; @@ -3209,26 +2989,34 @@ void readunlockable(MYFILE *f, INT32 num) { if (fastcmp(word2, "NONE")) unlockables[num].type = SECRET_NONE; + else if (fastcmp(word2, "HEADER")) + unlockables[num].type = SECRET_HEADER; + else if (fastcmp(word2, "SKIN")) + unlockables[num].type = SECRET_SKIN; + else if (fastcmp(word2, "WARP")) + unlockables[num].type = SECRET_WARP; + else if (fastcmp(word2, "LEVELSELECT")) + unlockables[num].type = SECRET_LEVELSELECT; + else if (fastcmp(word2, "TIMEATTACK")) + unlockables[num].type = SECRET_TIMEATTACK; + else if (fastcmp(word2, "BREAKTHECAPSULES")) + unlockables[num].type = SECRET_BREAKTHECAPSULES; + else if (fastcmp(word2, "SOUNDTEST")) + unlockables[num].type = SECRET_SOUNDTEST; + else if (fastcmp(word2, "CREDITS")) + unlockables[num].type = SECRET_CREDITS; else if (fastcmp(word2, "ITEMFINDER")) unlockables[num].type = SECRET_ITEMFINDER; else if (fastcmp(word2, "EMBLEMHINTS")) unlockables[num].type = SECRET_EMBLEMHINTS; + else if (fastcmp(word2, "ENCORE")) + unlockables[num].type = SECRET_ENCORE; + else if (fastcmp(word2, "HARDSPEED")) + unlockables[num].type = SECRET_HARDSPEED; + else if (fastcmp(word2, "HELLATTACK")) + unlockables[num].type = SECRET_HELLATTACK; else if (fastcmp(word2, "PANDORA")) unlockables[num].type = SECRET_PANDORA; - else if (fastcmp(word2, "CREDITS")) - unlockables[num].type = SECRET_CREDITS; - else if (fastcmp(word2, "RECORDATTACK")) - unlockables[num].type = SECRET_RECORDATTACK; - else if (fastcmp(word2, "NIGHTSMODE")) - unlockables[num].type = SECRET_NIGHTSMODE; - else if (fastcmp(word2, "HEADER")) - unlockables[num].type = SECRET_HEADER; - else if (fastcmp(word2, "LEVELSELECT")) - unlockables[num].type = SECRET_LEVELSELECT; - else if (fastcmp(word2, "WARP")) - unlockables[num].type = SECRET_WARP; - else if (fastcmp(word2, "SOUNDTEST")) - unlockables[num].type = SECRET_SOUNDTEST; else unlockables[num].type = (INT16)i; } @@ -3283,32 +3071,39 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) return; } - if (fastcmp(params[0], "PLAYTIME")) + if (fastcmp(params[0], "PLAYTIME") + || (++offset && fastcmp(params[0], "MATCHESPLAYED"))) { PARAMCHECK(1); - ty = UC_PLAYTIME; + ty = UC_PLAYTIME + offset; re = atoi(params[1]); } - else if (fastcmp(params[0], "GAMECLEAR") - || (++offset && fastcmp(params[0], "ALLEMERALDS")) - || (++offset && fastcmp(params[0], "ULTIMATECLEAR"))) + else if (fastcmp(params[0], "POWERLEVEL")) { - ty = UC_GAMECLEAR + offset; + PARAMCHECK(2); + ty = UC_POWERLEVEL; + re = atoi(params[1]); + x1 = atoi(params[2]); + + if (x1 < 0 || x1 >= PWRLV_NUMTYPES) + { + deh_warning("Power level type %d out of range (0 - %d)", x1, PWRLV_NUMTYPES-1); + return; + } + } + else if (fastcmp(params[0], "GAMECLEAR")) + { + ty = UC_GAMECLEAR; re = (params[1]) ? atoi(params[1]) : 1; } - else if ((offset=0) || fastcmp(params[0], "OVERALLSCORE") - || (++offset && fastcmp(params[0], "OVERALLTIME")) - || (++offset && fastcmp(params[0], "OVERALLRINGS"))) + else if (fastcmp(params[0], "OVERALLTIME")) { PARAMCHECK(1); - ty = UC_OVERALLSCORE + offset; + ty = UC_OVERALLTIME; re = atoi(params[1]); } else if ((offset=0) || fastcmp(params[0], "MAPVISITED") - || (++offset && fastcmp(params[0], "MAPBEATEN")) - || (++offset && fastcmp(params[0], "MAPALLEMERALDS")) - || (++offset && fastcmp(params[0], "MAPULTIMATE")) - || (++offset && fastcmp(params[0], "MAPPERFECT"))) + || (++offset && fastcmp(params[0], "MAPBEATEN"))) { PARAMCHECK(1); ty = UC_MAPVISITED + offset; @@ -3325,12 +3120,10 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) return; } } - else if ((offset=0) || fastcmp(params[0], "MAPSCORE") - || (++offset && fastcmp(params[0], "MAPTIME")) - || (++offset && fastcmp(params[0], "MAPRINGS"))) + else if (fastcmp(params[0], "MAPTIME")) { PARAMCHECK(2); - ty = UC_MAPSCORE + offset; + ty = UC_MAPTIME; re = atoi(params[2]); // Convert to map number if it appears to be one @@ -3341,52 +3134,10 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) if (x1 < 0 || x1 >= NUMMAPS) { - deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS); + deh_warning("Level number %d out of range (1 - %d)", x1, NUMMAPS); return; } } - else if ((offset=0) || fastcmp(params[0], "NIGHTSSCORE") - || (++offset && fastcmp(params[0], "NIGHTSTIME")) - || (++offset && fastcmp(params[0], "NIGHTSGRADE"))) - { - PARAMCHECK(2); // one optional one - - ty = UC_NIGHTSSCORE + offset; - i = (params[3] ? 3 : 2); - if (fastncmp("GRADE_",params[i],6)) - { - char *p = params[i]+6; - for (re = 0; NIGHTSGRADE_LIST[re]; re++) - if (*p == NIGHTSGRADE_LIST[re]) - break; - if (!NIGHTSGRADE_LIST[re]) - { - deh_warning("Invalid NiGHTS grade %s\n", params[i]); - return; - } - } - else - re = atoi(params[i]); - - // Convert to map number if it appears to be one - if (params[1][0] >= 'A' && params[1][0] <= 'Z') - x1 = (INT16)M_MapNumber(params[1][0], params[1][1]); - else - x1 = (INT16)atoi(params[1]); - - if (x1 < 0 || x1 >= NUMMAPS) - { - deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS); - return; - } - - // Mare number (0 for overall) - if (params[3]) // Only if we actually got 3 params (so the second one == mare and not requirement) - x2 = (INT16)atoi(params[2]); - else - x2 = 0; - - } else if (fastcmp(params[0], "TRIGGER")) { PARAMCHECK(1); @@ -3737,8 +3488,6 @@ void readmaincfg(MYFILE *f) { if (fastcmp(word2, "USER")) ttmode = TTMODE_USER; - else if (fastcmp(word2, "ALACROIX")) - ttmode = TTMODE_ALACROIX; else if (fastcmp(word2, "HIDE") || fastcmp(word2, "HIDDEN") || fastcmp(word2, "NONE")) { ttmode = TTMODE_USER; @@ -3749,32 +3498,6 @@ void readmaincfg(MYFILE *f) ttmode = TTMODE_OLD; titlechanged = true; } - else if (fastcmp(word, "TITLEPICSSCALE")) - { - ttscale = max(1, min(8, (UINT8)get_number(word2))); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSSCALESAVAILABLE")) - { - // SPECIAL CASE for Alacroix: Comma-separated list of resolutions that are available - // for gfx loading. - ttavailable[0] = ttavailable[1] = ttavailable[2] = ttavailable[3] =\ - ttavailable[4] = ttavailable[5] = false; - - if (strstr(word2, "1") != NULL) - ttavailable[0] = true; - if (strstr(word2, "2") != NULL) - ttavailable[1] = true; - if (strstr(word2, "3") != NULL) - ttavailable[2] = true; - if (strstr(word2, "4") != NULL) - ttavailable[3] = true; - if (strstr(word2, "5") != NULL) - ttavailable[4] = true; - if (strstr(word2, "6") != NULL) - ttavailable[5] = true; - titlechanged = true; - } else if (fastcmp(word, "TITLEPICSNAME")) { strncpy(ttname, word2, 9); @@ -3810,10 +3533,6 @@ void readmaincfg(MYFILE *f) titlescrollyspeed = get_number(word2); titlechanged = true; } - else if (fastcmp(word, "DISABLESPEEDADJUST")) - { - disableSpeedAdjust = (value || word2[0] == 'T' || word2[0] == 'Y'); - } else if (fastcmp(word, "NUMDEMOS")) { numDemos = (UINT8)get_number(word2); @@ -3855,10 +3574,12 @@ void readmaincfg(MYFILE *f) strlcpy(gamedatafilename, word2, sizeof (gamedatafilename)); strlwr(gamedatafilename); savemoddata = true; + majormods = false; // Also save a time attack folder filenamelen = strlen(gamedatafilename)-4; // Strip off the extension - strncpy(timeattackfolder, gamedatafilename, min(filenamelen, sizeof (timeattackfolder))); + filenamelen = min(filenamelen, sizeof (timeattackfolder)); + strncpy(timeattackfolder, gamedatafilename, filenamelen); timeattackfolder[min(filenamelen, sizeof (timeattackfolder) - 1)] = '\0'; strcpy(savegamename, timeattackfolder); @@ -3869,6 +3590,7 @@ void readmaincfg(MYFILE *f) strcpy(liveeventbackup, va("live%s.bkp", timeattackfolder)); strcatbf(liveeventbackup, srb2home, PATHSEP); + refreshdirmenu |= REFRESHDIR_GAMEDATA; gamedataadded = true; titlechanged = true; } @@ -3896,11 +3618,6 @@ void readmaincfg(MYFILE *f) bootmap = (INT16)value; //titlechanged = true; } - else if (fastcmp(word, "STARTCHAR")) - { - startchar = (INT16)value; - char_on = -1; - } else if (fastcmp(word, "TUTORIALMAP")) { // Support using the actual map name, @@ -3989,21 +3706,13 @@ void readwipes(MYFILE *f) else if (fastcmp(pword, "FINAL")) wipeoffset = wipe_intermission_final; } - else if (fastncmp(word, "SPECINTER_", 10)) + else if (fastncmp(word, "VOTING_", 7)) { - pword = word + 10; + pword = word + 7; if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_specinter_toblack; + wipeoffset = wipe_voting_toblack; else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_specinter_final; - } - else if (fastncmp(word, "MULTINTER_", 10)) - { - pword = word + 10; - if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_multinter_toblack; - else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_multinter_final; + wipeoffset = wipe_voting_final; } else if (fastncmp(word, "CONTINUING_", 11)) { @@ -4055,11 +3764,13 @@ void readwipes(MYFILE *f) else if (fastcmp(pword, "FINAL")) wipeoffset = wipe_gameend_final; } - else if (fastncmp(word, "SPECLEVEL_", 10)) + else if (fastncmp(word, "ENCORE_", 7)) { - pword = word + 10; - if (fastcmp(pword, "TOWHITE")) - wipeoffset = wipe_speclevel_towhite; + pword = word + 7; + if (fastcmp(pword, "TOINVERT")) + wipeoffset = wipe_encore_toinvert; + else if (fastcmp(pword, "TOWHITE")) + wipeoffset = wipe_encore_towhite; } if (wipeoffset < 0) @@ -4069,10 +3780,10 @@ void readwipes(MYFILE *f) } if (value == UINT8_MAX - && (wipeoffset <= wipe_level_toblack || wipeoffset >= wipe_speclevel_towhite)) + && (wipeoffset <= wipe_level_toblack || wipeoffset >= wipe_encore_toinvert)) { // Cannot disable non-toblack wipes - // (or the level toblack wipe, or the special towhite wipe) + // (or the level toblack wipe, or the special encore wipe) deh_warning("Wipes: can't disable wipe of type '%s'", word); continue; } @@ -4527,7 +4238,7 @@ playersprite_t get_sprite2(const char *word) if (!spr2names[i][4] && memcmp(word,spr2names[i],4)==0) return i; deh_warning("Couldn't find sprite named 'SPR2_%s'",word); - return SPR2_STND; + return SPR2_STIN; } sfxenum_t get_sfx(const char *word) diff --git a/src/deh_tables.c b/src/deh_tables.c index 365c84e96..fd5841367 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -22,6 +22,7 @@ #include "v_video.h" // video flags (for lua) #include "i_sound.h" // musictype_t (for lua) #include "g_state.h" // gamestate_t (for lua) +#include "r_data.h" // patchalphastyle #include "deh_tables.h" @@ -73,10 +74,6 @@ 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"}, @@ -89,7 +86,6 @@ actionpointer_t actionpointers[] = {{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"}, @@ -99,18 +95,14 @@ actionpointer_t actionpointers[] = {{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_SignSpin}, "A_SIGNSPIN"}, - {{A_SignPlayer}, "A_SIGNPLAYER"}, {{A_OverlayThink}, "A_OVERLAYTHINK"}, {{A_JetChase}, "A_JETCHASE"}, {{A_JetbThink}, "A_JETBTHINK"}, @@ -134,7 +126,6 @@ actionpointer_t actionpointers[] = {{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"}, @@ -173,7 +164,6 @@ actionpointer_t actionpointers[] = {{A_VultureBlast}, "A_VULTUREBLAST"}, {{A_VultureFly}, "A_VULTUREFLY"}, {{A_SkimChase}, "A_SKIMCHASE"}, - {{A_1upThinker}, "A_1UPTHINKER"}, {{A_SkullAttack}, "A_SKULLATTACK"}, {{A_LobShot}, "A_LOBSHOT"}, {{A_FireShot}, "A_FIRESHOT"}, @@ -307,7 +297,6 @@ actionpointer_t actionpointers[] = {{A_Boss5MakeJunk}, "A_BOSS5MAKEJUNK"}, {{A_LookForBetter}, "A_LOOKFORBETTER"}, {{A_Boss5BombExplode}, "A_BOSS5BOMBEXPLODE"}, - {{A_DustDevilThink}, "A_DUSTDEVILTHINK"}, {{A_TNTExplode}, "A_TNTEXPLODE"}, {{A_DebrisRandom}, "A_DEBRISRANDOM"}, {{A_TrainCameo}, "A_TRAINCAMEO"}, @@ -331,6 +320,25 @@ actionpointer_t actionpointers[] = {{A_DragonWing}, "A_DRAGONWING"}, {{A_DragonSegment}, "A_DRAGONSEGMENT"}, {{A_ChangeHeight}, "A_CHANGEHEIGHT"}, + + // SRB2Kart + {{A_ItemPop}, "A_ITEMPOP"}, + {{A_JawzChase}, "A_JAWZCHASE"}, + {{A_JawzExplode}, "A_JAWZEXPLODE"}, + {{A_SPBChase}, "A_SPBCHASE"}, + {{A_SSMineSearch}, "A_SSMINESEARCH"}, + {{A_SSMineExplode}, "A_SSMINEEXPLODE"}, + {{A_BallhogExplode}, "A_BALLHOGEXPLODE"}, + {{A_LightningFollowPlayer}, "A_LIGHTNINGFOLLOWPLAYER"}, + {{A_FZBoomFlash}, "A_FZBOOMFLASH"}, + {{A_FZBoomSmoke}, "A_FZBOOMSMOKE"}, + {{A_RandomShadowFrame}, "A_RANDOMSHADOWFRAME"}, + {{A_RoamingShadowThinker}, "A_ROAMINGSHADOWTHINKER"}, + {{A_MayonakaArrow}, "A_MAYONAKAARROW"}, + {{A_MementosTPParticles}, "A_MEMENTOSTPPARTICLES"}, + {{A_ReaperThinker}, "A_REAPERTHINKER"}, + {{A_FlameShieldPaper}, "A_FLAMESHIELDPAPER"}, + {{NULL}, "NONE"}, // This NULL entry must be the last in the list diff --git a/src/g_demo.c b/src/g_demo.c index db507d0cf..c17b94056 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -3049,7 +3049,8 @@ void G_DoPlayDemo(char *defdemoname) R_ExecuteSetViewSize(); LUAh_MapChange(gamemap); - displayplayer = consoleplayer = 0; + memset(displayplayers, 0, sizeof(displayplayers)); + consoleplayer = 0; memset(playeringame,0,sizeof(playeringame)); playeringame[0] = true; P_SetRandSeed(randseed); diff --git a/src/g_game.c b/src/g_game.c index 5c2b22168..a3e505469 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -916,8 +916,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // why build a ticcmd if we're paused? // Or, for that matter, if we're being reborn. - // Kart, don't build a ticcmd if someone is resynching or the server is stopped too so we don't fly off course in bad conditions - if (paused || P_AutoPause() || (gamestate == GS_LEVEL && player->playerstate == PST_REBORN) || hu_resynching) + if (paused || P_AutoPause() || (gamestate == GS_LEVEL && player->playerstate == PST_REBORN)) { return; } diff --git a/src/hu_stuff.c b/src/hu_stuff.c index a000c6db1..8c1089bba 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2183,11 +2183,6 @@ HU_drawMiniPing (INT32 x, INT32 y, UINT32 ping, INT32 flags) { w /= 2; } - else if (ping < UINT32_MAX) - { - numbars = 1; - barcolor = 35; - } patch = mping[Ping_gfx_num(ping)]; diff --git a/src/info.h b/src/info.h index 73f4dcf47..e41b394e5 100644 --- a/src/info.h +++ b/src/info.h @@ -29,10 +29,6 @@ enum actionnum A_EXPLODE = 0, A_PAIN, A_FALL, - A_MONITORPOP, - A_GOLDMONITORPOP, - A_GOLDMONITORRESTORE, - A_GOLDMONITORSPARKLE, A_LOOK, A_CHASE, A_FACESTABCHASE, @@ -45,7 +41,6 @@ enum actionnum A_SCREAM, A_BOSSDEATH, A_CUSTOMPOWER, - A_GIVEWEAPON, A_RINGBOX, A_INVINCIBILITY, A_SUPERSNEAKERS, @@ -55,18 +50,14 @@ enum actionnum A_BUBBLERISE, A_BUBBLECHECK, A_AWARDSCORE, - A_EXTRALIFE, A_GIVESHIELD, A_GRAVITYBOX, A_SCORERISE, A_ATTRACTCHASE, A_DROPMINE, A_FISHJUMP, - A_THROWNRING, A_SETSOLIDSTEAM, A_UNSETSOLIDSTEAM, - A_SIGNSPIN, - A_SIGNPLAYER, A_OVERLAYTHINK, A_JETCHASE, A_JETBTHINK, @@ -90,7 +81,6 @@ enum actionnum A_RINGEXPLODE, A_OLDRINGEXPLODE, A_MIXUP, - A_RECYCLEPOWERS, A_BOSS1CHASE, A_FOCUSTARGET, A_BOSS2CHASE, @@ -129,7 +119,6 @@ enum actionnum A_VULTUREBLAST, A_VULTUREFLY, A_SKIMCHASE, - A_1UPTHINKER, A_SKULLATTACK, A_LOBSHOT, A_FIRESHOT, @@ -263,7 +252,6 @@ enum actionnum A_BOSS5MAKEJUNK, A_LOOKFORBETTER, A_BOSS5BOMBEXPLODE, - A_DUSTDEVILTHINK, A_TNTEXPLODE, A_DEBRISRANDOM, A_TRAINCAMEO, @@ -287,6 +275,22 @@ enum actionnum A_DRAGONWING, A_DRAGONSEGMENT, A_CHANGEHEIGHT, + A_ITEMPOP, + A_JAWZCHASE, + A_JAWZEXPLODE, + A_SPBCHASE, + A_SSMINESEARCH, + A_SSMINEEXPLODE, + A_BALLHOGEXPLODE, + A_LIGHTNINGFOLLOWPLAYER, + A_FZBOOMFLASH, + A_FZBOOMSMOKE, + A_RANDOMSHADOWFRAME, + A_ROAMINGSHADOWTHINKER, + A_MAYONAKAARROW, + A_MEMENTOSTPPARTICLES, + A_REAPERTHINKER, + A_FLAMESHIELDPAPER, NUMACTIONS }; @@ -322,7 +326,6 @@ void A_ScoreRise(); // Rise the score logo void A_AttractChase(); // Ring Chase void A_DropMine(); // Drop Mine from Skim or Jetty-Syn Bomber void A_FishJump(); // Fish Jump -void A_GrenadeRing(); // SRB2kart void A_SetSolidSteam(); void A_UnsetSolidSteam(); void A_OverlayThink(); @@ -436,21 +439,6 @@ void A_RandomStateRange(); void A_DualAction(); void A_RemoteAction(); void A_ToggleFlameJet(); -void A_ItemPop(); // SRB2kart -void A_JawzChase(); // SRB2kart -void A_JawzExplode(); // SRB2kart -void A_SPBChase(); // SRB2kart -void A_SSMineExplode(); // SRB2kart -void A_BallhogExplode(); // SRB2kart -void A_LightningFollowPlayer(); // SRB2kart: Lightning shield effect player chasing -void A_FZBoomFlash(); // SRB2kart -void A_FZBoomSmoke(); // SRB2kart -void A_RandomShadowFrame(); //SRB2kart: Shadow spawner frame randomizer -void A_RoamingShadowThinker(); // SRB2kart: Roaming Shadow moving + attacking players. -void A_MayonakaArrow(); //SRB2kart: midnight channel arrow sign -void A_ReaperThinker(); //SRB2kart: mementos reaper -void A_MementosTPParticles(); //SRB2kart: mementos teleporter particles. Man that's a lot of actions for my shite. -void A_FlameShieldPaper(); void A_OrbitNights(); void A_GhostMe(); void A_SetObjectState(); @@ -558,6 +546,26 @@ void A_DragonWing(); void A_DragonSegment(); void A_ChangeHeight(); +// +// SRB2Kart +// +void A_ItemPop(); +void A_JawzChase(); +void A_JawzExplode(); +void A_SPBChase(); +void A_SSMineSearch(); +void A_SSMineExplode(); +void A_BallhogExplode(); +void A_LightningFollowPlayer(); +void A_FZBoomFlash(); +void A_FZBoomSmoke(); +void A_RandomShadowFrame(); +void A_RoamingShadowThinker(); +void A_MayonakaArrow(); +void A_ReaperThinker(); +void A_MementosTPParticles(); +void A_FlameShieldPaper(); + extern boolean actionsoverridden[NUMACTIONS]; // ratio of states to sprites to mobj types is roughly 6 : 1 : 1 diff --git a/src/p_enemy.c b/src/p_enemy.c index 4ff9bb58f..9519f3f2a 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -121,7 +121,6 @@ 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_GrenadeRing(mobj_t *actor); void A_SetSolidSteam(mobj_t *actor); void A_UnsetSolidSteam(mobj_t *actor); void A_OverlayThink(mobj_t *actor); @@ -196,21 +195,6 @@ 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_ItemPop(mobj_t *actor); // SRB2kart -void A_JawzChase(mobj_t *actor); // SRB2kart -void A_JawzExplode(mobj_t *actor); // SRB2kart -void A_SPBChase(mobj_t *actor); // SRB2kart -void A_SSMineExplode(mobj_t *actor); // SRB2kart -void A_BallhogExplode(mobj_t *actor); // SRB2kart -void A_LightningFollowPlayer(mobj_t *actor); // SRB2kart -void A_FZBoomFlash(mobj_t *actor); // SRB2kart -void A_FZBoomSmoke(mobj_t *actor); // SRB2kart -void A_RandomShadowFrame(mobj_t *actor); // SRB2kart -void A_RoamingShadowThinker(mobj_t *actor); //SRB2kart -void A_MayonakaArrow(mobj_t *actor); //SRB2kart -void A_ReaperThinker(mobj_t *actor); //SRB2kart -void A_MementosTPParticles(mobj_t *actor); //SRB2kart -void A_FlameShieldPaper(mobj_t *actor); // SRB2kart void A_OrbitNights(mobj_t *actor); void A_GhostMe(mobj_t *actor); void A_SetObjectState(mobj_t *actor); @@ -322,6 +306,26 @@ void A_DragonWing(mobj_t *actor); void A_DragonSegment(mobj_t *actor); void A_ChangeHeight(mobj_t *actor); +// +// SRB2Kart +// +void A_ItemPop(mobj_t *actor); +void A_JawzChase(mobj_t *actor); +void A_JawzExplode(mobj_t *actor); +void A_SPBChase(mobj_t *actor); +void A_SSMineSearch(mobj_t *actor); +void A_SSMineExplode(mobj_t *actor); +void A_BallhogExplode(mobj_t *actor); +void A_LightningFollowPlayer(mobj_t *actor); +void A_FZBoomFlash(mobj_t *actor); +void A_FZBoomSmoke(mobj_t *actor); +void A_RandomShadowFrame(mobj_t *actor); +void A_RoamingShadowThinker(mobj_t *actor); +void A_MayonakaArrow(mobj_t *actor); +void A_ReaperThinker(mobj_t *actor); +void A_MementosTPParticles(mobj_t *actor); +void A_FlameShieldPaper(mobj_t *actor); + //for p_enemy.c // @@ -4404,160 +4408,6 @@ void A_FishJump(mobj_t *actor) P_SetMobjStateNF(actor, actor->info->meleestate); } -//{ SRB2kart - A_GRENADERING -static mobj_t *grenade; -static fixed_t explodedist; - -static inline boolean PIT_GrenadeRing(mobj_t *thing) -{ - if (!grenade) - return false; - - if (grenade->flags2 & MF2_DEBRIS) - return false; - - if (thing->type != MT_PLAYER) // Don't explode for anything but an actual player. - return true; - - if (!(thing->flags & MF_SHOOTABLE)) - { - // didn't do any damage - return true; - } - - if (netgame && thing->player && thing->player->spectator) - return true; - - if (thing == grenade->target && grenade->threshold != 0) // Don't blow up at your owner. - return true; - - if (thing->player && (thing->player->kartstuff[k_hyudorotimer] - || ((gametyperules & GTR_BUMPERS) && thing->player && thing->player->bumpers <= 0 && thing->player->karmadelay))) - return true; - - // see if it went over / under - if (grenade->z - explodedist > thing->z + thing->height) - return true; // overhead - if (grenade->z + grenade->height + explodedist < thing->z) - return true; // underneath - - if (P_AproxDistance(P_AproxDistance(thing->x - grenade->x, thing->y - grenade->y), - thing->z - grenade->z) > explodedist) - return true; // Too far away - - // Explode! - P_SetMobjState(grenade, grenade->info->deathstate); - return false; -} - -void A_GrenadeRing(mobj_t *actor) -{ - INT32 bx, by, xl, xh, yl, yh; - explodedist = FixedMul(actor->info->painchance, mapobjectscale); - - if (LUA_CallAction(A_GRENADERING, actor)) - return; - - if (actor->flags2 & MF2_DEBRIS) - return; - - if (actor->state == &states[S_SSMINE_DEPLOY8]) - explodedist = (3*explodedist)/2; - - if (leveltime % 35 == 0) - S_StartSound(actor, actor->info->activesound); - - // Use blockmap to check for nearby shootables - yh = (unsigned)(actor->y + explodedist - bmaporgy)>>MAPBLOCKSHIFT; - yl = (unsigned)(actor->y - explodedist - bmaporgy)>>MAPBLOCKSHIFT; - xh = (unsigned)(actor->x + explodedist - bmaporgx)>>MAPBLOCKSHIFT; - xl = (unsigned)(actor->x - explodedist - bmaporgx)>>MAPBLOCKSHIFT; - - grenade = actor; - - for (by = yl; by <= yh; by++) - for (bx = xl; bx <= xh; bx++) - P_BlockThingsIterator(bx, by, PIT_GrenadeRing); -} - -static inline boolean PIT_MineExplode(mobj_t *thing) -{ - if (!grenade || P_MobjWasRemoved(grenade)) - return false; // There's the possibility these can chain react onto themselves after they've already died if there are enough all in one spot - - if (grenade->flags2 & MF2_DEBRIS) // don't explode twice - return false; - - if (thing == grenade || thing->type == MT_MINEEXPLOSIONSOUND) // Don't explode yourself! Endless loop! - return true; - - if (!(thing->flags & MF_SHOOTABLE) || (thing->flags & MF_SCENERY)) - return true; - - if (netgame && thing->player && thing->player->spectator) - return true; - - if ((gametyperules & GTR_BUMPERS) && grenade->target && grenade->target->player && grenade->target->player->bumpers <= 0 && thing == grenade->target) - return true; - - // see if it went over / under - if (grenade->z - explodedist > thing->z + thing->height) - return true; // overhead - if (grenade->z + grenade->height + explodedist < thing->z) - return true; // underneath - - if (P_AproxDistance(P_AproxDistance(thing->x - grenade->x, thing->y - grenade->y), - thing->z - grenade->z) > explodedist) - return true; // Too far away - - P_DamageMobj(thing, grenade, grenade->target, 1, DMG_EXPLODE); - return true; -} - -void A_SSMineExplode(mobj_t *actor) -{ - INT32 bx, by, xl, xh, yl, yh; - INT32 d; - INT32 locvar1 = var1; - mobjtype_t type; - explodedist = FixedMul((3*actor->info->painchance)/2, mapobjectscale); - - if (LUA_CallAction("A_SSMineExplode", actor)) - return; - - if (actor->flags2 & MF2_DEBRIS) - return; - - type = (mobjtype_t)locvar1; - - // Use blockmap to check for nearby shootables - yh = (unsigned)(actor->y + explodedist - bmaporgy)>>MAPBLOCKSHIFT; - yl = (unsigned)(actor->y - explodedist - bmaporgy)>>MAPBLOCKSHIFT; - xh = (unsigned)(actor->x + explodedist - bmaporgx)>>MAPBLOCKSHIFT; - xl = (unsigned)(actor->x - explodedist - bmaporgx)>>MAPBLOCKSHIFT; - - BMBOUNDFIX (xl, xh, yl, yh); - - grenade = actor; - - for (by = yl; by <= yh; by++) - for (bx = xl; bx <= xh; bx++) - P_BlockThingsIterator(bx, by, PIT_MineExplode); - - for (d = 0; d < 16; d++) - K_SpawnKartExplosion(actor->x, actor->y, actor->z, explodedist + 32*mapobjectscale, 32, type, d*(ANGLE_45/4), true, false, actor->target); // 32 <-> 64 - - if (actor->target && actor->target->player) - K_SpawnMineExplosion(actor, actor->target->player->skincolor); - else - K_SpawnMineExplosion(actor, SKINCOLOR_KETCHUP); - - P_SpawnMobj(actor->x, actor->y, actor->z, MT_MINEEXPLOSIONSOUND); - - actor->flags2 |= MF2_DEBRIS; // Set this flag to ensure that the explosion won't be effective more than 1 frame. -} -//} - // Function: A_SetSolidSteam // // Description: Makes steam solid so it collides with the player to boost them. @@ -6728,7 +6578,7 @@ void A_Boss2PogoTarget(mobj_t *actor) // 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, height; + fixed_t airtime, gravityadd, zoffs; // check gravity in the sector (for later math) P_CheckGravity(actor, true); @@ -8439,7 +8289,7 @@ void A_RandomStateRange(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_RandomStateRange", actor)) + if (LUA_CallAction(A_RANDOMSTATERANGE, actor)) return; P_SetMobjState(actor, P_RandomRange(locvar1, locvar2)); @@ -8457,7 +8307,7 @@ void A_DualAction(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_DualAction", actor)) + if (LUA_CallAction(A_DUALACTION, actor)) return; CONS_Debug(DBG_GAMELOGIC, "A_DualAction called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); @@ -8490,7 +8340,7 @@ void A_RemoteAction(mobj_t *actor) INT32 locvar2 = var2; mobj_t *originaltarget = actor->target; // Hold on to the target for later. - if (LUA_CallAction("A_RemoteAction", actor)) + if (LUA_CallAction(A_REMOTEACTION, actor)) return; // If >=0, find the closest target. @@ -8572,7 +8422,7 @@ void A_RemoteAction(mobj_t *actor) // void A_ToggleFlameJet(mobj_t* actor) { - if (LUA_CallAction("A_ToggleFlameJet", actor)) + if (LUA_CallAction(A_TOGGLEFLAMEJET, actor)) return; // threshold - off delay @@ -8594,1238 +8444,6 @@ void A_ToggleFlameJet(mobj_t* actor) } } -//{ SRB2kart Actions -void A_ItemPop(mobj_t *actor) -{ - mobj_t *remains; - mobjtype_t explode; - - if (LUA_CallAction("A_ItemPop", actor)) - return; - - if (!(actor->target && actor->target->player)) - { - if (cv_debug && !(actor->target && actor->target->player)) - CONS_Printf("ERROR: Powerup has no target!\n"); - return; - } - - // de-solidify - P_UnsetThingPosition(actor); - actor->flags &= ~MF_SOLID; - actor->flags |= MF_NOCLIP; - P_SetThingPosition(actor); - - // item explosion - explode = mobjinfo[actor->info->damage].mass; - remains = P_SpawnMobj(actor->x, actor->y, - ((actor->eflags & MFE_VERTICALFLIP) ? (actor->z + 3*(actor->height/4) - FixedMul(mobjinfo[explode].height, actor->scale)) : (actor->z + actor->height/4)), explode); - if (actor->eflags & MFE_VERTICALFLIP) - { - remains->eflags |= MFE_VERTICALFLIP; - remains->flags2 |= MF2_OBJECTFLIP; - } - remains->destscale = actor->destscale; - P_SetScale(remains, actor->scale); - - remains = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->damage); - remains->type = actor->type; // Transfer type information - P_UnsetThingPosition(remains); - if (sector_list) - { - P_DelSeclist(sector_list); - sector_list = NULL; - } - P_SetThingPosition(remains); - remains->destscale = actor->destscale; - P_SetScale(remains, actor->scale); - remains->flags = actor->flags; // Transfer flags - remains->flags2 = actor->flags2; // Transfer flags2 - remains->fuse = actor->fuse; // Transfer respawn timer - remains->threshold = (actor->threshold == 70 ? 70 : (actor->threshold == 69 ? 69 : 68)); - remains->skin = NULL; - remains->spawnpoint = actor->spawnpoint; - - P_SetTarget(&tmthing, remains); - - if (actor->info->deathsound) - S_StartSound(remains, actor->info->deathsound); - - if (!((gametyperules & GTR_BUMPERS) && actor->target->player->bumpers <= 0)) - actor->target->player->kartstuff[k_itemroulette] = 1; - - remains->flags2 &= ~MF2_AMBUSH; - - if ((gametyperules & GTR_BUMPERS) && (actor->threshold != 69 && actor->threshold != 70)) - numgotboxes++; - - P_RemoveMobj(actor); -} - -void A_JawzChase(mobj_t *actor) -{ - const fixed_t currentspeed = R_PointToDist2(0, 0, actor->momx, actor->momy); - player_t *player; - fixed_t thrustamount = 0; - fixed_t frictionsafety = (actor->friction == 0) ? 1 : actor->friction; - fixed_t topspeed = actor->movefactor; - - if (LUA_CallAction("A_JawzChase", actor)) - return; - - if (actor->tracer) - { - /*if ((gametyperules & GTR_CIRCUIT)) // Stop looking after first target in race - actor->extravalue1 = 1;*/ - - if (actor->tracer->health) - { - const angle_t targetangle = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); - mobj_t *ret; - angle_t angledelta = actor->angle - targetangle; - boolean turnclockwise = true; - - if (gametyperules & GTR_CIRCUIT) - { - const fixed_t distbarrier = FixedMul(512*mapobjectscale, FRACUNIT + ((gamespeed-1) * (FRACUNIT/4))); - const fixed_t distaway = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y); - if (distaway < distbarrier) - { - if (actor->tracer->player) - { - fixed_t speeddifference = abs(topspeed - min(actor->tracer->player->speed, K_GetKartSpeed(actor->tracer->player, false))); - topspeed = topspeed - FixedMul(speeddifference, FRACUNIT-FixedDiv(distaway, distbarrier)); - } - } - } - - if (angledelta != 0) - { - angle_t MAX_JAWZ_TURN = ANGLE_90/15; // We can turn a maximum of 6 degrees per frame at regular max speed - // MAX_JAWZ_TURN gets stronger the slower the top speed of jawz - if (topspeed < actor->movefactor) - { - if (topspeed == 0) - { - MAX_JAWZ_TURN = ANGLE_180; - } - else - { - fixed_t anglemultiplier = FixedDiv(actor->movefactor, topspeed); - MAX_JAWZ_TURN += FixedAngle(FixedMul(AngleFixed(MAX_JAWZ_TURN), anglemultiplier)); - } - } - - if (angledelta > ANGLE_180) - { - angledelta = InvAngle(angledelta); - turnclockwise = false; - } - - if (angledelta > MAX_JAWZ_TURN) - { - angledelta = MAX_JAWZ_TURN; - } - - if (turnclockwise) - { - actor->angle -= angledelta; - } - else - { - actor->angle += angledelta; - } - } - - ret = P_SpawnMobj(actor->tracer->x, actor->tracer->y, actor->tracer->z, MT_PLAYERRETICULE); - P_SetTarget(&ret->target, actor->tracer); - ret->frame |= ((leveltime % 10) / 2) + 5; - ret->color = actor->cvmem; - } - else - P_SetTarget(&actor->tracer, NULL); - } - - if (!P_IsObjectOnGround(actor)) - { - // No friction in the air - frictionsafety = FRACUNIT; - } - - if (currentspeed >= topspeed) - { - // Thrust as if you were at top speed, slow down naturally - thrustamount = FixedDiv(topspeed, frictionsafety) - topspeed; - } - else - { - const fixed_t beatfriction = FixedDiv(currentspeed, frictionsafety) - currentspeed; - // Thrust to immediately get to top speed - thrustamount = beatfriction + FixedDiv(topspeed - currentspeed, frictionsafety); - } - - if (!actor->tracer) - { - actor->angle = K_MomentumAngle(actor); - } - - P_Thrust(actor, actor->angle, thrustamount); - - if ((actor->tracer != NULL) && (actor->tracer->health > 0)) - return; - - if (actor->extravalue1) // Disable looking by setting this - return; - - if (!actor->target || P_MobjWasRemoved(actor->target)) // No source! - return; - - player = K_FindJawzTarget(actor, actor->target->player); - if (player) - P_SetTarget(&actor->tracer, player->mo); - - return; -} - -void A_JawzExplode(mobj_t *actor) -{ - INT32 shrapnel = 2; - mobj_t *truc; - - if (LUA_CallAction("A_JawzExplode", actor)) - return; - - truc = P_SpawnMobj(actor->x, actor->y, actor->z, MT_BOOMEXPLODE); - truc->scale = actor->scale*2; - truc->color = SKINCOLOR_KETCHUP; - - while (shrapnel) - { - INT32 speed, speed2; - - truc = P_SpawnMobj(actor->x + P_RandomRange(-8, 8)*FRACUNIT, actor->y + P_RandomRange(-8, 8)*FRACUNIT, - actor->z + P_RandomRange(0, 8)*FRACUNIT, MT_BOOMPARTICLE); - truc->scale = actor->scale*2; - - speed = FixedMul(7*FRACUNIT, actor->scale)>>FRACBITS; - truc->momx = P_RandomRange(-speed, speed)*FRACUNIT; - truc->momy = P_RandomRange(-speed, speed)*FRACUNIT; - - speed = FixedMul(5*FRACUNIT, actor->scale)>>FRACBITS; - speed2 = FixedMul(15*FRACUNIT, actor->scale)>>FRACBITS; - truc->momz = P_RandomRange(speed, speed2)*FRACUNIT; - truc->tics = TICRATE*2; - truc->color = SKINCOLOR_KETCHUP; - - shrapnel--; - } - - return; -} - -static void SpawnSPBTrailRings(mobj_t *actor) -{ - I_Assert(actor != NULL); - - if (leveltime % 6 == 0) - { - if (leveltime % (actor->extravalue1 == 2 ? 6 : 3) == 0) // Extravalue1 == 2 is seeking mode. Because the SPB is about twice as fast as normal in that mode, also spawn the rings twice as often to make up for it! - { - mobj_t *ring = P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momy, - actor->z - actor->momz + (24*mapobjectscale), MT_RING); - ring->threshold = 10; - ring->fuse = 35*TICRATE; - ring->colorized = true; - ring->color = SKINCOLOR_RED; - } - } -} - -// Spawns the V shaped dust. To be used when the SPB is going mostly forward. -static void SpawnSPBDust(mobj_t *mo) -{ - // The easiest way to spawn a V shaped cone of dust from the SPB is simply to spawn 2 particles, and to both move them to the sides in opposite direction. - mobj_t *dust; - fixed_t sx; - fixed_t sy; - fixed_t sz = mo->floorz; - angle_t sa = mo->angle - ANG1*60; - INT32 i; - - if (mo->eflags & MFE_VERTICALFLIP) - sz = mo->ceilingz; - - if (leveltime & 1 && abs(mo->z - sz) < FRACUNIT*64) // Only ever other frame. Also don't spawn it if we're way above the ground. - { - // Determine spawning position next to the SPB: - for (i=0; i < 2; i++) - { - sx = mo->x + FixedMul((mo->scale*96), FINECOSINE((sa)>>ANGLETOFINESHIFT)); - sy = mo->y + FixedMul((mo->scale*96), FINESINE((sa)>>ANGLETOFINESHIFT)); - - dust = P_SpawnMobj(sx, sy, sz, MT_SPBDUST); - dust->momx = mo->momx/2; - dust->momy = mo->momy/2; - dust->momz = mo->momz/2; // Give some of the momentum to the dust - P_SetScale(dust, mo->scale*2); - dust->colorized = true; - dust->color = SKINCOLOR_RED; - dust->angle = mo->angle - FixedAngle(FRACUNIT*90 - FRACUNIT*180*i); // The first one will spawn to the right of the spb, the second one to the left. - P_Thrust(dust, dust->angle, 6*dust->scale); - - K_MatchGenericExtraFlags(dust, mo); - - sa += ANG1*120; // Add 120 degrees to get to mo->angle + ANG1*60 - } - } -} - -// Spawns SPB slip tide. To be used when the SPB is turning. -// Modified version of K_SpawnAIZDust. Maybe we could merge those to be cleaner? - -// dir should be either 1 or -1 to determine where to spawn the dust. - -static void SpawnSPBAIZDust(mobj_t *mo, INT32 dir) -{ - fixed_t newx; - fixed_t newy; - mobj_t *spark; - angle_t travelangle; - fixed_t sz = mo->floorz; - - if (mo->eflags & MFE_VERTICALFLIP) - sz = mo->ceilingz; - - travelangle = K_MomentumAngle(mo); - if (leveltime & 1 && abs(mo->z - sz) < FRACUNIT*64) - { - newx = mo->x + P_ReturnThrustX(mo, travelangle - (dir*ANGLE_45), FixedMul(24*FRACUNIT, mo->scale)); - newy = mo->y + P_ReturnThrustY(mo, travelangle - (dir*ANGLE_45), FixedMul(24*FRACUNIT, mo->scale)); - spark = P_SpawnMobj(newx, newy, sz, MT_AIZDRIFTSTRAT); - spark->colorized = true; - spark->color = SKINCOLOR_RED; - spark->flags = MF_NOGRAVITY|MF_PAIN; - P_SetTarget(&spark->target, mo); - - spark->angle = travelangle+(dir*ANGLE_90); - P_SetScale(spark, (spark->destscale = mo->scale*3/2)); - - spark->momx = (6*mo->momx)/5; - spark->momy = (6*mo->momy)/5; - - K_MatchGenericExtraFlags(spark, mo); - } -} - -// Used for seeking and when SPB is trailing its target from way too close! -static void SpawnSPBSpeedLines(mobj_t *actor) -{ - mobj_t *fast = P_SpawnMobj(actor->x + (P_RandomRange(-24,24) * actor->scale), - actor->y + (P_RandomRange(-24,24) * actor->scale), - actor->z + (actor->height/2) + (P_RandomRange(-24,24) * actor->scale), - MT_FASTLINE); - - P_SetTarget(&fast->target, actor); - fast->angle = K_MomentumAngle(actor); - fast->color = SKINCOLOR_RED; - fast->colorized = true; - K_MatchGenericExtraFlags(fast, actor); -} - - -void A_SPBChase(mobj_t *actor) -{ - player_t *player = NULL; - player_t *scplayer = NULL; // secondary target for seeking - UINT8 i; - UINT8 bestrank = UINT8_MAX; - fixed_t dist; - angle_t hang, vang; - fixed_t wspeed, xyspeed, zspeed; - fixed_t pdist = 1536<threshold) // Just fired, go straight. - { - actor->lastlook = -1; - actor->cusval = -1; - spbplace = -1; - P_InstaThrust(actor, actor->angle, wspeed); - return; - } - - // Find the player with the best rank - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || players[i].exiting) - continue; // not in-game - - /*if (!players[i].mo) - continue; // no mobj - - if (players[i].mo->health <= 0) - continue; // dead - - if (players[i].respawn.state != RESPAWNST_NONE) - continue;*/ // respawning - - if (players[i].kartstuff[k_position] < bestrank) - { - bestrank = players[i].kartstuff[k_position]; - player = &players[i]; - } - } - - // lastlook = last player num targetted - // cvmem = stored speed - // cusval = next waypoint heap index - // extravalue1 = SPB movement mode - // extravalue2 = mode misc option - - if (actor->extravalue1 == 1) // MODE: TARGETING - { - actor->cusval = -1; // Reset waypoint - - if (actor->tracer && actor->tracer->health) - { - fixed_t defspeed = wspeed; - fixed_t range = (160*actor->tracer->scale); - fixed_t cx = 0, cy =0; - - // Play the intimidating gurgle - if (!S_SoundPlaying(actor, actor->info->activesound)) - S_StartSound(actor, actor->info->activesound); - - // Maybe we want SPB to target an object later? IDK lol - if (actor->tracer->player) - { - UINT8 fracmax = 32; - UINT8 spark = ((10-actor->tracer->player->kartspeed) + actor->tracer->player->kartweight) / 2; - fixed_t easiness = ((actor->tracer->player->kartspeed + (10-spark)) << FRACBITS) / 2; - - actor->lastlook = actor->tracer->player-players; // Save the player num for death scumming... - actor->tracer->player->kartstuff[k_ringlock] = 1; // set ring lock - - if (!P_IsObjectOnGround(actor->tracer)) - { - // In the air you have no control; basically don't hit unless you make a near complete stop - defspeed = (7 * actor->tracer->player->speed) / 8; - } - else - { - // 7/8ths max speed for Knuckles, 3/4ths max speed for min accel, exactly max speed for max accel - defspeed = FixedMul(((fracmax+1)<tracer->player, false)) / fracmax; - } - - // Be fairer on conveyors - cx = actor->tracer->player->cmomx; - cy = actor->tracer->player->cmomy; - - // Switch targets if you're no longer 1st for long enough - if (actor->tracer->player->kartstuff[k_position] <= bestrank) - actor->extravalue2 = 7*TICRATE; - else if (actor->extravalue2-- <= 0) - actor->extravalue1 = 0; // back to SEEKING - - spbplace = actor->tracer->player->kartstuff[k_position]; - } - - dist = P_AproxDistance(P_AproxDistance(actor->x-actor->tracer->x, actor->y-actor->tracer->y), actor->z-actor->tracer->z); - - wspeed = FixedMul(defspeed, FRACUNIT + FixedDiv(dist-range, range)); - if (wspeed < defspeed) - wspeed = defspeed; - if (wspeed > (3*defspeed)/2) - wspeed = (3*defspeed)/2; - if (wspeed < 20*actor->tracer->scale) - wspeed = 20*actor->tracer->scale; - if (actor->tracer->player->pflags & PF_SLIDING) - wspeed = actor->tracer->player->speed/2; - // ^^^^ current section: These are annoying, and grand metropolis in particular needs this. - - hang = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); - vang = R_PointToAngle2(0, actor->z, dist, actor->tracer->z); - - // Modify stored speed - if (wspeed > actor->cvmem) - actor->cvmem += (wspeed - actor->cvmem) / TICRATE; - else - actor->cvmem = wspeed; - - { - // Smoothly rotate horz angle - angle_t input = hang - actor->angle; - boolean invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); - - // Slow down when turning; it looks better and makes U-turns not unfair - xyspeed = FixedMul(actor->cvmem, max(0, (((180<angle += input; - - // Smoothly rotate vert angle - input = vang - actor->movedir; - invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); - - // Slow down when turning; might as well do it for momz, since we do it above too - zspeed = FixedMul(actor->cvmem, max(0, (((180<movedir += input; - } - - actor->momx = cx + FixedMul(FixedMul(xyspeed, FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); - actor->momy = cy + FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); - actor->momz = FixedMul(zspeed, FINESINE(actor->movedir>>ANGLETOFINESHIFT)); - - // Spawn a trail of rings behind the SPB! - SpawnSPBTrailRings(actor); - - // Red speed lines for when it's gaining on its target. A tell for when you're starting to lose too much speed! - if (R_PointToDist2(0, 0, actor->momx, actor->momy) > (actor->tracer->player ? (16*actor->tracer->player->speed)/15 - : (16*R_PointToDist2(0, 0, actor->tracer->momx, actor->tracer->momy))/15) // Going faster than the target - && xyspeed > K_GetKartSpeed(actor->tracer->player, false)/4) // Don't display speedup lines at pitifully low speeds - SpawnSPBSpeedLines(actor); - - return; - } - else // Target's gone, return to SEEKING - { - P_SetTarget(&actor->tracer, NULL); - actor->extravalue1 = 2; // WAIT... - actor->extravalue2 = 52; // Slightly over the respawn timer length - return; - } - } - else if (actor->extravalue1 == 2) // MODE: WAIT... - { - actor->momx = actor->momy = actor->momz = 0; // Stoooop - actor->cusval = -1; // Reset waypoint - - if (actor->lastlook != -1 - && playeringame[actor->lastlook] - && !players[actor->lastlook].spectator - && !players[actor->lastlook].exiting) - { - spbplace = players[actor->lastlook].kartstuff[k_position]; - players[actor->lastlook].kartstuff[k_ringlock] = 1; - if (actor->extravalue2-- <= 0 && players[actor->lastlook].mo) - { - P_SetTarget(&actor->tracer, players[actor->lastlook].mo); - actor->extravalue1 = 1; // TARGET ACQUIRED - actor->extravalue2 = 7*TICRATE; - actor->cvmem = wspeed; - } - } - else - { - actor->extravalue1 = 0; // SEEKING - actor->extravalue2 = 0; - spbplace = -1; - } - } - else // MODE: SEEKING - { - waypoint_t *lastwaypoint = NULL; - waypoint_t *bestwaypoint = NULL; - waypoint_t *nextwaypoint = NULL; - waypoint_t *tempwaypoint = NULL; - - actor->lastlook = -1; // Just make sure this is reset - - if (!player || !player->mo || player->mo->health <= 0 || (player->respawn.state != RESPAWNST_NONE)) - { - // No one there? Completely STOP. - actor->momx = actor->momy = actor->momz = 0; - if (!player) - spbplace = -1; - return; - } - - // Found someone, now get close enough to initiate the slaughter... - P_SetTarget(&actor->tracer, player->mo); - spbplace = bestrank; - dist = P_AproxDistance(P_AproxDistance(actor->x-actor->tracer->x, actor->y-actor->tracer->y), actor->z-actor->tracer->z); - - /* - K_GetBestWaypointForMobj returns the waypoint closest to the object that isn't its current waypoint. While this is usually good enough, - in cases where the track overlaps, this means that the SPB will sometimes target a waypoint on an earlier/later portion of the track instead of following along. - For this reason, we're going to try and make sure to avoid these situations. - */ - - // Move along the waypoints until you get close enough - if (actor->cusval > -1 && actor->extravalue2 > 0) - { - // Previously set nextwaypoint - lastwaypoint = K_GetWaypointFromIndex((size_t)actor->cusval); - tempwaypoint = K_GetBestWaypointForMobj(actor); - // check if the tempwaypoint corresponds to lastwaypoint's next ID at least; - // This is to avoid situations where the SPB decides to suicide jump down a bridge because it found a COMPLETELY unrelated waypoint down there. - - if (K_GetWaypointID(tempwaypoint) == K_GetWaypointNextID(lastwaypoint) || K_GetWaypointID(tempwaypoint) == K_GetWaypointID(lastwaypoint)) - // either our previous or curr waypoint ID, sure, take it - bestwaypoint = tempwaypoint; - else - bestwaypoint = K_GetWaypointFromIndex((size_t)actor->extravalue2); // keep going from the PREVIOUS wp. - } - else - bestwaypoint = K_GetBestWaypointForMobj(actor); - - if (bestwaypoint == NULL && lastwaypoint == NULL) - { - // We have invalid waypoints all around, so use closest to try and make it non-NULL. - bestwaypoint = K_GetClosestWaypointToMobj(actor); - } - - if (bestwaypoint != NULL) - { - const boolean huntbackwards = false; - boolean useshortcuts = false; - - // If the player is on a shortcut, use shortcuts. No escape. - if (K_GetWaypointIsShortcut(player->nextwaypoint)) - { - useshortcuts = true; - } - - nextwaypoint = K_GetNextWaypointToDestination( - bestwaypoint, player->nextwaypoint, useshortcuts, huntbackwards); - } - - if (nextwaypoint == NULL && lastwaypoint != NULL) - { - // Restore to the last nextwaypoint - nextwaypoint = lastwaypoint; - } - - if (nextwaypoint != NULL) - { - const fixed_t xywaypointdist = P_AproxDistance( - actor->x - nextwaypoint->mobj->x, actor->y - nextwaypoint->mobj->y); - - hang = R_PointToAngle2(actor->x, actor->y, nextwaypoint->mobj->x, nextwaypoint->mobj->y); - vang = R_PointToAngle2(0, actor->z, xywaypointdist, nextwaypoint->mobj->z); - - actor->cusval = (INT32)K_GetWaypointHeapIndex(nextwaypoint); - actor->extravalue2 = (INT32)K_GetWaypointHeapIndex(bestwaypoint); // save our last best, used above. - } - else - { - // continue straight ahead... Shouldn't happen. - hang = actor->angle; - vang = 0U; - } - - { - // Smoothly rotate horz angle - angle_t input = hang - actor->angle; - boolean invert = (input > ANGLE_180); - INT32 turnangle; - - if (invert) - input = InvAngle(input); - - input = FixedAngle(AngleFixed(input)/8); - - // Slow down when turning; it looks better and makes U-turns not unfair - xyspeed = FixedMul(wspeed, max(0, (((180<angle += input; - - // If input is small enough, spawn dust. Otherwise, spawn a slip tide! - turnangle = AngleFixed(input)/FRACUNIT; - - // The SPB is really turning if that value is >= 3 and <= 357. This looks pretty bad check-wise so feel free to change it for something that isn't as terrible. - if (turnangle >= 3 && turnangle <= 357) - SpawnSPBAIZDust(actor, turnangle < 180 ? 1 : -1); // 1 if turning left, -1 if turning right. Angles work counterclockwise, remember! - else - SpawnSPBDust(actor); // if we're mostly going straight, then spawn the V dust cone! - - SpawnSPBSpeedLines(actor); // Always spawn speed lines while seeking - - // Smoothly rotate vert angle - input = vang - actor->movedir; - invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); - - input = FixedAngle(AngleFixed(input)/8); - - // Slow down when turning; might as well do it for momz, since we do it above too - zspeed = FixedMul(wspeed, max(0, (((180<movedir += input; - } - - actor->momx = FixedMul(FixedMul(xyspeed, FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); - actor->momy = FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); - actor->momz = FixedMul(zspeed, FINESINE(actor->movedir>>ANGLETOFINESHIFT)); - - // see if a player is near us, if they are, try to hit them by slightly thrusting towards them, otherwise, bleh! - for (i=0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || players[i].exiting) - continue; // not in-game - - if (R_PointToDist2(actor->x, actor->y, players[i].mo->x, players[i].mo->y) < pdist) - { - pdist = R_PointToDist2(actor->x, actor->y, players[i].mo->x, players[i].mo->y); - scplayer = &players[i]; // it doesn't matter if we override this guy now. - } - } - - // different player from our main target, try and ram into em~! - if (scplayer && scplayer != player) - { - pangle = actor->angle - R_PointToAngle2(actor->x, actor->y,scplayer->mo->x, scplayer->mo->y); - // check if the angle wouldn't make us LOSE speed... - if ((INT32)pangle/ANG1 >= -80 && (INT32)pangle/ANG1 <= 80) // allow for around 80 degrees - { - // Thrust us towards the guy, try to screw em up! - P_Thrust(actor, R_PointToAngle2(actor->x, actor->y, scplayer->mo->x, scplayer->mo->y), actor->movefactor/4); // not too fast though. - } - } - - // Spawn a trail of rings behind the SPB! - SpawnSPBTrailRings(actor); - - if (dist <= (1024*actor->tracer->scale) && !(actor->flags2 & MF2_AMBUSH)) // Close enough to target? Use Ambush flag to disable targetting so I can have an easier time testing stuff... - { - S_StartSound(actor, actor->info->attacksound); - actor->extravalue1 = 1; // TARGET ACQUIRED - actor->extravalue2 = 7*TICRATE; - actor->cvmem = wspeed; - } - } - - // Finally, no matter what, the spb should not be able to be under the ground, or above the ceiling; - if (actor->z < actor->floorz) - actor->z = actor->floorz; - else if (actor->z > actor->ceilingz - actor->height) - actor->z = actor->ceilingz - actor->height; - - return; -} - -void A_BallhogExplode(mobj_t *actor) -{ - mobj_t *mo2; - - if (LUA_CallAction("A_BallhogExplode", actor)) - return; - - mo2 = P_SpawnMobj(actor->x, actor->y, actor->z, MT_BALLHOGBOOM); - P_SetScale(mo2, actor->scale*2); - mo2->destscale = mo2->scale; - S_StartSound(mo2, actor->info->deathsound); - return; -} - -// A_LightningFollowPlayer: -// Dumb simple function that gives a mobj its target's momentums without updating its angle. -void A_LightningFollowPlayer(mobj_t *actor) -{ - fixed_t sx, sy; - - if (LUA_CallAction("A_LightningFollowPlayer", actor)) - return; - - if (!actor->target) - return; - - { - if (actor->extravalue1) // Make the radius also follow the player somewhat accuratly - { - sx = actor->target->x + FixedMul((actor->target->scale*actor->extravalue1), FINECOSINE((actor->angle)>>ANGLETOFINESHIFT)); - sy = actor->target->y + FixedMul((actor->target->scale*actor->extravalue1), FINESINE((actor->angle)>>ANGLETOFINESHIFT)); - P_TeleportMove(actor, sx, sy, actor->target->z); - } - else // else just teleport to player directly - P_TeleportMove(actor, actor->target->x, actor->target->y, actor->target->z); - - K_MatchGenericExtraFlags(actor, actor->target); // copy our target for graviflip - actor->momx = actor->target->momx; - actor->momy = actor->target->momy; - actor->momz = actor->target->momz; // Give momentum since we don't teleport to our player literally every frame. - } -} - -// A_FZBoomFlash: -// Flash everyone close enough to the boom -void A_FZBoomFlash(mobj_t *actor) -{ - UINT8 i; - - if (LUA_CallAction("A_FZBoomFlash", actor)) - return; - - for (i = 0; i < MAXPLAYERS; i++) - { - fixed_t dist; - if (!playeringame[i] || !players[i].mo) - continue; - dist = P_AproxDistance(P_AproxDistance(actor->x-players[i].mo->x, actor->y-players[i].mo->y), actor->z-players[i].mo->z); - if (dist < 1536<x + (P_RandomRange(-rad, rad)*actor->scale), actor->y + (P_RandomRange(-rad, rad)*actor->scale), - actor->z + (P_RandomRange(0, 72)*actor->scale), MT_THOK); - - P_SetMobjState(smoke, S_FZEROSMOKE1); - smoke->tics += P_RandomRange(-3, 4); - smoke->scale = actor->scale*3; - } - return; -} - -// A_RandomShadowFrame -// Gives a random sprite for the Mayonaka static shadows. Dumb and simple. -void A_RandomShadowFrame(mobj_t *actor) -{ - mobj_t *fire; - mobj_t *fake; - - if (LUA_CallAction("A_RandomShadowFrame", (actor))) - return; - - if (!actor->extravalue1) // Hack that spawns thoks that look like random shadows. Otherwise the state would overwrite our frame and that's a pain. - { - fake = P_SpawnMobj(actor->x, actor->y, actor->z, MT_THOK); - fake->sprite = SPR_ENM1; - fake->frame = P_RandomRange(0, 6); - P_SetScale(fake, FRACUNIT*3/2); - fake->scale = FRACUNIT*3/2; - fake->destscale = FRACUNIT*3/2; - fake->angle = actor->angle; - fake->tics = -1; - actor->drawflags |= MFD_DONTDRAW; - actor->extravalue1 = 1; - } - - P_SetScale(actor, FRACUNIT*3/2); - - // I have NO CLUE how to hardcode all of that fancy Linedef Executor shit so the fire spinout will be done by these entities directly. - if (P_LookForPlayers(actor, false, false, 380<target && !actor->target->player->powers[pw_flashing] - && !actor->target->player->kartstuff[k_invincibilitytimer] - && !actor->target->player->kartstuff[k_growshrinktimer] - && !actor->target->player->kartstuff[k_spinouttimer] - && P_IsObjectOnGround(actor->target) - && actor->z == actor->target->z) - { - P_DamageMobj(actor->target, actor, actor, 1, DMG_NORMAL); - P_InstaThrust(actor->target, actor->angle, 16<target->x, actor->target->y, actor->target->z, MT_THOK); - P_SetMobjStateNF(fire, S_QUICKBOOM1); - P_SetScale(fire, 4<color = SKINCOLOR_KETCHUP; - S_StartSound(actor->target, sfx_fire2); - } - } - return; -} - -// A_RoamingShadowThinker -// Thinker for Midnight Channel's Roaming Shadows: -void A_RoamingShadowThinker(mobj_t *actor) -{ - mobj_t *wind; - - if (LUA_CallAction("A_RoamingShadowThinker", (actor))) - return; - - // extravalue1 replaces "movetimer" - // extravalue2 replaces "stoptimer" - - P_SetScale(actor, FRACUNIT*3/2); - if (!actor->extravalue2) - { - P_InstaThrust(actor, actor->angle, 8<extravalue1 = ((actor->extravalue1) ? (actor->extravalue1-1) : (TICRATE*5+1)); // deplete timer if set, set to 5 ticrate otherwise. - if (actor->extravalue1 == 1) // if timer reaches 1, do a u-turn. - { - actor->extravalue1 = 0; - actor->extravalue2 = 60; - } - // Search for and attack Players venturing too close in front of us. - - if (P_LookForPlayers(actor, false, false, 256<target && !actor->target->player->powers[pw_flashing] - && !actor->target->player->kartstuff[k_invincibilitytimer] - && !actor->target->player->kartstuff[k_growshrinktimer] - && !actor->target->player->kartstuff[k_spinouttimer]) - { - // send them flying and spawn the WIND! - P_InstaThrust(actor->target, 0, 0); - P_DamageMobj(actor->target, actor, actor, 1, DMG_NORMAL); - P_SetObjectMomZ(actor->target, 16<target, sfx_wind1); - - // Spawn the WIND: - wind = P_SpawnMobj(actor->target->x, actor->target->y, actor->target->z, MT_THOK); // Opaque layer: - P_SetMobjState(wind, S_GARU1); - P_SetScale(wind, FRACUNIT*3/2); - wind = P_SpawnMobj(actor->target->x, actor->target->y, actor->target->z, MT_THOK); // Translucent layer: - P_SetMobjState(wind, S_TGARU0); - P_SetScale(wind, FRACUNIT*3/2); - wind->destscale = 30<angle += ANG1*3; - actor->extravalue2--; - } - return; -} - -// A_MayonakaArrow -// Used for the arrow sprite animations in Mayonaka. It's only extra visual bullshit to make em more random. - -void A_MayonakaArrow(mobj_t *actor) -{ - INT32 flip = 0; - INT32 iswarning; - - if (LUA_CallAction("A_MayonakaArrow", (actor))) - return; - - iswarning = actor->spawnpoint->options & MTF_OBJECTSPECIAL; // is our object a warning sign? - // "animtimer" is replaced by "extravalue1" here. - actor->extravalue1 = ((actor->extravalue1) ? (actor->extravalue1+1) : (P_RandomRange(0, (iswarning) ? (TICRATE/2) : TICRATE*3))); - flip = ((actor->spawnpoint->options & 1) ? (3) : (0)); // flip adds 3 frames, which is the flipped version of the sign. - // special warning behavior: - if (iswarning) - flip = 6; - - actor->frame = flip + actor->extravalue2*3; - - if (actor->extravalue1 >= TICRATE*7/2) - { - actor->extravalue1 = 0; // reset to 0 and start a new cycle. - // special behavior for warning sign; swap from warning to sneaker & reverse - if (iswarning) - actor->extravalue2 = (actor->extravalue2) ? (0) : (1); - } - else if (actor->extravalue1 > TICRATE*7/2 -4) - actor->frame = flip+2; - else if (actor->extravalue1 > TICRATE*3 && leveltime%2 > 0) - actor->frame = flip+1; - - actor->frame |= FF_PAPERSPRITE; - actor->momz = 0; - return; -} - -// A_MementosTPParticles -// Mementos teleporters particles effects. Short and simple. - -void A_MementosTPParticles(mobj_t *actor) -{ - mobj_t *particle; - mobj_t *mo2; - int i = 0; - thinker_t *th; - - if (LUA_CallAction("A_MementosTPParticles", (actor))) - return; - - for (; i<4; i++) - { - particle = P_SpawnMobj(actor->x + (P_RandomRange(-256, 256)<y + (P_RandomRange(-256, 256)<z + (P_RandomRange(48, 256)<frame = 0; - particle->color = ((i%2) ? (SKINCOLOR_RED) : (SKINCOLOR_BLACK)); - particle->destscale = 1; - //P_HomingAttack(particle, actor); // Really now, Lat... - } - - // Although this is mostly used to spawn particles, we will also save the OTHER teleport inside actor->target. That way teleporting doesn't require a thinker iteration. - // Doesn't seem like much given the small amount of mobjs this map has but heh. - if (!actor->target) - { - for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) - { - if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) - continue; - - mo2 = (mobj_t *)th; - if (mo2->type == MT_MEMENTOSTP && mo2 != actor) - { - P_SetTarget(&actor->target, mo2); // The main target we're pursing. - break; - } - } - } -} - -// A_ReaperThinker -// Mementos's Reaper's thinker. A huge pain in the Derek Bum to translate from Lua to this shite if you ask me. - -void A_ReaperThinker(mobj_t *actor) -{ - mobj_t *particle; // particles to spawn - int i = 0; // for loops - angle_t an = ANGLE_22h; // Reminder that angle constants suck. - - //Waypoint stuff: - mobj_t *mo2; - thinker_t *th; - - //Player targetting stuff: - UINT32 maxscore = 0; // we target the player with the highest score so yeah there you go. - player_t *player; // used as a shortcut in a loop. - mobj_t *targetplayermo = NULL; // the player mo we can eventually target, or whatever. - - if (LUA_CallAction(A_REAPERTHINKER, (actor))) - return; - - // We don't have custom variables or whatever so we'll do with whatever the fuck we have left. - - if (actor->health == 1000) // if health is 1000, set it to a small scale and have it start growing with destscale. Then set the health to uh, not 1000. - { - actor->scale = 1; - actor->destscale = 2<scalespeed = FRACUNIT/24; // Should take a bit less than 2 seconds to fully grow. - S_StartSound(NULL, sfx_chain); - actor->health--; // now we have 999 health, so that above won't happen again. Awesome. - } - - if (actor->scale < 2<x + (P_RandomRange(-60, 60)<y + (P_RandomRange(-60, 60)<z, MT_THOK); - particle->momz = 20<color = ((i%2 !=0) ? (SKINCOLOR_RED) : (SKINCOLOR_BLACK)); - particle->frame = 0; - P_SetScale(particle, FRACUNIT/2); - } - - // Spawn particles in some edgy circle or w/e. - - if (leveltime%5 != 0) // spawn the thing under that every tic. - return; - - i=0; - for (; i<15; i++) // spawn in a circle formation or w/e. - { - particle = P_SpawnMobj(actor->x, actor->y, actor->z, MT_THOK); - particle->momz = 20<color = ((i%2 !=0) ? (SKINCOLOR_RED) : (SKINCOLOR_BLACK)); - particle->frame = 0; - P_SetScale(particle, FRACUNIT/2); - P_InstaThrust(particle, an*i, 30<flags = MF_NOGRAVITY|MF_PAIN|MF_SPECIAL|MF_NOCLIP|MF_NOCLIPHEIGHT; // set our flags to be a damaging thing. - // Handle animation: - if (!(leveltime%5)) - actor->extravalue2 = (actor->extravalue2 < 9) ? (actor->extravalue2+1) : (0); // Ghetto animation, but hey it works for what it's worth - - // Chain sfx - if (!S_SoundPlaying(actor, sfx_chain)) - S_StartSound(actor, sfx_chain); - - actor->frame = actor->extravalue2; // yes i'm that bad at maths don't @ me. - - if (!actor->target) - { - if (actor->hnext) - { - P_SetTarget(&actor->target, actor->hnext); // Default back to last waypoint. - return; - } - - // We have no target and oughta find one, so let's scan through thinkers for a waypoint of angle 0, or something. - for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) - { - if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) - continue; - - mo2 = (mobj_t *)th; - - if (mo2->type != MT_REAPERWAYPOINT) - continue; - if (mo2->spawnpoint->angle != 0) - continue; - - P_SetTarget(&actor->target, mo2); // The main target we're pursing. - P_SetTarget(&actor->hnext, mo2); // The last waypoint we hit. We will default back to that if a player goes out of our range! - actor->extravalue1 = 0; // This will store the angle of the last waypoint we touched. This will essentially be useful later on. - if (!actor->tracer) // If we already have a tracer (Waypoint #0), don't do anything. - { - P_SetTarget(&actor->tracer, mo2); // Because our target might be a player OR a waypoint, we need some sort of fallback option. This will always be waypoint 0. - break; - } - } - } - else // Awesome, we now have a target. - { - // Follow target: - P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y), 20<angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); - - // The player we should target if it's near us: - for (i=0; imo && player->bumpers && player->score >= maxscore) - { - targetplayermo = player->mo; - maxscore = player->score; - } - } - - // Try to target that player: - if (targetplayermo) - { - if (P_LookForPlayers(actor, false, false, 1024<target == targetplayermo && actor->target && !actor->target->player->powers[pw_flashing] - && !actor->target->player->kartstuff[k_invincibilitytimer] - && !actor->target->player->kartstuff[k_growshrinktimer] - && !actor->target->player->kartstuff[k_spinouttimer])) - P_SetTarget(&actor->target, actor->hnext); - // if the above isn't correct, then we should go back to targetting waypoints or something. - } - } - - // Waypoint behavior. - if (actor->target->type == MT_REAPERWAYPOINT) - { - if (R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y) < 22<target, NULL); // remove target so we can default back to first waypoint if things go ham. - - // If we reach close to a waypoint, then we should go to the NEXT one. - for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) - { - if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) - continue; - - mo2 = (mobj_t *)th; - - if (mo2->type != MT_REAPERWAYPOINT) - continue; - if (mo2->spawnpoint->angle != actor->extravalue1+1) - continue; - - P_SetTarget(&actor->target, mo2); // The main target we're pursing. - P_SetTarget(&actor->hnext, mo2); // The last waypoint we hit. We will default back to that if a player goes out of our range! - actor->extravalue1++; // This will store the angle of the last waypoint we touched. This will essentially be useful later on. - break; - } - } - - - if (!actor->target) // If we have no target, revert back to waypoint 0. - { - actor->extravalue1 = 0; - P_SetTarget(&actor->target, actor->tracer); - } - } - else // if our target ISN'T a waypoint, then it can only be a player. - { - if (!P_CheckSight(actor, actor->target) || R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y) > 1024<target, actor->hnext); - } - } -} - -void A_FlameShieldPaper(mobj_t *actor) -{ - INT32 framea = 0; - INT32 frameb = 0; - INT32 locvar1 = var1; - INT32 locvar2 = var2; - UINT8 i; - - if (LUA_CallAction(A_FLAMESHIELDPAPER, actor)) - return; - - framea = (locvar1 & FF_FRAMEMASK); - frameb = (locvar2 & FF_FRAMEMASK); - - for (i = 0; i < 2; i++) - { - INT32 perpendicular = ((i & 1) ? -ANGLE_90 : ANGLE_90); - fixed_t newx = actor->x + P_ReturnThrustX(NULL, actor->angle + perpendicular, 8*actor->scale); - fixed_t newy = actor->y + P_ReturnThrustY(NULL, actor->angle + perpendicular, 8*actor->scale); - mobj_t *paper = P_SpawnMobj(newx, newy, actor->z, MT_FLAMESHIELDPAPER); - - P_SetTarget(&paper->target, actor); - P_SetScale(paper, actor->scale); - paper->destscale = actor->destscale; - - P_SetMobjState(paper, S_FLAMESHIELDPAPER); - paper->frame &= ~FF_FRAMEMASK; - - paper->angle = actor->angle + ANGLE_45; - - if (i & 1) - { - paper->angle -= ANGLE_90; - paper->frame |= frameb; - } - else - { - paper->frame |= framea; - } - - paper->extravalue1 = i; - } -} - -//} - // Function: A_OrbitNights // // Description: Used by Chaos Emeralds to orbit around Nights (aka Super Sonic.) @@ -14792,3 +13410,1388 @@ void A_ChangeHeight(mobj_t *actor) } P_SetThingPosition(actor); } + +// +// SRB2Kart +// + +void A_ItemPop(mobj_t *actor) +{ + mobj_t *remains; + mobjtype_t explode; + + if (LUA_CallAction(A_ITEMPOP, actor)) + return; + + if (!(actor->target && actor->target->player)) + { + if (cv_debug && !(actor->target && actor->target->player)) + CONS_Printf("ERROR: Powerup has no target!\n"); + return; + } + + // de-solidify + P_UnsetThingPosition(actor); + actor->flags &= ~MF_SOLID; + actor->flags |= MF_NOCLIP; + P_SetThingPosition(actor); + + // item explosion + explode = mobjinfo[actor->info->damage].mass; + remains = P_SpawnMobj(actor->x, actor->y, + ((actor->eflags & MFE_VERTICALFLIP) ? (actor->z + 3*(actor->height/4) - FixedMul(mobjinfo[explode].height, actor->scale)) : (actor->z + actor->height/4)), explode); + if (actor->eflags & MFE_VERTICALFLIP) + { + remains->eflags |= MFE_VERTICALFLIP; + remains->flags2 |= MF2_OBJECTFLIP; + } + remains->destscale = actor->destscale; + P_SetScale(remains, actor->scale); + + remains = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->damage); + remains->type = actor->type; // Transfer type information + P_UnsetThingPosition(remains); + if (sector_list) + { + P_DelSeclist(sector_list); + sector_list = NULL; + } + P_SetThingPosition(remains); + remains->destscale = actor->destscale; + P_SetScale(remains, actor->scale); + remains->flags = actor->flags; // Transfer flags + remains->flags2 = actor->flags2; // Transfer flags2 + remains->fuse = actor->fuse; // Transfer respawn timer + remains->threshold = (actor->threshold == 70 ? 70 : (actor->threshold == 69 ? 69 : 68)); + remains->skin = NULL; + remains->spawnpoint = actor->spawnpoint; + + P_SetTarget(&tmthing, remains); + + if (actor->info->deathsound) + S_StartSound(remains, actor->info->deathsound); + + if (!((gametyperules & GTR_BUMPERS) && actor->target->player->bumpers <= 0)) + actor->target->player->kartstuff[k_itemroulette] = 1; + + remains->flags2 &= ~MF2_AMBUSH; + + if ((gametyperules & GTR_BUMPERS) && (actor->threshold != 69 && actor->threshold != 70)) + numgotboxes++; + + P_RemoveMobj(actor); +} + +void A_JawzChase(mobj_t *actor) +{ + const fixed_t currentspeed = R_PointToDist2(0, 0, actor->momx, actor->momy); + player_t *player; + fixed_t thrustamount = 0; + fixed_t frictionsafety = (actor->friction == 0) ? 1 : actor->friction; + fixed_t topspeed = actor->movefactor; + + if (LUA_CallAction(A_JAWZCHASE, actor)) + return; + + if (actor->tracer) + { + /*if ((gametyperules & GTR_CIRCUIT)) // Stop looking after first target in race + actor->extravalue1 = 1;*/ + + if (actor->tracer->health) + { + const angle_t targetangle = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); + mobj_t *ret; + angle_t angledelta = actor->angle - targetangle; + boolean turnclockwise = true; + + if (gametyperules & GTR_CIRCUIT) + { + const fixed_t distbarrier = FixedMul(512*mapobjectscale, FRACUNIT + ((gamespeed-1) * (FRACUNIT/4))); + const fixed_t distaway = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y); + if (distaway < distbarrier) + { + if (actor->tracer->player) + { + fixed_t speeddifference = abs(topspeed - min(actor->tracer->player->speed, K_GetKartSpeed(actor->tracer->player, false))); + topspeed = topspeed - FixedMul(speeddifference, FRACUNIT-FixedDiv(distaway, distbarrier)); + } + } + } + + if (angledelta != 0) + { + angle_t MAX_JAWZ_TURN = ANGLE_90/15; // We can turn a maximum of 6 degrees per frame at regular max speed + // MAX_JAWZ_TURN gets stronger the slower the top speed of jawz + if (topspeed < actor->movefactor) + { + if (topspeed == 0) + { + MAX_JAWZ_TURN = ANGLE_180; + } + else + { + fixed_t anglemultiplier = FixedDiv(actor->movefactor, topspeed); + MAX_JAWZ_TURN += FixedAngle(FixedMul(AngleFixed(MAX_JAWZ_TURN), anglemultiplier)); + } + } + + if (angledelta > ANGLE_180) + { + angledelta = InvAngle(angledelta); + turnclockwise = false; + } + + if (angledelta > MAX_JAWZ_TURN) + { + angledelta = MAX_JAWZ_TURN; + } + + if (turnclockwise) + { + actor->angle -= angledelta; + } + else + { + actor->angle += angledelta; + } + } + + ret = P_SpawnMobj(actor->tracer->x, actor->tracer->y, actor->tracer->z, MT_PLAYERRETICULE); + P_SetTarget(&ret->target, actor->tracer); + ret->frame |= ((leveltime % 10) / 2) + 5; + ret->color = actor->cvmem; + } + else + P_SetTarget(&actor->tracer, NULL); + } + + if (!P_IsObjectOnGround(actor)) + { + // No friction in the air + frictionsafety = FRACUNIT; + } + + if (currentspeed >= topspeed) + { + // Thrust as if you were at top speed, slow down naturally + thrustamount = FixedDiv(topspeed, frictionsafety) - topspeed; + } + else + { + const fixed_t beatfriction = FixedDiv(currentspeed, frictionsafety) - currentspeed; + // Thrust to immediately get to top speed + thrustamount = beatfriction + FixedDiv(topspeed - currentspeed, frictionsafety); + } + + if (!actor->tracer) + { + actor->angle = K_MomentumAngle(actor); + } + + P_Thrust(actor, actor->angle, thrustamount); + + if ((actor->tracer != NULL) && (actor->tracer->health > 0)) + return; + + if (actor->extravalue1) // Disable looking by setting this + return; + + if (!actor->target || P_MobjWasRemoved(actor->target)) // No source! + return; + + player = K_FindJawzTarget(actor, actor->target->player); + if (player) + P_SetTarget(&actor->tracer, player->mo); + + return; +} + +void A_JawzExplode(mobj_t *actor) +{ + INT32 shrapnel = 2; + mobj_t *truc; + + if (LUA_CallAction(A_JAWZEXPLODE, actor)) + return; + + truc = P_SpawnMobj(actor->x, actor->y, actor->z, MT_BOOMEXPLODE); + truc->scale = actor->scale*2; + truc->color = SKINCOLOR_KETCHUP; + + while (shrapnel) + { + INT32 speed, speed2; + + truc = P_SpawnMobj(actor->x + P_RandomRange(-8, 8)*FRACUNIT, actor->y + P_RandomRange(-8, 8)*FRACUNIT, + actor->z + P_RandomRange(0, 8)*FRACUNIT, MT_BOOMPARTICLE); + truc->scale = actor->scale*2; + + speed = FixedMul(7*FRACUNIT, actor->scale)>>FRACBITS; + truc->momx = P_RandomRange(-speed, speed)*FRACUNIT; + truc->momy = P_RandomRange(-speed, speed)*FRACUNIT; + + speed = FixedMul(5*FRACUNIT, actor->scale)>>FRACBITS; + speed2 = FixedMul(15*FRACUNIT, actor->scale)>>FRACBITS; + truc->momz = P_RandomRange(speed, speed2)*FRACUNIT; + truc->tics = TICRATE*2; + truc->color = SKINCOLOR_KETCHUP; + + shrapnel--; + } + + return; +} + +static void SpawnSPBTrailRings(mobj_t *actor) +{ + I_Assert(actor != NULL); + + if (leveltime % 6 == 0) + { + if (leveltime % (actor->extravalue1 == 2 ? 6 : 3) == 0) // Extravalue1 == 2 is seeking mode. Because the SPB is about twice as fast as normal in that mode, also spawn the rings twice as often to make up for it! + { + mobj_t *ring = P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momy, + actor->z - actor->momz + (24*mapobjectscale), MT_RING); + ring->threshold = 10; + ring->fuse = 35*TICRATE; + ring->colorized = true; + ring->color = SKINCOLOR_RED; + } + } +} + +// Spawns the V shaped dust. To be used when the SPB is going mostly forward. +static void SpawnSPBDust(mobj_t *mo) +{ + // The easiest way to spawn a V shaped cone of dust from the SPB is simply to spawn 2 particles, and to both move them to the sides in opposite direction. + mobj_t *dust; + fixed_t sx; + fixed_t sy; + fixed_t sz = mo->floorz; + angle_t sa = mo->angle - ANG1*60; + INT32 i; + + if (mo->eflags & MFE_VERTICALFLIP) + sz = mo->ceilingz; + + if (leveltime & 1 && abs(mo->z - sz) < FRACUNIT*64) // Only ever other frame. Also don't spawn it if we're way above the ground. + { + // Determine spawning position next to the SPB: + for (i=0; i < 2; i++) + { + sx = mo->x + FixedMul((mo->scale*96), FINECOSINE((sa)>>ANGLETOFINESHIFT)); + sy = mo->y + FixedMul((mo->scale*96), FINESINE((sa)>>ANGLETOFINESHIFT)); + + dust = P_SpawnMobj(sx, sy, sz, MT_SPBDUST); + dust->momx = mo->momx/2; + dust->momy = mo->momy/2; + dust->momz = mo->momz/2; // Give some of the momentum to the dust + P_SetScale(dust, mo->scale*2); + dust->colorized = true; + dust->color = SKINCOLOR_RED; + dust->angle = mo->angle - FixedAngle(FRACUNIT*90 - FRACUNIT*180*i); // The first one will spawn to the right of the spb, the second one to the left. + P_Thrust(dust, dust->angle, 6*dust->scale); + + K_MatchGenericExtraFlags(dust, mo); + + sa += ANG1*120; // Add 120 degrees to get to mo->angle + ANG1*60 + } + } +} + +// Spawns SPB slip tide. To be used when the SPB is turning. +// Modified version of K_SpawnAIZDust. Maybe we could merge those to be cleaner? + +// dir should be either 1 or -1 to determine where to spawn the dust. + +static void SpawnSPBAIZDust(mobj_t *mo, INT32 dir) +{ + fixed_t newx; + fixed_t newy; + mobj_t *spark; + angle_t travelangle; + fixed_t sz = mo->floorz; + + if (mo->eflags & MFE_VERTICALFLIP) + sz = mo->ceilingz; + + travelangle = K_MomentumAngle(mo); + if (leveltime & 1 && abs(mo->z - sz) < FRACUNIT*64) + { + newx = mo->x + P_ReturnThrustX(mo, travelangle - (dir*ANGLE_45), FixedMul(24*FRACUNIT, mo->scale)); + newy = mo->y + P_ReturnThrustY(mo, travelangle - (dir*ANGLE_45), FixedMul(24*FRACUNIT, mo->scale)); + spark = P_SpawnMobj(newx, newy, sz, MT_AIZDRIFTSTRAT); + spark->colorized = true; + spark->color = SKINCOLOR_RED; + spark->flags = MF_NOGRAVITY|MF_PAIN; + P_SetTarget(&spark->target, mo); + + spark->angle = travelangle+(dir*ANGLE_90); + P_SetScale(spark, (spark->destscale = mo->scale*3/2)); + + spark->momx = (6*mo->momx)/5; + spark->momy = (6*mo->momy)/5; + + K_MatchGenericExtraFlags(spark, mo); + } +} + +// Used for seeking and when SPB is trailing its target from way too close! +static void SpawnSPBSpeedLines(mobj_t *actor) +{ + mobj_t *fast = P_SpawnMobj(actor->x + (P_RandomRange(-24,24) * actor->scale), + actor->y + (P_RandomRange(-24,24) * actor->scale), + actor->z + (actor->height/2) + (P_RandomRange(-24,24) * actor->scale), + MT_FASTLINE); + + P_SetTarget(&fast->target, actor); + fast->angle = K_MomentumAngle(actor); + fast->color = SKINCOLOR_RED; + fast->colorized = true; + K_MatchGenericExtraFlags(fast, actor); +} + + +void A_SPBChase(mobj_t *actor) +{ + player_t *player = NULL; + player_t *scplayer = NULL; // secondary target for seeking + UINT8 i; + UINT8 bestrank = UINT8_MAX; + fixed_t dist; + angle_t hang, vang; + fixed_t wspeed, xyspeed, zspeed; + fixed_t pdist = 1536<threshold) // Just fired, go straight. + { + actor->lastlook = -1; + actor->cusval = -1; + spbplace = -1; + P_InstaThrust(actor, actor->angle, wspeed); + return; + } + + // Find the player with the best rank + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator || players[i].exiting) + continue; // not in-game + + /*if (!players[i].mo) + continue; // no mobj + + if (players[i].mo->health <= 0) + continue; // dead + + if (players[i].respawn.state != RESPAWNST_NONE) + continue;*/ // respawning + + if (players[i].kartstuff[k_position] < bestrank) + { + bestrank = players[i].kartstuff[k_position]; + player = &players[i]; + } + } + + // lastlook = last player num targetted + // cvmem = stored speed + // cusval = next waypoint heap index + // extravalue1 = SPB movement mode + // extravalue2 = mode misc option + + if (actor->extravalue1 == 1) // MODE: TARGETING + { + actor->cusval = -1; // Reset waypoint + + if (actor->tracer && actor->tracer->health) + { + fixed_t defspeed = wspeed; + fixed_t range = (160*actor->tracer->scale); + fixed_t cx = 0, cy =0; + + // Play the intimidating gurgle + if (!S_SoundPlaying(actor, actor->info->activesound)) + S_StartSound(actor, actor->info->activesound); + + // Maybe we want SPB to target an object later? IDK lol + if (actor->tracer->player) + { + UINT8 fracmax = 32; + UINT8 spark = ((10-actor->tracer->player->kartspeed) + actor->tracer->player->kartweight) / 2; + fixed_t easiness = ((actor->tracer->player->kartspeed + (10-spark)) << FRACBITS) / 2; + + actor->lastlook = actor->tracer->player-players; // Save the player num for death scumming... + actor->tracer->player->kartstuff[k_ringlock] = 1; // set ring lock + + if (!P_IsObjectOnGround(actor->tracer)) + { + // In the air you have no control; basically don't hit unless you make a near complete stop + defspeed = (7 * actor->tracer->player->speed) / 8; + } + else + { + // 7/8ths max speed for Knuckles, 3/4ths max speed for min accel, exactly max speed for max accel + defspeed = FixedMul(((fracmax+1)<tracer->player, false)) / fracmax; + } + + // Be fairer on conveyors + cx = actor->tracer->player->cmomx; + cy = actor->tracer->player->cmomy; + + // Switch targets if you're no longer 1st for long enough + if (actor->tracer->player->kartstuff[k_position] <= bestrank) + actor->extravalue2 = 7*TICRATE; + else if (actor->extravalue2-- <= 0) + actor->extravalue1 = 0; // back to SEEKING + + spbplace = actor->tracer->player->kartstuff[k_position]; + } + + dist = P_AproxDistance(P_AproxDistance(actor->x-actor->tracer->x, actor->y-actor->tracer->y), actor->z-actor->tracer->z); + + wspeed = FixedMul(defspeed, FRACUNIT + FixedDiv(dist-range, range)); + if (wspeed < defspeed) + wspeed = defspeed; + if (wspeed > (3*defspeed)/2) + wspeed = (3*defspeed)/2; + if (wspeed < 20*actor->tracer->scale) + wspeed = 20*actor->tracer->scale; + if (actor->tracer->player->pflags & PF_SLIDING) + wspeed = actor->tracer->player->speed/2; + // ^^^^ current section: These are annoying, and grand metropolis in particular needs this. + + hang = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); + vang = R_PointToAngle2(0, actor->z, dist, actor->tracer->z); + + // Modify stored speed + if (wspeed > actor->cvmem) + actor->cvmem += (wspeed - actor->cvmem) / TICRATE; + else + actor->cvmem = wspeed; + + { + // Smoothly rotate horz angle + angle_t input = hang - actor->angle; + boolean invert = (input > ANGLE_180); + if (invert) + input = InvAngle(input); + + // Slow down when turning; it looks better and makes U-turns not unfair + xyspeed = FixedMul(actor->cvmem, max(0, (((180<angle += input; + + // Smoothly rotate vert angle + input = vang - actor->movedir; + invert = (input > ANGLE_180); + if (invert) + input = InvAngle(input); + + // Slow down when turning; might as well do it for momz, since we do it above too + zspeed = FixedMul(actor->cvmem, max(0, (((180<movedir += input; + } + + actor->momx = cx + FixedMul(FixedMul(xyspeed, FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); + actor->momy = cy + FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); + actor->momz = FixedMul(zspeed, FINESINE(actor->movedir>>ANGLETOFINESHIFT)); + + // Spawn a trail of rings behind the SPB! + SpawnSPBTrailRings(actor); + + // Red speed lines for when it's gaining on its target. A tell for when you're starting to lose too much speed! + if (R_PointToDist2(0, 0, actor->momx, actor->momy) > (actor->tracer->player ? (16*actor->tracer->player->speed)/15 + : (16*R_PointToDist2(0, 0, actor->tracer->momx, actor->tracer->momy))/15) // Going faster than the target + && xyspeed > K_GetKartSpeed(actor->tracer->player, false)/4) // Don't display speedup lines at pitifully low speeds + SpawnSPBSpeedLines(actor); + + return; + } + else // Target's gone, return to SEEKING + { + P_SetTarget(&actor->tracer, NULL); + actor->extravalue1 = 2; // WAIT... + actor->extravalue2 = 52; // Slightly over the respawn timer length + return; + } + } + else if (actor->extravalue1 == 2) // MODE: WAIT... + { + actor->momx = actor->momy = actor->momz = 0; // Stoooop + actor->cusval = -1; // Reset waypoint + + if (actor->lastlook != -1 + && playeringame[actor->lastlook] + && !players[actor->lastlook].spectator + && !players[actor->lastlook].exiting) + { + spbplace = players[actor->lastlook].kartstuff[k_position]; + players[actor->lastlook].kartstuff[k_ringlock] = 1; + if (actor->extravalue2-- <= 0 && players[actor->lastlook].mo) + { + P_SetTarget(&actor->tracer, players[actor->lastlook].mo); + actor->extravalue1 = 1; // TARGET ACQUIRED + actor->extravalue2 = 7*TICRATE; + actor->cvmem = wspeed; + } + } + else + { + actor->extravalue1 = 0; // SEEKING + actor->extravalue2 = 0; + spbplace = -1; + } + } + else // MODE: SEEKING + { + waypoint_t *lastwaypoint = NULL; + waypoint_t *bestwaypoint = NULL; + waypoint_t *nextwaypoint = NULL; + waypoint_t *tempwaypoint = NULL; + + actor->lastlook = -1; // Just make sure this is reset + + if (!player || !player->mo || player->mo->health <= 0 || (player->respawn.state != RESPAWNST_NONE)) + { + // No one there? Completely STOP. + actor->momx = actor->momy = actor->momz = 0; + if (!player) + spbplace = -1; + return; + } + + // Found someone, now get close enough to initiate the slaughter... + P_SetTarget(&actor->tracer, player->mo); + spbplace = bestrank; + dist = P_AproxDistance(P_AproxDistance(actor->x-actor->tracer->x, actor->y-actor->tracer->y), actor->z-actor->tracer->z); + + /* + K_GetBestWaypointForMobj returns the waypoint closest to the object that isn't its current waypoint. While this is usually good enough, + in cases where the track overlaps, this means that the SPB will sometimes target a waypoint on an earlier/later portion of the track instead of following along. + For this reason, we're going to try and make sure to avoid these situations. + */ + + // Move along the waypoints until you get close enough + if (actor->cusval > -1 && actor->extravalue2 > 0) + { + // Previously set nextwaypoint + lastwaypoint = K_GetWaypointFromIndex((size_t)actor->cusval); + tempwaypoint = K_GetBestWaypointForMobj(actor); + // check if the tempwaypoint corresponds to lastwaypoint's next ID at least; + // This is to avoid situations where the SPB decides to suicide jump down a bridge because it found a COMPLETELY unrelated waypoint down there. + + if (K_GetWaypointID(tempwaypoint) == K_GetWaypointNextID(lastwaypoint) || K_GetWaypointID(tempwaypoint) == K_GetWaypointID(lastwaypoint)) + // either our previous or curr waypoint ID, sure, take it + bestwaypoint = tempwaypoint; + else + bestwaypoint = K_GetWaypointFromIndex((size_t)actor->extravalue2); // keep going from the PREVIOUS wp. + } + else + bestwaypoint = K_GetBestWaypointForMobj(actor); + + if (bestwaypoint == NULL && lastwaypoint == NULL) + { + // We have invalid waypoints all around, so use closest to try and make it non-NULL. + bestwaypoint = K_GetClosestWaypointToMobj(actor); + } + + if (bestwaypoint != NULL) + { + const boolean huntbackwards = false; + boolean useshortcuts = false; + + // If the player is on a shortcut, use shortcuts. No escape. + if (K_GetWaypointIsShortcut(player->nextwaypoint)) + { + useshortcuts = true; + } + + nextwaypoint = K_GetNextWaypointToDestination( + bestwaypoint, player->nextwaypoint, useshortcuts, huntbackwards); + } + + if (nextwaypoint == NULL && lastwaypoint != NULL) + { + // Restore to the last nextwaypoint + nextwaypoint = lastwaypoint; + } + + if (nextwaypoint != NULL) + { + const fixed_t xywaypointdist = P_AproxDistance( + actor->x - nextwaypoint->mobj->x, actor->y - nextwaypoint->mobj->y); + + hang = R_PointToAngle2(actor->x, actor->y, nextwaypoint->mobj->x, nextwaypoint->mobj->y); + vang = R_PointToAngle2(0, actor->z, xywaypointdist, nextwaypoint->mobj->z); + + actor->cusval = (INT32)K_GetWaypointHeapIndex(nextwaypoint); + actor->extravalue2 = (INT32)K_GetWaypointHeapIndex(bestwaypoint); // save our last best, used above. + } + else + { + // continue straight ahead... Shouldn't happen. + hang = actor->angle; + vang = 0U; + } + + { + // Smoothly rotate horz angle + angle_t input = hang - actor->angle; + boolean invert = (input > ANGLE_180); + INT32 turnangle; + + if (invert) + input = InvAngle(input); + + input = FixedAngle(AngleFixed(input)/8); + + // Slow down when turning; it looks better and makes U-turns not unfair + xyspeed = FixedMul(wspeed, max(0, (((180<angle += input; + + // If input is small enough, spawn dust. Otherwise, spawn a slip tide! + turnangle = AngleFixed(input)/FRACUNIT; + + // The SPB is really turning if that value is >= 3 and <= 357. This looks pretty bad check-wise so feel free to change it for something that isn't as terrible. + if (turnangle >= 3 && turnangle <= 357) + SpawnSPBAIZDust(actor, turnangle < 180 ? 1 : -1); // 1 if turning left, -1 if turning right. Angles work counterclockwise, remember! + else + SpawnSPBDust(actor); // if we're mostly going straight, then spawn the V dust cone! + + SpawnSPBSpeedLines(actor); // Always spawn speed lines while seeking + + // Smoothly rotate vert angle + input = vang - actor->movedir; + invert = (input > ANGLE_180); + if (invert) + input = InvAngle(input); + + input = FixedAngle(AngleFixed(input)/8); + + // Slow down when turning; might as well do it for momz, since we do it above too + zspeed = FixedMul(wspeed, max(0, (((180<movedir += input; + } + + actor->momx = FixedMul(FixedMul(xyspeed, FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); + actor->momy = FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); + actor->momz = FixedMul(zspeed, FINESINE(actor->movedir>>ANGLETOFINESHIFT)); + + // see if a player is near us, if they are, try to hit them by slightly thrusting towards them, otherwise, bleh! + for (i=0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator || players[i].exiting) + continue; // not in-game + + if (R_PointToDist2(actor->x, actor->y, players[i].mo->x, players[i].mo->y) < pdist) + { + pdist = R_PointToDist2(actor->x, actor->y, players[i].mo->x, players[i].mo->y); + scplayer = &players[i]; // it doesn't matter if we override this guy now. + } + } + + // different player from our main target, try and ram into em~! + if (scplayer && scplayer != player) + { + pangle = actor->angle - R_PointToAngle2(actor->x, actor->y,scplayer->mo->x, scplayer->mo->y); + // check if the angle wouldn't make us LOSE speed... + if ((INT32)pangle/ANG1 >= -80 && (INT32)pangle/ANG1 <= 80) // allow for around 80 degrees + { + // Thrust us towards the guy, try to screw em up! + P_Thrust(actor, R_PointToAngle2(actor->x, actor->y, scplayer->mo->x, scplayer->mo->y), actor->movefactor/4); // not too fast though. + } + } + + // Spawn a trail of rings behind the SPB! + SpawnSPBTrailRings(actor); + + if (dist <= (1024*actor->tracer->scale) && !(actor->flags2 & MF2_AMBUSH)) // Close enough to target? Use Ambush flag to disable targetting so I can have an easier time testing stuff... + { + S_StartSound(actor, actor->info->attacksound); + actor->extravalue1 = 1; // TARGET ACQUIRED + actor->extravalue2 = 7*TICRATE; + actor->cvmem = wspeed; + } + } + + // Finally, no matter what, the spb should not be able to be under the ground, or above the ceiling; + if (actor->z < actor->floorz) + actor->z = actor->floorz; + else if (actor->z > actor->ceilingz - actor->height) + actor->z = actor->ceilingz - actor->height; + + return; +} + +static mobj_t *grenade; +static fixed_t explodedist; + +static inline boolean PIT_SSMineSearch(mobj_t *thing) +{ + if (!grenade) + return false; + + if (grenade->flags2 & MF2_DEBRIS) + return false; + + if (thing->type != MT_PLAYER) // Don't explode for anything but an actual player. + return true; + + if (!(thing->flags & MF_SHOOTABLE)) + { + // didn't do any damage + return true; + } + + if (netgame && thing->player && thing->player->spectator) + return true; + + if (thing == grenade->target && grenade->threshold != 0) // Don't blow up at your owner. + return true; + + if (thing->player && (thing->player->kartstuff[k_hyudorotimer] + || ((gametyperules & GTR_BUMPERS) && thing->player && thing->player->bumpers <= 0 && thing->player->karmadelay))) + return true; + + // see if it went over / under + if (grenade->z - explodedist > thing->z + thing->height) + return true; // overhead + if (grenade->z + grenade->height + explodedist < thing->z) + return true; // underneath + + if (P_AproxDistance(P_AproxDistance(thing->x - grenade->x, thing->y - grenade->y), + thing->z - grenade->z) > explodedist) + return true; // Too far away + + // Explode! + P_SetMobjState(grenade, grenade->info->deathstate); + return false; +} + +void A_SSMineSearch(mobj_t *actor) +{ + INT32 bx, by, xl, xh, yl, yh; + explodedist = FixedMul(actor->info->painchance, mapobjectscale); + + if (LUA_CallAction(A_SSMINESEARCH, actor)) + return; + + if (actor->flags2 & MF2_DEBRIS) + return; + + if (actor->state == &states[S_SSMINE_DEPLOY8]) + explodedist = (3*explodedist)/2; + + if (leveltime % 35 == 0) + S_StartSound(actor, actor->info->activesound); + + // Use blockmap to check for nearby shootables + yh = (unsigned)(actor->y + explodedist - bmaporgy)>>MAPBLOCKSHIFT; + yl = (unsigned)(actor->y - explodedist - bmaporgy)>>MAPBLOCKSHIFT; + xh = (unsigned)(actor->x + explodedist - bmaporgx)>>MAPBLOCKSHIFT; + xl = (unsigned)(actor->x - explodedist - bmaporgx)>>MAPBLOCKSHIFT; + + grenade = actor; + + for (by = yl; by <= yh; by++) + for (bx = xl; bx <= xh; bx++) + P_BlockThingsIterator(bx, by, PIT_SSMineSearch); +} + +static inline boolean PIT_MineExplode(mobj_t *thing) +{ + if (!grenade || P_MobjWasRemoved(grenade)) + return false; // There's the possibility these can chain react onto themselves after they've already died if there are enough all in one spot + + if (grenade->flags2 & MF2_DEBRIS) // don't explode twice + return false; + + if (thing == grenade || thing->type == MT_MINEEXPLOSIONSOUND) // Don't explode yourself! Endless loop! + return true; + + if (!(thing->flags & MF_SHOOTABLE) || (thing->flags & MF_SCENERY)) + return true; + + if (netgame && thing->player && thing->player->spectator) + return true; + + if ((gametyperules & GTR_BUMPERS) && grenade->target && grenade->target->player && grenade->target->player->bumpers <= 0 && thing == grenade->target) + return true; + + // see if it went over / under + if (grenade->z - explodedist > thing->z + thing->height) + return true; // overhead + if (grenade->z + grenade->height + explodedist < thing->z) + return true; // underneath + + if (P_AproxDistance(P_AproxDistance(thing->x - grenade->x, thing->y - grenade->y), + thing->z - grenade->z) > explodedist) + return true; // Too far away + + P_DamageMobj(thing, grenade, grenade->target, 1, DMG_EXPLODE); + return true; +} + +void A_SSMineExplode(mobj_t *actor) +{ + INT32 bx, by, xl, xh, yl, yh; + INT32 d; + INT32 locvar1 = var1; + mobjtype_t type; + explodedist = FixedMul((3*actor->info->painchance)/2, mapobjectscale); + + if (LUA_CallAction(A_SSMINEEXPLODE, actor)) + return; + + if (actor->flags2 & MF2_DEBRIS) + return; + + type = (mobjtype_t)locvar1; + + // Use blockmap to check for nearby shootables + yh = (unsigned)(actor->y + explodedist - bmaporgy)>>MAPBLOCKSHIFT; + yl = (unsigned)(actor->y - explodedist - bmaporgy)>>MAPBLOCKSHIFT; + xh = (unsigned)(actor->x + explodedist - bmaporgx)>>MAPBLOCKSHIFT; + xl = (unsigned)(actor->x - explodedist - bmaporgx)>>MAPBLOCKSHIFT; + + BMBOUNDFIX (xl, xh, yl, yh); + + grenade = actor; + + for (by = yl; by <= yh; by++) + for (bx = xl; bx <= xh; bx++) + P_BlockThingsIterator(bx, by, PIT_MineExplode); + + for (d = 0; d < 16; d++) + K_SpawnKartExplosion(actor->x, actor->y, actor->z, explodedist + 32*mapobjectscale, 32, type, d*(ANGLE_45/4), true, false, actor->target); // 32 <-> 64 + + if (actor->target && actor->target->player) + K_SpawnMineExplosion(actor, actor->target->player->skincolor); + else + K_SpawnMineExplosion(actor, SKINCOLOR_KETCHUP); + + P_SpawnMobj(actor->x, actor->y, actor->z, MT_MINEEXPLOSIONSOUND); + + actor->flags2 |= MF2_DEBRIS; // Set this flag to ensure that the explosion won't be effective more than 1 frame. +} + +void A_BallhogExplode(mobj_t *actor) +{ + mobj_t *mo2; + + if (LUA_CallAction(A_BALLHOGEXPLODE, actor)) + return; + + mo2 = P_SpawnMobj(actor->x, actor->y, actor->z, MT_BALLHOGBOOM); + P_SetScale(mo2, actor->scale*2); + mo2->destscale = mo2->scale; + S_StartSound(mo2, actor->info->deathsound); + return; +} + +// A_LightningFollowPlayer: +// Dumb simple function that gives a mobj its target's momentums without updating its angle. +void A_LightningFollowPlayer(mobj_t *actor) +{ + fixed_t sx, sy; + + if (LUA_CallAction(A_LIGHTNINGFOLLOWPLAYER, actor)) + return; + + if (!actor->target) + return; + + { + if (actor->extravalue1) // Make the radius also follow the player somewhat accuratly + { + sx = actor->target->x + FixedMul((actor->target->scale*actor->extravalue1), FINECOSINE((actor->angle)>>ANGLETOFINESHIFT)); + sy = actor->target->y + FixedMul((actor->target->scale*actor->extravalue1), FINESINE((actor->angle)>>ANGLETOFINESHIFT)); + P_TeleportMove(actor, sx, sy, actor->target->z); + } + else // else just teleport to player directly + P_TeleportMove(actor, actor->target->x, actor->target->y, actor->target->z); + + K_MatchGenericExtraFlags(actor, actor->target); // copy our target for graviflip + actor->momx = actor->target->momx; + actor->momy = actor->target->momy; + actor->momz = actor->target->momz; // Give momentum since we don't teleport to our player literally every frame. + } +} + +// A_FZBoomFlash: +// Flash everyone close enough to the boom +void A_FZBoomFlash(mobj_t *actor) +{ + UINT8 i; + + if (LUA_CallAction(A_FZBOOMFLASH, actor)) + return; + + for (i = 0; i < MAXPLAYERS; i++) + { + fixed_t dist; + if (!playeringame[i] || !players[i].mo) + continue; + dist = P_AproxDistance(P_AproxDistance(actor->x-players[i].mo->x, actor->y-players[i].mo->y), actor->z-players[i].mo->z); + if (dist < 1536<x + (P_RandomRange(-rad, rad)*actor->scale), actor->y + (P_RandomRange(-rad, rad)*actor->scale), + actor->z + (P_RandomRange(0, 72)*actor->scale), MT_THOK); + + P_SetMobjState(smoke, S_FZEROSMOKE1); + smoke->tics += P_RandomRange(-3, 4); + smoke->scale = actor->scale*3; + } + return; +} + +// A_RandomShadowFrame +// Gives a random sprite for the Mayonaka static shadows. Dumb and simple. +void A_RandomShadowFrame(mobj_t *actor) +{ + mobj_t *fire; + mobj_t *fake; + + if (LUA_CallAction(A_RANDOMSHADOWFRAME, (actor))) + return; + + if (!actor->extravalue1) // Hack that spawns thoks that look like random shadows. Otherwise the state would overwrite our frame and that's a pain. + { + fake = P_SpawnMobj(actor->x, actor->y, actor->z, MT_THOK); + fake->sprite = SPR_ENM1; + fake->frame = P_RandomRange(0, 6); + P_SetScale(fake, FRACUNIT*3/2); + fake->scale = FRACUNIT*3/2; + fake->destscale = FRACUNIT*3/2; + fake->angle = actor->angle; + fake->tics = -1; + actor->drawflags |= MFD_DONTDRAW; + actor->extravalue1 = 1; + } + + P_SetScale(actor, FRACUNIT*3/2); + + // I have NO CLUE how to hardcode all of that fancy Linedef Executor shit so the fire spinout will be done by these entities directly. + if (P_LookForPlayers(actor, false, false, 380<target && !actor->target->player->powers[pw_flashing] + && !actor->target->player->kartstuff[k_invincibilitytimer] + && !actor->target->player->kartstuff[k_growshrinktimer] + && !actor->target->player->kartstuff[k_spinouttimer] + && P_IsObjectOnGround(actor->target) + && actor->z == actor->target->z) + { + P_DamageMobj(actor->target, actor, actor, 1, DMG_NORMAL); + P_InstaThrust(actor->target, actor->angle, 16<target->x, actor->target->y, actor->target->z, MT_THOK); + P_SetMobjStateNF(fire, S_QUICKBOOM1); + P_SetScale(fire, 4<color = SKINCOLOR_KETCHUP; + S_StartSound(actor->target, sfx_fire2); + } + } + return; +} + +// A_RoamingShadowThinker +// Thinker for Midnight Channel's Roaming Shadows: +void A_RoamingShadowThinker(mobj_t *actor) +{ + mobj_t *wind; + + if (LUA_CallAction(A_ROAMINGSHADOWTHINKER, (actor))) + return; + + // extravalue1 replaces "movetimer" + // extravalue2 replaces "stoptimer" + + P_SetScale(actor, FRACUNIT*3/2); + if (!actor->extravalue2) + { + P_InstaThrust(actor, actor->angle, 8<extravalue1 = ((actor->extravalue1) ? (actor->extravalue1-1) : (TICRATE*5+1)); // deplete timer if set, set to 5 ticrate otherwise. + if (actor->extravalue1 == 1) // if timer reaches 1, do a u-turn. + { + actor->extravalue1 = 0; + actor->extravalue2 = 60; + } + // Search for and attack Players venturing too close in front of us. + + if (P_LookForPlayers(actor, false, false, 256<target && !actor->target->player->powers[pw_flashing] + && !actor->target->player->kartstuff[k_invincibilitytimer] + && !actor->target->player->kartstuff[k_growshrinktimer] + && !actor->target->player->kartstuff[k_spinouttimer]) + { + // send them flying and spawn the WIND! + P_InstaThrust(actor->target, 0, 0); + P_DamageMobj(actor->target, actor, actor, 1, DMG_NORMAL); + P_SetObjectMomZ(actor->target, 16<target, sfx_wind1); + + // Spawn the WIND: + wind = P_SpawnMobj(actor->target->x, actor->target->y, actor->target->z, MT_THOK); // Opaque layer: + P_SetMobjState(wind, S_GARU1); + P_SetScale(wind, FRACUNIT*3/2); + wind = P_SpawnMobj(actor->target->x, actor->target->y, actor->target->z, MT_THOK); // Translucent layer: + P_SetMobjState(wind, S_TGARU0); + P_SetScale(wind, FRACUNIT*3/2); + wind->destscale = 30<angle += ANG1*3; + actor->extravalue2--; + } + return; +} + +// A_MayonakaArrow +// Used for the arrow sprite animations in Mayonaka. It's only extra visual bullshit to make em more random. + +void A_MayonakaArrow(mobj_t *actor) +{ + INT32 flip = 0; + INT32 iswarning; + + if (LUA_CallAction(A_MAYONAKAARROW, (actor))) + return; + + iswarning = actor->spawnpoint->options & MTF_OBJECTSPECIAL; // is our object a warning sign? + // "animtimer" is replaced by "extravalue1" here. + actor->extravalue1 = ((actor->extravalue1) ? (actor->extravalue1+1) : (P_RandomRange(0, (iswarning) ? (TICRATE/2) : TICRATE*3))); + flip = ((actor->spawnpoint->options & 1) ? (3) : (0)); // flip adds 3 frames, which is the flipped version of the sign. + // special warning behavior: + if (iswarning) + flip = 6; + + actor->frame = flip + actor->extravalue2*3; + + if (actor->extravalue1 >= TICRATE*7/2) + { + actor->extravalue1 = 0; // reset to 0 and start a new cycle. + // special behavior for warning sign; swap from warning to sneaker & reverse + if (iswarning) + actor->extravalue2 = (actor->extravalue2) ? (0) : (1); + } + else if (actor->extravalue1 > TICRATE*7/2 -4) + actor->frame = flip+2; + else if (actor->extravalue1 > TICRATE*3 && leveltime%2 > 0) + actor->frame = flip+1; + + actor->frame |= FF_PAPERSPRITE; + actor->momz = 0; + return; +} + +// A_MementosTPParticles +// Mementos teleporters particles effects. Short and simple. + +void A_MementosTPParticles(mobj_t *actor) +{ + mobj_t *particle; + mobj_t *mo2; + int i = 0; + thinker_t *th; + + if (LUA_CallAction(A_MEMENTOSTPPARTICLES, (actor))) + return; + + for (; i<4; i++) + { + particle = P_SpawnMobj(actor->x + (P_RandomRange(-256, 256)<y + (P_RandomRange(-256, 256)<z + (P_RandomRange(48, 256)<frame = 0; + particle->color = ((i%2) ? (SKINCOLOR_RED) : (SKINCOLOR_BLACK)); + particle->destscale = 1; + //P_HomingAttack(particle, actor); // Really now, Lat... + } + + // Although this is mostly used to spawn particles, we will also save the OTHER teleport inside actor->target. That way teleporting doesn't require a thinker iteration. + // Doesn't seem like much given the small amount of mobjs this map has but heh. + if (!actor->target) + { + for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) + { + if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + continue; + + mo2 = (mobj_t *)th; + if (mo2->type == MT_MEMENTOSTP && mo2 != actor) + { + P_SetTarget(&actor->target, mo2); // The main target we're pursing. + break; + } + } + } +} + +// A_ReaperThinker +// Mementos's Reaper's thinker. A huge pain in the Derek Bum to translate from Lua to this shite if you ask me. + +void A_ReaperThinker(mobj_t *actor) +{ + mobj_t *particle; // particles to spawn + int i = 0; // for loops + angle_t an = ANGLE_22h; // Reminder that angle constants suck. + + //Waypoint stuff: + mobj_t *mo2; + thinker_t *th; + + //Player targetting stuff: + UINT32 maxscore = 0; // we target the player with the highest score so yeah there you go. + player_t *player; // used as a shortcut in a loop. + mobj_t *targetplayermo = NULL; // the player mo we can eventually target, or whatever. + + if (LUA_CallAction(A_REAPERTHINKER, (actor))) + return; + + // We don't have custom variables or whatever so we'll do with whatever the fuck we have left. + + if (actor->health == 1000) // if health is 1000, set it to a small scale and have it start growing with destscale. Then set the health to uh, not 1000. + { + actor->scale = 1; + actor->destscale = 2<scalespeed = FRACUNIT/24; // Should take a bit less than 2 seconds to fully grow. + S_StartSound(NULL, sfx_chain); + actor->health--; // now we have 999 health, so that above won't happen again. Awesome. + } + + if (actor->scale < 2<x + (P_RandomRange(-60, 60)<y + (P_RandomRange(-60, 60)<z, MT_THOK); + particle->momz = 20<color = ((i%2 !=0) ? (SKINCOLOR_RED) : (SKINCOLOR_BLACK)); + particle->frame = 0; + P_SetScale(particle, FRACUNIT/2); + } + + // Spawn particles in some edgy circle or w/e. + + if (leveltime%5 != 0) // spawn the thing under that every tic. + return; + + i=0; + for (; i<15; i++) // spawn in a circle formation or w/e. + { + particle = P_SpawnMobj(actor->x, actor->y, actor->z, MT_THOK); + particle->momz = 20<color = ((i%2 !=0) ? (SKINCOLOR_RED) : (SKINCOLOR_BLACK)); + particle->frame = 0; + P_SetScale(particle, FRACUNIT/2); + P_InstaThrust(particle, an*i, 30<flags = MF_NOGRAVITY|MF_PAIN|MF_SPECIAL|MF_NOCLIP|MF_NOCLIPHEIGHT; // set our flags to be a damaging thing. + // Handle animation: + if (!(leveltime%5)) + actor->extravalue2 = (actor->extravalue2 < 9) ? (actor->extravalue2+1) : (0); // Ghetto animation, but hey it works for what it's worth + + // Chain sfx + if (!S_SoundPlaying(actor, sfx_chain)) + S_StartSound(actor, sfx_chain); + + actor->frame = actor->extravalue2; // yes i'm that bad at maths don't @ me. + + if (!actor->target) + { + if (actor->hnext) + { + P_SetTarget(&actor->target, actor->hnext); // Default back to last waypoint. + return; + } + + // We have no target and oughta find one, so let's scan through thinkers for a waypoint of angle 0, or something. + for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) + { + if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + continue; + + mo2 = (mobj_t *)th; + + if (mo2->type != MT_REAPERWAYPOINT) + continue; + if (mo2->spawnpoint->angle != 0) + continue; + + P_SetTarget(&actor->target, mo2); // The main target we're pursing. + P_SetTarget(&actor->hnext, mo2); // The last waypoint we hit. We will default back to that if a player goes out of our range! + actor->extravalue1 = 0; // This will store the angle of the last waypoint we touched. This will essentially be useful later on. + if (!actor->tracer) // If we already have a tracer (Waypoint #0), don't do anything. + { + P_SetTarget(&actor->tracer, mo2); // Because our target might be a player OR a waypoint, we need some sort of fallback option. This will always be waypoint 0. + break; + } + } + } + else // Awesome, we now have a target. + { + // Follow target: + P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y), 20<angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); + + // The player we should target if it's near us: + for (i=0; imo && player->bumpers && player->score >= maxscore) + { + targetplayermo = player->mo; + maxscore = player->score; + } + } + + // Try to target that player: + if (targetplayermo) + { + if (P_LookForPlayers(actor, false, false, 1024<target == targetplayermo && actor->target && !actor->target->player->powers[pw_flashing] + && !actor->target->player->kartstuff[k_invincibilitytimer] + && !actor->target->player->kartstuff[k_growshrinktimer] + && !actor->target->player->kartstuff[k_spinouttimer])) + P_SetTarget(&actor->target, actor->hnext); + // if the above isn't correct, then we should go back to targetting waypoints or something. + } + } + + // Waypoint behavior. + if (actor->target->type == MT_REAPERWAYPOINT) + { + if (R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y) < 22<target, NULL); // remove target so we can default back to first waypoint if things go ham. + + // If we reach close to a waypoint, then we should go to the NEXT one. + for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) + { + if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + continue; + + mo2 = (mobj_t *)th; + + if (mo2->type != MT_REAPERWAYPOINT) + continue; + if (mo2->spawnpoint->angle != actor->extravalue1+1) + continue; + + P_SetTarget(&actor->target, mo2); // The main target we're pursing. + P_SetTarget(&actor->hnext, mo2); // The last waypoint we hit. We will default back to that if a player goes out of our range! + actor->extravalue1++; // This will store the angle of the last waypoint we touched. This will essentially be useful later on. + break; + } + } + + + if (!actor->target) // If we have no target, revert back to waypoint 0. + { + actor->extravalue1 = 0; + P_SetTarget(&actor->target, actor->tracer); + } + } + else // if our target ISN'T a waypoint, then it can only be a player. + { + if (!P_CheckSight(actor, actor->target) || R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y) > 1024<target, actor->hnext); + } + } +} + +void A_FlameShieldPaper(mobj_t *actor) +{ + INT32 framea = 0; + INT32 frameb = 0; + INT32 locvar1 = var1; + INT32 locvar2 = var2; + UINT8 i; + + if (LUA_CallAction(A_FLAMESHIELDPAPER, actor)) + return; + + framea = (locvar1 & FF_FRAMEMASK); + frameb = (locvar2 & FF_FRAMEMASK); + + for (i = 0; i < 2; i++) + { + INT32 perpendicular = ((i & 1) ? -ANGLE_90 : ANGLE_90); + fixed_t newx = actor->x + P_ReturnThrustX(NULL, actor->angle + perpendicular, 8*actor->scale); + fixed_t newy = actor->y + P_ReturnThrustY(NULL, actor->angle + perpendicular, 8*actor->scale); + mobj_t *paper = P_SpawnMobj(newx, newy, actor->z, MT_FLAMESHIELDPAPER); + + P_SetTarget(&paper->target, actor); + P_SetScale(paper, actor->scale); + paper->destscale = actor->destscale; + + P_SetMobjState(paper, S_FLAMESHIELDPAPER); + paper->frame &= ~FF_FRAMEMASK; + + paper->angle = actor->angle + ANGLE_45; + + if (i & 1) + { + paper->angle -= ANGLE_90; + paper->frame |= frameb; + } + else + { + paper->frame |= framea; + } + + paper->extravalue1 = i; + } +} diff --git a/src/p_mobj.c b/src/p_mobj.c index cace18492..df05e6983 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6599,7 +6599,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) if ((mobj->state >= &states[S_SSMINE1] && mobj->state <= &states[S_SSMINE4]) || (mobj->state >= &states[S_SSMINE_DEPLOY8] && mobj->state <= &states[S_SSMINE_DEPLOY13])) - A_GrenadeRing(mobj); + A_SSMineSearch(mobj); if (mobj->threshold > 0) mobj->threshold--; @@ -11717,6 +11717,7 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean { const fixed_t mobjscale = mapheaderinfo[gamemap-1]->default_waypoint_radius; + mtag_t tag = Tag_FGet(&mthing->tags); if (mthing->args[1] > 0) mobj->radius = (mthing->args[1]) * FRACUNIT; @@ -11732,7 +11733,7 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean // extravalue1 is used for indicating the waypoint is disabled // extravalue2 is used for indicating the waypoint is the finishline mobj->threshold = mthing->args[0]; - mobj->movecount = mthing->tag; + mobj->movecount = tag; if (mthing->options & MTF_EXTRA) { mobj->extravalue1 = 0; // The waypoint is disabled if extra is on diff --git a/src/r_defs.h b/src/r_defs.h index c958fa8a7..0fe713dc5 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -731,28 +731,28 @@ typedef struct typedef enum { - RF_HORIZONTALFLIP = 0x0001, // Flip sprite horizontally - RF_VERTICALFLIP = 0x0002, // Flip sprite vertically - RF_ABSOLUTEOFFSETS = 0x0004, // Sprite uses the object's offsets absolutely, instead of relatively - RF_FLIPOFFSETS = 0x0008, // Relative object offsets are flipped with the sprite + RF_HORIZONTALFLIP = 0x00000001, // Flip sprite horizontally + RF_VERTICALFLIP = 0x00000002, // Flip sprite vertically + RF_ABSOLUTEOFFSETS = 0x00000004, // Sprite uses the object's offsets absolutely, instead of relatively + RF_FLIPOFFSETS = 0x00000008, // Relative object offsets are flipped with the sprite - RF_SPLATMASK = 0x00F0, // --Floor sprite flags - RF_SLOPESPLAT = 0x0010, // Rotate floor sprites by a slope - RF_OBJECTSLOPESPLAT = 0x0020, // Rotate floor sprites by the object's standing slope - RF_NOSPLATBILLBOARD = 0x0040, // Don't billboard floor sprites (faces forward from the view angle) - RF_NOSPLATROLLANGLE = 0x0080, // Don't rotate floor sprites by the object's rollangle (uses rotated patches instead) + RF_SPLATMASK = 0x000000F0, // --Floor sprite flags + RF_SLOPESPLAT = 0x00000010, // Rotate floor sprites by a slope + RF_OBJECTSLOPESPLAT = 0x00000020, // Rotate floor sprites by the object's standing slope + RF_NOSPLATBILLBOARD = 0x00000040, // Don't billboard floor sprites (faces forward from the view angle) + RF_NOSPLATROLLANGLE = 0x00000080, // Don't rotate floor sprites by the object's rollangle (uses rotated patches instead) - RF_BLENDMASK = 0x0F00, // --Blending modes - RF_FULLBRIGHT = 0x0100, // Sprite is drawn at full brightness - RF_FULLDARK = 0x0200, // Sprite is drawn completely dark - RF_NOCOLORMAPS = 0x0400, // Sprite is not drawn with colormaps + RF_BLENDMASK = 0x00000F00, // --Blending modes + RF_FULLBRIGHT = 0x00000100, // Sprite is drawn at full brightness + RF_FULLDARK = 0x00000200, // Sprite is drawn completely dark + RF_NOCOLORMAPS = 0x00000400, // Sprite is not drawn with colormaps - RF_SPRITETYPEMASK = 0x7000, // ---Different sprite types - RF_PAPERSPRITE = 0x1000, // Paper sprite - RF_FLOORSPRITE = 0x2000, // Floor sprite + RF_SPRITETYPEMASK = 0x00007000, // ---Different sprite types + RF_PAPERSPRITE = 0x00001000, // Paper sprite + RF_FLOORSPRITE = 0x00002000, // Floor sprite - RF_SHADOWDRAW = 0x10000, // Stretches and skews the sprite like a shadow. - RF_SHADOWEFFECTS = 0x20000, // Scales and becomes transparent like a shadow. + RF_SHADOWDRAW = 0x00010000, // Stretches and skews the sprite like a shadow. + RF_SHADOWEFFECTS = 0x00020000, // Scales and becomes transparent like a shadow. RF_DROPSHADOW = (RF_SHADOWDRAW | RF_SHADOWEFFECTS | RF_FULLDARK), } renderflags_t; diff --git a/src/r_things.c b/src/r_things.c index 7e6ce0a4e..4b9520ce1 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -3108,6 +3108,11 @@ boolean R_ThingIsFullBright(mobj_t *thing) return (thing->frame & FF_FULLBRIGHT || thing->renderflags & RF_FULLBRIGHT); } +boolean R_ThingIsSemiBright(mobj_t *thing) +{ + return (thing->frame & FF_SEMIBRIGHT || thing->renderflags & RF_SEMIBRIGHT); +} + boolean R_ThingIsFullDark(mobj_t *thing) { return (thing->renderflags & RF_FULLDARK); diff --git a/src/r_things.h b/src/r_things.h index 7345ca503..ba7d03880 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -81,6 +81,7 @@ boolean R_ThingIsPaperSprite (mobj_t *thing); boolean R_ThingIsFloorSprite (mobj_t *thing); boolean R_ThingIsFullBright (mobj_t *thing); +boolean R_ThingIsSemiBright (mobj_t *thing); boolean R_ThingIsFullDark (mobj_t *thing); // -------------- diff --git a/src/y_inter.c b/src/y_inter.c index 028068055..e9c3fcf51 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -464,7 +464,6 @@ void Y_IntermissionDrawer(void) else if (bgtile) V_DrawPatchFill(bgtile); -dontdrawbg: LUAh_IntermissionHUD(); if (!LUA_HudEnabled(hud_intermissiontally)) goto skiptallydrawer;