diff --git a/src/Makefile b/src/Makefile index 00db0c55b..a6f18a72a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -495,6 +495,8 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/y_inter.o \ $(OBJDIR)/st_stuff.o \ $(OBJDIR)/k_kart.o \ + $(OBJDIR)/k_collide.o\ + $(OBJDIR)/k_battle.o \ $(OBJDIR)/k_pwrlv.o \ $(OBJDIR)/k_waypoint.o\ $(OBJDIR)/k_pathfind.o\ diff --git a/src/d_clisrv.c b/src/d_clisrv.c index eadff6726..b96842510 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -47,6 +47,7 @@ #include "lua_script.h" #include "lua_hook.h" #include "k_kart.h" +#include "k_battle.h" #include "k_pwrlv.h" #ifdef CLIENT_LOADINGSCREEN @@ -3516,7 +3517,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) splitscreenplayer = (UINT8)READUINT8(*p); CONS_Debug(DBG_NETPLAY, "addplayer: %d %d %d\n", node, newplayernum, splitscreenplayer); - + // Clear player before joining, lest some things get set incorrectly CL_ClearPlayer(newplayernum); @@ -3767,7 +3768,11 @@ void SV_StartSinglePlayerServer(void) server = true; netgame = false; multiplayer = false; - gametype = GT_RACE; //srb2kart + + if (modeattacking == ATTACKING_CAPSULES) + gametype = GT_MATCH; //srb2kart + else + gametype = GT_RACE; //srb2kart // no more tic the game with this settings! SV_StopServer(); diff --git a/src/d_main.c b/src/d_main.c index d6839f1b3..80bc1c72c 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -468,7 +468,7 @@ static void D_Display(void) break; } - + topleft = screens[0] + viewwindowy*vid.width + viewwindowx; } diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f553538c2..e3435b39f 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -47,6 +47,7 @@ #include "m_cond.h" #include "m_anigif.h" #include "k_kart.h" // SRB2kart +#include "k_battle.h" #include "k_pwrlv.h" #include "y_inter.h" diff --git a/src/dehacked.c b/src/dehacked.c index a9d46b2a4..f053a080c 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -2473,10 +2473,10 @@ static void readunlockable(MYFILE *f, INT32 num) 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, "TIMEATTACK")) + unlockables[num].type = SECRET_TIMEATTACK; + else if (fastcmp(word2, "BREAKTHECAPSULES")) + unlockables[num].type = SECRET_BREAKTHECAPSULES; else if (fastcmp(word2, "HEADER")) unlockables[num].type = SECRET_HEADER; else if (fastcmp(word2, "LEVELSELECT")) @@ -7219,6 +7219,13 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_OVERTIMEORB", "S_OVERTIMEBEAM", + "S_BATTLECAPSULE_SIDE1", + "S_BATTLECAPSULE_SIDE2", + "S_BATTLECAPSULE_TOP", + "S_BATTLECAPSULE_BUTTON", + "S_BATTLECAPSULE_SUPPORT", + "S_BATTLECAPSULE_SUPPORTFLY", + #ifdef SEENAMES "S_NAMECHECK", #endif @@ -8018,6 +8025,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_OVERTIMEORB", "MT_OVERTIMEBEAM", + "MT_BATTLECAPSULE", + "MT_BATTLECAPSULE_PIECE", + #ifdef SEENAMES "MT_NAMECHECK", #endif diff --git a/src/djgppdos/internal.h b/src/djgppdos/internal.h index 94c1a052b..b34b5ebb7 100644 --- a/src/djgppdos/internal.h +++ b/src/djgppdos/internal.h @@ -1,6 +1,6 @@ -/* ______ ___ ___ - * /\ _ \ /\_ \ /\_ \ - * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ +/* ______ ___ ___ + * /\ _ \ /\_ \ /\_ \ + * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ @@ -22,9 +22,9 @@ #include "allegro.h" -/* ______ ___ ___ - * /\ _ \ /\_ \ /\_ \ - * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ +/* ______ ___ ___ + * /\ _ \ /\_ \ /\_ \ + * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ @@ -57,7 +57,7 @@ #define FILE_CREATE(filename, handle) handle = open(filename, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR) #define FILE_CLOSE(handle) close(handle) #define FILE_READ(handle, buf, size, sz) sz = read(handle, buf, size) -#define FILE_WRITE(handle, buf, size, sz) sz = write(handle, buf, size) +#define FILE_WRITE(handle, buf, size, sz) sz = write(handle, buf, size) #define FILE_SEARCH_STRUCT struct ffblk #define FILE_FINDFIRST(filename, attrib, dta) findfirst(filename, dta, attrib) #define FILE_FINDNEXT(dta) findnext(dta) @@ -73,11 +73,11 @@ #define ENABLE() asm volatile ("sti") -__INLINE__ void enter_critical(void) +__INLINE__ void enter_critical(void) { if (windows_version >= 3) { __dpmi_regs r; - r.x.ax = 0x1681; + r.x.ax = 0x1681; __dpmi_int(0x2F, &r); } @@ -85,11 +85,11 @@ __INLINE__ void enter_critical(void) } -__INLINE__ void exit_critical(void) +__INLINE__ void exit_critical(void) { if (windows_version >= 3) { __dpmi_regs r; - r.x.ax = 0x1682; + r.x.ax = 0x1682; __dpmi_int(0x2F, &r); } @@ -176,9 +176,9 @@ extern int _fm_port; extern int _mpu_port; extern int _mpu_irq; extern int _sb_freq; -extern int _sb_port; -extern int _sb_dma; -extern int _sb_irq; +extern int _sb_port; +extern int _sb_dma; +extern int _sb_irq; int _sb_read_dsp_version(void); int _sb_reset_dsp(int data); @@ -238,8 +238,8 @@ extern volatile int _retrace_hpp_value; /* caches and tables for svga bank switching */ -extern int _last_bank_1, _last_bank_2; -extern int *_gfx_bank; +extern int _last_bank_1, _last_bank_2; +extern int *_gfx_bank; /* bank switching routines */ @@ -282,7 +282,7 @@ extern int _crtc; __INLINE__ int _read_vga_register(int port, int index) { if (port==0x3C0) - inportb(_crtc+6); + inportb(_crtc+6); outportb(port, index); return inportb(port+1); @@ -292,7 +292,7 @@ __INLINE__ int _read_vga_register(int port, int index) /* _write_vga_register: * Writes a byte to a VGA register. */ -__INLINE__ void _write_vga_register(int port, int index, int v) +__INLINE__ void _write_vga_register(int port, int index, int v) { if (port==0x3C0) { inportb(_crtc+6); @@ -345,7 +345,7 @@ __INLINE__ void _vsync_out_v(void) __INLINE__ void _vsync_in(void) { if (_timer_use_retrace) { - int t = retrace_count; + int t = retrace_count; do { } while (t == retrace_count); @@ -555,7 +555,7 @@ typedef struct POLYGON_SEGMENT /* an active polygon edge */ -typedef struct POLYGON_EDGE +typedef struct POLYGON_EDGE { int top; /* top y position */ int bottom; /* bottom y position */ @@ -673,7 +673,7 @@ void _poly_scanline_ptex_mask_lit32d(unsigned long addr, int w, POLYGON_SEGMENT /* sound lib stuff */ extern int _digi_volume; extern int _midi_volume; -extern int _flip_pan; +extern int _flip_pan; extern int _sound_hq; extern int (*_midi_init)(void); diff --git a/src/doomstat.h b/src/doomstat.h index 163c04265..3a89e8d28 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -82,7 +82,7 @@ extern boolean metalrecording; #define ATTACKING_NONE 0 #define ATTACKING_RECORD 1 -//#define ATTACKING_NIGHTS 2 +#define ATTACKING_CAPSULES 2 extern UINT8 modeattacking; // menu demo things @@ -385,7 +385,7 @@ extern UINT16 emeralds; #define EMERALD7 64 #define ALL7EMERALDS(v) ((v & (EMERALD1|EMERALD2|EMERALD3|EMERALD4|EMERALD5|EMERALD6|EMERALD7)) == (EMERALD1|EMERALD2|EMERALD3|EMERALD4|EMERALD5|EMERALD6|EMERALD7)) -extern INT32 nummaprings, nummapboxes, numgotboxes; //keep track of spawned rings/coins/battle mode items +extern INT32 nummaprings; // keep track of spawned rings/coins /** Time attack information, currently a very small structure. */ @@ -518,15 +518,6 @@ extern INT16 votelevels[5][2]; extern SINT8 votes[MAXPLAYERS]; extern SINT8 pickedvote; -/** Battle overtime information - */ -extern struct battleovertime -{ - UINT16 enabled; ///< Has this been initalized yet? - fixed_t radius, minradius; ///< Radius of kill field - fixed_t x, y, z; ///< Position to center on -} battleovertime; - extern tic_t hidetime; extern UINT32 timesBeaten; // # of times the game has been beaten. diff --git a/src/f_wipe.c b/src/f_wipe.c index 8d73c1fe7..e42042516 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -204,7 +204,7 @@ static void F_DoWipe(fademask_t *fademask, lighttable_t *fadecolormap, boolean r // --- // Sal: I kinda destroyed some of this code by introducing Genesis-style fades. // A colormap can be provided in F_RunWipe, which the white/black values will be - // remapped to the appropriate entry in the fade colormap. + // remapped to the appropriate entry in the fade colormap. { // wipe screen, start, end UINT8 *w = wipe_scr; @@ -434,7 +434,7 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu, const char *colormap, boolean r reverse = false; } - paldiv = FixedDiv(257<DM->Coop - if (gametype == GT_CTF && players[playernum].ctfteam) + else if (gametype == GT_CTF && players[playernum].ctfteam) { if (!(spawnpoint = G_FindCTFStart(playernum)) // find a CTF start && !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start @@ -4805,8 +4803,8 @@ char *G_BuildMapTitle(INT32 mapnum) #define DEMOHEADER "\xF0" "KartReplay" "\x0F" #define DF_GHOST 0x01 // This demo contains ghost data too! -#define DF_RECORDATTACK 0x02 // This demo is from record attack and contains its final completion time! -#define DF_NIGHTSATTACK 0x04 // This demo is from NiGHTS attack and contains its time left, score, and mares! +#define DF_TIMEATTACK 0x02 // This demo is from Time Attack and contains its final completion time & best lap! +#define DF_BREAKTHECAPSULES 0x04 // This demo is from Break the Capsules and contains its final completion time! #define DF_ATTACKMASK 0x06 // This demo is from ??? attack and contains ??? #define DF_ATTACKSHIFT 1 #define DF_ENCORE 0x40 @@ -4844,7 +4842,6 @@ static ticcmd_t oldcmd[MAXPLAYERS]; // Not used for Metal Sonic #define GZT_SPRITE 0x10 // Animation frame #define GZT_EXTRA 0x20 -#define GZT_NIGHTS 0x40 // NiGHTS Mode stuff! // GZT_EXTRA flags #define EZT_THOK 0x01 // Spawned a thok object @@ -5350,13 +5347,6 @@ void G_WriteGhostTic(mobj_t *ghost, INT32 playernum) if (!(demoflags & DF_GHOST)) return; // No ghost data to write. - if (ghost->player && ghost->player->pflags & PF_NIGHTSMODE && ghost->tracer) - { - // We're talking about the NiGHTS thing, not the normal platforming thing! - ziptic |= GZT_NIGHTS; - ghost = ghost->tracer; - } - ziptic_p = demo_p++; // the ziptic, written at the end of this function #define MAXMOM (0x7FFF<<8) @@ -5573,12 +5563,6 @@ void G_ConsGhostTic(INT32 playernum) demo_p++; if (ziptic & GZT_SPRITE) demo_p++; - if(ziptic & GZT_NIGHTS) { - if (!testmo || !testmo->player || !(testmo->player->pflags & PF_NIGHTSMODE) || !testmo->tracer) - nightsfail = true; - else - testmo = testmo->tracer; - } if (ziptic & GZT_EXTRA) { // But wait, there's more! @@ -6409,6 +6393,10 @@ void G_BeginRecording(void) WRITEUINT32(demo_p,UINT32_MAX); // time WRITEUINT32(demo_p,UINT32_MAX); // lap break; + case ATTACKING_CAPSULES: // 2 + demotime_p = demo_p; + WRITEUINT32(demo_p,UINT32_MAX); // time + break; default: // 3 break; } @@ -6551,13 +6539,18 @@ void G_SetDemoTime(UINT32 ptime, UINT32 plap) { if (!demo.recording || !demotime_p) return; - - if (demoflags & DF_RECORDATTACK) + if (demoflags & DF_TIMEATTACK) { WRITEUINT32(demotime_p, ptime); WRITEUINT32(demotime_p, plap); demotime_p = NULL; } + else if (demoflags & DF_BREAKTHECAPSULES) + { + WRITEUINT32(demotime_p, ptime); + (void)plap; + demotime_p = NULL; + } } static void G_LoadDemoExtraFiles(UINT8 **pp) @@ -6733,6 +6726,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) UINT8 c; UINT16 s ATTRUNUSED; UINT8 aflags = 0; + boolean uselaps = false; // load the new file FIL_DefaultExtension(newname, ".lmp"); @@ -6759,20 +6753,17 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) p++; // gametype G_SkipDemoExtraFiles(&p); - aflags = flags & (DF_RECORDATTACK|DF_NIGHTSATTACK); + aflags = flags & (DF_TIMEATTACK|DF_BREAKTHECAPSULES); I_Assert(aflags); - if (flags & DF_RECORDATTACK) - { - newtime = READUINT32(p); + + if (flags & DF_TIMEATTACK) + uselaps = true; // get around uninitalized error + + newtime = READUINT32(p); + if (uselaps) newlap = READUINT32(p); - } - /*else if (flags & DF_NIGHTSATTACK) - { - newtime = READUINT32(p); - newscore = READUINT32(p); - }*/ - else // appease compiler - return 0; + else + newlap = UINT32_MAX; Z_Free(buffer); @@ -6824,28 +6815,32 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) Z_Free(buffer); return UINT8_MAX; } - if (flags & DF_RECORDATTACK) - { - oldtime = READUINT32(p); + + oldtime = READUINT32(p); + if (uselaps) oldlap = READUINT32(p); - } - /*else if (flags & DF_NIGHTSATTACK) - { - oldtime = READUINT32(p); - oldscore = READUINT32(p); - }*/ - else // appease compiler - return UINT8_MAX; + else + oldlap = 0; Z_Free(buffer); c = 0; - if (newtime < oldtime - || (newtime == oldtime && (newlap < oldlap))) - c |= 1; // Better time - if (newlap < oldlap - || (newlap == oldlap && newtime < oldtime)) - c |= 1<<1; // Better lap time + + if (uselaps) + { + if (newtime < oldtime + || (newtime == oldtime && (newlap < oldlap))) + c |= 1; // Better time + if (newlap < oldlap + || (newlap == oldlap && newtime < oldtime)) + c |= 1<<1; // Better lap time + } + else + { + if (newtime < oldtime) + c |= 1; // Better time + } + return c; } @@ -7220,19 +7215,18 @@ void G_DoPlayDemo(char *defdemoname) switch (modeattacking) { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_RECORD: // 1 - hu_demotime = READUINT32(demo_p); - hu_demolap = READUINT32(demo_p); - break; - /*case ATTACKING_NIGHTS: // 2 - hu_demotime = READUINT32(demo_p); - hu_demoscore = READUINT32(demo_p); - break;*/ - default: // 3 - modeattacking = ATTACKING_NONE; - break; + case ATTACKING_NONE: // 0 + break; + case ATTACKING_RECORD: // 1 + hu_demotime = READUINT32(demo_p); + hu_demolap = READUINT32(demo_p); + break; + case ATTACKING_CAPSULES: // 2 + hu_demotime = READUINT32(demo_p); + break; + default: // 3 + modeattacking = ATTACKING_NONE; + break; } // Random seed @@ -7527,16 +7521,16 @@ void G_AddGhost(char *defdemoname) switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_RECORD: // 1 - p += 8; // demo time, lap - break; - /*case ATTACKING_NIGHTS: // 2 - p += 8; // demo time left, score - break;*/ - default: // 3 - break; + case ATTACKING_NONE: // 0 + break; + case ATTACKING_RECORD: // 1 + p += 8; // demo time, lap + break; + case ATTACKING_CAPSULES: // 2 + p += 4; // demo time + break; + default: // 3 + break; } p += 4; // random seed @@ -7728,6 +7722,9 @@ void G_UpdateStaffGhostName(lumpnum_t l) case ATTACKING_RECORD: // 1 p += 8; // demo time, lap break; + case ATTACKING_CAPSULES: // 2 + p += 4; // demo time + break; default: // 3 break; } @@ -8059,7 +8056,7 @@ void G_SaveDemo(void) free(demobuffer); demo.recording = false; - if (modeattacking != ATTACKING_RECORD) + if (!modeattacking) { if (demo.savemode == DSM_SAVED) CONS_Printf(M_GetText("Demo %s recorded\n"), demoname); diff --git a/src/info.c b/src/info.c index cdccdb420..ab9dd44eb 100644 --- a/src/info.c +++ b/src/info.c @@ -3460,6 +3460,13 @@ state_t states[NUMSTATES] = {SPR_OTFG, 2|FF_FULLBRIGHT|FF_PAPERSPRITE, 1, {NULL}, 0, 0, S_NULL}, // S_OVERTIMEORB {SPR_OTFG, 1|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_NULL}, // S_OVERTIMEBEAM + {SPR_CAPS, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_BATTLECAPSULE_SIDE1 + {SPR_CAPS, FF_PAPERSPRITE|1, -1, {NULL}, 0, 0, S_NULL}, // S_BATTLECAPSULE_SIDE2 + {SPR_CAPS, 2, -1, {NULL}, 0, 0, S_NULL}, // S_BATTLECAPSULE_TOP + {SPR_CAPS, 3, -1, {NULL}, 0, 0, S_NULL}, // S_BATTLECAPSULE_BUTTON + {SPR_CAPS, 4, -1, {NULL}, 0, 0, S_NULL}, // S_BATTLECAPSULE_SUPPORT + {SPR_CAPS, FF_ANIMATE|5, -1, {NULL}, 3, 1, S_NULL}, // S_BATTLECAPSULE_SUPPORTFLY + #ifdef SEENAMES {SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK #endif @@ -20441,6 +20448,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_BATTLECAPSULE + 2333, // doomednum + S_INVISIBLE, // spawnstate + 1, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_INVISIBLE, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 28< bestbumper) + { + bestbumper = players[i].kartstuff[k_bumper]; + bestbumperplayer = i; + } + else if (players[i].kartstuff[k_bumper] == bestbumper) + bestbumperplayer = -1; // Tie, no one has best bumper. + + for (j = 0; j < MAXPLAYERS; j++) + { + if (!playeringame[j] || players[j].spectator) + continue; + if (players[j].kartstuff[k_bumper] <= 0) + continue; + if (j == i) + continue; + if (players[j].kartstuff[k_wanted] == players[i].kartstuff[k_wanted] && players[j].marescore > players[i].marescore) + position++; + else if (players[j].kartstuff[k_wanted] > players[i].kartstuff[k_wanted]) + position++; + } + + position--; // Make zero based + + while (camppos[position] != -1) // Port priority! + position++; + + camppos[position] = i; + } + + if (numplaying <= 2 || (numingame <= 2 && bestbumper == 1)) // In 1v1s then there's no need for WANTED. In bigger netgames, don't show anyone as WANTED when they're equally matched. + numwanted = 0; + else + numwanted = min(4, 1 + ((numingame-2) / 4)); + + for (i = 0; i < 4; i++) + { + if (i+1 > numwanted) // Not enough players for this slot to be wanted! + battlewanted[i] = -1; + else if (bestbumperplayer != -1 && !setbumper) // If there's a player who has an untied bumper lead over everyone else, they are the first to be wanted. + { + battlewanted[i] = bestbumperplayer; + setbumper = true; // Don't set twice + } + else + { + // Don't accidentally set the same player, if the bestbumperplayer is also a huge camper. + while (bestbumperplayer != -1 && camppos[nextcamppos] != -1 + && bestbumperplayer == camppos[nextcamppos]) + nextcamppos++; + + // Do not add *any* more people if there's too many times that are tied with others. + // This could theoretically happen very easily if people don't hit each other for a while after the start of a match. + // (I will be sincerely impressed if more than 2 people tie after people start hitting each other though) + + if (camppos[nextcamppos] == -1 // Out of entries + || ties >= (numwanted-i)) // Already counted ties + { + battlewanted[i] = -1; + continue; + } + + if (ties < (numwanted-i)) + { + ties = 0; // Reset + for (j = 0; j < 2; j++) + { + if (camppos[nextcamppos+(j+1)] == -1) // Nothing beyond, cancel + break; + if (players[camppos[nextcamppos]].kartstuff[k_wanted] == players[camppos[nextcamppos+(j+1)]].kartstuff[k_wanted]) + ties++; + } + } + + if (ties < (numwanted-i)) // Is it still low enough after counting? + { + battlewanted[i] = camppos[nextcamppos]; + nextcamppos++; + } + else + battlewanted[i] = -1; + } + } +} + +void K_SpawnBattlePoints(player_t *source, player_t *victim, UINT8 amount) +{ + statenum_t st; + mobj_t *pt; + + if (!source || !source->mo) + return; + + if (amount == 1) + st = S_BATTLEPOINT1A; + else if (amount == 2) + st = S_BATTLEPOINT2A; + else if (amount == 3) + st = S_BATTLEPOINT3A; + else + return; // NO STATE! + + pt = P_SpawnMobj(source->mo->x, source->mo->y, source->mo->z, MT_BATTLEPOINT); + P_SetTarget(&pt->target, source->mo); + P_SetMobjState(pt, st); + if (victim && victim->skincolor) + pt->color = victim->skincolor; + else + pt->color = source->skincolor; +} + +void K_CheckBumpers(void) +{ + UINT8 i; + UINT8 numingame = 0; + SINT8 winnernum = -1; + INT32 winnerscoreadd = 0; + boolean nobumpers = false; + + if (!G_BattleGametype()) + return; + + if (gameaction == ga_completed) + return; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) // not even in-game + continue; + + if (players[i].exiting) // we're already exiting! stop! + return; + + numingame++; + winnerscoreadd += players[i].marescore; + + if (players[i].kartstuff[k_bumper] <= 0) // if you don't have any bumpers, you're probably not a winner + { + nobumpers = true; + continue; + } + else if (winnernum != -1) // TWO winners? that's dumb :V + return; + + winnernum = i; + winnerscoreadd -= players[i].marescore; + } + + if (numingame <= 1) + { + if (!battlecapsules) + { + // Reset map to turn on battle capsules + D_MapChange(gamemap, gametype, encoremode, true, 0, false, false); + } + else + { + if (nobumpers) + { + for (i = 0; i < MAXPLAYERS; i++) + { + players[i].pflags |= PF_TIMEOVER; + //players[i].lives = 0; + P_DoPlayerExit(&players[i]); + } + } + } + + return; + } + + if (winnernum > -1 && playeringame[winnernum]) + { + players[winnernum].marescore += winnerscoreadd; + CONS_Printf(M_GetText("%s recieved %d point%s for winning!\n"), player_names[winnernum], winnerscoreadd, (winnerscoreadd == 1 ? "" : "s")); + } + + for (i = 0; i < MAXPLAYERS; i++) // This can't go in the earlier loop because winning adds points + K_KartUpdatePosition(&players[i]); + + for (i = 0; i < MAXPLAYERS; i++) // and it can't be merged with this loop because it needs to be all updated before exiting... multi-loops suck... + P_DoPlayerExit(&players[i]); +} + +#define MAXPLANESPERSECTOR (MAXFFLOORS+1)*2 + +static void K_SpawnOvertimeParticles(fixed_t x, fixed_t y, fixed_t scale, mobjtype_t type, boolean ceiling) +{ + UINT8 i; + fixed_t flatz[MAXPLANESPERSECTOR]; + boolean flip[MAXPLANESPERSECTOR]; + UINT8 numflats = 0; + mobj_t *mo; + subsector_t *ss = R_IsPointInSubsector(x, y); + sector_t *sec; + + if (!ss) + return; + sec = ss->sector; + + // convoluted stuff JUST to get all of the planes we need to draw orbs on :V + + for (i = 0; i < MAXPLANESPERSECTOR; i++) + flip[i] = false; + + if (sec->floorpic != skyflatnum) + { +#ifdef ESLOPE + flatz[numflats] = (sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : sec->floorheight); +#else + flatz[numflats] = (sec->floorheight); +#endif + numflats++; + } + if (sec->ceilingpic != skyflatnum && ceiling) + { +#ifdef ESLOPE + flatz[numflats] = (sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : sec->ceilingheight) - FixedMul(mobjinfo[type].height, scale); +#else + flatz[numflats] = (sec->ceilingheight) - FixedMul(mobjinfo[type].height, scale); +#endif + flip[numflats] = true; + numflats++; + } + + if (sec->ffloors) + { + ffloor_t *rover; + for (rover = sec->ffloors; rover; rover = rover->next) + { + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER)) + continue; + if (*rover->toppic != skyflatnum) + { +#ifdef ESLOPE + flatz[numflats] = (*rover->t_slope ? P_GetZAt(*rover->t_slope, x, y) : *rover->topheight); +#else + flatz[numflats] = (*rover->topheight); +#endif + numflats++; + } + if (*rover->bottompic != skyflatnum && ceiling) + { +#ifdef ESLOPE + flatz[numflats] = (*rover->b_slope ? P_GetZAt(*rover->b_slope, x, y) : *rover->bottomheight) - FixedMul(mobjinfo[type].height, scale); +#else + flatz[numflats] = (*rover->bottomheight) - FixedMul(mobjinfo[type].height, scale); +#endif + flip[numflats] = true; + numflats++; + } + } + } + + if (numflats <= 0) // no flats + return; + + for (i = 0; i < numflats; i++) + { + mo = P_SpawnMobj(x, y, flatz[i], type); + + // Lastly, if this can see the skybox mobj, then... we just wasted our time :V + if (skyboxmo[0] && !P_MobjWasRemoved(skyboxmo[0])) + { + const fixed_t sbz = skyboxmo[0]->z; + fixed_t checkz = sec->floorheight; + + while (checkz < sec->ceilingheight) + { + P_TeleportMove(skyboxmo[0], skyboxmo[0]->x, skyboxmo[0]->y, checkz); + if (P_CheckSight(skyboxmo[0], mo)) + { + P_RemoveMobj(mo); + break; + } + else + checkz += 32*mapobjectscale; + } + + P_TeleportMove(skyboxmo[0], skyboxmo[0]->x, skyboxmo[0]->y, sbz); + + if (P_MobjWasRemoved(mo)) + continue; + } + + P_SetScale(mo, scale); + + if (flip[i]) + { + mo->flags2 |= MF2_OBJECTFLIP; + mo->eflags |= MFE_VERTICALFLIP; + } + + switch(type) + { + case MT_OVERTIMEFOG: + mo->destscale = 8*mo->scale; + mo->momz = P_RandomRange(1,8)*mo->scale; + break; + case MT_OVERTIMEORB: + //mo->destscale = mo->scale/4; + mo->frame += ((leveltime/4) % 8); + /*if (battleovertime.enabled < 10*TICRATE) + mo->flags2 |= MF2_SHADOW;*/ + mo->angle = R_PointToAngle2(mo->x, mo->y, battleovertime.x, battleovertime.y) + ANGLE_90; + mo->z += P_RandomRange(0,48) * mo->scale; + break; + default: + break; + } + } +} + +#undef MAXPLANESPERSECTOR + +void K_RunBattleOvertime(void) +{ + UINT16 i, j; + + if (battleovertime.enabled < 10*TICRATE) + { + battleovertime.enabled++; + if (battleovertime.enabled == TICRATE) + S_StartSound(NULL, sfx_bhurry); + if (battleovertime.enabled == 10*TICRATE) + S_StartSound(NULL, sfx_kc40); + } + else + { + if (battleovertime.radius > battleovertime.minradius) + battleovertime.radius -= mapobjectscale; + else + battleovertime.radius = battleovertime.minradius; + } + + if (leveltime & 1) + { + UINT8 transparency = tr_trans50; + + if (!splitscreen && players[displayplayers[0]].mo) + { + INT32 dist = P_AproxDistance(battleovertime.x-players[displayplayers[0]].mo->x, battleovertime.y-players[displayplayers[0]].mo->y); + transparency = max(0, NUMTRANSMAPS - ((256 + (dist>>FRACBITS)) / 256)); + } + + if (transparency < NUMTRANSMAPS) + { + mobj_t *beam = P_SpawnMobj(battleovertime.x, battleovertime.y, battleovertime.z + (mobjinfo[MT_RANDOMITEM].height/2), MT_OVERTIMEBEAM); + P_SetScale(beam, beam->scale*2); + if (transparency > 0) + beam->frame |= transparency<>FRACBITS / 2));*/ + + for (i = 0; i < 16; i++) + { + j = 0; + while (j < 32) // max attempts + { + fixed_t x = battleovertime.x + ((P_RandomRange(-64,64) * 128)<extrainfo-1; + fixed_t speed = (FRACUNIT >> 3) * mt->angle; + boolean backandforth = (mt->options & MTF_AMBUSH); + boolean reverse = (mt->options & MTF_OBJECTSPECIAL); + mobj_t *mo2; + mobj_t *target = NULL; + thinker_t *th; + + // Find the inital target + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker + continue; + + mo2 = (mobj_t *)th; + + if (mo2->type != MT_TUBEWAYPOINT) + continue; + + if (mo2->threshold == sequence) + { + if (reverse) // Use the highest waypoint number as first + { + if (mo2->health != 0) + { + if (target == NULL) + target = mo2; + else if (mo2->health > target->health) + target = mo2; + } + } + else // Use the lowest waypoint number as first + { + if (mo2->health == 0) + target = mo2; + } + } + } + + if (!target) + { + CONS_Alert(CONS_WARNING, "No target waypoint found for moving capsule (seq: #%d)\n", sequence); + return; + } + + P_SetTarget(&mobj->target, target); + mobj->lastlook = sequence; + mobj->movecount = target->health; + mobj->movefactor = speed; + + if (backandforth) { + mobj->flags2 |= MF2_AMBUSH; + } else { + mobj->flags2 &= ~MF2_AMBUSH; + } + + if (reverse) { + mobj->cvmem = -1; + } else { + mobj->cvmem = 1; + } +} + +void K_SpawnBattleCapsules(void) +{ + mapthing_t *mt; + size_t i; + + if (battlecapsules) + return; + + if (!G_BattleGametype()) + return; + + if (modeattacking != ATTACKING_CAPSULES) + { + UINT8 n = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator) + n++; + if (players[i].exiting) + return; + if (n > 1) + break; + } + + if (n > 1) + return; + } + + mt = mapthings; + for (i = 0; i < nummapthings; i++, mt++) + { + if (mt->type == mobjinfo[MT_BATTLECAPSULE].doomednum) + { + sector_t *mtsector, *sec; + fixed_t x, y, z; + fixed_t floorheights[MAXFFLOORS+1]; + UINT8 numfloors = 1; + mobj_t *mobj = NULL; + boolean fly = true; + UINT8 j; + + mt->mobj = NULL; + + mtsector = R_PointInSubsector(mt->x << FRACBITS, mt->y << FRACBITS)->sector; + mt->z = (INT16)( +#ifdef ESLOPE + mtsector->f_slope ? P_GetZAt(mtsector->f_slope, mt->x << FRACBITS, mt->y << FRACBITS) : +#endif + mtsector->floorheight)>>FRACBITS; + + x = mt->x << FRACBITS; + y = mt->y << FRACBITS; + + sec = R_PointInSubsector(x, y)->sector; + + if (mt->options & MTF_OBJECTFLIP) + { + z = ( +#ifdef ESLOPE + sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : +#endif + sec->ceilingheight) - mobjinfo[MT_BATTLECAPSULE].height; + + floorheights[0] = z; + + if (mt->options >> ZSHIFT) + z -= ((mt->options >> ZSHIFT) << FRACBITS); + } + else + { + z = +#ifdef ESLOPE + sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : +#endif + sec->floorheight; + + floorheights[0] = z; + + if (mt->options >> ZSHIFT) + z += ((mt->options >> ZSHIFT) << FRACBITS); + } + + + if (sec->ffloors) + { + ffloor_t *rover; + for (rover = sec->ffloors; rover; rover = rover->next) + { + if ((rover->flags & FF_EXISTS) && (rover->flags & FF_BLOCKOTHERS)) + { + if (mt->options & MTF_OBJECTFLIP) + { + floorheights[numfloors] = ( +#ifdef ESLOPE + *rover->b_slope ? P_GetZAt(*rover->b_slope, x, y) : +#endif + *rover->bottomheight) - mobjinfo[MT_BATTLECAPSULE].height; + } + else + { + floorheights[numfloors] = ( +#ifdef ESLOPE + *rover->t_slope ? P_GetZAt(*rover->t_slope, x, y) : +#endif + *rover->topheight); + } + + numfloors++; + } + } + } + + mt->z = (INT16)(z>>FRACBITS); + + mobj = P_SpawnMobj(x, y, z, MT_BATTLECAPSULE); + mobj->spawnpoint = mt; + + if (mt->options & MTF_OBJECTFLIP) + { + mobj->eflags |= MFE_VERTICALFLIP; + mobj->flags2 |= MF2_OBJECTFLIP; + } + + for (j = 0; j < numfloors; j++) + { + if (z == floorheights[j]) + { + fly = false; + break; + } + } + + // Flying capsules + if (fly) + { + mobj->flags |= MF_NOGRAVITY; + mobj->extravalue1 = 1; // Set extravalue1 for later reference + } + + // Moving capsules! + if (mt->extrainfo && mt->angle) + K_SetupMovingCapsule(mt, mobj); + + // Moved from P_SpawnMobj due to order of operations mumbo jumbo + { + mobj_t *cur, *prev = mobj; + + // Init hnext list + // Spherical top + cur = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_BATTLECAPSULE_PIECE); + P_SetMobjState(cur, S_BATTLECAPSULE_TOP); + + P_SetTarget(&cur->target, mobj); + P_SetTarget(&cur->hprev, prev); + P_SetTarget(&prev->hnext, cur); + prev = cur; + + // Tippity-top decorational button + cur = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_BATTLECAPSULE_PIECE); + P_SetMobjState(cur, S_BATTLECAPSULE_BUTTON); + + P_SetTarget(&cur->target, mobj); + P_SetTarget(&cur->hprev, prev); + P_SetTarget(&prev->hnext, cur); + prev = cur; + + // Supports on the bottom + for (j = 0; j < 4; j++) + { + cur = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_BATTLECAPSULE_PIECE); + cur->extravalue1 = j; + + if (mobj->extravalue1) // Flying capsule, moving or not + P_SetMobjState(cur, S_BATTLECAPSULE_SUPPORTFLY); + else if (mobj->target && !P_MobjWasRemoved(mobj->target)) // Grounded, moving capsule + P_SetMobjState(cur, S_KARMAWHEEL); + else + P_SetMobjState(cur, S_BATTLECAPSULE_SUPPORT); // Grounded, stationary capsule + + P_SetTarget(&cur->target, mobj); + P_SetTarget(&cur->hprev, prev); + P_SetTarget(&prev->hnext, cur); + prev = cur; + } + + // Side paneling + for (j = 0; j < 8; j++) + { + cur = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_BATTLECAPSULE_PIECE); + cur->extravalue1 = j; + + if (j & 1) + P_SetMobjState(cur, S_BATTLECAPSULE_SIDE2); + else + P_SetMobjState(cur, S_BATTLECAPSULE_SIDE1); + + P_SetTarget(&cur->target, mobj); + P_SetTarget(&cur->hprev, prev); + P_SetTarget(&prev->hnext, cur); + prev = cur; + } + } + + mt->mobj = mobj; + } + } + + battlecapsules = true; +} diff --git a/src/k_battle.h b/src/k_battle.h new file mode 100644 index 000000000..b69938d39 --- /dev/null +++ b/src/k_battle.h @@ -0,0 +1,26 @@ +#ifndef __K_BATTLE__ +#define __K_BATTLE__ + +#include "doomtype.h" +#include "d_player.h" + +extern struct battleovertime +{ + UINT16 enabled; ///< Has this been initalized yet? + fixed_t radius, minradius; ///< Radius of kill field + fixed_t x, y, z; ///< Position to center on +} battleovertime; + +extern boolean battlecapsules; +extern INT32 nummapboxes, numgotboxes; // keep track of spawned battle mode items +extern UINT8 maptargets, numtargets; + +INT32 K_StartingBumperCount(void); +boolean K_IsPlayerWanted(player_t *player); +void K_CalculateBattleWanted(void); +void K_SpawnBattlePoints(player_t *source, player_t *victim, UINT8 amount); +void K_CheckBumpers(void); +void K_RunBattleOvertime(void); +void K_SpawnBattleCapsules(void); + +#endif diff --git a/src/k_collide.c b/src/k_collide.c new file mode 100644 index 000000000..7edbe3e87 --- /dev/null +++ b/src/k_collide.c @@ -0,0 +1,319 @@ +/// \file k_collide.c +/// \brief SRB2Kart item collision hooks + +#include "k_collide.h" +#include "doomtype.h" +#include "p_mobj.h" +#include "k_kart.h" +#include "p_local.h" +#include "s_sound.h" +#include "r_main.h" // R_PointToAngle2, R_PointToDist2 +#include "hu_stuff.h" // Sink snipe print +#include "doomdef.h" // Sink snipe print +#include "g_game.h" // Sink snipe print + +boolean K_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2) +{ + boolean damageitem = false; + + if (((t1->target == t2) || (t1->target == t2->target)) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0))) + return true; + + if (t1->health <= 0 || t2->health <= 0) + return true; + + if ((t1->type == MT_ORBINAUT_SHIELD || t1->type == MT_JAWZ_SHIELD) && t1->lastlook + && (t2->type == MT_ORBINAUT_SHIELD || t2->type == MT_JAWZ_SHIELD) && t2->lastlook + && (t1->target == t2->target)) // Don't hit each other if you have the same target + return true; + + if (t2->player) + { + if (t2->player->powers[pw_flashing] + && !(t1->type == MT_ORBINAUT || t1->type == MT_JAWZ || t1->type == MT_JAWZ_DUD)) + return true; + + if (t2->player->kartstuff[k_hyudorotimer]) + return true; // no interaction + + // Player Damage + P_DamageMobj(t2, t1, t1->target, 1); + K_KartBouncing(t2, t1, false, false); + S_StartSound(t2, sfx_s3k7b); + + damageitem = true; + } + else if (t2->type == MT_ORBINAUT || t2->type == MT_JAWZ || t2->type == MT_JAWZ_DUD + || t2->type == MT_ORBINAUT_SHIELD || t2->type == MT_JAWZ_SHIELD + || t2->type == MT_BANANA || t2->type == MT_BANANA_SHIELD + || t2->type == MT_BALLHOG) + { + // Other Item Damage + if (t2->eflags & MFE_VERTICALFLIP) + t2->z -= t2->height; + else + t2->z += t2->height; + + S_StartSound(t2, t2->info->deathsound); + P_KillMobj(t2, t1, t1); + + P_SetObjectMomZ(t2, 8*FRACUNIT, false); + P_InstaThrust(t2, R_PointToAngle2(t1->x, t1->y, t2->x, t2->y)+ANGLE_90, 16*FRACUNIT); + + P_SpawnMobj(t2->x/2 + t1->x/2, t2->y/2 + t1->y/2, t2->z/2 + t1->z/2, MT_ITEMCLASH); + + damageitem = true; + } + else if (t2->type == MT_SSMINE_SHIELD || t2->type == MT_SSMINE) + { + damageitem = true; + // Bomb death + P_KillMobj(t2, t1, t1); + } + else if (t2->flags & MF_SPRING && (t1->type != MT_ORBINAUT_SHIELD && t1->type != MT_JAWZ_SHIELD)) + { + // Let thrown items hit springs! + P_DoSpring(t2, t1); + } + else if (t2->flags & MF_SHOOTABLE) + { + // Shootable damage + P_DamageMobj(t2, t2, t1->target, 1); + damageitem = true; + } + + if (damageitem) + { + // This Item Damage + if (t1->eflags & MFE_VERTICALFLIP) + t1->z -= t1->height; + else + t1->z += t1->height; + + S_StartSound(t1, t1->info->deathsound); + P_KillMobj(t1, t2, t2); + + P_SetObjectMomZ(t1, 8*FRACUNIT, false); + P_InstaThrust(t1, R_PointToAngle2(t2->x, t2->y, t1->x, t1->y)+ANGLE_90, 16*FRACUNIT); + } + + return true; +} + +boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2) +{ + boolean damageitem = false; + + if (((t1->target == t2) || (t1->target == t2->target)) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0))) + return true; + + if (t1->health <= 0 || t2->health <= 0) + return true; + + if (((t1->type == MT_BANANA_SHIELD) && (t2->type == MT_BANANA_SHIELD)) + && (t1->target == t2->target)) // Don't hit each other if you have the same target + return true; + + if (t1->type == MT_BALLHOG && t2->type == MT_BALLHOG) + return true; // Ballhogs don't collide with eachother + + if (t2->player) + { + if (t2->player->powers[pw_flashing]) + return true; + + // Banana snipe! + if (t1->type == MT_BANANA && t1->health > 1) + S_StartSound(t2, sfx_bsnipe); + + // Player Damage + K_SpinPlayer(t2->player, t1->target, 0, t1, (t1->type == MT_BANANA || t1->type == MT_BANANA_SHIELD)); + + damageitem = true; + } + else if (t2->type == MT_BANANA || t2->type == MT_BANANA_SHIELD + || t2->type == MT_ORBINAUT || t2->type == MT_ORBINAUT_SHIELD + || t2->type == MT_JAWZ || t2->type == MT_JAWZ_DUD || t2->type == MT_JAWZ_SHIELD + || t2->type == MT_BALLHOG) + { + // Other Item Damage + if (t2->eflags & MFE_VERTICALFLIP) + t2->z -= t2->height; + else + t2->z += t2->height; + + S_StartSound(t2, t2->info->deathsound); + P_KillMobj(t2, t1, t1); + + P_SetObjectMomZ(t2, 8*FRACUNIT, false); + P_InstaThrust(t2, R_PointToAngle2(t1->x, t1->y, t2->x, t2->y)+ANGLE_90, 16*FRACUNIT); + + P_SpawnMobj(t2->x/2 + t1->x/2, t2->y/2 + t1->y/2, t2->z/2 + t1->z/2, MT_ITEMCLASH); + + damageitem = true; + } + else if (t2->flags & MF_SHOOTABLE) + { + // Shootable damage + P_DamageMobj(t2, t2, t1->target, 1); + damageitem = true; + } + + if (damageitem) + { + // This Item Damage + if (t1->eflags & MFE_VERTICALFLIP) + t1->z -= t1->height; + else + t1->z += t1->height; + + S_StartSound(t1, t1->info->deathsound); + P_KillMobj(t1, t2, t2); + + P_SetObjectMomZ(t1, 8*FRACUNIT, false); + P_InstaThrust(t1, R_PointToAngle2(t2->x, t2->y, t1->x, t1->y)+ANGLE_90, 16*FRACUNIT); + } + + return true; +} + +boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) +{ + // Push fakes out of other item boxes + if (t2->type == MT_RANDOMITEM || t2->type == MT_EGGMANITEM) + P_InstaThrust(t1, R_PointToAngle2(t2->x, t2->y, t1->x, t1->y), t2->radius/4); + + // Player collision is handled by TouchSpecial + return true; +} + +boolean K_MineCollide(mobj_t *t1, mobj_t *t2) +{ + if (((t1->target == t2) || (t1->target == t2->target)) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0))) + return true; + + if (t1->health <= 0 || t2->health <= 0) + return true; + + if (t2->player) + { + if (t2->player->powers[pw_flashing]) + return true; + + // Bomb punting + if ((t1->state >= &states[S_SSMINE1] && t1->state <= &states[S_SSMINE4]) + || (t1->state >= &states[S_SSMINE_DEPLOY8] && t1->state <= &states[S_SSMINE_DEPLOY13])) + P_KillMobj(t1, t2, t2); + else + K_PuntMine(t1, t2); + } + else if (t2->type == MT_ORBINAUT || t2->type == MT_JAWZ || t2->type == MT_JAWZ_DUD + || t2->type == MT_ORBINAUT_SHIELD || t2->type == MT_JAWZ_SHIELD) + { + // Bomb death + P_KillMobj(t1, t2, t2); + + // Other Item Damage + if (t2->eflags & MFE_VERTICALFLIP) + t2->z -= t2->height; + else + t2->z += t2->height; + + S_StartSound(t2, t2->info->deathsound); + P_KillMobj(t2, t1, t1); + + P_SetObjectMomZ(t2, 8*FRACUNIT, false); + P_InstaThrust(t2, R_PointToAngle2(t1->x, t1->y, t2->x, t2->y)+ANGLE_90, 16*FRACUNIT); + } + else if (t2->flags & MF_SHOOTABLE) + { + // Bomb death + P_KillMobj(t1, t2, t2); + // Shootable damage + P_DamageMobj(t2, t2, t1->target, 1); + } + + return true; +} + +boolean K_MineExplosionCollide(mobj_t *t1, mobj_t *t2) +{ + if (t2->player) + { + if (t2->player->powers[pw_flashing]) + return true; + + if (t1->state == &states[S_MINEEXPLOSION1]) + K_ExplodePlayer(t2->player, t1->target, t1); + else + K_SpinPlayer(t2->player, t1->target, 0, t1, false); + } + else if (t2->flags & MF_SHOOTABLE) + { + // Shootable damage + P_DamageMobj(t2, t2, t1->target, 1); + } + + return true; +} + +boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2) +{ + if (((t1->target == t2) || (t1->target == t2->target)) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0))) + return true; + + if (t2->player) + { + if (t2->player->powers[pw_flashing]) + return true; + + S_StartSound(NULL, sfx_bsnipe); // let all players hear it. + HU_SetCEchoFlags(0); + HU_SetCEchoDuration(5); + HU_DoCEcho(va("%s\\was hit by a kitchen sink.\\\\\\\\", player_names[t2->player-players])); + I_OutputMsg("%s was hit by a kitchen sink.\n", player_names[t2->player-players]); + P_DamageMobj(t2, t1, t1->target, 10000); + P_KillMobj(t1, t2, t2); + } + else if (t2->flags & MF_SHOOTABLE) + { + // Shootable damage + P_KillMobj(t2, t2, t1->target); + // This item damage + P_KillMobj(t1, t2, t2); + } + + return true; +} + +boolean K_FallingRockCollide(mobj_t *t1, mobj_t *t2) +{ + if (t2->player || t2->type == MT_FALLINGROCK) + K_KartBouncing(t2, t1, false, false); + return true; +} + +boolean K_SMKIceBlockCollide(mobj_t *t1, mobj_t *t2) +{ + if (!(t2->flags & MF_SOLID || t2->flags & MF_SHOOTABLE || t2->flags & MF_BOUNCE)) + return true; + + if (!(t2->health)) + return true; + + if (t2->type == MT_BANANA || t2->type == MT_BANANA_SHIELD + || t2->type == MT_EGGMANITEM || t2->type == MT_EGGMANITEM_SHIELD + || t2->type == MT_SSMINE || t2->type == MT_SSMINE_SHIELD + || t2->type == MT_ORBINAUT_SHIELD || t2->type == MT_JAWZ_SHIELD) + return false; + + if (t1->health) + P_KillMobj(t1, t2, t2); + + /*if (t2->player && (t2->player->kartstuff[k_invincibilitytimer] > 0 + || t2->player->kartstuff[k_growshrinktimer] > 0)) + return true;*/ + + K_KartBouncing(t2, t1, false, true); + return false; +} diff --git a/src/k_collide.h b/src/k_collide.h new file mode 100644 index 000000000..86f643b3f --- /dev/null +++ b/src/k_collide.h @@ -0,0 +1,16 @@ +#ifndef __K_COLLIDE__ +#define __K_COLLIDE__ + +#include "doomtype.h" +#include "p_mobj.h" + +boolean K_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2); +boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2); +boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2); +boolean K_MineCollide(mobj_t *t1, mobj_t *t2); +boolean K_MineExplosionCollide(mobj_t *t1, mobj_t *t2); +boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2); +boolean K_FallingRockCollide(mobj_t *t1, mobj_t *t2); +boolean K_SMKIceBlockCollide(mobj_t *t1, mobj_t *t2); + +#endif diff --git a/src/k_kart.c b/src/k_kart.c index fd5ca4859..3badb2d68 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5,6 +5,7 @@ /// All of the SRB2kart-unique stuff. #include "k_kart.h" +#include "k_battle.h" #include "k_pwrlv.h" #include "doomdef.h" #include "hu_stuff.h" @@ -604,6 +605,9 @@ boolean K_IsPlayerLosing(player_t *player) INT32 winningpos = 1; UINT8 i, pcount = 0; + if (battlecapsules && player->kartstuff[k_bumper] <= 0) + return true; // DNF in break the capsules + if (player->kartstuff[k_position] == 1) return false; @@ -625,21 +629,6 @@ boolean K_IsPlayerLosing(player_t *player) return (player->kartstuff[k_position] > winningpos); } -boolean K_IsPlayerWanted(player_t *player) -{ - UINT8 i; - if (!(G_BattleGametype())) - return false; - for (i = 0; i < 4; i++) - { - if (battlewanted[i] == -1) - break; - if (player == &players[battlewanted[i]]) - return true; - } - return false; -} - fixed_t K_GetKartGameSpeedScalar(SINT8 value) { // Easy = 81.25% @@ -1179,8 +1168,8 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) } else { - if (modeattacking || cv_orbinaut.value) // Waited patiently? You get an orbinaut! - K_KartGetItemResult(player, KITEM_ORBINAUT); + if (modeattacking || cv_tripleorbinaut.value) // Waited patiently? You get Orbinaut x3! + K_KartGetItemResult(player, KRITEM_TRIPLEORBINAUT); else // Default to sad if nothing's enabled... K_KartGetItemResult(player, KITEM_SAD); player->karthud[khud_itemblinkmode] = 0; @@ -2630,32 +2619,6 @@ void K_DoInstashield(player_t *player) P_SetTarget(&layerb->target, player->mo); } -void K_SpawnBattlePoints(player_t *source, player_t *victim, UINT8 amount) -{ - statenum_t st; - mobj_t *pt; - - if (!source || !source->mo) - return; - - if (amount == 1) - st = S_BATTLEPOINT1A; - else if (amount == 2) - st = S_BATTLEPOINT2A; - else if (amount == 3) - st = S_BATTLEPOINT3A; - else - return; // NO STATE! - - pt = P_SpawnMobj(source->mo->x, source->mo->y, source->mo->z, MT_BATTLEPOINT); - P_SetTarget(&pt->target, source->mo); - P_SetMobjState(pt, st); - if (victim && victim->skincolor) - pt->color = victim->skincolor; - else - pt->color = source->skincolor; -} - void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflictor, boolean trapitem) { UINT8 scoremultiply = 1; @@ -3076,7 +3039,7 @@ void K_StealBumper(player_t *player, player_t *victim, boolean force) if (!force) { - if (victim->kartstuff[k_bumper] <= 0) // || player->kartstuff[k_bumper] >= cv_kartbumpers.value+2 + if (victim->kartstuff[k_bumper] <= 0) // || player->kartstuff[k_bumper] >= K_StartingBumperCount()+2 return; if (player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0) @@ -7665,181 +7628,6 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } } -void K_CalculateBattleWanted(void) -{ - UINT8 numingame = 0, numplaying = 0, numwanted = 0; - SINT8 bestbumperplayer = -1, bestbumper = -1; - SINT8 camppos[MAXPLAYERS]; // who is the biggest camper - UINT8 ties = 0, nextcamppos = 0; - boolean setbumper = false; - UINT8 i, j; - - if (!G_BattleGametype()) - { - for (i = 0; i < 4; i++) - battlewanted[i] = -1; - return; - } - - wantedcalcdelay = wantedfrequency; - - for (i = 0; i < MAXPLAYERS; i++) - camppos[i] = -1; // initialize - - for (i = 0; i < MAXPLAYERS; i++) - { - UINT8 position = 1; - - if (!playeringame[i] || players[i].spectator) // Not playing - continue; - - if (players[i].exiting) // We're done, don't calculate. - return; - - numplaying++; - - if (players[i].kartstuff[k_bumper] <= 0) // Not alive, so don't do anything else - continue; - - numingame++; - - if (bestbumper == -1 || players[i].kartstuff[k_bumper] > bestbumper) - { - bestbumper = players[i].kartstuff[k_bumper]; - bestbumperplayer = i; - } - else if (players[i].kartstuff[k_bumper] == bestbumper) - bestbumperplayer = -1; // Tie, no one has best bumper. - - for (j = 0; j < MAXPLAYERS; j++) - { - if (!playeringame[j] || players[j].spectator) - continue; - if (players[j].kartstuff[k_bumper] <= 0) - continue; - if (j == i) - continue; - if (players[j].kartstuff[k_wanted] == players[i].kartstuff[k_wanted] && players[j].marescore > players[i].marescore) - position++; - else if (players[j].kartstuff[k_wanted] > players[i].kartstuff[k_wanted]) - position++; - } - - position--; // Make zero based - - while (camppos[position] != -1) // Port priority! - position++; - - camppos[position] = i; - } - - if (numplaying <= 2 || (numingame <= 2 && bestbumper == 1)) // In 1v1s then there's no need for WANTED. In bigger netgames, don't show anyone as WANTED when they're equally matched. - numwanted = 0; - else - numwanted = min(4, 1 + ((numingame-2) / 4)); - - for (i = 0; i < 4; i++) - { - if (i+1 > numwanted) // Not enough players for this slot to be wanted! - battlewanted[i] = -1; - else if (bestbumperplayer != -1 && !setbumper) // If there's a player who has an untied bumper lead over everyone else, they are the first to be wanted. - { - battlewanted[i] = bestbumperplayer; - setbumper = true; // Don't set twice - } - else - { - // Don't accidentally set the same player, if the bestbumperplayer is also a huge camper. - while (bestbumperplayer != -1 && camppos[nextcamppos] != -1 - && bestbumperplayer == camppos[nextcamppos]) - nextcamppos++; - - // Do not add *any* more people if there's too many times that are tied with others. - // This could theoretically happen very easily if people don't hit each other for a while after the start of a match. - // (I will be sincerely impressed if more than 2 people tie after people start hitting each other though) - - if (camppos[nextcamppos] == -1 // Out of entries - || ties >= (numwanted-i)) // Already counted ties - { - battlewanted[i] = -1; - continue; - } - - if (ties < (numwanted-i)) - { - ties = 0; // Reset - for (j = 0; j < 2; j++) - { - if (camppos[nextcamppos+(j+1)] == -1) // Nothing beyond, cancel - break; - if (players[camppos[nextcamppos]].kartstuff[k_wanted] == players[camppos[nextcamppos+(j+1)]].kartstuff[k_wanted]) - ties++; - } - } - - if (ties < (numwanted-i)) // Is it still low enough after counting? - { - battlewanted[i] = camppos[nextcamppos]; - nextcamppos++; - } - else - battlewanted[i] = -1; - } - } -} - -void K_CheckBumpers(void) -{ - UINT8 i; - UINT8 numingame = 0; - SINT8 winnernum = -1; - INT32 winnerscoreadd = 0; - - if (!multiplayer) - return; - - if (!G_BattleGametype()) - return; - - if (gameaction == ga_completed) - return; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) // not even in-game - continue; - - if (players[i].exiting) // we're already exiting! stop! - return; - - numingame++; - winnerscoreadd += players[i].marescore; - - if (players[i].kartstuff[k_bumper] <= 0) // if you don't have any bumpers, you're probably not a winner - continue; - else if (winnernum > -1) // TWO winners? that's dumb :V - return; - - winnernum = i; - winnerscoreadd -= players[i].marescore; - } - - if (numingame <= 1) - return; - - if (winnernum > -1 && playeringame[winnernum]) - { - players[winnernum].marescore += winnerscoreadd; - CONS_Printf(M_GetText("%s recieved %d point%s for winning!\n"), player_names[winnernum], winnerscoreadd, (winnerscoreadd == 1 ? "" : "s")); - } - - for (i = 0; i < MAXPLAYERS; i++) // This can't go in the earlier loop because winning adds points - K_KartUpdatePosition(&players[i]); - - for (i = 0; i < MAXPLAYERS; i++) // and it can't be merged with this loop because it needs to be all updated before exiting... multi-loops suck... - P_DoPlayerExit(&players[i]); -} - void K_CheckSpectateStatus(void) { UINT8 respawnlist[MAXPLAYERS]; @@ -7953,6 +7741,8 @@ static patch_t *kp_lapstickernarrow; static patch_t *kp_splitlapflag; static patch_t *kp_bumpersticker; static patch_t *kp_bumperstickerwide; +static patch_t *kp_capsulesticker; +static patch_t *kp_capsulestickerwide; static patch_t *kp_karmasticker; static patch_t *kp_splitkarmabomb; static patch_t *kp_timeoutsticker; @@ -7983,6 +7773,7 @@ static patch_t *kp_speedometerlabel[4]; static patch_t *kp_rankbumper; static patch_t *kp_tinybumper[2]; static patch_t *kp_ranknobumpers; +static patch_t *kp_rankcapsule; static patch_t *kp_battlewin; static patch_t *kp_battlecool; @@ -8055,6 +7846,8 @@ void K_LoadKartHUDGraphics(void) kp_splitlapflag = W_CachePatchName("K_SPTLAP", PU_HUDGFX); kp_bumpersticker = W_CachePatchName("K_STBALN", PU_HUDGFX); kp_bumperstickerwide = W_CachePatchName("K_STBALW", PU_HUDGFX); + kp_capsulesticker = W_CachePatchName("K_STCAPN", PU_HUDGFX); + kp_capsulestickerwide = W_CachePatchName("K_STCAPW", PU_HUDGFX); kp_karmasticker = W_CachePatchName("K_STKARM", PU_HUDGFX); kp_splitkarmabomb = W_CachePatchName("K_SPTKRM", PU_HUDGFX); kp_timeoutsticker = W_CachePatchName("K_STTOUT", PU_HUDGFX); @@ -8181,6 +7974,7 @@ void K_LoadKartHUDGraphics(void) kp_tinybumper[0] = W_CachePatchName("K_BLNA", PU_HUDGFX); kp_tinybumper[1] = W_CachePatchName("K_BLNB", PU_HUDGFX); kp_ranknobumpers = W_CachePatchName("K_NOBLNS", PU_HUDGFX); + kp_rankcapsule = W_CachePatchName("K_CAPICO", PU_HUDGFX); // Battle graphics kp_battlewin = W_CachePatchName("K_BWIN", PU_HUDGFX); @@ -9524,8 +9318,8 @@ static void K_drawKartLapsAndRings(void) if (cv_numlaps.value >= 10) { UINT8 ln[2]; - ln[0] = ((abs(stplyr->laps) / 10) % 10); - ln[1] = (abs(stplyr->laps) % 10); + ln[0] = ((stplyr->laps / 10) % 10); + ln[1] = (stplyr->laps % 10); V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, pingnum[ln[0]]); V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, pingnum[ln[1]]); @@ -9705,52 +9499,95 @@ static void K_drawKartBumpersOrKarma(void) V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[0]); V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|splitflags, frameslash); - if (stplyr->kartstuff[k_bumper] <= 0) + if (battlecapsules) { - V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|splitflags, kp_splitkarmabomb, colormap); - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartstuff[k_comebackpoints]) % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[2]); - } - else - { - V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|splitflags, kp_rankbumper, colormap); + V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|splitflags, kp_rankcapsule, NULL); - if (stplyr->kartstuff[k_bumper] > 9 || cv_kartbumpers.value > 9) + if (numtargets > 9 || maptargets > 9) { UINT8 ln[2]; - ln[0] = ((abs(stplyr->kartstuff[k_bumper]) / 10) % 10); - ln[1] = (abs(stplyr->kartstuff[k_bumper]) % 10); + ln[0] = ((numtargets / 10) % 10); + ln[1] = (numtargets % 10); V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, pingnum[ln[0]]); V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, pingnum[ln[1]]); - ln[0] = ((abs(cv_kartbumpers.value) / 10) % 10); - ln[1] = (abs(cv_kartbumpers.value) % 10); + ln[0] = ((maptargets / 10) % 10); + ln[1] = (maptargets % 10); V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, pingnum[ln[0]]); V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, pingnum[ln[1]]); } else { - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartstuff[k_bumper]) % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[(cv_kartbumpers.value) % 10]); + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[numtargets % 10]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[maptargets % 10]); + } + } + else + { + if (stplyr->kartstuff[k_bumper] <= 0) + { + V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|splitflags, kp_splitkarmabomb, colormap); + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartstuff[k_comebackpoints]) % 10]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[2]); + } + else + { + INT32 maxbumper = K_StartingBumperCount(); + V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|splitflags, kp_rankbumper, colormap); + + if (stplyr->kartstuff[k_bumper] > 9 || maxbumper > 9) + { + UINT8 ln[2]; + ln[0] = ((abs(stplyr->kartstuff[k_bumper]) / 10) % 10); + ln[1] = (abs(stplyr->kartstuff[k_bumper]) % 10); + + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, pingnum[ln[0]]); + V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, pingnum[ln[1]]); + + ln[0] = ((abs(maxbumper) / 10) % 10); + ln[1] = (abs(maxbumper) % 10); + + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, pingnum[ln[0]]); + V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, pingnum[ln[1]]); + } + else + { + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartstuff[k_bumper]) % 10]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[(maxbumper) % 10]); + } } } } else { - if (stplyr->kartstuff[k_bumper] <= 0) + if (battlecapsules) { - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_karmasticker, colormap); - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/2", stplyr->kartstuff[k_comebackpoints])); + if (numtargets > 9 && maptargets > 9) + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_capsulestickerwide, NULL); + else + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_capsulesticker, NULL); + V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", numtargets, maptargets)); } else { - if (stplyr->kartstuff[k_bumper] > 9 && cv_kartbumpers.value > 9) - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumperstickerwide, colormap); + if (stplyr->kartstuff[k_bumper] <= 0) + { + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_karmasticker, colormap); + V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/2", stplyr->kartstuff[k_comebackpoints])); + } else - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumpersticker, colormap); - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->kartstuff[k_bumper], cv_kartbumpers.value)); + { + INT32 maxbumper = K_StartingBumperCount(); + + if (stplyr->kartstuff[k_bumper] > 9 && maxbumper > 9) + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumperstickerwide, colormap); + else + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumpersticker, colormap); + + V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->kartstuff[k_bumper], maxbumper)); + } } } } @@ -10308,10 +10145,14 @@ static void K_drawBattleFullscreen(void) V_DrawFadeScreen(0xFF00, 16); if (stplyr->exiting < 6*TICRATE && !stplyr->spectator) { - if (stplyr->kartstuff[k_position] == 1) - V_DrawFixedPatch(x<kartstuff[k_position] == 1) + p = kp_battlewin; + + V_DrawFixedPatch(x<menuflags & LF2_RECORDATTACK)) return false;*/ - if (!(mapheaderinfo[mapnum]->typeoflevel & TOL_RACE)) + if ((levellistmode == LLM_TIMEATTACK && !(mapheaderinfo[mapnum]->typeoflevel & TOL_RACE)) + || (levellistmode == LLM_BREAKTHECAPSULES && !(mapheaderinfo[mapnum]->typeoflevel & TOL_MATCH))) return false; if (M_MapLocked(mapnum+1)) @@ -4286,20 +4241,6 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) return false;*/ return true; - /*case LLM_NIGHTSATTACK: - if (!(mapheaderinfo[mapnum]->menuflags & LF2_NIGHTSATTACK)) - return false; - - if (M_MapLocked(mapnum+1)) - return false; // not unlocked - - if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED) - return true; - - if (!mapvisited[mapnum]) - return false; - - return true;*/ default: return false; } @@ -6627,18 +6568,16 @@ static void M_Credits(INT32 choice) // SINGLE PLAYER MENU // ================== -#if 0 // Bring this back when we have actual single-player static void M_SinglePlayerMenu(INT32 choice) { (void)choice; - SP_MainMenu[sprecordattack].status = - (M_SecretUnlocked(SECRET_RECORDATTACK)) ? IT_CALL|IT_STRING : IT_SECRET; - /*SP_MainMenu[spnightsmode].status = - (M_SecretUnlocked(SECRET_NIGHTSMODE)) ? IT_CALL|IT_STRING : IT_SECRET;*/ + SP_MainMenu[sptimeattack].status = + (M_SecretUnlocked(SECRET_TIMEATTACK)) ? IT_CALL|IT_STRING : IT_SECRET; + SP_MainMenu[spbreakthecapsules].status = + (M_SecretUnlocked(SECRET_BREAKTHECAPSULES)) ? IT_CALL|IT_STRING : IT_SECRET; M_SetupNextMenu(&SP_MainDef); } -#endif /*static void M_LoadGameLevelSelect(INT32 choice) { @@ -7624,8 +7563,11 @@ void M_DrawTimeAttackMenu(void) V_DrawFill((BASEVIDWIDTH - dupadjust)>>1, 78, dupadjust, 36, 159); - V_DrawRightAlignedString(149, 80, highlightflags, "BEST LAP:"); - K_drawKartTimestamp(lap, 19, 86, 0, 2); + if (levellistmode != LLM_BREAKTHECAPSULES) + { + V_DrawRightAlignedString(149, 80, highlightflags, "BEST LAP:"); + K_drawKartTimestamp(lap, 19, 86, 0, 2); + } V_DrawRightAlignedString(292, 80, highlightflags, "BEST TIME:"); K_drawKartTimestamp(time, 162, 86, cv_nextmap.value, 1); @@ -7714,11 +7656,43 @@ static void M_TimeAttack(INT32 choice) memset(skins_cons_t, 0, sizeof (skins_cons_t)); - levellistmode = LLM_RECORDATTACK; // Don't be dependent on cv_newgametype + levellistmode = LLM_TIMEATTACK; // Don't be dependent on cv_newgametype if (M_CountLevelsToShowInList() == 0) { - M_StartMessage(M_GetText("No record-attackable levels found.\n"),NULL,MM_NOTHING); + M_StartMessage(M_GetText("No levels found for Time Attack.\n"),NULL,MM_NOTHING); + return; + } + + M_PatchSkinNameTable(); + + M_PrepareLevelSelect(); + M_SetupNextMenu(&SP_TimeAttackDef); + + G_SetGamestate(GS_TIMEATTACK); + + if (cv_nextmap.value) + Nextmap_OnChange(); + else + CV_AddValue(&cv_nextmap, 1); + + itemOn = tastart; // "Start" is selected. + + S_ChangeMusicInternal("racent", true); +} + +// Same as above, but sets a different levellistmode. Should probably be merged... +static void M_BreakTheCapsules(INT32 choice) +{ + (void)choice; + + memset(skins_cons_t, 0, sizeof (skins_cons_t)); + + levellistmode = LLM_BREAKTHECAPSULES; // Don't be dependent on cv_newgametype + + if (M_CountLevelsToShowInList() == 0) + { + M_StartMessage(M_GetText("No levels found for Break the Capsules.\n"),NULL,MM_NOTHING); return; } @@ -7852,13 +7826,13 @@ static boolean M_QuitTimeAttackMenu(void) }*/ // Going to Nights Attack menu... -/*static void M_NightsAttack(INT32 choice) +/*static void M_BreakTheCapsules(INT32 choice) { (void)choice; memset(skins_cons_t, 0, sizeof (skins_cons_t)); - levellistmode = LLM_NIGHTSATTACK; // Don't be dependent on cv_newgametype + levellistmode = LLM_BREAKTHECAPSULES; // Don't be dependent on cv_newgametype if (M_CountLevelsToShowInList() == 0) { @@ -7886,7 +7860,7 @@ static boolean M_QuitTimeAttackMenu(void) (void)choice; emeralds = 0; M_ClearMenus(true); - modeattacking = ATTACKING_NIGHTS; + modeattacking = ATTACKING_CAPSULES; I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755); I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755); @@ -7910,7 +7884,7 @@ static void M_ChooseTimeAttack(INT32 choice) (void)choice; emeralds = 0; M_ClearMenus(true); - modeattacking = ATTACKING_RECORD; + modeattacking = (levellistmode == LLM_BREAKTHECAPSULES ? ATTACKING_CAPSULES : ATTACKING_RECORD); I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755); I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755); @@ -7960,7 +7934,7 @@ static void M_HandleStaffReplay(INT32 choice) if (l == LUMPERROR) break; M_ClearMenus(true); - modeattacking = ATTACKING_RECORD; + modeattacking = (levellistmode == LLM_BREAKTHECAPSULES ? ATTACKING_CAPSULES : ATTACKING_RECORD); demo.loadfiles = false; demo.ignorefiles = true; // Just assume that record attack replays have the files needed G_DoPlayDemo(va("%sS%02u",G_BuildMapName(cv_nextmap.value),cv_dummystaff.value)); break; @@ -7981,7 +7955,7 @@ static void M_ReplayTimeAttack(INT32 choice) { const char *which; M_ClearMenus(true); - modeattacking = ATTACKING_RECORD; // set modeattacking before G_DoPlayDemo so the map loader knows + modeattacking = (levellistmode == LLM_BREAKTHECAPSULES ? ATTACKING_CAPSULES : ATTACKING_RECORD); // set modeattacking before G_DoPlayDemo so the map loader knows demo.loadfiles = false; demo.ignorefiles = true; // Just assume that record attack replays have the files needed if (currentMenu == &SP_ReplayDef) @@ -8128,10 +8102,8 @@ static void M_ModeAttackRetry(INT32 choice) { (void)choice; G_CheckDemoStatus(); // Cancel recording - if (modeattacking == ATTACKING_RECORD) + if (modeattacking) M_ChooseTimeAttack(0); - /*else if (modeattacking == ATTACKING_NIGHTS) - M_ChooseNightsAttack(0);*/ } static void M_ModeAttackEndGame(INT32 choice) @@ -8143,16 +8115,10 @@ static void M_ModeAttackEndGame(INT32 choice) Command_ExitGame_f(); M_StartControlPanel(); - switch(modeattacking) - { - default: - case ATTACKING_RECORD: + + if (modeattacking) currentMenu = &SP_TimeAttackDef; - break; - /*case ATTACKING_NIGHTS: - currentMenu = &SP_NightsAttackDef; - break;*/ - } + itemOn = currentMenu->lastOn; G_SetGamestate(GS_TIMEATTACK); modeattacking = ATTACKING_NONE; diff --git a/src/p_enemy.c b/src/p_enemy.c index 4097db059..49416aec4 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -25,6 +25,7 @@ #include "lua_hook.h" #include "k_kart.h" // SRB2kart #include "k_waypoint.h" +#include "k_battle.h" #ifdef HW3SOUND #include "hardware/hw3sound.h" diff --git a/src/p_inter.c b/src/p_inter.c index e98740655..a1f507445 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -27,6 +27,7 @@ #include "m_misc.h" #include "v_video.h" // video flags for CEchos #include "k_kart.h" // SRB2kart +#include "k_battle.h" #include "k_pwrlv.h" // CTF player names @@ -1785,7 +1786,7 @@ void P_CheckTimeLimit(void) if (!(multiplayer || netgame)) return; - if (G_RaceGametype()) + if (G_RaceGametype() || battlecapsules) return; if (leveltime < (timelimitintics + starttime)) @@ -2516,6 +2517,45 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source) S_StartSound(target, sfx_s3k80); } break; + case MT_BATTLECAPSULE: + { + mobj_t *cur; + + numtargets++; + target->fuse = 16; + target->flags |= MF_NOCLIP|MF_NOCLIPTHING; + + cur = target->hnext; + + while (cur && !P_MobjWasRemoved(cur)) + { + // Shoot every piece outward + if (!(cur->x == target->x && cur->y == target->y)) + { + P_InstaThrust(cur, + R_PointToAngle2(target->x, target->y, cur->x, cur->y), + R_PointToDist2(target->x, target->y, cur->x, cur->y) / 12 + ); + } + + cur->momz = 8 * target->scale * P_MobjFlip(target); + + cur->flags &= ~MF_NOGRAVITY; + cur->tics = TICRATE; + cur->frame &= ~FF_ANIMATE; // Stop animating the propellers + + cur = cur->hnext; + } + + // All targets busted! + if (numtargets >= maptargets) + { + UINT8 i; + for (i = 0; i < MAXPLAYERS; i++) + P_DoPlayerExit(&players[i]); + } + } + break; default: break; diff --git a/src/p_local.h b/src/p_local.h index a4e661bfc..0d7d3ec3e 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -227,7 +227,6 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state); boolean P_SetMobjState(mobj_t *mobj, statenum_t state); //void P_RunShields(void); void P_RunOverlays(void); -void P_RunBattleOvertime(void); void P_MobjThinker(mobj_t *mobj); boolean P_RailThinker(mobj_t *mobj); void P_PushableThinker(mobj_t *mobj); diff --git a/src/p_map.c b/src/p_map.c index 9c602f70b..9df9339dc 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -25,6 +25,7 @@ #include "s_sound.h" #include "w_wad.h" #include "k_kart.h" // SRB2kart 011617 +#include "k_collide.h" #include "hu_stuff.h" // SRB2kart #include "i_system.h" // SRB2kart @@ -819,7 +820,7 @@ static boolean PIT_CheckThing(mobj_t *thing) // SRB2kart 011617 - Colission[sic] code for kart items //{ - if (thing->type == MT_SMK_ICEBLOCK) + if (tmthing->type == MT_SMK_ICEBLOCK) { // see if it went over / under if (tmthing->z > thing->z + thing->height) @@ -827,31 +828,20 @@ static boolean PIT_CheckThing(mobj_t *thing) if (tmthing->z + tmthing->height < thing->z) return true; // underneath - if (!(tmthing->flags & MF_SOLID || tmthing->flags & MF_SHOOTABLE || tmthing->flags & MF_BOUNCE)) - return true; + return K_SMKIceBlockCollide(tmthing, thing); + } + else if (thing->type == MT_SMK_ICEBLOCK) + { + // see if it went over / under + if (tmthing->z > thing->z + thing->height) + return true; // overhead + if (tmthing->z + tmthing->height < thing->z) + return true; // underneath - if (!(tmthing->health)) - return true; - - if (tmthing->type == MT_BANANA || tmthing->type == MT_BANANA_SHIELD - || tmthing->type == MT_EGGMANITEM || tmthing->type == MT_EGGMANITEM_SHIELD - || tmthing->type == MT_SSMINE || tmthing->type == MT_SSMINE_SHIELD - || tmthing->type == MT_ORBINAUT_SHIELD || tmthing->type == MT_JAWZ_SHIELD) - return false; - - if (thing->health) - P_KillMobj(thing, tmthing, tmthing); - - /*if (tmthing->player && (tmthing->player->kartstuff[k_invincibilitytimer] > 0 - || tmthing->player->kartstuff[k_growshrinktimer] > 0)) - return true;*/ - - K_KartBouncing(tmthing, thing, false, true); - return false; + return K_SMKIceBlockCollide(thing, tmthing); } - // Push fakes out of other items - if (tmthing->type == MT_EGGMANITEM && (thing->type == MT_RANDOMITEM || thing->type == MT_EGGMANITEM)) + if (tmthing->type == MT_EGGMANITEM) { // see if it went over / under if (tmthing->z > thing->z + thing->height) @@ -859,10 +849,10 @@ static boolean PIT_CheckThing(mobj_t *thing) if (tmthing->z + tmthing->height < thing->z) return true; // underneath - P_InstaThrust(tmthing, R_PointToAngle2(thing->x, thing->y, tmthing->x, tmthing->y), thing->radius/4); + K_EggItemCollide(tmthing, thing); return true; } - else if (thing->type == MT_EGGMANITEM && (tmthing->type == MT_RANDOMITEM || tmthing->type == MT_EGGMANITEM)) + else if (thing->type == MT_EGGMANITEM) { // see if it went over / under if (tmthing->z > thing->z + thing->height) @@ -870,7 +860,7 @@ static boolean PIT_CheckThing(mobj_t *thing) if (tmthing->z + tmthing->height < thing->z) return true; // underneath - P_InstaThrust(thing, R_PointToAngle2(tmthing->x, tmthing->y, thing->x, thing->y), tmthing->radius/4); + K_EggItemCollide(thing, tmthing); return true; } @@ -886,97 +876,10 @@ static boolean PIT_CheckThing(mobj_t *thing) if (tmthing->z + tmthing->height < thing->z) return true; // underneath - if (((tmthing->target == thing) || (tmthing->target == thing->target)) && (tmthing->threshold > 0 || (thing->type != MT_PLAYER && thing->threshold > 0))) - return true; - - if (tmthing->health <= 0 || thing->health <= 0) - return true; - - if ((tmthing->type == MT_ORBINAUT_SHIELD || tmthing->type == MT_JAWZ_SHIELD) && tmthing->lastlook - && (thing->type == MT_ORBINAUT_SHIELD || thing->type == MT_JAWZ_SHIELD) && thing->lastlook - && (tmthing->target == thing->target)) // Don't hit each other if you have the same target - return true; - - if (thing->player && thing->player->powers[pw_flashing] - && !(tmthing->type == MT_ORBINAUT || tmthing->type == MT_JAWZ || tmthing->type == MT_JAWZ_DUD)) - return true; - - if (thing->player && thing->player->kartstuff[k_hyudorotimer]) - return true; // no interaction - - if (thing->type == MT_PLAYER) - { - // Player Damage - P_DamageMobj(thing, tmthing, tmthing->target, 1); - K_KartBouncing(thing, tmthing, false, false); - S_StartSound(thing, sfx_s3k7b); - - // This Item Damage - if (tmthing->eflags & MFE_VERTICALFLIP) - tmthing->z -= tmthing->height; - else - tmthing->z += tmthing->height; - - S_StartSound(tmthing, tmthing->info->deathsound); - P_KillMobj(tmthing, thing, thing); - - P_SetObjectMomZ(tmthing, 12*FRACUNIT, false); - P_InstaThrust(tmthing, R_PointToAngle2(thing->x, thing->y, tmthing->x, tmthing->y)+ANGLE_90, 16*FRACUNIT); - } - else if (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ || thing->type == MT_JAWZ_DUD - || thing->type == MT_ORBINAUT_SHIELD || thing->type == MT_JAWZ_SHIELD - || thing->type == MT_BANANA || thing->type == MT_BANANA_SHIELD - || thing->type == MT_BALLHOG) - { - // Other Item Damage - if (thing->eflags & MFE_VERTICALFLIP) - thing->z -= thing->height; - else - thing->z += thing->height; - - S_StartSound(thing, thing->info->deathsound); - P_KillMobj(thing, tmthing, tmthing); - - P_SetObjectMomZ(thing, 12*FRACUNIT, false); - P_InstaThrust(thing, R_PointToAngle2(tmthing->x, tmthing->y, thing->x, thing->y)+ANGLE_90, 16*FRACUNIT); - - P_SpawnMobj(thing->x/2 + tmthing->x/2, thing->y/2 + tmthing->y/2, thing->z/2 + tmthing->z/2, MT_ITEMCLASH); - - // This Item Damage - if (tmthing->eflags & MFE_VERTICALFLIP) - tmthing->z -= tmthing->height; - else - tmthing->z += tmthing->height; - - S_StartSound(tmthing, tmthing->info->deathsound); - P_KillMobj(tmthing, thing, thing); - - P_SetObjectMomZ(tmthing, 12*FRACUNIT, false); - P_InstaThrust(tmthing, R_PointToAngle2(thing->x, thing->y, tmthing->x, tmthing->y)+ANGLE_90, 16*FRACUNIT); - } - else if (thing->type == MT_SSMINE_SHIELD || thing->type == MT_SSMINE) - { - // This Item Damage - if (tmthing->eflags & MFE_VERTICALFLIP) - tmthing->z -= tmthing->height; - else - tmthing->z += tmthing->height; - - S_StartSound(tmthing, tmthing->info->deathsound); - P_KillMobj(tmthing, thing, thing); - - P_SetObjectMomZ(tmthing, 12*FRACUNIT, false); - P_InstaThrust(tmthing, R_PointToAngle2(thing->x, thing->y, tmthing->x, tmthing->y)+ANGLE_90, 16*FRACUNIT); - - // Bomb death - P_KillMobj(thing, tmthing, tmthing); - } - else if (thing->flags & MF_SPRING && (tmthing->type == MT_JAWZ || tmthing->type == MT_JAWZ_DUD || tmthing->type == MT_ORBINAUT)) - P_DoSpring(thing, tmthing); - - return true; + return K_OrbinautJawzCollide(tmthing, thing); } - else if (tmthing->flags & MF_SPRING && (thing->type == MT_JAWZ || thing->type == MT_JAWZ_DUD || thing->type == MT_ORBINAUT)) + else if (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ || thing->type == MT_JAWZ_DUD + || thing->type == MT_ORBINAUT_SHIELD || thing->type == MT_JAWZ_SHIELD) { // see if it went over / under if (tmthing->z > thing->z + thing->height) @@ -984,14 +887,10 @@ static boolean PIT_CheckThing(mobj_t *thing) if (tmthing->z + tmthing->height < thing->z) return true; // underneath - if (thing->health <= 0) - return true; - - P_DoSpring(tmthing, thing); - - return true; + return K_OrbinautJawzCollide(thing, tmthing); } - else if (tmthing->type == MT_SINK) + + if (tmthing->type == MT_BANANA || tmthing->type == MT_BANANA_SHIELD || tmthing->type == MT_BALLHOG) { // see if it went over / under if (tmthing->z > thing->z + thing->height) @@ -999,34 +898,9 @@ static boolean PIT_CheckThing(mobj_t *thing) if (tmthing->z + tmthing->height < thing->z) return true; // underneath - if (((tmthing->target == thing) || (tmthing->target == thing->target)) && (tmthing->threshold > 0 || (thing->type != MT_PLAYER && thing->threshold > 0))) - return true; - - if (thing->player && thing->player->powers[pw_flashing]) - return true; - - if (thing->type == MT_PLAYER) - { - mobj_t *explosion; - - S_StartSound(NULL, sfx_bsnipe); // let all players hear it. - - HU_SetCEchoFlags(0); - HU_SetCEchoDuration(5); - HU_DoCEcho(va("%s\\was hit by a kitchen sink.\\\\\\\\", player_names[thing->player-players])); - I_OutputMsg("%s was hit by a kitchen sink.\n", player_names[thing->player-players]); - - explosion = P_SpawnMobj(thing->x, thing->y, thing->z, MT_SPBEXPLOSION); - explosion->extravalue1 = 1; // Tell K_ExplodePlayer to use extra knockback - if (tmthing->target && !P_MobjWasRemoved(tmthing->target)) - P_SetTarget(&explosion->target, tmthing->target); - - P_KillMobj(tmthing, thing, thing); - } - - return true; + return K_BananaBallhogCollide(tmthing, thing); } - else if (tmthing->type == MT_MINEEXPLOSION) + else if (thing->type == MT_BANANA || thing->type == MT_BANANA_SHIELD || thing->type == MT_BALLHOG) { // see if it went over / under if (tmthing->z > thing->z + thing->height) @@ -1034,24 +908,10 @@ static boolean PIT_CheckThing(mobj_t *thing) if (tmthing->z + tmthing->height < thing->z) return true; // underneath - if (!(thing->type == MT_PLAYER)) - return true; - - if (thing->player && thing->player->powers[pw_flashing]) - return true; - - if (thing->type == MT_PLAYER && thing->player) - { - if (tmthing->state == &states[S_MINEEXPLOSION1]) - K_ExplodePlayer(thing->player, tmthing->target, tmthing); - else - K_SpinPlayer(thing->player, tmthing->target, 0, tmthing, false); - } - - return true; // This doesn't collide with anything, but we want it to effect the player anyway. + return K_BananaBallhogCollide(thing, tmthing); } - else if (tmthing->type == MT_BANANA_SHIELD || tmthing->type == MT_BANANA - || tmthing->type == MT_BALLHOG) + + if (tmthing->type == MT_SSMINE || tmthing->type == MT_SSMINE_SHIELD) { // see if it went over / under if (tmthing->z > thing->z + thing->height) @@ -1059,78 +919,9 @@ static boolean PIT_CheckThing(mobj_t *thing) if (tmthing->z + tmthing->height < thing->z) return true; // underneath - if (((tmthing->target == thing) || (tmthing->target == thing->target)) && (tmthing->threshold > 0 || (thing->type != MT_PLAYER && thing->threshold > 0))) - return true; - - if (tmthing->health <= 0 || thing->health <= 0) - return true; - - if (((tmthing->type == MT_BANANA_SHIELD) && (thing->type == MT_BANANA_SHIELD)) - && (tmthing->target == thing->target)) // Don't hit each other if you have the same target - return true; - - if (tmthing->type == MT_BALLHOG && thing->type == MT_BALLHOG) - return true; // Ballhogs don't collide with eachother - - if (thing->player && thing->player->powers[pw_flashing]) - return true; - - if (thing->type == MT_PLAYER) - { - // Banana snipe! - if (tmthing->type == MT_BANANA && tmthing->health > 1) - S_StartSound(thing, sfx_bsnipe); - - // Player Damage - K_SpinPlayer(thing->player, tmthing->target, 0, tmthing, (tmthing->type == MT_BANANA || tmthing->type == MT_BANANA_SHIELD)); - - // This Item Damage - if (tmthing->eflags & MFE_VERTICALFLIP) - tmthing->z -= tmthing->height; - else - tmthing->z += tmthing->height; - - S_StartSound(tmthing, tmthing->info->deathsound); - P_KillMobj(tmthing, thing, thing); - - P_SetObjectMomZ(tmthing, 12*FRACUNIT, false); - P_InstaThrust(tmthing, R_PointToAngle2(thing->x, thing->y, tmthing->x, tmthing->y)+ANGLE_90, 16*FRACUNIT); - } - else if (thing->type == MT_BANANA || thing->type == MT_BANANA_SHIELD - || thing->type == MT_ORBINAUT || thing->type == MT_ORBINAUT_SHIELD - || thing->type == MT_JAWZ || thing->type == MT_JAWZ_DUD || thing->type == MT_JAWZ_SHIELD - || thing->type == MT_BALLHOG) - { - // Other Item Damage - if (thing->eflags & MFE_VERTICALFLIP) - thing->z -= thing->height; - else - thing->z += thing->height; - - S_StartSound(thing, thing->info->deathsound); - P_KillMobj(thing, tmthing, tmthing); - - P_SetObjectMomZ(thing, 12*FRACUNIT, false); - P_InstaThrust(thing, R_PointToAngle2(tmthing->x, tmthing->y, thing->x, thing->y)+ANGLE_90, 16*FRACUNIT); - - P_SpawnMobj(thing->x/2 + tmthing->x/2, thing->y/2 + tmthing->y/2, thing->z/2 + tmthing->z/2, MT_ITEMCLASH); - - // This Item Damage - if (tmthing->eflags & MFE_VERTICALFLIP) - tmthing->z -= tmthing->height; - else - tmthing->z += tmthing->height; - - S_StartSound(tmthing, tmthing->info->deathsound); - P_KillMobj(tmthing, thing, thing); - - P_SetObjectMomZ(tmthing, 12*FRACUNIT, false); - P_InstaThrust(tmthing, R_PointToAngle2(thing->x, thing->y, tmthing->x, tmthing->y)+ANGLE_90, 16*FRACUNIT); - } - - return true; + return K_MineCollide(tmthing, thing); } - else if (tmthing->type == MT_SSMINE_SHIELD || tmthing->type == MT_SSMINE) + else if (thing->type == MT_SSMINE || thing->type == MT_SSMINE_SHIELD) { // see if it went over / under if (tmthing->z > thing->z + thing->height) @@ -1138,52 +929,10 @@ static boolean PIT_CheckThing(mobj_t *thing) if (tmthing->z + tmthing->height < thing->z) return true; // underneath - if (((tmthing->target == thing) || (tmthing->target == thing->target)) && (tmthing->threshold > 0 || (thing->type != MT_PLAYER && thing->threshold > 0))) - return true; - - if (tmthing->health <= 0 || thing->health <= 0) - return true; - - if (thing->player && thing->player->powers[pw_flashing]) - return true; - - if (thing->type == MT_PLAYER) - { - // Bomb punting - if ((tmthing->state >= &states[S_SSMINE1] && tmthing->state <= &states[S_SSMINE4]) - || (tmthing->state >= &states[S_SSMINE_DEPLOY8] && tmthing->state <= &states[S_SSMINE_DEPLOY13])) - P_KillMobj(tmthing, thing, thing); - else - K_PuntMine(tmthing, thing); - } - else if (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ || thing->type == MT_JAWZ_DUD - || thing->type == MT_ORBINAUT_SHIELD || thing->type == MT_JAWZ_SHIELD) - { - P_KillMobj(tmthing, thing, thing); - - // Other Item Damage - if (thing->eflags & MFE_VERTICALFLIP) - thing->z -= thing->height; - else - thing->z += thing->height; - - S_StartSound(thing, thing->info->deathsound); - P_KillMobj(thing, tmthing, tmthing); - - P_SetObjectMomZ(thing, 12*FRACUNIT, false); - P_InstaThrust(thing, R_PointToAngle2(tmthing->x, tmthing->y, thing->x, thing->y)+ANGLE_90, 16*FRACUNIT); - } - - return true; + return K_MineCollide(thing, tmthing); } - else if (tmthing->type == MT_PLAYER && - (thing->type == MT_ORBINAUT_SHIELD || thing->type == MT_ORBINAUT - || thing->type == MT_JAWZ_SHIELD || thing->type == MT_JAWZ || thing->type == MT_JAWZ_DUD - || thing->type == MT_BANANA_SHIELD || thing->type == MT_BANANA - || thing->type == MT_SSMINE_SHIELD || thing->type == MT_SSMINE - || thing->type == MT_MINEEXPLOSION - || thing->type == MT_SINK || thing->type == MT_BALLHOG - )) + + if (tmthing->type == MT_MINEEXPLOSION) { // see if it went over / under if (tmthing->z > thing->z + thing->height) @@ -1191,130 +940,63 @@ static boolean PIT_CheckThing(mobj_t *thing) if (tmthing->z + tmthing->height < thing->z) return true; // underneath - if (tmthing->player && tmthing->player->powers[pw_flashing] - && !(thing->type == MT_ORBINAUT || thing->type == MT_JAWZ || thing->type == MT_JAWZ_DUD)) - return true; + return K_MineExplosionCollide(tmthing, thing); + } + else if (thing->type == MT_MINEEXPLOSION) + { + // see if it went over / under + if (tmthing->z > thing->z + thing->height) + return true; // overhead + if (tmthing->z + tmthing->height < thing->z) + return true; // underneath - if (tmthing->player && tmthing->player->kartstuff[k_hyudorotimer]) // I thought about doing this for just the objects below but figured it should apply to everything. - return true; // no interaction + return K_MineExplosionCollide(thing, tmthing); + } - if (thing->type == MT_ORBINAUT_SHIELD || thing->type == MT_JAWZ_SHIELD - || thing->type == MT_ORBINAUT || thing->type == MT_JAWZ || thing->type == MT_JAWZ_DUD) - { - if ((thing->target == tmthing) && (thing->threshold > 0)) - return true; + if (tmthing->type == MT_SINK) + { + // see if it went over / under + if (tmthing->z > thing->z + thing->height) + return true; // overhead + if (tmthing->z + tmthing->height < thing->z) + return true; // underneath - if (tmthing->health <= 0 || thing->health <= 0) - return true; + return K_KitchenSinkCollide(tmthing, thing); + } + else if (thing->type == MT_SINK) + { + // see if it went over / under + if (tmthing->z > thing->z + thing->height) + return true; // overhead + if (tmthing->z + tmthing->height < thing->z) + return true; // underneath - // Player Damage - P_DamageMobj(tmthing, thing, thing->target, 1); - K_KartBouncing(tmthing, thing, false, false); - S_StartSound(tmthing, sfx_s3k7b); + return K_KitchenSinkCollide(thing, tmthing); + } - // Other Item Damage - if (thing->eflags & MFE_VERTICALFLIP) - thing->z -= thing->height; - else - thing->z += thing->height; + if (tmthing->type == MT_FALLINGROCK) + { + // see if it went over / under + if (tmthing->z > thing->z + thing->height) + return true; // overhead + if (tmthing->z + tmthing->height < thing->z) + return true; // underneath - S_StartSound(thing, thing->info->deathsound); - P_KillMobj(thing, tmthing, tmthing); + return K_FallingRockCollide(tmthing, thing); + } + else if (thing->type == MT_FALLINGROCK) + { + // see if it went over / under + if (tmthing->z > thing->z + thing->height) + return true; // overhead + if (tmthing->z + tmthing->height < thing->z) + return true; // underneath - P_SetObjectMomZ(thing, 12*FRACUNIT, false); - P_InstaThrust(thing, R_PointToAngle2(tmthing->x, tmthing->y, thing->x, thing->y)+ANGLE_90, 16*FRACUNIT); - } - else if (thing->type == MT_BANANA_SHIELD || thing->type == MT_BANANA - || thing->type == MT_BALLHOG) - { - if ((thing->target == tmthing) && (thing->threshold > 0)) - return true; - - if (tmthing->health <= 0 || thing->health <= 0) - return true; - - // Banana snipe! - if (thing->type == MT_BANANA && thing->health > 1) - S_StartSound(tmthing, sfx_bsnipe); - - // Player Damage - K_SpinPlayer(tmthing->player, thing->target, 0, thing, (thing->type == MT_BANANA || thing->type == MT_BANANA_SHIELD)); - - // Other Item Damage - if (thing->eflags & MFE_VERTICALFLIP) - thing->z -= thing->height; - else - thing->z += thing->height; - - S_StartSound(thing, thing->info->deathsound); - P_KillMobj(thing, tmthing, tmthing); - - P_SetObjectMomZ(thing, 12*FRACUNIT, false); - P_InstaThrust(thing, R_PointToAngle2(tmthing->x, tmthing->y, thing->x, thing->y)+ANGLE_90, 16*FRACUNIT); - } - else if (thing->type == MT_SSMINE_SHIELD || thing->type == MT_SSMINE) - { - if ((thing->target == tmthing) && (thing->threshold > 0)) - return true; - - if (tmthing->health <= 0 || thing->health <= 0) - return true; - - // Bomb punting - if ((thing->state >= &states[S_SSMINE1] && thing->state <= &states[S_SSMINE4]) - || (thing->state >= &states[S_SSMINE_DEPLOY8] && thing->state <= &states[S_SSMINE_DEPLOY13])) - P_KillMobj(thing, tmthing, tmthing); - else - K_PuntMine(thing, tmthing); - } - else if (thing->type == MT_MINEEXPLOSION && tmthing->player) - { - // Player Damage - if (thing->state == &states[S_MINEEXPLOSION1]) - K_ExplodePlayer(tmthing->player, thing->target, thing); - else - K_SpinPlayer(tmthing->player, thing->target, 0, thing, false); - - return true; - } - else if (thing->type == MT_SINK) - { - mobj_t *explosion; - - if ((thing->target == tmthing) && (thing->threshold > 0)) - return true; - - S_StartSound(NULL, sfx_bsnipe); // let all players hear it. - - HU_SetCEchoFlags(0); - HU_SetCEchoDuration(5); - HU_DoCEcho(va("%s\\was hit by a kitchen sink.\\\\\\\\", player_names[tmthing->player-players])); - I_OutputMsg("%s was hit by a kitchen sink.\n", player_names[tmthing->player-players]); - - explosion = P_SpawnMobj(tmthing->x, tmthing->y, tmthing->z, MT_SPBEXPLOSION); - explosion->extravalue1 = 1; // Tell K_ExplodePlayer to use extra knockback - if (thing->target && !P_MobjWasRemoved(thing->target)) - P_SetTarget(&explosion->target, thing->target); - - P_KillMobj(thing, tmthing, tmthing); - } - - return true; + return K_FallingRockCollide(thing, tmthing); } //} - if ((thing->type == MT_FALLINGROCK && (tmthing->player || tmthing->type == MT_FALLINGROCK)) - || (tmthing->type == MT_FALLINGROCK && (thing->player || thing->type == MT_FALLINGROCK))) - { - // see if it went over / under - if (tmthing->z > thing->z + thing->height) - return true; // overhead - if (tmthing->z + tmthing->height < thing->z) - return true; // underneath - K_KartBouncing(thing, tmthing, false, false); - } - if ((thing->type == MT_SPRINGSHELL || thing->type == MT_YELLOWSHELL) && thing->health > 0 && (tmthing->player || (tmthing->flags & MF_PUSHABLE)) && tmthing->health > 0) { diff --git a/src/p_mobj.c b/src/p_mobj.c index 20119b6f5..8c85a05e2 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -36,6 +36,7 @@ #endif #include "k_kart.h" +#include "k_battle.h" // protos. //static CV_PossibleValue_t viewheight_cons_t[] = {{16, "MIN"}, {56, "MAX"}, {0, NULL}}; @@ -3614,12 +3615,15 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled dummy.y = thiscam->y; dummy.z = thiscam->z; dummy.height = thiscam->height; - if (player->pflags & PF_TIMEOVER) + + if ((player->pflags & PF_TIMEOVER) && G_RaceGametype()) player->karthud[khud_timeovercam] = (2*TICRATE)+1; + if (!resetcalled && !(player->pflags & PF_NOCLIP || leveltime < introtime) && !P_CheckSight(&dummy, player->mo)) // TODO: "P_CheckCameraSight" instead. P_ResetCamera(player, thiscam); else P_SlideCameraMove(thiscam); + if (resetcalled) // Okay this means the camera is fully reset. return true; } @@ -6268,216 +6272,6 @@ static void P_RemoveOverlay(mobj_t *thing) } } -// SAL'S KART BATTLE MODE OVERTIME HANDLER -#define MAXPLANESPERSECTOR (MAXFFLOORS+1)*2 -static void P_SpawnOvertimeParticles(fixed_t x, fixed_t y, fixed_t scale, mobjtype_t type, boolean ceiling) -{ - UINT8 i; - fixed_t flatz[MAXPLANESPERSECTOR]; - boolean flip[MAXPLANESPERSECTOR]; - UINT8 numflats = 0; - mobj_t *mo; - subsector_t *ss = R_IsPointInSubsector(x, y); - sector_t *sec; - - if (!ss) - return; - sec = ss->sector; - - // convoluted stuff JUST to get all of the planes we need to draw orbs on :V - - for (i = 0; i < MAXPLANESPERSECTOR; i++) - flip[i] = false; - - if (sec->floorpic != skyflatnum) - { -#ifdef ESLOPE - flatz[numflats] = (sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : sec->floorheight); -#else - flatz[numflats] = (sec->floorheight); -#endif - numflats++; - } - if (sec->ceilingpic != skyflatnum && ceiling) - { -#ifdef ESLOPE - flatz[numflats] = (sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : sec->ceilingheight) - FixedMul(mobjinfo[type].height, scale); -#else - flatz[numflats] = (sec->ceilingheight) - FixedMul(mobjinfo[type].height, scale); -#endif - flip[numflats] = true; - numflats++; - } - - if (sec->ffloors) - { - ffloor_t *rover; - for (rover = sec->ffloors; rover; rover = rover->next) - { - if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER)) - continue; - if (*rover->toppic != skyflatnum) - { -#ifdef ESLOPE - flatz[numflats] = (*rover->t_slope ? P_GetZAt(*rover->t_slope, x, y) : *rover->topheight); -#else - flatz[numflats] = (*rover->topheight); -#endif - numflats++; - } - if (*rover->bottompic != skyflatnum && ceiling) - { -#ifdef ESLOPE - flatz[numflats] = (*rover->b_slope ? P_GetZAt(*rover->b_slope, x, y) : *rover->bottomheight) - FixedMul(mobjinfo[type].height, scale); -#else - flatz[numflats] = (*rover->bottomheight) - FixedMul(mobjinfo[type].height, scale); -#endif - flip[numflats] = true; - numflats++; - } - } - } - - if (numflats <= 0) // no flats - return; - - for (i = 0; i < numflats; i++) - { - mo = P_SpawnMobj(x, y, flatz[i], type); - - // Lastly, if this can see the skybox mobj, then... we just wasted our time :V - if (skyboxmo[0] && !P_MobjWasRemoved(skyboxmo[0])) - { - const fixed_t sbz = skyboxmo[0]->z; - fixed_t checkz = sec->floorheight; - - while (checkz < sec->ceilingheight) - { - P_TeleportMove(skyboxmo[0], skyboxmo[0]->x, skyboxmo[0]->y, checkz); - if (P_CheckSight(skyboxmo[0], mo)) - { - P_RemoveMobj(mo); - break; - } - else - checkz += 32*mapobjectscale; - } - - P_TeleportMove(skyboxmo[0], skyboxmo[0]->x, skyboxmo[0]->y, sbz); - - if (P_MobjWasRemoved(mo)) - continue; - } - - P_SetScale(mo, scale); - - if (flip[i]) - { - mo->flags2 |= MF2_OBJECTFLIP; - mo->eflags |= MFE_VERTICALFLIP; - } - - switch(type) - { - case MT_OVERTIMEFOG: - mo->destscale = 8*mo->scale; - mo->momz = P_RandomRange(1,8)*mo->scale; - break; - case MT_OVERTIMEORB: - //mo->destscale = mo->scale/4; - mo->frame += ((leveltime/4) % 8); - /*if (battleovertime.enabled < 10*TICRATE) - mo->flags2 |= MF2_SHADOW;*/ - mo->angle = R_PointToAngle2(mo->x, mo->y, battleovertime.x, battleovertime.y) + ANGLE_90; - mo->z += P_RandomRange(0,48) * mo->scale; - break; - default: - break; - } - } -} -#undef MAXPLANESPERSECTOR - -void P_RunBattleOvertime(void) -{ - UINT16 i, j; - - if (battleovertime.enabled < 10*TICRATE) - { - battleovertime.enabled++; - if (battleovertime.enabled == TICRATE) - S_StartSound(NULL, sfx_bhurry); - if (battleovertime.enabled == 10*TICRATE) - S_StartSound(NULL, sfx_kc40); - } - else - { - if (battleovertime.radius > battleovertime.minradius) - battleovertime.radius -= mapobjectscale; - else - battleovertime.radius = battleovertime.minradius; - } - - if (leveltime & 1) - { - UINT8 transparency = tr_trans50; - - if (!r_splitscreen && players[displayplayers[0]].mo) - { - INT32 dist = P_AproxDistance(battleovertime.x-players[displayplayers[0]].mo->x, battleovertime.y-players[displayplayers[0]].mo->y); - transparency = max(0, NUMTRANSMAPS - ((256 + (dist>>FRACBITS)) / 256)); - } - - if (transparency < NUMTRANSMAPS) - { - mobj_t *beam = P_SpawnMobj(battleovertime.x, battleovertime.y, battleovertime.z + (mobjinfo[MT_RANDOMITEM].height/2), MT_OVERTIMEBEAM); - P_SetScale(beam, beam->scale*2); - if (transparency > 0) - beam->frame |= transparency<>FRACBITS / 2));*/ - - for (i = 0; i < 16; i++) - { - j = 0; - while (j < 32) // max attempts - { - fixed_t x = battleovertime.x + ((P_RandomRange(-64,64) * 128)<info->painstate); // same function, diff sprite } break; + case MT_BATTLECAPSULE_PIECE: + if (mobj->extravalue2) + mobj->frame |= FF_VERTICALFLIP; + else + mobj->frame &= ~FF_VERTICALFLIP; + + if (mobj->flags2 & MF2_OBJECTFLIP) + mobj->eflags |= MFE_VERTICALFLIP; + + if (mobj->tics > 0) + mobj->flags2 ^= MF2_DONTDRAW; + break; //} case MT_WATERDROP: P_SceneryCheckWater(mobj); @@ -7571,6 +7377,27 @@ void P_MobjThinker(mobj_t *mobj) return; } break; + case MT_BATTLECAPSULE: + if (!(mobj->fuse & 1)) + { + const SINT8 amt = 96; + mobj_t *dust; + UINT8 i; + + for (i = 0; i < 2; i++) + { + fixed_t xoffset = P_RandomRange(-amt, amt) * mobj->scale; + fixed_t yoffset = P_RandomRange(-amt, amt) * mobj->scale; + fixed_t zoffset = P_RandomRange(-(amt >> 1), (amt >> 1)) * mobj->scale; + + dust = P_SpawnMobj(mobj->x + xoffset, mobj->y + yoffset, + mobj->z + (mobj->height >> 1) + zoffset, MT_EXPLODE); + } + + if (dust && !P_MobjWasRemoved(dust)) // Only do for 1 explosion + S_StartSound(dust, sfx_s3k3d); + } + break; //} default: break; @@ -9392,6 +9219,229 @@ void P_MobjThinker(mobj_t *mobj) trail->color = mobj->color; } break; + case MT_BATTLECAPSULE: + { + SINT8 realflip = P_MobjFlip(mobj); + SINT8 flip = realflip; // Flying capsules needs flipped sprites, but not flipped gravity + fixed_t bottom; + mobj_t *cur; + + if (mobj->extravalue1) + { + const INT32 speed = 6*TICRATE; // longer is slower + const fixed_t pi = 22*FRACUNIT/7; // Inaccurate, but is close enough for our usage + fixed_t sine = FINESINE((((2*pi*speed) * leveltime) >> ANGLETOFINESHIFT) & FINEMASK) * flip; + + // Flying capsules are flipped upside-down, like S3K + flip = -flip; + + // ALL CAPSULE MOVEMENT NEEDS TO HAPPEN AFTER THIS & ADD TO MOMENTUM FOR BOBBING TO BE ACCURATE + mobj->momz = sine/2; + } + + // Moving capsules + if (mobj->target && !P_MobjWasRemoved(mobj->target)) + { + fixed_t speed = mobj->movefactor; + UINT8 sequence = mobj->lastlook; + UINT8 num = mobj->movecount; + boolean backandforth = (mobj->flags2 & MF2_AMBUSH); + SINT8 direction = mobj->cvmem; + mobj_t *next = NULL; + thinker_t *th; + fixed_t dist, momx, momy, momz; + + dist = P_AproxDistance(mobj->target->x - mobj->x, mobj->target->y - mobj->y); + if (mobj->extravalue1) + dist = P_AproxDistance(dist, mobj->target->z - mobj->z); + if (dist < 1) + dist = 1; + + if (speed <= dist) + { + momx = FixedMul(FixedDiv(mobj->target->x - mobj->x, dist), speed); + momy = FixedMul(FixedDiv(mobj->target->y - mobj->y, dist), speed); + if (mobj->extravalue1) + momz = mobj->momz + FixedMul(FixedDiv(mobj->target->z - mobj->z, dist), speed); + + mobj->momx = momx; + mobj->momy = momy; + if (mobj->extravalue1) + mobj->momz = momz; + } + else + { + mobj_t *mo2; + + speed -= dist; + + P_UnsetThingPosition(mobj); + mobj->x = mobj->target->x; + mobj->y = mobj->target->y; + mobj->z = mobj->target->z; + P_SetThingPosition(mobj); + + mobj->floorz = mobj->subsector->sector->floorheight; + mobj->ceilingz = mobj->subsector->sector->ceilingheight; + + // Onto the next waypoint! + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker + continue; + + mo2 = (mobj_t *)th; + + if (mo2->type != MT_TUBEWAYPOINT) + continue; + + if (mo2->threshold == sequence) + { + if (mo2->health == num + direction) + { + next = mo2; + break; + } + } + } + + // Are we at the end of the waypoint chain? + // If so, search again for the first/previous waypoint (depending on settings) + if (next == NULL) + { + if (backandforth) + { + mobj->cvmem = -mobj->cvmem; + direction = mobj->cvmem; + } + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker + continue; + + mo2 = (mobj_t *)th; + + if (mo2->type != MT_TUBEWAYPOINT) + continue; + + if (mo2->threshold == sequence) + { + if (backandforth) + { + if (mo2->health == num + direction) + { + next = mo2; + break; + } + } + else + { + if (direction < 0) + { + if (next == NULL || mo2->health > next->health) + next = mo2; + } + else + { + if (next == NULL || mo2->health < next->health) + next = mo2; + } + } + } + } + } + + if (next && !P_MobjWasRemoved(next)) + { + P_SetTarget(&mobj->target, next); + mobj->movecount = next->health; + + dist = P_AproxDistance(mobj->target->x - mobj->x, mobj->target->y - mobj->y); + if (mobj->extravalue1) + dist = P_AproxDistance(dist, mobj->target->z - mobj->z); + if (dist < 1) + dist = 1; + + momx = FixedMul(FixedDiv(mobj->target->x - mobj->x, dist), speed); + momy = FixedMul(FixedDiv(mobj->target->y - mobj->y, dist), speed); + if (mobj->extravalue1) + momz = mobj->momz + FixedMul(FixedDiv(mobj->target->z - mobj->z, dist), speed); + + mobj->momx = momx; + mobj->momy = momy; + if (mobj->extravalue1) + mobj->momz = momz; + } + else + { + CONS_Alert(CONS_WARNING, "Moving capsule could not find next waypoint! (seq: %d)\n", sequence); + P_SetTarget(&mobj->target, NULL); + } + } + } + + if (flip == -1) + bottom = mobj->z + mobj->height; + else + bottom = mobj->z; + + cur = mobj->hnext; + + // Move each piece to the proper position + while (cur && !P_MobjWasRemoved(cur)) + { + fixed_t newx = mobj->x; + fixed_t newy = mobj->y; + fixed_t newz = bottom; + statenum_t state = (statenum_t)(cur->state-states); + + cur->scale = mobj->scale; + cur->destscale = mobj->destscale; + cur->scalespeed = mobj->scalespeed; + + cur->extravalue2 = mobj->extravalue1; + + cur->flags2 = (cur->flags2 & ~MF2_OBJECTFLIP)|(mobj->flags2 & MF2_OBJECTFLIP); + + if (state == S_BATTLECAPSULE_TOP) + newz += (80 * mobj->scale * flip); + else if (state == S_BATTLECAPSULE_BUTTON) + newz += (108 * mobj->scale * flip); + else if (state == S_BATTLECAPSULE_SUPPORT + || state == S_BATTLECAPSULE_SUPPORTFLY + || state == S_KARMAWHEEL) + { + fixed_t offx = mobj->radius; + fixed_t offy = mobj->radius; + + if (cur->extravalue1 & 1) + offx = -offx; + + if (cur->extravalue1 > 1) + offy = -offy; + + newx += offx; + newy += offy; + } + else if (state == S_BATTLECAPSULE_SIDE1 + || state == S_BATTLECAPSULE_SIDE2) + { + fixed_t offset = 48 * mobj->scale; + angle_t angle = (ANGLE_45 * cur->extravalue1); + + newx += FixedMul(offset, FINECOSINE(angle >> ANGLETOFINESHIFT)); + newy += FixedMul(offset, FINESINE(angle >> ANGLETOFINESHIFT)); + newz += (12 * mobj->scale * flip); + + cur->angle = angle + ANGLE_90; + } + + P_TeleportMove(cur, newx, newy, newz); + + cur = cur->hnext; + } + } case MT_RANDOMITEM: if (G_BattleGametype() && mobj->threshold == 70) { @@ -11276,7 +11326,7 @@ void P_SpawnPlayer(INT32 playernum) || (p->jointime <= 1 && pcount <= 1)) { if (leveltime < 1 || (p->jointime <= 1 && pcount <= 1)) // Start of the map? - p->kartstuff[k_bumper] = cv_kartbumpers.value; // Reset those bumpers! + p->kartstuff[k_bumper] = K_StartingBumperCount(); // Reset those bumpers! if (p->kartstuff[k_bumper]) { diff --git a/src/p_saveg.c b/src/p_saveg.c index 4767b09d1..ee2dc62ea 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -35,6 +35,7 @@ #endif // SRB2Kart +#include "k_battle.h" #include "k_pwrlv.h" savedata_t savedata; @@ -3320,6 +3321,8 @@ static void P_NetArchiveMisc(void) // SRB2kart WRITEINT32(save_p, numgotboxes); + WRITEUINT8(save_p, numtargets); + WRITEUINT8(save_p, battlecapsules); WRITEUINT8(save_p, gamespeed); WRITEUINT8(save_p, franticitems); @@ -3443,6 +3446,8 @@ static inline boolean P_NetUnArchiveMisc(void) // SRB2kart numgotboxes = READINT32(save_p); + numtargets = READUINT8(save_p); + battlecapsules = (boolean)READUINT8(save_p); gamespeed = READUINT8(save_p); franticitems = (boolean)READUINT8(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index 3647aa6ea..40ea643b7 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -84,6 +84,7 @@ // SRB2Kart #include "k_kart.h" +#include "k_battle.h" // K_SpawnBattleCapsules #include "k_pwrlv.h" #include "k_waypoint.h" @@ -1026,6 +1027,12 @@ static void P_LoadThings(void) if (mt->type == mobjinfo[MT_RANDOMITEM].doomednum) nummapboxes++; + if (mt->type == mobjinfo[MT_BATTLECAPSULE].doomednum) + { + maptargets++; + continue; // These should not be spawned *yet* + } + mt->mobj = NULL; P_SpawnMapThing(mt); } @@ -1082,14 +1089,19 @@ static void P_LoadThings(void) for (i = 0; i < nummapthings; i++, mt++) { if (mt->type == 300 || mt->type == 308 || mt->type == 309 - || mt->type == 1706 || (mt->type >= 600 && mt->type <= 609) - || mt->type == 1705 || mt->type == 1713 || mt->type == 1800) + || mt->type == 1706 || (mt->type >= 600 && mt->type <= 609) + || mt->type == 1705 || mt->type == 1713 || mt->type == 1800) { + sector_t *mtsector = R_PointInSubsector(mt->x << FRACBITS, mt->y << FRACBITS)->sector; + mt->mobj = NULL; - // Z for objects Tails 05-26-2002 - mt->z = (INT16)(R_PointInSubsector(mt->x << FRACBITS, mt->y << FRACBITS) - ->sector->floorheight>>FRACBITS); + // Z for objects + mt->z = (INT16)( +#ifdef ESLOPE + mtsector->f_slope ? P_GetZAt(mtsector->f_slope, mt->x << FRACBITS, mt->y << FRACBITS) : +#endif + mtsector->floorheight)>>FRACBITS; P_SpawnHoopsAndRings (mt); } @@ -2297,15 +2309,13 @@ static void P_LevelInitStuff(void) memset(localaiming, 0, sizeof(localaiming)); - // map object scale - mapobjectscale = mapheaderinfo[gamemap-1]->mobj_scale; - // special stage tokens, emeralds, and ring total tokenbits = 0; runemeraldmanager = false; nummaprings = 0; - nummapboxes = 0; - numgotboxes = 0; + nummapboxes = numgotboxes = 0; + maptargets = numtargets = 0; + battlecapsules = false; // emerald hunt hunt1 = hunt2 = hunt3 = NULL; @@ -2634,15 +2644,18 @@ static void P_LoadRecordGhosts(void) } // Best Lap ghost - if (cv_ghost_bestlap.value) + if (modeattacking != ATTACKING_CAPSULES) { - for (i = 0; i < numskins; ++i) + if (cv_ghost_bestlap.value) { - if (cv_ghost_bestlap.value == 1 && players[consoleplayer].skin != i) - continue; + for (i = 0; i < numskins; ++i) + { + if (cv_ghost_bestlap.value == 1 && players[consoleplayer].skin != i) + continue; - if (FIL_FileExists(va("%s-%s-lap-best.lmp", gpath, skins[i].name))) - G_AddGhost(va("%s-%s-lap-best.lmp", gpath, skins[i].name)); + if (FIL_FileExists(va("%s-%s-lap-best.lmp", gpath, skins[i].name))) + G_AddGhost(va("%s-%s-lap-best.lmp", gpath, skins[i].name)); + } } } @@ -3175,49 +3188,10 @@ boolean P_SetupLevel(boolean skipprecip) } } - if (modeattacking == ATTACKING_RECORD && !demo.playback) + if (modeattacking && !demo.playback) P_LoadRecordGhosts(); - /*else if (modeattacking == ATTACKING_NIGHTS && !demo.playback) - P_LoadNightsGhosts();*/ - if (G_TagGametype()) - { - INT32 realnumplayers = 0; - INT32 playersactive[MAXPLAYERS]; - - //I just realized how problematic this code can be. - //D_NumPlayers() will not always cover the scope of the netgame. - //What if one player is node 0 and the other node 31? - //The solution? Make a temp array of all players that are currently playing and pick from them. - //Future todo? When a player leaves, shift all nodes down so D_NumPlayers() can be used as intended? - //Also, you'd never have to loop through all 32 players slots to find anything ever again. - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator) - { - playersactive[realnumplayers] = i; //stores the player's node in the array. - realnumplayers++; - } - } - - if (realnumplayers) //this should also fix the dedicated crash bug. You only pick a player if one exists to be picked. - { - i = P_RandomKey(realnumplayers); - players[playersactive[i]].pflags |= PF_TAGIT; //choose our initial tagger before map starts. - - // Taken and modified from G_DoReborn() - // Remove the player so he can respawn elsewhere. - // first dissasociate the corpse - if (players[playersactive[i]].mo) - P_RemoveMobj(players[playersactive[i]].mo); - - G_SpawnPlayer(playersactive[i], false); //respawn the lucky player in his dedicated spawn location. - } - else - CONS_Printf(M_GetText("No player currently available to become IT. Awaiting available players.\n")); - - } - else if (G_RaceGametype() && server) + if (G_RaceGametype() && server) CV_StealthSetValue(&cv_numlaps, ((netgame || multiplayer) && cv_basenumlaps.value && (!(mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) @@ -3347,6 +3321,8 @@ boolean P_SetupLevel(boolean skipprecip) if (!(netgame || multiplayer) && !majormods) mapvisited[gamemap-1] |= MV_VISITED; + G_AddMapToBuffer(gamemap-1); + levelloading = false; P_RunCachedActions(); @@ -3384,7 +3360,8 @@ boolean P_SetupLevel(boolean skipprecip) #endif } - G_AddMapToBuffer(gamemap-1); + // NOW you can try to spawn in the Battle capsules, if there's not enough players for a match + K_SpawnBattleCapsules(); return true; } diff --git a/src/p_spec.c b/src/p_spec.c index 2b1fe535b..9aebf574f 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1983,7 +1983,7 @@ void P_SwitchWeather(UINT8 newWeather) return; // Nothing to do. purge = true; } - else + else { if (precipprops[curWeather].type != MT_NULL) { @@ -2024,7 +2024,7 @@ void P_SwitchWeather(UINT8 newWeather) precipmobj->flags = mobjinfo[swap].flags; st = mobjinfo[swap].spawnstate; - + if (randomstates > 0) { UINT8 mrand = M_RandomByte(); @@ -5699,6 +5699,9 @@ static void P_RunLevelLoadExecutors(void) */ void P_InitSpecials(void) { + // Set the map object scale + mapobjectscale = mapheaderinfo[gamemap-1]->mobj_scale; + // Set the default gravity. Custom gravity overrides this setting. gravity = (FRACUNIT*8)/10; diff --git a/src/p_tick.c b/src/p_tick.c index c514905a9..d7359b307 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -23,6 +23,7 @@ #include "lua_script.h" #include "lua_hook.h" #include "k_kart.h" +#include "k_battle.h" #include "k_waypoint.h" // Object place @@ -636,14 +637,15 @@ void P_Ticker(boolean run) if (run) { P_RunThinkers(); - if (G_BattleGametype() && battleovertime.enabled) - P_RunBattleOvertime(); // Run any "after all the other thinkers" stuff for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) P_PlayerAfterThink(&players[i]); + if (G_BattleGametype() && battleovertime.enabled) + K_RunBattleOvertime(); + #ifdef HAVE_BLUA LUAh_ThinkFrame(); #endif @@ -793,14 +795,15 @@ void P_PreTicker(INT32 frames) } P_RunThinkers(); - if (G_BattleGametype() && battleovertime.enabled) - P_RunBattleOvertime(); // Run any "after all the other thinkers" stuff for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) P_PlayerAfterThink(&players[i]); + if (G_BattleGametype() && battleovertime.enabled) + K_RunBattleOvertime(); + #ifdef HAVE_BLUA LUAh_ThinkFrame(); #endif diff --git a/src/p_user.c b/src/p_user.c index 34e68de3a..22149f408 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1158,10 +1158,10 @@ boolean P_EndingMusic(player_t *player) sprintf(buffer, "k*fail"); // F-Zero death results theme else { - if (bestlocalpos == 1) - sprintf(buffer, "k*win"); - else if (K_IsPlayerLosing(bestlocalplayer)) + if (K_IsPlayerLosing(bestlocalplayer)) sprintf(buffer, "k*lose"); + else if (bestlocalpos == 1) + sprintf(buffer, "k*win"); else sprintf(buffer, "k*ok"); } @@ -6969,9 +6969,10 @@ static void P_DeathThink(player_t *player) if (player->bot) // don't allow bots to do any of the below, B_CheckRespawn does all they need for respawning already goto notrealplayer; - if (player->pflags & PF_TIMEOVER) + if ((player->pflags & PF_TIMEOVER) && G_RaceGametype()) { player->karthud[khud_timeovercam]++; + if (player->mo) { player->mo->flags |= (MF_NOGRAVITY|MF_NOCLIP); @@ -7186,7 +7187,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall || (leveltime < introtime)); // Kart intro cam #endif - if (player->pflags & PF_TIMEOVER) // 1 for momentum keep, 2 for turnaround + if ((player->pflags & PF_TIMEOVER) && G_RaceGametype()) // 1 for momentum keep, 2 for turnaround timeover = (player->karthud[khud_timeovercam] > 2*TICRATE ? 2 : 1); else timeover = 0; diff --git a/src/sounds.h b/src/sounds.h index e3a8560ca..528e21a6b 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -14,6 +14,8 @@ #ifndef __SOUNDS__ #define __SOUNDS__ +#include "doomdef.h" + // Customisable sounds for Skins typedef enum { diff --git a/src/st_stuff.c b/src/st_stuff.c index 8033dca70..7e783c116 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1272,7 +1272,7 @@ static void ST_drawNiGHTSHUD(void) // SRB2kart - unused. #endif ) { - if (modeattacking == ATTACKING_NIGHTS) + if (modeattacking == ATTACKING_CAPSULES) { INT32 maretime = max(stplyr->realtime - stplyr->marebegunat, 0); fixed_t cornerx = vid.width, cornery = vid.height-SCZ(20); diff --git a/src/y_inter.c b/src/y_inter.c index 444e0bd7d..e9e243b04 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -39,6 +39,7 @@ #include "m_random.h" // M_RandomKey #include "g_input.h" // PLAYER1INPUTDOWN #include "k_kart.h" // colortranslations +#include "k_battle.h" #include "k_pwrlv.h" #include "console.h" // cons_menuhighlight #include "lua_hook.h" // IntermissionThinker hook @@ -168,7 +169,7 @@ static void Y_UnloadVoteData(void); // // SRB2Kart - Y_CalculateMatchData and ancillary functions // -static void Y_CompareRace(INT32 i) +static void Y_CompareTime(INT32 i) { UINT32 val = ((players[i].pflags & PF_TIMEOVER || players[i].realtime == UINT32_MAX) ? (UINT32_MAX-1) : players[i].realtime); @@ -180,7 +181,7 @@ static void Y_CompareRace(INT32 i) data.match.num[data.match.numplayers] = i; } -static void Y_CompareBattle(INT32 i) +static void Y_CompareScore(INT32 i) { UINT32 val = ((players[i].pflags & PF_TIMEOVER) ? (UINT32_MAX-1) : players[i].marescore); @@ -440,7 +441,7 @@ void Y_IntermissionDrawer(void) if (data.match.rankingsmode) timeheader = "PWR.LV"; else - timeheader = (intertype == int_race ? "TIME" : "SCORE"); + timeheader = ((intertype == int_race || (intertype == int_match && battlecapsules)) ? "TIME" : "SCORE"); // draw the level name V_DrawCenteredString(-4 + x + BASEVIDWIDTH/2, 12, 0, data.match.levelstring); @@ -559,7 +560,7 @@ void Y_IntermissionDrawer(void) V_DrawRightAlignedThinString(x+152+gutter, y-1, (data.match.numplayers > NUMFORNEWCOLUMN ? V_6WIDTHSPACE : 0), "NO CONTEST."); else { - if (intertype == int_race) + if (intertype == int_race || (intertype == int_match && battlecapsules)) { snprintf(strtime, sizeof strtime, "%i'%02i\"%02i", G_TicsToMinutes(data.match.val[i], true), G_TicsToSeconds(data.match.val[i]), G_TicsToCentiseconds(data.match.val[i])); @@ -745,7 +746,7 @@ void Y_Ticker(void) remove = 10; // Remove 10 points at a time - data.match.increase[data.match.num[q]] -= remove; + data.match.increase[data.match.num[q]] -= remove; // Still not zero, no kaching yet if (data.match.increase[data.match.num[q]] != 0) @@ -795,11 +796,26 @@ static void Y_UpdateRecordReplays(void) if (!mainrecords[gamemap-1]) G_AllocMainRecordData(gamemap-1); - if ((mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < mainrecords[gamemap-1]->time)) - mainrecords[gamemap-1]->time = players[consoleplayer].realtime; + if (players[consoleplayer].pflags & PF_TIMEOVER) + { + players[consoleplayer].realtime = UINT32_MAX; + } - if ((mainrecords[gamemap-1]->lap == 0) || (bestlap < mainrecords[gamemap-1]->lap)) - mainrecords[gamemap-1]->lap = bestlap; + if (((mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < mainrecords[gamemap-1]->time)) + && (players[consoleplayer].realtime < UINT32_MAX)) // DNF + { + mainrecords[gamemap-1]->time = players[consoleplayer].realtime; + } + + if (modeattacking == ATTACKING_RECORD) + { + if ((mainrecords[gamemap-1]->lap == 0) || (bestlap < mainrecords[gamemap-1]->lap)) + mainrecords[gamemap-1]->lap = bestlap; + } + else + { + mainrecords[gamemap-1]->lap = 0; + } // Save demo! bestdemo[255] = '\0'; @@ -830,13 +846,16 @@ static void Y_UpdateRecordReplays(void) CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo); } - snprintf(bestdemo, 255, "%s-%s-lap-best.lmp", gpath, cv_chooseskin.string); - if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)) - { // Better lap time, save this demo. - if (FIL_FileExists(bestdemo)) - remove(bestdemo); - FIL_WriteFile(bestdemo, buf, len); - CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD LAP!"), M_GetText("Saved replay as"), bestdemo); + if (modeattacking == ATTACKING_RECORD) + { + snprintf(bestdemo, 255, "%s-%s-lap-best.lmp", gpath, cv_chooseskin.string); + if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)) + { // Better lap time, save this demo. + if (FIL_FileExists(bestdemo)) + remove(bestdemo); + FIL_WriteFile(bestdemo, buf, len); + CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD LAP!"), M_GetText("Saved replay as"), bestdemo); + } } //CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo); @@ -1036,12 +1055,20 @@ void Y_StartIntermission(void) { timer = 0; - /* // srb2kart: time attack tally is UGLY rn - if (modeattacking) - intertype = int_timeattack; - else - */ - intertype = int_race; + if (!majormods && !multiplayer && !demo.playback) // move this once we have a proper time attack screen + { + // Update visitation flags + mapvisited[gamemap-1] |= MV_BEATEN; + if (ALL7EMERALDS(emeralds)) + mapvisited[gamemap-1] |= MV_ALLEMERALDS; + /*if (ultimatemode) + mapvisited[gamemap-1] |= MV_ULTIMATE; + if (data.coop.gotperfbonus) + mapvisited[gamemap-1] |= MV_PERFECT;*/ + + if (modeattacking) + Y_UpdateRecordReplays(); + } } else { @@ -1056,13 +1083,13 @@ void Y_StartIntermission(void) if (!timer) timer = 1; } - - if (gametype == GT_MATCH) - intertype = int_match; - else //if (gametype == GT_RACE) - intertype = int_race; } + if (gametype == GT_MATCH) + intertype = int_match; + else //if (gametype == GT_RACE) + intertype = int_race; + // We couldn't display the intermission even if we wanted to. // But we still need to give the players their score bonuses, dummy. //if (dedicated) return; @@ -1076,30 +1103,24 @@ void Y_StartIntermission(void) case int_match: { // Calculate who won - Y_CalculateMatchData(0, Y_CompareBattle); + if (battlecapsules) + { + Y_CalculateMatchData(0, Y_CompareTime); + } + else + { + Y_CalculateMatchData(0, Y_CompareScore); + } + if (cv_inttime.value > 0) S_ChangeMusicInternal("racent", true); // loop it + break; } case int_race: // (time-only race) { - if (!majormods && !multiplayer && !demo.playback) // remove this once we have a proper time attack screen - { - // Update visitation flags - mapvisited[gamemap-1] |= MV_BEATEN; - if (ALL7EMERALDS(emeralds)) - mapvisited[gamemap-1] |= MV_ALLEMERALDS; - /*if (ultimatemode) - mapvisited[gamemap-1] |= MV_ULTIMATE; - if (data.coop.gotperfbonus) - mapvisited[gamemap-1] |= MV_PERFECT;*/ - - if (modeattacking == ATTACKING_RECORD) - Y_UpdateRecordReplays(); - } - // Calculate who won - Y_CalculateMatchData(0, Y_CompareRace); + Y_CalculateMatchData(0, Y_CompareTime); break; }