diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1d461a820..42006e423 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -170,6 +170,7 @@ set(SRB2_CORE_GAME_SOURCES p_telept.c p_tick.c p_user.c + k_race.c k_battle.c k_bheap.c k_collide.c @@ -195,6 +196,7 @@ set(SRB2_CORE_GAME_SOURCES p_slopes.h p_spec.h p_tick.h + k_race.h k_battle.h k_bheap.h k_collide.h diff --git a/src/Makefile b/src/Makefile index de857fd1d..3499dd24a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -558,6 +558,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/k_respawn.o\ $(OBJDIR)/k_collide.o\ $(OBJDIR)/k_color.o \ + $(OBJDIR)/k_race.o \ $(OBJDIR)/k_battle.o \ $(OBJDIR)/k_pwrlv.o \ $(OBJDIR)/k_waypoint.o\ diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 1ebf4960a..e63cafe33 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -616,6 +616,9 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->kartstuff[j] = LONG(players[i].kartstuff[j]); rsp->airtime = (tic_t)LONG(players[i].airtime); + rsp->driftInput = players[i].driftInput; + rsp->airFailsafe = players[i].airFailsafe; + rsp->trickpanel = (UINT8)players[i].trickpanel; rsp->trickdelay = (boolean)players[i].trickdelay; rsp->trickmomx = (fixed_t)LONG(players[i].trickmomx); @@ -631,6 +634,8 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->tumbleLastBounce = players[i].tumbleLastBounce; rsp->tumbleSound = players[i].tumbleSound; + rsp->glanceDir = players[i].glanceDir; + // respawnvars_t rsp->respawn_state = players[i].respawn.state; rsp->respawn_pointx = (fixed_t)LONG(players[i].respawn.pointx); @@ -774,6 +779,9 @@ static void resynch_read_player(resynch_pak *rsp) players[i].kartstuff[j] = LONG(rsp->kartstuff[j]); players[i].airtime = (tic_t)LONG(rsp->airtime); + players[i].driftInput = (boolean)rsp->driftInput; + players[i].airFailsafe = (boolean)rsp->airFailsafe; + players[i].trickpanel = (UINT8)rsp->trickpanel; players[i].trickdelay = (boolean)rsp->trickdelay; players[i].trickmomx = (fixed_t)LONG(rsp->trickmomx); @@ -789,6 +797,8 @@ static void resynch_read_player(resynch_pak *rsp) players[i].tumbleLastBounce = (boolean)rsp->tumbleLastBounce; players[i].tumbleSound = (boolean)rsp->tumbleSound; + players[i].glanceDir = (SINT8)rsp->glanceDir; + // respawnvars_t players[i].respawn.state = rsp->respawn_state; players[i].respawn.pointx = (fixed_t)LONG(rsp->respawn_pointx); diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 8e7e9aee9..82baf6581 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -280,6 +280,8 @@ typedef struct // SRB2kart INT32 kartstuff[NUMKARTSTUFF]; tic_t airtime; + boolean driftInput; + boolean airFailsafe; UINT8 trickpanel; boolean trickdelay; fixed_t trickmomx; @@ -295,6 +297,8 @@ typedef struct boolean tumbleLastBounce; boolean tumbleSound; + SINT8 glanceDir; + // respawnvars_t UINT8 respawn_state; fixed_t respawn_pointx; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index e86bef0a0..8bc1bc905 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3555,12 +3555,16 @@ static void Command_Login_f(void) boolean IsPlayerAdmin(INT32 playernum) { +#ifdef DEVELOP + return playernum != serverplayer; +#else INT32 i; for (i = 0; i < MAXPLAYERS; i++) if (playernum == adminplayers[i]) return true; return false; +#endif } void SetAdminPlayer(INT32 playernum) diff --git a/src/d_player.h b/src/d_player.h index cf5bafd24..95fd8adea 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -115,19 +115,11 @@ typedef enum { // Are animation frames playing? PA_ETC=0, - PA_IDLE, - PA_EDGE, - PA_WALK, - PA_RUN, - PA_DASH, - PA_PAIN, - PA_ROLL, - PA_JUMP, - PA_SPRING, - PA_FALL, - PA_ABILITY, - PA_ABILITY2, - PA_RIDE + PA_STILL, + PA_SLOW, + PA_FAST, + PA_DRIFT, + PA_HURT } panim_t; // @@ -454,6 +446,8 @@ typedef enum #define TRICKMOMZRAMP (30) #define TRICKLAG (9) +#define TUMBLEBOUNCES 3 + //} // player_t struct for all respawn variables @@ -525,10 +519,13 @@ typedef struct player_s // SRB2kart stuff INT32 kartstuff[NUMKARTSTUFF]; INT32 karthud[NUMKARTHUD]; + UINT32 distancetofinish; waypoint_t *nextwaypoint; respawnvars_t respawn; // Respawn info tic_t airtime; // Keep track of how long you've been in the air + boolean driftInput; // Whenever or not try drifting. + boolean airFailsafe; // Whenever or not try the air boost UINT8 trickpanel; // Trick panel state boolean trickdelay; // Prevent tricks until control stick is neutral @@ -575,6 +572,8 @@ typedef struct player_s boolean tumbleLastBounce; boolean tumbleSound; + SINT8 glanceDir; // Direction the player is trying to look backwards in + // UINT32 charflags; // Extra abilities/settings for skins (combinable stuff) diff --git a/src/dehacked.c b/src/dehacked.c index 4e2db6975..5d77e61dc 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -2031,6 +2031,13 @@ static void readlevelheader(MYFILE *f, INT32 num) else mapheaderinfo[num-1]->levelflags &= ~LF_SECTIONRACE; } + else if (fastcmp(word, "SUBTRACTNUM")) + { + if (i || word2[0] == 'T' || word2[0] == 'Y') + mapheaderinfo[num-1]->levelflags |= LF_SUBTRACTNUM; + else + mapheaderinfo[num-1]->levelflags &= ~LF_SUBTRACTNUM; + } // Individual triggers for menu flags else if (fastcmp(word, "HIDDEN")) @@ -5204,12 +5211,24 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_KART_STILL", "S_KART_STILL_L", "S_KART_STILL_R", + "S_KART_STILL_GLANCE_L", + "S_KART_STILL_GLANCE_R", + "S_KART_STILL_LOOK_L", + "S_KART_STILL_LOOK_R", "S_KART_SLOW", "S_KART_SLOW_L", "S_KART_SLOW_R", + "S_KART_SLOW_GLANCE_L", + "S_KART_SLOW_GLANCE_R", + "S_KART_SLOW_LOOK_L", + "S_KART_SLOW_LOOK_R", "S_KART_FAST", "S_KART_FAST_L", "S_KART_FAST_R", + "S_KART_FAST_GLANCE_L", + "S_KART_FAST_GLANCE_R", + "S_KART_FAST_LOOK_L", + "S_KART_FAST_LOOK_R", "S_KART_DRIFT_L", "S_KART_DRIFT_L_OUT", "S_KART_DRIFT_L_IN", @@ -5217,12 +5236,18 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_KART_DRIFT_R_OUT", "S_KART_DRIFT_R_IN", "S_KART_SPINOUT", - "S_KART_SQUISH", + "S_KART_DEAD", "S_KART_SIGN", // technically the player goes here but it's an infinite tic state "S_OBJPLACE_DUMMY", + "S_KART_LEFTOVER", + "S_KART_LEFTOVER_NOTIRES", + + "S_KART_TIRE1", + "S_KART_TIRE2", + // Blue Crawla "S_POSS_STND", "S_POSS_RUN1", @@ -9398,6 +9423,31 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_SPINDASHDUST", "S_SPINDASHWIND", + // Finish line beam + "S_FINISHBEAM1", + "S_FINISHBEAM2", + "S_FINISHBEAM3", + "S_FINISHBEAM4", + "S_FINISHBEAM5", + "S_FINISHBEAMEND1", + "S_FINISHBEAMEND2", + + // Funny Spike + "S_DEBTSPIKE1", + "S_DEBTSPIKE2", + "S_DEBTSPIKE3", + "S_DEBTSPIKE4", + "S_DEBTSPIKE5", + "S_DEBTSPIKE6", + "S_DEBTSPIKE7", + "S_DEBTSPIKE8", + "S_DEBTSPIKE9", + "S_DEBTSPIKEA", + "S_DEBTSPIKEB", + "S_DEBTSPIKEC", + "S_DEBTSPIKED", + "S_DEBTSPIKEE", + #ifdef SEENAMES "S_NAMECHECK", #endif @@ -9412,6 +9462,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_THOK", // Thok! mobj "MT_PLAYER", + "MT_KART_LEFTOVER", + "MT_KART_TIRE", // Enemies "MT_BLUECRAWLA", // Crawla (Blue) @@ -9537,6 +9589,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s // Collectible Items "MT_RING", "MT_FLINGRING", // Lost ring + "MT_DEBTSPIKE", // Ring debt funny spike "MT_BLUESPHERE", // Blue sphere for special stages "MT_FLINGBLUESPHERE", // Lost blue sphere "MT_BOMBSPHERE", @@ -10480,6 +10533,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_PAPERITEMSPOT", + "MT_BEAMPOINT", + #ifdef SEENAMES "MT_NAMECHECK", #endif @@ -11002,6 +11057,32 @@ static const char *const KARTSTUFF_LIST[] = { "WRONGWAY" }; +static const char *const KARTHUD_LIST[] = { + "ITEMBLINK", + "ITEMBLINKMODE", + + "RINGFRAME", + "RINGTICS", + "RINGDELAY", + "RINGSPBBLOCK", + + "LAPANIMATION", + "LAPHAND", + + "FAULT", + + "BOOSTCAM", + "DESTBOOSTCAM", + "TIMEOVERCAM", + + "ENGINESND", + "VOICES", + "TAUNTVOICES", + + "CARDANIMATION", + "YOUGOTEM", +}; + static const char *const HUDITEMS_LIST[] = { "LIVES", @@ -11245,6 +11326,7 @@ struct { {"LF_SCRIPTISFILE",LF_SCRIPTISFILE}, {"LF_NOZONE",LF_NOZONE}, {"LF_SECTIONRACE",LF_SECTIONRACE}, + {"LF_SUBTRACTNUM",LF_SUBTRACTNUM}, // And map flags {"LF2_HIDEINMENU",LF2_HIDEINMENU}, {"LF2_HIDEINSTATS",LF2_HIDEINSTATS}, @@ -11405,19 +11487,11 @@ struct { // Player animation (panim_t) {"PA_ETC",PA_ETC}, - {"PA_IDLE",PA_IDLE}, - {"PA_EDGE",PA_EDGE}, - {"PA_WALK",PA_WALK}, - {"PA_RUN",PA_RUN}, - {"PA_DASH",PA_DASH}, - {"PA_PAIN",PA_PAIN}, - {"PA_ROLL",PA_ROLL}, - {"PA_JUMP",PA_JUMP}, - {"PA_SPRING",PA_SPRING}, - {"PA_FALL",PA_FALL}, - {"PA_ABILITY",PA_ABILITY}, - {"PA_ABILITY2",PA_ABILITY2}, - {"PA_RIDE",PA_RIDE}, + {"PA_STILL",PA_STILL}, + {"PA_SLOW",PA_SLOW}, + {"PA_FAST",PA_FAST}, + {"PA_DRIFT",PA_DRIFT}, + {"PA_HURT",PA_HURT}, // Value for infinite lives {"INFLIVES",INFLIVES}, @@ -11644,6 +11718,8 @@ struct { {"V_70TRANS",V_70TRANS}, {"V_80TRANS",V_80TRANS}, {"V_90TRANS",V_90TRANS}, + {"V_ADDTRANS",V_ADDTRANS}, + {"V_SUBTRANS",V_SUBTRANS}, {"V_HUDTRANSHALF",V_HUDTRANSHALF}, {"V_HUDTRANS",V_HUDTRANS}, {"V_RETURN8",V_RETURN8}, @@ -12244,6 +12320,7 @@ void DEH_Check(void) const size_t dehmobjs = sizeof(MOBJTYPE_LIST)/sizeof(const char*); const size_t dehpowers = sizeof(POWERS_LIST)/sizeof(const char*); const size_t dehkartstuff = sizeof(KARTSTUFF_LIST)/sizeof(const char*); + const size_t dehkarthud = sizeof(KARTHUD_LIST)/sizeof(const char*); const size_t dehcolors = sizeof(COLOR_ENUMS)/sizeof(const char*); if (dehstates != S_FIRSTFREESLOT) @@ -12256,7 +12333,10 @@ void DEH_Check(void) I_Error("You forgot to update the Dehacked powers list, you dolt!\n(%d powers defined, versus %s in the Dehacked list)\n", NUMPOWERS, sizeu1(dehpowers)); if (dehkartstuff != NUMKARTSTUFF) - I_Error("You forgot to update the Dehacked powers list, you dolt!\n(%d kartstuff defined, versus %s in the Dehacked list)\n", NUMKARTSTUFF, sizeu1(dehkartstuff)); + I_Error("You forgot to update the Dehacked kartstuff list, you dolt!\n(%d kartstuff defined, versus %s in the Dehacked list)\n", NUMKARTSTUFF, sizeu1(dehkartstuff)); + + if (dehkarthud != NUMKARTHUD) + I_Error("You forgot to update the Dehacked karthud list, you dolt!\n(%d karthud defined, versus %s in the Dehacked list)\n", NUMKARTSTUFF, sizeu1(dehkartstuff)); if (dehcolors != SKINCOLOR_FIRSTFREESLOT) I_Error("You forgot to update the Dehacked colors list, you dolt!\n(%d colors defined, versus %s in the Dehacked list)\n", SKINCOLOR_FIRSTFREESLOT, sizeu1(dehcolors)); @@ -12718,6 +12798,15 @@ static inline int lib_getenum(lua_State *L) } return 0; } + else if (!mathlib && fastncmp("khud_",word,5)) { + p = word+5; + for (i = 0; i < NUMKARTHUD; i++) + if (fasticmp(p, KARTHUD_LIST[i])) { + lua_pushinteger(L, i); + return 1; + } + return 0; + } else if (mathlib && fastncmp("K_",word,2)) { // SOCs are ALL CAPS! p = word+2; for (i = 0; i < NUMKARTSTUFF; i++) @@ -12727,6 +12816,15 @@ static inline int lib_getenum(lua_State *L) } return luaL_error(L, "kartstuff '%s' could not be found.\n", word); } + else if (mathlib && fastncmp("KHUD_",word,5)) { // SOCs are ALL CAPS! + p = word+5; + for (i = 0; i < NUMKARTHUD; i++) + if (fastcmp(p, KARTHUD_LIST[i])) { + lua_pushinteger(L, i); + return 1; + } + return luaL_error(L, "karthud '%s' could not be found.\n", word); + } else if (fastncmp("HUD_",word,4)) { p = word+4; for (i = 0; i < NUMHUDITEMS; i++) diff --git a/src/doomstat.h b/src/doomstat.h index 038944ef1..691d29fa1 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -408,6 +408,7 @@ typedef struct #define LF_SCRIPTISFILE (1<<0) ///< True if the script is a file, not a lump. #define LF_NOZONE (1<<1) ///< Don't include "ZONE" on level title #define LF_SECTIONRACE (1<<2) ///< Section race level +#define LF_SUBTRACTNUM (1<<3) ///< Use subtractive position number (for bright levels) #define LF2_HIDEINMENU (1<<0) ///< Hide in the multiplayer menu #define LF2_HIDEINSTATS (1<<1) ///< Hide in the statistics screen diff --git a/src/g_game.c b/src/g_game.c index b62e7a6ce..6cc1f9aa2 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1309,9 +1309,11 @@ void G_PreLevelTitleCard(void) // boolean G_IsTitleCardAvailable(void) { +#if 0 // The current level has no name. if (!mapheaderinfo[gamemap-1]->lvlttl[0]) return false; +#endif // The title card is available. return true; @@ -2261,7 +2263,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->pflags |= PF_JUMPDOWN; p->playerstate = PST_LIVE; - p->panim = PA_IDLE; // standing animation + p->panim = PA_STILL; // standing animation // Check to make sure their color didn't change somehow... if (G_GametypeHasTeams()) diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c index 0fe237e24..69f37a29b 100644 --- a/src/hardware/hw_draw.c +++ b/src/hardware/hw_draw.c @@ -141,7 +141,7 @@ void HWR_DrawStretchyFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t // 0--1 float dupx, dupy, fscalew, fscaleh, fwidth, fheight; - if (alphalevel >= 10 && alphalevel < 13) + if (alphalevel >= 12 && alphalevel < 13) return; // make patch ready in hardware cache @@ -268,7 +268,9 @@ void HWR_DrawStretchyFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t { FSurfaceInfo Surf; Surf.PolyColor.s.red = Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0xff; - if (alphalevel == 13) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency]; + if (alphalevel == 10) { flags &= ~PF_Translucent; flags |= PF_Additive; } + else if (alphalevel == 11) { flags &= ~PF_Translucent; flags |= PF_Substractive; } + else if (alphalevel == 13) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency]; else if (alphalevel == 14) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency]; else if (alphalevel == 15) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency]; else Surf.PolyColor.s.alpha = softwaretranstogl[10-alphalevel]; @@ -293,7 +295,7 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal // 0--1 float dupx, dupy, fscale, fwidth, fheight; - if (alphalevel >= 10 && alphalevel < 13) + if (alphalevel >= 12 && alphalevel < 13) return; // make patch ready in hardware cache @@ -426,7 +428,9 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal { FSurfaceInfo Surf; Surf.PolyColor.s.red = Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0xff; - if (alphalevel == 13) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency]; + if (alphalevel == 10) { flags &= ~PF_Translucent; flags |= PF_Additive; } + else if (alphalevel == 11) { flags &= ~PF_Translucent; flags |= PF_Substractive; } + else if (alphalevel == 13) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency]; else if (alphalevel == 14) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency]; else if (alphalevel == 15) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency]; else Surf.PolyColor.s.alpha = softwaretranstogl[10-alphalevel]; diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 1ded58204..9e9bbee81 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -150,8 +150,8 @@ FUNCPRINTF void GL_DBG_Printf(const char *format, ...) char str[4096] = ""; va_list arglist; - if (gllogstream) - { + if (gllogstream) + { va_start(arglist, format); vsnprintf(str, 4096, format, arglist); va_end(arglist); @@ -698,7 +698,38 @@ static INT32 shader_leveltime = 0; "float fd = fe - fs;\n" \ "darkness = clamp((darkness - fs) * (1.0 / fd), 0.0, 1.0);\n" \ "}\n" \ - "final_color = mix(final_color, fade_color, darkness);\n" + "float colorBrightness = sqrt((final_color.r * final_color.r) + (final_color.g * final_color.g) + (final_color.b * final_color.b));\n" \ + "float fogBrightness = sqrt((fade_color.r * fade_color.r) + (fade_color.g * fade_color.g) + (fade_color.b * fade_color.b));\n" \ + "float colorIntensity = 0.0;\n" \ + "if (fogBrightness > colorBrightness) {\n" \ + "colorIntensity = 1.0 - min(final_color.r, min(final_color.g, final_color.b));\n" \ + "colorIntensity = abs(colorIntensity - (1.0 - fogBrightness));\n" \ + "} else {\n" \ + "colorIntensity = max(final_color.r, max(final_color.g, final_color.b));\n" \ + "colorIntensity = abs(colorIntensity - (fogBrightness));\n" \ + "}\n" \ + "colorIntensity *= darkness;\n" \ + "if (abs(final_color.r - fade_color.r) <= colorIntensity) {\n" \ + "final_color.r = fade_color.r;\n" \ + "} else if (final_color.r < fade_color.r) {\n" \ + "final_color.r += colorIntensity;\n" \ + "} else {\n" \ + "final_color.r -= colorIntensity;\n" \ + "}\n" \ + "if (abs(final_color.g - fade_color.g) <= colorIntensity) {\n" \ + "final_color.g = fade_color.g;\n" \ + "} else if (final_color.g < fade_color.g) {\n" \ + "final_color.g += colorIntensity;\n" \ + "} else {\n" \ + "final_color.g -= colorIntensity;\n" \ + "}\n" \ + "if (abs(final_color.b - fade_color.b) <= colorIntensity) {\n" \ + "final_color.b = fade_color.b;\n" \ + "} else if (final_color.b < fade_color.b) {\n" \ + "final_color.b += colorIntensity;\n" \ + "} else {\n" \ + "final_color.b -= colorIntensity;\n" \ + "}\n" \ #define GLSL_SOFTWARE_FRAGMENT_SHADER \ "uniform sampler2D tex;\n" \ diff --git a/src/info.c b/src/info.c index 73666e730..32bb5fb17 100644 --- a/src/info.c +++ b/src/info.c @@ -32,6 +32,8 @@ char sprnames[NUMSPRITES + 1][5] = "THOK", // Thok! mobj "PLAY", + "KART", + "TIRE", // Enemies "POSS", // Crawla (Blue) @@ -131,6 +133,7 @@ char sprnames[NUMSPRITES + 1][5] = // Collectible Items "RING", + "DEBT", "TRNG", // Team Rings "TOKE", // Special Stage Token "RFLG", // Red CTF Flag @@ -732,26 +735,39 @@ char sprnames[NUMSPRITES + 1][5] = "GCHA", // follower: generic chao "CHEZ", // follower: cheese - // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later - "VIEW", - "DBCL", // Drift boost clip "DBNC", // Drift boost clip's sparks "DBST", // Drift boost plume "SDDS", // Spindash dust "SDWN", // Spindash wind + + "FLBM", + + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later + "VIEW", }; char spr2names[NUMPLAYERSPRITES][5] = { "STIN", "STIL", "STIR", // Still + "STGL", "STGR", // Still (glance back) + "STLL", "STLR", // Still (look back) + "SLWN", "SLWL", "SLWR", // Slow driving + "SLGL", "SLGR", // Slow (glance back) + "SLLL", "SLLR", // Slow (look back) + "FSTN", "FSTL", "FSTR", // Fast driving + "FSGL", "FSGR", // Fast (glance back) + "FSLL", "FSLR", // Fast (look back) + "DRLN", "DRLO", "DRLI", // Drifting left "DRRN", "DRRO", "DRRI", // Drifting right + "SPIN", // Spinout - "SQSH", // Squish + "DEAD", // Dead + "SIGN", // Finish signpost "XTRA", // Three Faces of Darkness }; @@ -761,14 +777,26 @@ playersprite_t spr2defaults[NUMPLAYERSPRITES] = { 0, // SPR2_STIN SPR2_STIN, // SPR2_STIL SPR2_STIN, // SPR2_STIR + SPR2_STIN, // SPR2_STGL + SPR2_STIN, // SPR2_STGR + SPR2_STGL, // SPR2_STLL + SPR2_STGR, // SPR2_STLR 0, // SPR2_SLWN SPR2_SLWN, // SPR2_SLWL SPR2_SLWN, // SPR2_SLWR + SPR2_SLWN, // SPR2_SLGL + SPR2_SLWN, // SPR2_SLGR + SPR2_SLGL, // SPR2_SLLL + SPR2_SLGR, // SPR2_SLLR 0, // SPR2_FSTN SPR2_FSTN, // SPR2_FSTL SPR2_FSTN, // SPR2_FSTR + SPR2_FSTN, // SPR2_FSGL + SPR2_FSTN, // SPR2_FSGR + SPR2_FSGL, // SPR2_FSLL + SPR2_FSGR, // SPR2_FSLR 0, // SPR2_DRLN SPR2_DRLN, // SPR2_DRLO @@ -779,9 +807,10 @@ playersprite_t spr2defaults[NUMPLAYERSPRITES] = { SPR2_DRRN, // SPR2_DRRI 0, // SPR2_SPIN - SPR2_SPIN, // SPR2_SQSH + 0, // SPR2_DEAD 0, // SPR2_SIGN + 0, // SPR2_XTRA }; // Doesn't work with g++, needs actionf_p1 (don't modify this comment) @@ -812,12 +841,24 @@ state_t states[NUMSTATES] = {SPR_PLAY, SPR2_STIN, 1, {NULL}, 0, 0, S_KART_STILL}, // S_KART_STILL {SPR_PLAY, SPR2_STIL, 1, {NULL}, 0, 0, S_KART_STILL_L}, // S_KART_STILL_L {SPR_PLAY, SPR2_STIR, 1, {NULL}, 0, 0, S_KART_STILL_R}, // S_KART_STILL_R + {SPR_PLAY, SPR2_STGL, 1, {NULL}, 0, 0, S_KART_STILL_GLANCE_L}, // S_KART_STILL_GLANCE_L + {SPR_PLAY, SPR2_STGR, 1, {NULL}, 0, 0, S_KART_STILL_GLANCE_R}, // S_KART_STILL_GLANCE_R + {SPR_PLAY, SPR2_STLL, 1, {NULL}, 0, 0, S_KART_STILL_LOOK_L}, // S_KART_STILL_LOOK_L + {SPR_PLAY, SPR2_STLR, 1, {NULL}, 0, 0, S_KART_STILL_LOOK_R}, // S_KART_STILL_LOOK_R {SPR_PLAY, SPR2_SLWN, 1, {NULL}, 0, 0, S_KART_SLOW}, // S_KART_SLOW {SPR_PLAY, SPR2_SLWL, 1, {NULL}, 0, 0, S_KART_SLOW_L}, // S_KART_SLOW_L {SPR_PLAY, SPR2_SLWR, 1, {NULL}, 0, 0, S_KART_SLOW_R}, // S_KART_SLOW_R + {SPR_PLAY, SPR2_SLGL, 1, {NULL}, 0, 0, S_KART_SLOW_GLANCE_L}, // S_KART_SLOW_GLANCE_L + {SPR_PLAY, SPR2_SLGR, 1, {NULL}, 0, 0, S_KART_SLOW_GLANCE_R}, // S_KART_SLOW_GLANCE_R + {SPR_PLAY, SPR2_SLLL, 1, {NULL}, 0, 0, S_KART_SLOW_LOOK_L}, // S_KART_SLOW_LOOK_L + {SPR_PLAY, SPR2_SLLR, 1, {NULL}, 0, 0, S_KART_SLOW_LOOK_R}, // S_KART_SLOW_LOOK_R {SPR_PLAY, SPR2_FSTN, 1, {NULL}, 0, 0, S_KART_FAST}, // S_KART_FAST {SPR_PLAY, SPR2_FSTL, 1, {NULL}, 0, 0, S_KART_FAST_L}, // S_KART_FAST_L {SPR_PLAY, SPR2_FSTR, 1, {NULL}, 0, 0, S_KART_FAST_R}, // S_KART_FAST_R + {SPR_PLAY, SPR2_FSGL, 1, {NULL}, 0, 0, S_KART_FAST_GLANCE_L}, // S_KART_FAST_GLANCE_L + {SPR_PLAY, SPR2_FSGR, 1, {NULL}, 0, 0, S_KART_FAST_GLANCE_R}, // S_KART_FAST_GLANCE_R + {SPR_PLAY, SPR2_FSLL, 1, {NULL}, 0, 0, S_KART_FAST_LOOK_L}, // S_KART_FAST_LOOK_L + {SPR_PLAY, SPR2_FSLR, 1, {NULL}, 0, 0, S_KART_FAST_LOOK_R}, // S_KART_FAST_LOOK_R {SPR_PLAY, SPR2_DRLN, 1, {NULL}, 0, 0, S_KART_DRIFT_L}, // S_KART_DRIFT_L {SPR_PLAY, SPR2_DRLO, 1, {NULL}, 0, 0, S_KART_DRIFT_L_OUT}, // S_KART_DRIFT_L_OUT {SPR_PLAY, SPR2_DRLI, 1, {NULL}, 0, 0, S_KART_DRIFT_L_IN}, // S_KART_DRIFT_L_IN @@ -825,11 +866,17 @@ state_t states[NUMSTATES] = {SPR_PLAY, SPR2_DRRO, 1, {NULL}, 0, 0, S_KART_DRIFT_R_OUT}, // S_KART_DRIFT_R_OUT {SPR_PLAY, SPR2_DRRI, 1, {NULL}, 0, 0, S_KART_DRIFT_R_IN}, // S_KART_DRIFT_R_IN {SPR_PLAY, SPR2_SPIN|FF_ANIMATE, 350, {NULL}, 0, 1, S_KART_STILL}, // S_KART_SPINOUT - {SPR_PLAY, SPR2_SQSH|FF_ANIMATE, 350, {NULL}, 0, 1, S_KART_STILL}, // S_KART_SQUISH + {SPR_PLAY, SPR2_DEAD, 3, {NULL}, 0, 0, S_KART_DEAD}, // S_KART_DEAD {SPR_PLAY, SPR2_SIGN|FF_PAPERSPRITE, 1, {NULL}, 0, 0, S_KART_SIGN}, // S_KART_SIGN {SPR_NULL, 0, -1, {NULL}, 0, 0, S_OBJPLACE_DUMMY}, // S_OBJPLACE_DUMMY + {SPR_KART, 0, -1, {NULL}, 0, 0, S_NULL}, // S_KART_LEFTOVER + {SPR_KART, 1, -1, {NULL}, 0, 0, S_NULL}, // S_KART_LEFTOVER_NOTIRES + + {SPR_TIRE, 0, -1, {NULL}, 0, 0, S_NULL}, // S_KART_TIRE1 + {SPR_TIRE, 1, -1, {NULL}, 0, 0, S_NULL}, // S_KART_TIRE2 + // Blue Crawla {SPR_POSS, 0, 5, {A_Look}, 0, 0, S_POSS_STND}, // S_POSS_STND {SPR_POSS, 0, 3, {A_Chase}, 0, 0, S_POSS_RUN2}, // S_POSS_RUN1 @@ -5115,6 +5162,31 @@ state_t states[NUMSTATES] = {SPR_SDDS, FF_ANIMATE, 9, {NULL}, 9, 1, S_NULL}, // S_SPINDASHDUST {SPR_SDWN, FF_ANIMATE|FF_PAPERSPRITE, 18, {NULL}, 9, 2, S_NULL}, // S_SPINDASHWIND + // Finish line beam + {SPR_FLBM, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_NULL}, // S_FINISHBEAM1 + {SPR_FLBM, FF_FULLBRIGHT|1, 1, {NULL}, 0, 0, S_NULL}, // S_FINISHBEAM2 + {SPR_FLBM, FF_FULLBRIGHT|2, 1, {NULL}, 0, 0, S_NULL}, // S_FINISHBEAM3 + {SPR_FLBM, FF_FULLBRIGHT|3, 1, {NULL}, 0, 0, S_NULL}, // S_FINISHBEAM4 + {SPR_FLBM, FF_FULLBRIGHT|4, 1, {NULL}, 0, 0, S_NULL}, // S_FINISHBEAM5 + {SPR_FLBM, FF_PAPERSPRITE|5, 1, {NULL}, 0, 0, S_NULL}, // S_FINISHBEAMEND1 + {SPR_FLBM, FF_PAPERSPRITE|6, 1, {NULL}, 0, 0, S_NULL}, // S_FINISHBEAMEND2 + + // Funny Spike + {SPR_DEBT, 0|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_DEBTSPIKE2}, // S_DEBTSPIKE1 + {SPR_DEBT, 7|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_DEBTSPIKE3}, // S_DEBTSPIKE2 + {SPR_DEBT, 1|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_DEBTSPIKE4}, // S_DEBTSPIKE3 + {SPR_DEBT, 7|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_DEBTSPIKE5}, // S_DEBTSPIKE4 + {SPR_DEBT, 2|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_DEBTSPIKE6}, // S_DEBTSPIKE5 + {SPR_DEBT, 7|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_DEBTSPIKE7}, // S_DEBTSPIKE6 + {SPR_DEBT, 3|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_DEBTSPIKE8}, // S_DEBTSPIKE7 + {SPR_DEBT, 7|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_DEBTSPIKE9}, // S_DEBTSPIKE8 + {SPR_DEBT, 4|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_DEBTSPIKEA}, // S_DEBTSPIKE9 + {SPR_DEBT, 7|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_DEBTSPIKEB}, // S_DEBTSPIKEA + {SPR_DEBT, 5|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_DEBTSPIKEC}, // S_DEBTSPIKEB + {SPR_DEBT, 7|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_DEBTSPIKED}, // S_DEBTSPIKEC + {SPR_DEBT, 6|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_DEBTSPIKEE}, // S_DEBTSPIKED + {SPR_DEBT, 7|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_DEBTSPIKE1}, // S_DEBTSPIKEE + #ifdef SEENAMES {SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK #endif @@ -5216,7 +5288,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_KART_SPINOUT, // deathstate + S_KART_DEAD, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 1, // speed @@ -5230,6 +5302,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = (statenum_t)MT_THOK // raisestate }, + { // MT_KART_LEFTOVER + 4095, // doomednum + S_KART_LEFTOVER, // spawnstate + 2, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 1, // speed + 16*FRACUNIT, // radius + 48*FRACUNIT, // height + -1, // display offset + 1000, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_KART_TIRE + -1, // doomednum + S_KART_TIRE1, // spawnstate + 1, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 1, // speed + 6*FRACUNIT, // radius + 12*FRACUNIT, // height + -1, // display offset + 1000, // mass + 0, // damage + sfx_None, // activesound + MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_BLUECRAWLA 100, // doomednum S_POSS_STND, // spawnstate @@ -8011,6 +8137,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_DEBTSPIKE + -1, // doomednum + S_DEBTSPIKE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + MT_FLINGRING, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + MT_RING, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_SPRK1, // deathstate + S_NULL, // xdeathstate + sfx_itemup, // deathsound + 38*FRACUNIT, // speed + 24*FRACUNIT, // radius + 48*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_BLUESPHERE -1, // doomednum S_BLUESPHERE_SPAWN, // spawnstate @@ -28743,6 +28896,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_BEAMPOINT + 2424, // doomednum + S_INVISIBLE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 48*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 100, // mass + 1, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIPTHING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + // ============================================================================================================================// #ifdef SEENAMES diff --git a/src/info.h b/src/info.h index 6a2749672..3fc3e718b 100644 --- a/src/info.h +++ b/src/info.h @@ -303,6 +303,8 @@ typedef enum sprite SPR_THOK, // Thok! mobj SPR_PLAY, + SPR_KART, + SPR_TIRE, // Enemies SPR_POSS, // Crawla (Blue) @@ -402,6 +404,7 @@ typedef enum sprite // Collectible Items SPR_RING, + SPR_DEBT, SPR_TRNG, // Team Rings SPR_TOKE, // Special Stage Token SPR_RFLG, // Red CTF Flag @@ -1003,9 +1006,6 @@ typedef enum sprite SPR_GCHA, // follower: generic chao SPR_CHEZ, // follower: cheese - // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later - SPR_VIEW, - SPR_DBCL, // Drift boost clip SPR_DBNC, // Drift boost clip's sparks SPR_DBST, // Drift boost plume @@ -1013,6 +1013,11 @@ typedef enum sprite SPR_SDDS, // Spindash dust SPR_SDWN, // Spindash wind + SPR_FLBM, // Finish line beam + + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later + SPR_VIEW, + SPR_FIRSTFREESLOT, SPR_LASTFREESLOT = SPR_FIRSTFREESLOT + NUMSPRITEFREESLOTS - 1, NUMSPRITES @@ -1024,12 +1029,15 @@ typedef enum sprite typedef enum playersprite { SPR2_STIN = 0, SPR2_STIL, SPR2_STIR, + SPR2_STGL, SPR2_STGR, SPR2_STLL, SPR2_STLR, SPR2_SLWN, SPR2_SLWL, SPR2_SLWR, + SPR2_SLGL, SPR2_SLGR, SPR2_SLLL, SPR2_SLLR, SPR2_FSTN, SPR2_FSTL, SPR2_FSTR, + SPR2_FSGL, SPR2_FSGR, SPR2_FSLL, SPR2_FSLR, SPR2_DRLN, SPR2_DRLO, SPR2_DRLI, SPR2_DRRN, SPR2_DRRO, SPR2_DRRI, SPR2_SPIN, - SPR2_SQSH, + SPR2_DEAD, SPR2_SIGN, SPR2_XTRA, SPR2_FIRSTFREESLOT, @@ -1057,12 +1065,24 @@ typedef enum state S_KART_STILL, S_KART_STILL_L, S_KART_STILL_R, + S_KART_STILL_GLANCE_L, + S_KART_STILL_GLANCE_R, + S_KART_STILL_LOOK_L, + S_KART_STILL_LOOK_R, S_KART_SLOW, S_KART_SLOW_L, S_KART_SLOW_R, + S_KART_SLOW_GLANCE_L, + S_KART_SLOW_GLANCE_R, + S_KART_SLOW_LOOK_L, + S_KART_SLOW_LOOK_R, S_KART_FAST, S_KART_FAST_L, S_KART_FAST_R, + S_KART_FAST_GLANCE_L, + S_KART_FAST_GLANCE_R, + S_KART_FAST_LOOK_L, + S_KART_FAST_LOOK_R, S_KART_DRIFT_L, S_KART_DRIFT_L_OUT, S_KART_DRIFT_L_IN, @@ -1070,12 +1090,18 @@ typedef enum state S_KART_DRIFT_R_OUT, S_KART_DRIFT_R_IN, S_KART_SPINOUT, - S_KART_SQUISH, + S_KART_DEAD, S_KART_SIGN, // technically the player goes here but it's an infinite tic state S_OBJPLACE_DUMMY, + S_KART_LEFTOVER, + S_KART_LEFTOVER_NOTIRES, + + S_KART_TIRE1, + S_KART_TIRE2, + // Blue Crawla S_POSS_STND, S_POSS_RUN1, @@ -5271,6 +5297,29 @@ typedef enum state S_SPINDASHDUST, S_SPINDASHWIND, + S_FINISHBEAM1, + S_FINISHBEAM2, + S_FINISHBEAM3, + S_FINISHBEAM4, + S_FINISHBEAM5, + S_FINISHBEAMEND1, + S_FINISHBEAMEND2, + + S_DEBTSPIKE1, + S_DEBTSPIKE2, + S_DEBTSPIKE3, + S_DEBTSPIKE4, + S_DEBTSPIKE5, + S_DEBTSPIKE6, + S_DEBTSPIKE7, + S_DEBTSPIKE8, + S_DEBTSPIKE9, + S_DEBTSPIKEA, + S_DEBTSPIKEB, + S_DEBTSPIKEC, + S_DEBTSPIKED, + S_DEBTSPIKEE, + #ifdef SEENAMES S_NAMECHECK, #endif @@ -5305,6 +5354,8 @@ typedef enum mobj_type MT_THOK, // Thok! mobj MT_PLAYER, + MT_KART_LEFTOVER, + MT_KART_TIRE, // Enemies MT_BLUECRAWLA, // Crawla (Blue) @@ -5430,6 +5481,7 @@ typedef enum mobj_type // Collectible Items MT_RING, MT_FLINGRING, // Lost ring + MT_DEBTSPIKE, // Ring debt funny spike MT_BLUESPHERE, // Blue sphere for special stages MT_FLINGBLUESPHERE, // Lost blue sphere MT_BOMBSPHERE, @@ -6373,6 +6425,8 @@ typedef enum mobj_type MT_PAPERITEMSPOT, + MT_BEAMPOINT, + #ifdef SEENAMES MT_NAMECHECK, #endif diff --git a/src/k_bot.c b/src/k_bot.c index 5219b5c96..32270b419 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -26,6 +26,7 @@ #include "d_ticcmd.h" #include "m_random.h" #include "r_things.h" // numskins +#include "k_race.h" // finishBeamLine /*-------------------------------------------------- @@ -296,7 +297,7 @@ boolean K_BotCanTakeCut(player_t *player) --------------------------------------------------*/ static UINT32 K_BotRubberbandDistance(player_t *player) { - const UINT32 spacing = FixedDiv(512 * FRACUNIT, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; + const UINT32 spacing = FixedDiv(640 * FRACUNIT, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; const UINT8 portpriority = player - players; UINT8 pos = 0; UINT8 i; @@ -429,8 +430,8 @@ fixed_t K_BotTopSpeedRubberband(player_t *player) } else { - // Max at +25% for level 9 bots - rubberband = FRACUNIT + ((rubberband - FRACUNIT) / 4); + // Max at +10% for level 9 bots + rubberband = FRACUNIT + ((rubberband - FRACUNIT) / 10); } // Only allow you to go faster than your regular top speed if you're facing the right direction @@ -519,28 +520,35 @@ fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict) See header file for description. --------------------------------------------------*/ -fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy) +fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t px, fixed_t py) { - fixed_t v1toc[2] = {cx - v1x, cy - v1y}; - fixed_t v1tov2[2] = {v2x - v1x, v2y - v1y}; - - fixed_t mag = FixedMul(v1tov2[0], v1tov2[0]) + FixedMul(v1tov2[1], v1tov2[1]); - fixed_t dot = FixedMul(v1toc[0], v1tov2[0]) + FixedMul(v1toc[1], v1tov2[1]); + // Copy+paste from P_ClosestPointOnLine :pensive: + fixed_t startx = v1x; + fixed_t starty = v1y; + fixed_t dx = v2x - v1x; + fixed_t dy = v2y - v1y; + fixed_t cx, cy; + fixed_t vx, vy; + fixed_t magnitude; fixed_t t; - fixed_t px, py; - if (mag == 0) - { - return 0; - } + cx = px - startx; + cy = py - starty; - t = FixedDiv(dot, mag); + vx = dx; + vy = dy; - px = v1x + FixedMul(v1tov2[0], t); - py = v1y + FixedMul(v1tov2[1], t); + magnitude = R_PointToDist2(v2x, v2y, startx, starty); + vx = FixedDiv(vx, magnitude); + vy = FixedDiv(vy, magnitude); - return P_AproxDistance(cx - px, cy - py); + t = (FixedMul(vx, cx) + FixedMul(vy, cy)); + + vx = FixedMul(vx, t); + vy = FixedMul(vy, t); + + return R_PointToDist2(px, py, startx + vx, starty + vy); } /*-------------------------------------------------- @@ -690,6 +698,145 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) return predict; } +/*-------------------------------------------------- + static UINT8 K_TrySpindash(player_t *player) + + Determines conditions where the bot should attempt to spindash. + + Input Arguments:- + player - Bot player to check. + + Return:- + 0 to make the bot drive normally, 1 to e-brake, 2 to e-brake & charge spindash. + (TODO: make this an enum) +--------------------------------------------------*/ +static UINT8 K_TrySpindash(player_t *player) +{ + const tic_t difficultyModifier = (TICRATE/6); + + if (player->kartstuff[k_spindashboost] || player->kartstuff[k_tiregrease]) + { + // You just released a spindash, you don't need to try again yet, jeez. + return 0; + } + + // Try "start boosts" first + if (leveltime == starttime) + { + // Forces them to release, even if they haven't fully charged. + // Don't want them to keep charging if they didn't have time to. + return 0; + } + + if (leveltime < starttime) + { + INT32 boosthold = starttime - K_GetSpindashChargeTime(player); + + boosthold -= (MAXBOTDIFFICULTY - player->botvars.difficulty) * difficultyModifier; + + if (leveltime >= (unsigned)boosthold) + { + // Start charging... + return 2; + } + else + { + // Just hold your ground and e-brake. + return 1; + } + } + + // Logic for normal racing. + if (player->powers[pw_flashing] > 0) + { + // Don't bother trying to spindash. + // Trying to spindash while flashing is fine during POSITION, but not during the actual race. + return 0; + } + + if (player->speed < 10*mapobjectscale // Below the speed threshold + && player->kartstuff[k_speedboost] < (FRACUNIT/8)) // If you have other boosts, you can probably trust it. + { + INT32 chargingPoint = (K_GetSpindashChargeTime(player) + difficultyModifier); + + // Release quicker the higher the difficulty is. + // Sounds counter-productive, but that's actually the best strategy after the race has started. + chargingPoint -= player->botvars.difficulty * difficultyModifier; + + if (player->kartstuff[k_spindash] > chargingPoint) + { + // Time to release. + return 0; + } + + return 2; + } + + // We're doing just fine, we don't need to spindash, thanks. + return 0; +} + +/*-------------------------------------------------- + static INT16 K_FindBotController(mobj_t *mo) + + Finds if any bot controller linedefs are tagged to the bot's sector. + + Input Arguments:- + mo - The bot player's mobj. + + Return:- + Line number of the bot controller. -1 if it doesn't exist. +--------------------------------------------------*/ +static INT16 K_FindBotController(mobj_t *mo) +{ + msecnode_t *node; + ffloor_t *rover; + INT16 lineNum = -1; + + I_Assert(mo != NULL); + I_Assert(!P_MobjWasRemoved(mo)); + + for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next) + { + if (!node->m_sector) + { + continue; + } + + lineNum = P_FindSpecialLineFromTag(2004, node->m_sector->tag, -1); + + if (lineNum != -1) + { + return lineNum; + } + + for (rover = node->m_sector->ffloors; rover; rover = rover->next) + { + sector_t *rs = NULL; + + if (!(rover->flags & FF_EXISTS)) + { + continue; + } + + if (mo->z > *rover->topheight || mo->z + mo->height < *rover->bottomheight) + { + continue; + } + + rs = §ors[rover->secnum]; + lineNum = P_FindSpecialLineFromTag(2004, rs->tag, -1); + + if (lineNum != -1) + { + return lineNum; + } + } + } + + return -1; +} + /*-------------------------------------------------- void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) @@ -698,7 +845,10 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { botprediction_t *predict = NULL; + boolean trySpindash = true; + UINT8 spindash = 0; INT32 turnamt = 0; + INT16 botController = -1; // Can't build a ticcmd if we aren't spawned... if (!player->mo) @@ -709,53 +859,87 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Remove any existing controls memset(cmd, 0, sizeof(ticcmd_t)); - if (gamestate != GS_LEVEL - || player->mo->scale <= 1) // funny post-finish death + if ( + gamestate != GS_LEVEL + || player->mo->scale <= 1 + || player->playerstate == PST_DEAD + ) { // No need to do anything else. return; } - if (player->playerstate == PST_DEAD) - { - cmd->buttons |= BT_ACCELERATE; - return; - } - // Complete override of all ticcmd functionality if (LUAh_BotTiccmd(player, cmd)) - return; - - // Start boost handler - if (leveltime <= starttime) { - tic_t length = (TICRATE/6); - tic_t boosthold = starttime - K_GetSpindashChargeTime(player); - - cmd->buttons |= BT_EBRAKEMASK; - - boosthold -= (MAXBOTDIFFICULTY - player->botvars.difficulty) * length; - - if (leveltime >= boosthold) - { - cmd->buttons |= BT_DRIFT; - } - return; } - // Handle steering towards waypoints! - if (player->nextwaypoint != NULL && player->nextwaypoint->mobj != NULL && !P_MobjWasRemoved(player->nextwaypoint->mobj)) + botController = K_FindBotController(player->mo); + + if (player->trickpanel != 0) { + // Trick panel state -- do nothing until a controller line is found, in which case do a trick. + + if (player->trickpanel == 1 && botController != -1) + { + line_t *controllerLine = &lines[botController]; + INT32 type = (sides[controllerLine->sidenum[0]].rowoffset / FRACUNIT); + + // Y Offset: Trick type + switch (type) + { + case 1: + cmd->turning = KART_FULLTURN; + break; + case 2: + cmd->turning = -KART_FULLTURN; + break; + case 3: + cmd->buttons |= BT_FORWARD; + break; + case 4: + cmd->buttons |= BT_BACKWARD; + break; + } + } + + // Don't do anything else. + return; + } + + if ((player->nextwaypoint != NULL + && player->nextwaypoint->mobj != NULL + && !P_MobjWasRemoved(player->nextwaypoint->mobj)) + || (botController != -1)) + { + // Handle steering towards waypoints! SINT8 turnsign = 0; angle_t destangle, moveangle, angle; INT16 anglediff; - predict = K_CreateBotPrediction(player); + if (botController != -1) + { + const fixed_t dist = (player->mo->radius * 4); + line_t *controllerLine = &lines[botController]; + + // X Offset: Movement direction + destangle = FixedAngle(sides[controllerLine->sidenum[0]].textureoffset); + + // Overwritten prediction + predict = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); + + predict->x = player->mo->x + FixedMul(dist, FINECOSINE(destangle >> ANGLETOFINESHIFT)); + predict->y = player->mo->y + FixedMul(dist, FINESINE(destangle >> ANGLETOFINESHIFT)); + predict->radius = (DEFAULT_WAYPOINT_RADIUS / 4) * mapobjectscale; + } + else + { + predict = K_CreateBotPrediction(player); + destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y); + } - destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y); moveangle = player->mo->angle; - angle = (moveangle - destangle); if (angle < ANGLE_180) @@ -781,7 +965,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) else { const fixed_t playerwidth = (player->mo->radius * 2); - const fixed_t realrad = predict->radius - (playerwidth * 4); // Remove a "safe" distance away from the edges of the road + fixed_t realrad = predict->radius - (playerwidth * 4); // Remove a "safe" distance away from the edges of the road fixed_t rad = realrad; fixed_t dirdist = K_DistanceOfLineFromPoint( player->mo->x, player->mo->y, @@ -857,8 +1041,77 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } } - // Handle item usage - K_BotItemUsage(player, cmd, turnamt); + if (leveltime <= starttime && finishBeamLine != NULL) + { + const fixed_t distBase = 384*mapobjectscale; + const fixed_t distAdjust = 64*mapobjectscale; + + const fixed_t closeDist = distBase + (distAdjust * (9 - player->kartweight)); + const fixed_t farDist = closeDist + (distAdjust * 2); + + fixed_t distToFinish = K_DistanceOfLineFromPoint( + finishBeamLine->v1->x, finishBeamLine->v1->y, + finishBeamLine->v2->x, finishBeamLine->v2->y, + player->mo->x, player->mo->y + ); + + // Don't run the spindash code at all until we're in the right place + trySpindash = false; + + // If you're too far, enable spindash & stay still. + // If you're too close, start backing up. + + if (distToFinish < closeDist) + { + // Silly way of getting us to reverse, but it respects the above code + // where we figure out what the shape of the track looks like. + UINT16 oldButtons = cmd->buttons; + + cmd->buttons &= ~(BT_ACCELERATE|BT_BRAKE); + + if (oldButtons & BT_ACCELERATE) + { + cmd->buttons |= BT_BRAKE; + } + + if (oldButtons & BT_BRAKE) + { + cmd->buttons |= BT_ACCELERATE; + } + + cmd->forwardmove = -cmd->forwardmove; + } + else if (distToFinish < farDist) + { + // We're in about the right place, spindash now. + cmd->forwardmove = 0; + trySpindash = true; + } + } + + if (trySpindash == true) + { + // Spindashing + spindash = K_TrySpindash(player); + + if (spindash > 0) + { + cmd->buttons |= BT_EBRAKEMASK; + cmd->forwardmove = 0; + + if (spindash == 2 && player->speed < 6*mapobjectscale) + { + cmd->buttons |= BT_DRIFT; + } + } + } + + if (spindash == 0) + { + // Don't pointlessly try to use rings/sneakers while charging a spindash. + // TODO: Allowing projectile items like orbinaut while e-braking would be nice, maybe just pass in the spindash variable? + K_BotItemUsage(player, cmd, turnamt); + } if (turnamt != 0) { @@ -873,6 +1126,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (turnamt > 0) { + // Count up if (player->botvars.turnconfirm < BOTTURNCONFIRM) { player->botvars.turnconfirm++; @@ -880,11 +1134,24 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } else if (turnamt < 0) { + // Count down if (player->botvars.turnconfirm > -BOTTURNCONFIRM) { player->botvars.turnconfirm--; } } + else + { + // Back to neutral + if (player->botvars.turnconfirm < 0) + { + player->botvars.turnconfirm++; + } + else if (player->botvars.turnconfirm > 0) + { + player->botvars.turnconfirm--; + } + } if (abs(player->botvars.turnconfirm) >= BOTTURNCONFIRM) { diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 57e31a885..e46a6a7be 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -167,9 +167,9 @@ void K_InitGrandPrixBots(void) difficultylevels[10] = max(1, startingdifficulty-5); difficultylevels[11] = max(1, startingdifficulty-6); difficultylevels[12] = max(1, startingdifficulty-6); - difficultylevels[13] = max(1, startingdifficulty-6); + difficultylevels[13] = max(1, startingdifficulty-7); difficultylevels[14] = max(1, startingdifficulty-7); - difficultylevels[15] = max(1, startingdifficulty-7); + difficultylevels[15] = max(1, startingdifficulty-8); } for (i = 0; i < MAXPLAYERS; i++) diff --git a/src/k_hud.c b/src/k_hud.c index 55f84ca4c..8736eb8e2 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1415,17 +1415,26 @@ static void K_DrawKartPositionNum(INT32 num) fixed_t scale = FRACUNIT; patch_t *localpatch = kp_positionnum[0][0]; INT32 fx = 0, fy = 0, fflags = 0; + INT32 addOrSub = V_ADDTRANS; boolean flipdraw = false; // flip the order we draw it in for MORE splitscreen bs. fun. boolean flipvdraw = false; // used only for 2p splitscreen so overtaking doesn't make 1P's position fly off the screen. boolean overtake = false; + if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SUBTRACTNUM) == LF_SUBTRACTNUM) + { + addOrSub = V_SUBTRANS; + } + if (stplyr->kartstuff[k_positiondelay] || stplyr->exiting) { scale *= 2; overtake = true; // this is used for splitscreen stuff in conjunction with flipdraw. } + if (r_splitscreen) + { scale /= 2; + } W = FixedMul(W<>FRACBITS; @@ -1472,38 +1481,46 @@ static void K_DrawKartPositionNum(INT32 num) } // Special case for 0 - if (!num) + if (num <= 0) { - V_DrawFixedPatch(fx<= 0); // This function does not draw negative numbers - // Draw the number while (num) { if (win) // 1st place winner? You get rainbows!! + { localpatch = kp_winnernum[(leveltime % (NUMWINFRAMES*3)) / 3]; + } else if (stplyr->laps >= cv_numlaps.value || stplyr->exiting) // Check for the final lap, or won { - // Alternate frame every three frames - switch (leveltime % 9) + boolean useRedNums = K_IsPlayerLosing(stplyr); + + if (addOrSub == V_SUBTRANS) { - case 1: case 2: case 3: - if (K_IsPlayerLosing(stplyr)) + // Subtracting RED will look BLUE, and vice versa. + useRedNums = !useRedNums; + } + + // Alternate frame every three frames + switch ((leveltime % 9) / 3) + { + case 0: + if (useRedNums == true) localpatch = kp_positionnum[num % 10][4]; else localpatch = kp_positionnum[num % 10][1]; break; - case 4: case 5: case 6: - if (K_IsPlayerLosing(stplyr)) + case 1: + if (useRedNums == true) localpatch = kp_positionnum[num % 10][5]; else localpatch = kp_positionnum[num % 10][2]; break; - case 7: case 8: case 9: - if (K_IsPlayerLosing(stplyr)) + case 2: + if (useRedNums == true) localpatch = kp_positionnum[num % 10][6]; else localpatch = kp_positionnum[num % 10][3]; @@ -1514,9 +1531,15 @@ static void K_DrawKartPositionNum(INT32 num) } } else + { localpatch = kp_positionnum[num % 10][0]; + } - V_DrawFixedPatch((fx<width)*scale/2) : 0), (fy<height)*scale/2) : 0), scale, V_HUDTRANSHALF|V_SLIDEIN|fflags, localpatch, NULL); + V_DrawFixedPatch( + (fx<width)*scale/2) : 0), + (fy<height)*scale/2) : 0), + scale, addOrSub|V_SLIDEIN|fflags, localpatch, NULL + ); // ^ if we overtake as p1 or p3 in splitscren, we shift it so that it doesn't go off screen. // ^ if we overtake as p1 in 2p splits, shift vertically so that this doesn't happen either. @@ -2043,7 +2066,7 @@ static void K_drawKartSpeedometer(void) { case 1: // Sonic Drift 2 style percentage default: - convSpeed = (((25*stplyr->speed)/24) * 100) / K_GetKartSpeed(stplyr, false); // Based on top speed! (cheats with the numbers due to some weird discrepancy) + convSpeed = (stplyr->speed * 100) / K_GetKartSpeed(stplyr, false); // Based on top speed! labeln = 0; break; case 2: // Kilometers @@ -2062,7 +2085,8 @@ static void K_drawKartSpeedometer(void) } // Don't overflow - if (convSpeed > 999) + // (negative speed IS really high speed :V) + if (convSpeed > 999 || convSpeed < 0) convSpeed = 999; numbers[0] = ((convSpeed / 100) % 10); @@ -2402,6 +2426,7 @@ static void K_drawKartPlayerCheck(void) UINT8 cnum = 0; UINT8 i; INT32 splitflags = V_SNAPTOBOTTOM|V_SPLITSCREEN; + fixed_t y = CHEK_Y * FRACUNIT; if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) { @@ -2420,6 +2445,8 @@ static void K_drawKartPlayerCheck(void) if (r_splitscreen) { + y /= 2; + for (i = 1; i <= r_splitscreen; i++) { if (stplyr == &players[displayplayers[i]]) @@ -2492,7 +2519,7 @@ static void K_drawKartPlayerCheck(void) K_ObjectTracking(&x, NULL, &c, thiscam->angle + ANGLE_180, 0, &v, cnum); colormap = R_GetTranslationColormap(TC_DEFAULT, checkplayer->mo->color, GTC_CACHE); - V_DrawFixedPatch(x, CHEK_Y * FRACUNIT, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN|splitflags, kp_check[pnum], colormap); + V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN|splitflags, kp_check[pnum], colormap); } } diff --git a/src/k_kart.c b/src/k_kart.c index c8a5ba147..4d5e1770e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1010,6 +1010,14 @@ fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against) break; weight = K_PlayerWeight(mobj, against); break; + case MT_KART_LEFTOVER: + weight = 5*FRACUNIT/2; + + if (mobj->extravalue1 > 0) + { + weight = mobj->extravalue1 * (FRACUNIT >> 1); + } + break; case MT_BUBBLESHIELD: weight = K_PlayerWeight(mobj->target, against); break; @@ -1726,6 +1734,102 @@ void K_SpawnDriftBoostClipSpark(mobj_t *clip) spark->momy = clip->momx/2; } +static SINT8 K_GlanceAtPlayers(player_t *glancePlayer) +{ + const fixed_t maxdistance = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); + const angle_t blindSpotSize = ANG10; // ANG5 + UINT8 i; + SINT8 glanceDir = 0; + SINT8 lastValidGlance = 0; + + // See if there's any players coming up behind us. + // If so, your character will glance at 'em. + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *p; + angle_t back; + angle_t diff; + fixed_t distance; + SINT8 dir = -1; + + if (!playeringame[i]) + { + // Invalid player + continue; + } + + p = &players[i]; + + if (p == glancePlayer) + { + // FOOL! Don't glance at yerself! + continue; + } + + if (!p->mo || P_MobjWasRemoved(p->mo)) + { + // Invalid mobj + continue; + } + + if (p->spectator || p->kartstuff[k_hyudorotimer] > 0) + { + // Not playing / invisible + continue; + } + + distance = R_PointToDist2(glancePlayer->mo->x, glancePlayer->mo->y, p->mo->x, p->mo->y); + + if (distance > maxdistance) + { + continue; + } + + back = glancePlayer->mo->angle + ANGLE_180; + diff = R_PointToAngle2(glancePlayer->mo->x, glancePlayer->mo->y, p->mo->x, p->mo->y) - back; + + if (diff > ANGLE_180) + { + diff = InvAngle(diff); + dir = -dir; + } + + if (diff > ANGLE_90) + { + // Not behind the player + continue; + } + + if (diff < blindSpotSize) + { + // Small blindspot directly behind your back, gives the impression of smoothly turning. + continue; + } + + if (P_CheckSight(glancePlayer->mo, p->mo) == true) + { + // Not blocked by a wall, we can glance at 'em! + // Adds, so that if there's more targets on one of your sides, it'll glance on that side. + glanceDir += dir; + + // That poses a limitation if there's an equal number of targets on both sides... + // In that case, we'll pick the last chosen glance direction. + lastValidGlance = dir; + } + } + + if (glanceDir > 0) + { + return 1; + } + else if (glanceDir < 0) + { + return -1; + } + + return lastValidGlance; +} + /** \brief Handles the state changing for moving players, moved here to eliminate duplicate code \param player player data @@ -1735,7 +1839,6 @@ void K_SpawnDriftBoostClipSpark(mobj_t *clip) void K_KartMoveAnimation(player_t *player) { const INT16 minturn = KART_FULLTURN/8; - SINT8 turndir = 0; const fixed_t fastspeed = (K_GetKartSpeed(player, false) * 17) / 20; // 85% const fixed_t speedthreshold = player->mo->scale / 8; @@ -1743,7 +1846,12 @@ void K_KartMoveAnimation(player_t *player) const boolean onground = P_IsObjectOnGround(player->mo); ticcmd_t *cmd = &player->cmd; - const boolean spinningwheels = ((cmd->buttons & BT_ACCELERATE) || (onground && player->speed > 0)); + const boolean spinningwheels = (((cmd->buttons & BT_ACCELERATE) == BT_ACCELERATE) || (onground && player->speed > 0)); + const boolean lookback = ((cmd->buttons & BT_LOOKBACK) == BT_LOOKBACK); + + SINT8 turndir = 0; + SINT8 destGlanceDir = 0; + SINT8 drift = player->kartstuff[k_drift]; if (cmd->turning < -minturn) { @@ -1754,20 +1862,89 @@ void K_KartMoveAnimation(player_t *player) turndir = 1; } + if (lookback == true && drift == 0) + { + // Prioritize looking back frames over turning + turndir = 0; + } + + if (turndir == 0 && drift == 0) + { + // Only try glancing if you're driving straight. + // This avoids all-players loops when we don't need it. + destGlanceDir = K_GlanceAtPlayers(player); + + if (lookback == true) + { + if (destGlanceDir == 0) + { + if (player->glanceDir != 0) + { + // Keep to the side you were already on. + if (player->glanceDir < 0) + { + destGlanceDir = -1; + } + else + { + destGlanceDir = 1; + } + } + else + { + // Look to your right by default + destGlanceDir = -1; + } + } + else + { + // Looking back AND glancing? Amplify the look! + destGlanceDir *= 2; + } + } + else if (K_GetForwardMove(player) < 0 && destGlanceDir == 0) + { + // Reversing -- like looking back, but doesn't stack on the other glances. + if (player->glanceDir != 0) + { + // Keep to the side you were already on. + if (player->glanceDir < 0) + { + destGlanceDir = -1; + } + else + { + destGlanceDir = 1; + } + } + else + { + // Look to your right by default + destGlanceDir = -1; + } + } + } + else + { + // Not glancing + destGlanceDir = 0; + player->glanceDir = 0; + } + #define SetState(sn) \ if (player->mo->state != &states[sn]) \ P_SetPlayerMobjState(player->mo, sn) - if (!onground) + if (onground == false) { // Only use certain frames in the air, to make it look like your tires are spinning fruitlessly! - if (player->kartstuff[k_drift] > 0) + if (drift > 0) { // Neutral drift SetState(S_KART_DRIFT_L); } - else if (player->kartstuff[k_drift] > 0) + else if (drift < 0) { // Neutral drift SetState(S_KART_DRIFT_R); @@ -1782,22 +1959,41 @@ void K_KartMoveAnimation(player_t *player) { SetState(S_KART_FAST_L); } - else if (turndir == 0) + else { - SetState(S_KART_FAST); + switch (player->glanceDir) + { + case -2: + SetState(S_KART_FAST_LOOK_R); + break; + case 2: + SetState(S_KART_FAST_LOOK_L); + break; + case -1: + SetState(S_KART_FAST_GLANCE_R); + break; + case 1: + SetState(S_KART_FAST_GLANCE_L); + break; + default: + SetState(S_KART_FAST); + break; + } } } if (!spinningwheels) { - // TODO: These should prooobably be different SPR2s - // Just a quick hack to prevent needing to do that :V + // TODO: The "tires still in the air" states should have it's own SPR2s. + // This was a quick hack to get the same functionality with less work, + // but it's really dunderheaded & isn't customizable at all. player->mo->frame = (player->mo->frame & ~FF_FRAMEMASK); + player->mo->tics++; // Makes it properly use frame 0 } } else { - if (player->kartstuff[k_drift] > 0) + if (drift > 0) { // Drifting LEFT! @@ -1817,7 +2013,7 @@ void K_KartMoveAnimation(player_t *player) SetState(S_KART_DRIFT_L); } } - else if (player->kartstuff[k_drift] < 0) + else if (drift < 0) { // Drifting RIGHT! @@ -1853,7 +2049,24 @@ void K_KartMoveAnimation(player_t *player) } else { - SetState(S_KART_FAST); + switch (player->glanceDir) + { + case -2: + SetState(S_KART_FAST_LOOK_R); + break; + case 2: + SetState(S_KART_FAST_LOOK_L); + break; + case -1: + SetState(S_KART_FAST_GLANCE_R); + break; + case 1: + SetState(S_KART_FAST_GLANCE_L); + break; + default: + SetState(S_KART_FAST); + break; + } } } else @@ -1872,7 +2085,24 @@ void K_KartMoveAnimation(player_t *player) } else { - SetState(S_KART_SLOW); + switch (player->glanceDir) + { + case -2: + SetState(S_KART_SLOW_LOOK_R); + break; + case 2: + SetState(S_KART_SLOW_LOOK_L); + break; + case -1: + SetState(S_KART_SLOW_GLANCE_R); + break; + case 1: + SetState(S_KART_SLOW_GLANCE_L); + break; + default: + SetState(S_KART_SLOW); + break; + } } } else @@ -1889,7 +2119,24 @@ void K_KartMoveAnimation(player_t *player) } else { - SetState(S_KART_STILL); + switch (player->glanceDir) + { + case -2: + SetState(S_KART_STILL_LOOK_R); + break; + case 2: + SetState(S_KART_STILL_LOOK_L); + break; + case -1: + SetState(S_KART_STILL_GLANCE_R); + break; + case 1: + SetState(S_KART_STILL_GLANCE_L); + break; + default: + SetState(S_KART_STILL); + break; + } } } } @@ -1898,6 +2145,16 @@ void K_KartMoveAnimation(player_t *player) #undef SetState + // Update your glance value to smooth it out. + if (player->glanceDir > destGlanceDir) + { + player->glanceDir--; + } + else if (player->glanceDir < destGlanceDir) + { + player->glanceDir++; + } + // Update lastspeed value -- we use to display slow driving frames instead of fast driving when slowing down. player->lastspeed = player->speed; } @@ -2030,6 +2287,13 @@ boolean K_ApplyOffroad(player_t *player) return true; } +boolean K_SlopeResistance(player_t *player) +{ + if (player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_sneakertimer] || player->kartstuff[k_tiregrease]) + return true; + return false; +} + static fixed_t K_FlameShieldDashVar(INT32 val) { // 1 second = 75% + 50% top speed @@ -2048,7 +2312,7 @@ fixed_t K_GetSpindashChargeSpeed(player_t *player) // more speed for higher weight & speed // Tails = +6.25%, Fang = +20.31%, Mighty = +20.31%, Metal = +25% // (can be higher than this value when overcharged) - return (player->kartspeed + player->kartweight) * (FRACUNIT/64); + return (player->kartspeed + player->kartweight) * (FRACUNIT/32); } @@ -2124,11 +2388,12 @@ static void K_GetKartBoostPower(player_t *player) if (player->kartstuff[k_spindashboost]) // Spindash boost { const fixed_t MAXCHARGESPEED = K_GetSpindashChargeSpeed(player); + const fixed_t exponent = FixedMul(player->kartstuff[k_spindashspeed], player->kartstuff[k_spindashspeed]); // character & charge dependent ADDBOOST( - FixedMul(MAXCHARGESPEED, player->kartstuff[k_spindashspeed]), // + 0 to K_GetSpindashChargeSpeed()% top speed - (4*FRACUNIT) + (36*player->kartstuff[k_spindashspeed]), // + 400% to 4000% acceleration + FixedMul(MAXCHARGESPEED, exponent), // + 0 to K_GetSpindashChargeSpeed()% top speed + (40 * exponent), // + 0% to 4000% acceleration 0 // + 0% handling ); } @@ -2205,10 +2470,17 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) finalspeed = FixedMul(finalspeed, FRACUNIT + (sphereAdd * player->spheres)); } - if (K_PlayerUsesBotMovement(player) && player->botvars.rival == true) + if (K_PlayerUsesBotMovement(player)) { - // +10% top speed for the rival - finalspeed = FixedMul(finalspeed, 11*FRACUNIT/10); + // Increase bot speed by 1-10% depending on difficulty + fixed_t add = (player->botvars.difficulty * (FRACUNIT/10)) / MAXBOTDIFFICULTY; + finalspeed = FixedMul(finalspeed, FRACUNIT + add); + + if (player->botvars.rival == true) + { + // +10% top speed for the rival + finalspeed = FixedMul(finalspeed, 11*FRACUNIT/10); + } } if (player->mo && !P_MobjWasRemoved(player->mo)) @@ -2546,7 +2818,7 @@ void K_TumblePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) static boolean K_LastTumbleBounceCondition(player_t *player) { - return (player->tumbleBounces > 4 && player->tumbleHeight < 40); + return (player->tumbleBounces > TUMBLEBOUNCES && player->tumbleHeight < 40); } static void K_HandleTumbleBounce(player_t *player) @@ -2616,8 +2888,10 @@ static void K_HandleTumbleSound(player_t *player) } } -void K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) // A bit of a hack, we just throw the player up higher here and extend their spinout timer +INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) // A bit of a hack, we just throw the player up higher here and extend their spinout timer { + INT32 ringburst = 10; + (void)source; player->mo->momz = 18*mapobjectscale*P_MobjFlip(player->mo); // please stop forgetting mobjflip checks!!!! @@ -2632,6 +2906,7 @@ void K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) // A b { player->kartstuff[k_spinouttimer] = ((5*player->kartstuff[k_spinouttimer])/2)+1; player->mo->momz *= 2; + ringburst = 20; } } @@ -2642,6 +2917,8 @@ void K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) // A b if (P_IsDisplayPlayer(player)) P_StartQuake(64<buttons & BT_DRIFT) - player->kartstuff[k_jmp] = 1; + { + // Only allow drifting while NOT trying to do an spindash input. + if ((cmd->buttons & BT_EBRAKEMASK) != BT_EBRAKEMASK) + { + player->driftInput = true; + } + // else, keep the previous value, because it might be brake-drifting. + } else - player->kartstuff[k_jmp] = 0; + { + player->driftInput = false; + } // Roulette Code K_KartItemRoulette(player, cmd); @@ -6569,7 +6855,15 @@ void K_UpdateDistanceFromFinishLine(player_t *const player) INT32 K_GetKartRingPower(player_t *player) { - return (((9 - player->kartspeed) + (9 - player->kartweight)) / 2); + INT32 ringPower = ((9 - player->kartspeed) + (9 - player->kartweight)) / 2; + + if (K_PlayerUsesBotMovement(player)) + { + // Double for Lv. 9 + ringPower += (player->botvars.difficulty * ringPower) / MAXBOTDIFFICULTY; + } + + return ringPower; } // Returns false if this player being placed here causes them to collide with any other player @@ -6715,6 +7009,7 @@ INT32 K_GetKartDriftSparkValue(player_t *player) Stage 1: red sparks Stage 2: blue sparks Stage 3: big large rainbow sparks +Stage 0: air failsafe */ void K_SpawnDriftBoostExplosion(player_t *player, int stage) { @@ -6745,6 +7040,11 @@ void K_SpawnDriftBoostExplosion(player_t *player, int stage) S_StartSound(player->mo, sfx_kc5b); S_StartSound(player->mo, sfx_s3kc4l); break; + + case 0: + overlay->color = SKINCOLOR_SILVER; + overlay->fuse = 16; + break; } overlay->extravalue1 = stage; @@ -6752,13 +7052,15 @@ void K_SpawnDriftBoostExplosion(player_t *player, int stage) static void K_KartDrift(player_t *player, boolean onground) { - fixed_t minspeed = (10 * player->mo->scale); - INT32 dsone = K_GetKartDriftSparkValue(player); - INT32 dstwo = dsone*2; - INT32 dsthree = dstwo*2; + const fixed_t minspeed = (10 * player->mo->scale); + + const INT32 dsone = K_GetKartDriftSparkValue(player); + const INT32 dstwo = dsone*2; + const INT32 dsthree = dstwo*2; // Drifting is actually straffing + automatic turning. // Holding the Jump button will enable drifting. + // (This comment is extremely funny) // Drift Release (Moved here so you can't "chain" drifts) if (player->kartstuff[k_drift] != -5 && player->kartstuff[k_drift] != 5) @@ -6819,21 +7121,21 @@ static void K_KartDrift(player_t *player, boolean onground) } // Drifting: left or right? - if ((player->cmd.turning > 0) && player->speed > minspeed && player->kartstuff[k_jmp] == 1 + if ((player->cmd.turning > 0) && player->speed > minspeed && player->driftInput == true && (player->kartstuff[k_drift] == 0 || player->kartstuff[k_driftend] == 1)) // && player->kartstuff[k_drift] != 1) { // Starting left drift player->kartstuff[k_drift] = 1; player->kartstuff[k_driftend] = player->kartstuff[k_driftcharge] = 0; } - else if ((player->cmd.turning < 0) && player->speed > minspeed && player->kartstuff[k_jmp] == 1 + else if ((player->cmd.turning < 0) && player->speed > minspeed && player->driftInput == true && (player->kartstuff[k_drift] == 0 || player->kartstuff[k_driftend] == 1)) // && player->kartstuff[k_drift] != -1) { // Starting right drift player->kartstuff[k_drift] = -1; player->kartstuff[k_driftend] = player->kartstuff[k_driftcharge] = 0; } - else if (player->kartstuff[k_jmp] == 0) // || player->kartstuff[k_turndir] == 0) + else if (player->driftInput == false) // || player->kartstuff[k_turndir] == 0) { // drift is not being performed so if we're just finishing set driftend and decrement counters if (player->kartstuff[k_drift] > 0) @@ -6857,7 +7159,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_aizdriftstrat] = player->kartstuff[k_brakedrift] = 0; player->kartstuff[k_getsparks] = 0; } - else if (player->kartstuff[k_jmp] == 1 && player->kartstuff[k_drift] != 0) + else if (player->driftInput == true && player->kartstuff[k_drift] != 0) { // Incease/decrease the drift value to continue drifting in that direction fixed_t driftadditive = 24; @@ -7209,6 +7511,11 @@ static void K_KartSpindash(player_t *player) ticcmd_t *cmd = &player->cmd; boolean spawnWind = (leveltime % 2 == 0); + if (player->mo->hitlag > 0 || P_PlayerInPain(player)) + { + player->kartstuff[k_spindash] = 0; + } + if (player->kartstuff[k_spindash] > 0 && (cmd->buttons & (BT_DRIFT|BT_BRAKE)) != (BT_DRIFT|BT_BRAKE)) { player->kartstuff[k_spindashspeed] = (player->kartstuff[k_spindash] * FRACUNIT) / MAXCHARGETIME; @@ -7250,11 +7557,14 @@ static void K_KartSpindash(player_t *player) return; } - if (player->speed < 6*mapobjectscale && player->powers[pw_flashing] == 0) + if (player->speed == 0 && cmd->turning != 0 && leveltime % 8 == 0) { - if (cmd->turning != 0 && leveltime % 8 == 0) - S_StartSound(player->mo, sfx_ruburn); + // Rubber burn turn sfx + S_StartSound(player->mo, sfx_ruburn); + } + if (player->speed < 6*player->mo->scale) + { if ((cmd->buttons & (BT_DRIFT|BT_BRAKE)) == (BT_DRIFT|BT_BRAKE)) { INT16 chargetime = MAXCHARGETIME - ++player->kartstuff[k_spindash]; @@ -7271,6 +7581,15 @@ static void K_KartSpindash(player_t *player) K_KartSpindashWind(player->mo); } + if (player->powers[pw_flashing] > 0 && (leveltime & 1) && player->kartstuff[k_hyudorotimer] == 0) + { + // Every frame that you're invisible from flashing, spill a ring. + // Intentionally a lop-sided trade-off, so the game doesn't become + // Funky Kong's Ring Racers. + + P_PlayerRingBurst(player, 1); + } + if (chargetime > 0) { UINT16 soundcharge = 0; @@ -7298,6 +7617,39 @@ static void K_KartSpindash(player_t *player) } } +static void K_AirFailsafe(player_t *player) +{ + const fixed_t maxSpeed = 6*player->mo->scale; + const fixed_t thrustSpeed = 6*player->mo->scale; // 10*player->mo->scale + + ticcmd_t *cmd = &player->cmd; + + if (player->speed > maxSpeed // Above the max speed that you're allowed to use this technique. + || player->respawn.state != RESPAWNST_NONE) // Respawning, you don't need this AND drop dash :V + { + player->airFailsafe = false; + return; + } + + if ((cmd->buttons & BT_ACCELERATE) || K_GetForwardMove(player) != 0) + { + // Queue up later + player->airFailsafe = true; + return; + } + + if (player->airFailsafe == true) + { + // Push the player forward + P_Thrust(player->mo, K_MomentumAngle(player->mo), thrustSpeed); + + S_StartSound(player->mo, sfx_s23c); + K_SpawnDriftBoostExplosion(player, 0); + + player->airFailsafe = false; + } +} + // // K_AdjustPlayerFriction // @@ -8171,9 +8523,18 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } } - K_KartDrift(player, P_IsObjectOnGround(player->mo)); // Not using onground, since we don't want this affected by spring pads + K_KartDrift(player, onground); K_KartSpindash(player); + if (onground == false) + { + K_AirFailsafe(player); + } + else + { + player->airFailsafe = false; + } + // Play the starting countdown sounds if (player == &players[g_localplayers[0]]) // Don't play louder in splitscreen { diff --git a/src/k_kart.h b/src/k_kart.h index 37a31e27b..9b92fff7c 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -45,13 +45,14 @@ void K_DoInstashield(player_t *player); void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UINT8 bumpersRemoved); void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type); void K_TumblePlayer(player_t *player, mobj_t *inflictor, mobj_t *source); -void K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source); +INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source); void K_DebtStingPlayer(player_t *player, mobj_t *source); void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers); void K_DestroyBumpers(player_t *player, UINT8 amount); void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount); void K_SpawnKartExplosion(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 number, mobjtype_t type, angle_t rotangle, boolean spawncenter, boolean ghostit, mobj_t *source); void K_SpawnMineExplosion(mobj_t *source, UINT8 color); +void K_RunFinishLineBeam(void); UINT16 K_DriftSparkColor(player_t *player, INT32 charge); void K_SpawnBoostTrail(player_t *player); void K_SpawnSparkleTrail(mobj_t *mo); @@ -81,6 +82,7 @@ void K_StripItems(player_t *player); void K_StripOther(player_t *player); void K_MomentumToFacing(player_t *player); boolean K_ApplyOffroad(player_t *player); +boolean K_SlopeResistance(player_t *player); INT16 K_GetSpindashChargeTime(player_t *player); fixed_t K_GetSpindashChargeSpeed(player_t *player); fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed); diff --git a/src/k_race.c b/src/k_race.c new file mode 100644 index 000000000..0ac9cb679 --- /dev/null +++ b/src/k_race.c @@ -0,0 +1,421 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2018-2020 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_race.c +/// \brief Race Mode specific code. + +#include "k_race.h" + +#include "k_kart.h" +#include "k_battle.h" +#include "k_pwrlv.h" +#include "k_color.h" +#include "k_respawn.h" +#include "doomdef.h" +#include "hu_stuff.h" +#include "g_game.h" +#include "m_random.h" +#include "p_local.h" +#include "p_slopes.h" +#include "p_setup.h" +#include "r_draw.h" +#include "r_local.h" +#include "s_sound.h" +#include "st_stuff.h" +#include "v_video.h" +#include "z_zone.h" +#include "m_misc.h" +#include "m_cond.h" +#include "f_finale.h" +#include "lua_hud.h" // For Lua hud checks +#include "lua_hook.h" // For MobjDamage and ShouldDamage +#include "m_cheat.h" // objectplacing +#include "p_spec.h" + +#include "k_waypoint.h" +#include "k_bot.h" +#include "k_hud.h" + +line_t *finishBeamLine = NULL; + +static mobj_t *beamPoints[2]; +static UINT8 numBeamPoints = 0; + +/*-------------------------------------------------- + void K_ClearFinishBeamLine(void) + + See header file for description. +--------------------------------------------------*/ +void K_ClearFinishBeamLine(void) +{ + size_t i; + + finishBeamLine = NULL; + + for (i = 0; i < 2; i++) + { + beamPoints[i] = NULL; + } + + numBeamPoints = 0; +} + +/*-------------------------------------------------- + static void K_FreeFinishBeamLine(void) + + See header file for description. +--------------------------------------------------*/ +static void K_FreeFinishBeamLine(void) +{ + if (finishBeamLine != NULL) + { + if (finishBeamLine->v1 != NULL) + { + Z_Free(finishBeamLine->v1); + } + + if (finishBeamLine->v2 != NULL) + { + Z_Free(finishBeamLine->v2); + } + + Z_Free(finishBeamLine); + } + + K_ClearFinishBeamLine(); +} + +/*-------------------------------------------------- + static void K_CreateFinishLineFromPoints(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) + + See header file for description. +--------------------------------------------------*/ +static void K_CreateFinishLineFromPoints(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) +{ + I_Assert(finishBeamLine == NULL); // Needs to be NULL + + finishBeamLine = Z_Calloc(sizeof (*finishBeamLine), PU_LEVEL, NULL); + + finishBeamLine->v1 = Z_Calloc(sizeof (*finishBeamLine->v1), PU_LEVEL, NULL); + finishBeamLine->v1->x = x1; + finishBeamLine->v1->y = y1; + + finishBeamLine->v2 = Z_Calloc(sizeof (*finishBeamLine->v2), PU_LEVEL, NULL); + finishBeamLine->v2->x = x2; + finishBeamLine->v2->y = y2; + + finishBeamLine->dx = x2 - x1; + finishBeamLine->dy = y2 - y1; + + finishBeamLine->flags = 0; +} + +/*-------------------------------------------------- + boolean K_GenerateFinishBeamLine(void) + + See header file for description. +--------------------------------------------------*/ +boolean K_GenerateFinishBeamLine(void) +{ + mapthing_t *mt; + + INT64 bounds[4]; + angle_t angle; + + boolean valid = false; + size_t i; + + // Ensure everything's freed by this time. + K_FreeFinishBeamLine(); + + // First: attempt to create from beam point objects + for (i = 0, mt = mapthings; i < nummapthings; i++, mt++) + { + if (numBeamPoints >= 2) + { + break; + } + + if (mt->type == mobjinfo[MT_BEAMPOINT].doomednum) + { + beamPoints[numBeamPoints] = mt->mobj; + numBeamPoints++; + } + } + + if (numBeamPoints == 2) + { + // Found 'em! Really easy to generate a line out of these :) + + K_CreateFinishLineFromPoints( + beamPoints[0]->x, beamPoints[0]->y, + beamPoints[1]->x, beamPoints[1]->y + ); + + return true; + } + + bounds[0] = INT64_MAX; // min x + bounds[1] = INT64_MIN; // max x + bounds[2] = INT64_MAX; // min y + bounds[3] = INT64_MIN; // max y + + for (i = 0; i < numlines; i++) + { + angle_t thisAngle; + + if (lines[i].special != 2001) + { + continue; + } + + thisAngle = R_PointToAngle2(0, 0, lines[i].dx, lines[i].dy); + + bounds[0] = min(bounds[0], min(lines[i].v1->x, lines[i].v2->x)); // min x + bounds[1] = max(bounds[1], max(lines[i].v1->x, lines[i].v2->x)); // max x + bounds[2] = min(bounds[2], min(lines[i].v1->y, lines[i].v2->y)); // min y + bounds[3] = max(bounds[3], max(lines[i].v1->y, lines[i].v2->y)); // max y + + if (valid == false) + { + angle = thisAngle; + valid = true; + } + else if (angle != thisAngle) + { + // Do not even attempt to bother with curved finish lines. + // Will likely just crash. + valid = false; + + break; + } + } + + if (valid == true) + { + fixed_t span = P_AproxDistance(bounds[1] - bounds[0], bounds[3] - bounds[2]) / 2; + + fixed_t cx = (bounds[0] + bounds[1]) / 2; + fixed_t cy = (bounds[2] + bounds[3]) / 2; + + fixed_t spanC = FixedMul(span, FINECOSINE(angle >> ANGLETOFINESHIFT)); + fixed_t spanS = FixedMul(span, FINESINE(angle >> ANGLETOFINESHIFT)); + + K_CreateFinishLineFromPoints( + cx - spanC, cy - spanS, + cx + spanC, cy + spanS + ); + + return true; + } + + return false; +} + +/*-------------------------------------------------- + static void K_DrawFinishLineBeamForLine(fixed_t offset, angle_t aiming, line_t *line, boolean reverse) + + Draws a helix out of rainbow colored orbs along a line, unique for each display player. + Called twice for the finish line beam effect. + + Input Arguments:- + offset - Offset value for positioning. Changed every tick to make it animate. + aiming - Starting vertical angle value. Changed every tick to make it animate. + line - Linedef to draw along. + reverse - Draw in reverse. Call twice with this toggled to make a double helix. + + Return:- + None +--------------------------------------------------*/ + +static void K_DrawFinishLineBeamForLine(fixed_t offset, angle_t aiming, line_t *line, boolean reverse) +{ + const fixed_t linelength = P_AproxDistance(line->dx, line->dy); + const fixed_t xstep = FixedDiv(line->dx, linelength); + const fixed_t ystep = FixedDiv(line->dy, linelength); + + fixed_t linex = line->v1->x; + fixed_t liney = line->v1->y; + angle_t lineangle = R_PointToAngle2(0, 0, line->dx, line->dy) + ANGLE_90; + + UINT8 i; + + if (line->flags & ML_NOCLIMB) + { + // Line is flipped + lineangle += ANGLE_180; + } + + linex += FixedMul(offset, xstep); + liney += FixedMul(offset, ystep); + + while (offset < linelength) + { +#define COLORCYCLELEN 10 + const UINT8 colorcycle[COLORCYCLELEN] = { + SKINCOLOR_PERIWINKLE, + SKINCOLOR_SLATE, + SKINCOLOR_BLOSSOM, + SKINCOLOR_RASPBERRY, + SKINCOLOR_ORANGE, + SKINCOLOR_YELLOW, + SKINCOLOR_LIME, + SKINCOLOR_TURTLE, + SKINCOLOR_ROBIN, + SKINCOLOR_JAWZ + }; + + const UINT8 numframes = 5; + const angle_t framethreshold = ANGLE_180 / (numframes-1); + const angle_t frameaim = aiming + (framethreshold / 2); + + fixed_t x, y, z; + UINT8 spriteframe = 0; + + x = linex + FixedMul(FixedMul(FINISHLINEBEAM_SPACING, FINECOSINE(lineangle >> ANGLETOFINESHIFT)), FINECOSINE(aiming >> ANGLETOFINESHIFT)); + y = liney + FixedMul(FixedMul(FINISHLINEBEAM_SPACING, FINESINE(lineangle >> ANGLETOFINESHIFT)), FINECOSINE(aiming >> ANGLETOFINESHIFT)); + z = FINISHLINEBEAM_SPACING + FixedMul(FINISHLINEBEAM_SPACING, FINESINE(aiming >> ANGLETOFINESHIFT)); + + if (leveltime >= starttime) + { + spriteframe = 4; // Weakest sprite when passable + } + else if (frameaim > ANGLE_180) + { + spriteframe = (ANGLE_MAX - frameaim) / framethreshold; + } + else + { + spriteframe = frameaim / framethreshold; + } + + for (i = 0; i <= r_splitscreen; i++) + { + if (playeringame[displayplayers[i]] && players[displayplayers[i]].mo && !P_MobjWasRemoved(players[displayplayers[i]].mo)) + { + mobj_t *beam; + + beam = P_SpawnMobj(x, y, players[displayplayers[i]].mo->z + z, MT_THOK); + P_SetMobjState(beam, S_FINISHBEAM1 + spriteframe); + + beam->colorized = true; + beam->drawflags = MFD_DONTDRAW & ~K_GetPlayerDontDrawFlag(&players[displayplayers[i]]); + + if (reverse) + { + beam->color = colorcycle[((leveltime / 4) + (COLORCYCLELEN/2)) % COLORCYCLELEN]; + } + else + { + beam->color = colorcycle[(leveltime / 4) % COLORCYCLELEN]; + } + } + } + + offset += FINISHLINEBEAM_SPACING; + linex += FixedMul(FINISHLINEBEAM_SPACING, xstep); + liney += FixedMul(FINISHLINEBEAM_SPACING, ystep); + + if (reverse) + { + aiming -= ANGLE_45; + } + else + { + aiming += ANGLE_45; + } + } + + for (i = 0; i <= r_splitscreen; i++) + { + if (playeringame[displayplayers[i]] && players[displayplayers[i]].mo && !P_MobjWasRemoved(players[displayplayers[i]].mo)) + { + UINT8 j; + for (j = 0; j < 2; j++) + { + vertex_t *v = line->v1; + mobj_t *end1, *end2; + angle_t a = R_PointToAngle2(0, 0, line->dx, line->dy); + fixed_t sx; + fixed_t sy; + + /* + if (line->flags & ML_NOCLIMB) + { + a += ANGLE_180; + } + */ + + sx = FixedMul(3*mapobjectscale, FINECOSINE(a >> ANGLETOFINESHIFT)); + sy = FixedMul(3*mapobjectscale, FINESINE(a >> ANGLETOFINESHIFT)); + + if (j == 1) + { + v = line->v2; + sx = -sx; + sy = -sy; + } + + end1 = P_SpawnMobj( + v->x + sx, + v->y + sy, + players[displayplayers[i]].mo->z + FINISHLINEBEAM_SPACING, + MT_THOK + ); + + P_SetMobjState(end1, S_FINISHBEAMEND1); + end1->drawflags = MFD_DONTDRAW & ~K_GetPlayerDontDrawFlag(&players[displayplayers[i]]); + end1->angle = lineangle; + + end2 = P_SpawnMobj( + v->x + (8*sx), + v->y + (8*sy), + players[displayplayers[i]].mo->z + FINISHLINEBEAM_SPACING, + MT_THOK + ); + + P_SetMobjState(end2, S_FINISHBEAMEND2); + end2->drawflags = MFD_DONTDRAW & ~K_GetPlayerDontDrawFlag(&players[displayplayers[i]]); + end2->angle = lineangle; + + P_SetTarget(&end2->tracer, end1); + end2->flags2 |= MF2_LINKDRAW; + } + } + } +} + +/*-------------------------------------------------- + void K_RunFinishLineBeam(void) + + See header file for description. +--------------------------------------------------*/ + +void K_RunFinishLineBeam(void) +{ + if (!(leveltime < starttime || rainbowstartavailable == true)) + { + return; + } + + if (finishBeamLine != NULL) + { + const angle_t angoffset = ANGLE_45; + const angle_t angadd = ANGLE_11hh; + const fixed_t speed = 6 * mapobjectscale; + + fixed_t offseta = (leveltime * speed) % FINISHLINEBEAM_SPACING; + angle_t aiminga = (angoffset * -((leveltime * speed) / FINISHLINEBEAM_SPACING)) + (angadd * leveltime); + + fixed_t offsetb = FINISHLINEBEAM_SPACING - offseta; + angle_t aimingb = (angoffset * -((leveltime * speed) / FINISHLINEBEAM_SPACING)) - (angadd * leveltime); + + K_DrawFinishLineBeamForLine(offseta, aiminga, finishBeamLine, false); + K_DrawFinishLineBeamForLine(offsetb, aimingb, finishBeamLine, true); + } +} diff --git a/src/k_race.h b/src/k_race.h new file mode 100644 index 000000000..b9021893e --- /dev/null +++ b/src/k_race.h @@ -0,0 +1,69 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2018-2020 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_race.h +/// \brief Race Mode specific code. + +#ifndef __K_RACE__ +#define __K_RACE__ + +#include "r_defs.h" + +extern line_t *finishBeamLine; + +#define FINISHLINEBEAM_SPACING (48*mapobjectscale) + +/*-------------------------------------------------- + void K_ClearFinishBeamLine(void); + + Clears variables for finishBeamLine. + Separate from K_FreeFinishBeamLine since this + needs called when PU_LEVEL is freed. + + Input Arguments:- + None + + Return:- + None +--------------------------------------------------*/ + +void K_ClearFinishBeamLine(void); + + +/*-------------------------------------------------- + boolean K_GenerateFinishBeamLine(void); + + Finds pre-placed "beam points" to create a finish line out of, + or tries to automatically create it from a finish linedef in the map. + The result is stored in the "finishBeamLine" variable. + + Input Arguments:- + None + + Return:- + True if successful, otherwise false. +--------------------------------------------------*/ + +boolean K_GenerateFinishBeamLine(void); + + +/*-------------------------------------------------- + void K_RunFinishLineBeam(void); + + Updates the finish line beam effect. + + Input Arguments:- + None + + Return:- + None +--------------------------------------------------*/ + +void K_RunFinishLineBeam(void); + +#endif diff --git a/src/k_respawn.c b/src/k_respawn.c index c05e1262f..7052fe466 100644 --- a/src/k_respawn.c +++ b/src/k_respawn.c @@ -122,9 +122,9 @@ void K_DoIngameRespawn(player_t *player) // If player was tumbling, set variables so that they don't tumble like crazy after they're done respawning if (player->tumbleBounces > 0) { - player->tumbleBounces = 3; // Max # of bounces-1 (so you still tumble once) - player->tumbleLastBounce = 0; // Still force them to bounce at least once for the funny - players->tumbleHeight = 20; // force tumble height + player->tumbleBounces = TUMBLEBOUNCES-1; // Max # of bounces-1 (so you still tumble once) + player->tumbleLastBounce = false; // Still force them to bounce at least once for the funny + players->tumbleHeight = 20; // force tumble height } P_ResetPlayer(player); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index eff2264c1..b33db8728 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3440,8 +3440,8 @@ static int lib_kExplodePlayer(lua_State *L) inflictor = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); if (!lua_isnone(L, 3) && lua_isuserdata(L, 3)) source = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); - K_ExplodePlayer(player, inflictor, source); - return 0; + lua_pushinteger(L, K_ExplodePlayer(player, inflictor, source)); + return 1; } static int lib_kTakeBumpersFromPlayer(lua_State *L) diff --git a/src/lua_libs.h b/src/lua_libs.h index 756459a55..ea0d9d965 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -34,6 +34,8 @@ extern lua_State *gL; #define META_SKIN "SKIN_T*" #define META_POWERS "PLAYER_T*POWERS" #define META_KARTSTUFF "PLAYER_T*KARTSTUFF" +#define META_KARTHUD "PLAYER_T*KARTHUD" +#define META_RESPAWN "PLAYER_T*RESPAWN" #define META_COLLIDE "PLAYER_T*COLLIDE" #define META_SOUNDSID "SKIN_T*SOUNDSID" diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 81adeebc5..61c6e85b1 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -192,6 +192,8 @@ static int player_get(lua_State *L) LUA_PushUserdata(L, plr->mo, META_MOBJ); else if (fastcmp(field,"cmd")) LUA_PushUserdata(L, &plr->cmd, META_TICCMD); + else if (fastcmp(field,"respawn")) + LUA_PushUserdata(L, &plr->respawn, META_RESPAWN); else if (fastcmp(field,"playerstate")) lua_pushinteger(L, plr->playerstate); else if (fastcmp(field,"viewz")) @@ -212,8 +214,14 @@ static int player_get(lua_State *L) LUA_PushUserdata(L, plr->powers, META_POWERS); else if (fastcmp(field,"kartstuff")) LUA_PushUserdata(L, plr->kartstuff, META_KARTSTUFF); + else if (fastcmp(field,"karthud")) + LUA_PushUserdata(L, plr->karthud, META_KARTHUD); else if (fastcmp(field,"airtime")) lua_pushinteger(L, plr->airtime); + else if (fastcmp(field,"driftInput")) + lua_pushboolean(L, plr->driftInput); + else if (fastcmp(field,"airFailsafe")) + lua_pushboolean(L, plr->airFailsafe); else if (fastcmp(field,"tumbleBounces")) lua_pushinteger(L, plr->tumbleBounces); else if (fastcmp(field,"tumbleHeight")) @@ -222,6 +230,8 @@ static int player_get(lua_State *L) lua_pushboolean(L, plr->tumbleLastBounce); else if (fastcmp(field,"tumbleSound")) lua_pushboolean(L, plr->tumbleSound); + else if (fastcmp(field,"glanceDir")) + lua_pushinteger(L, plr->glanceDir); else if (fastcmp(field,"trickpanel")) lua_pushinteger(L, plr->trickpanel); else if (fastcmp(field,"trickdelay")) @@ -471,6 +481,8 @@ static int player_set(lua_State *L) } else if (fastcmp(field,"cmd")) return NOSET; + else if (fastcmp(field,"respawn")) + return NOSET; else if (fastcmp(field,"playerstate")) plr->playerstate = luaL_checkinteger(L, 3); else if (fastcmp(field,"viewz")) @@ -521,6 +533,10 @@ static int player_set(lua_State *L) return NOSET; else if (fastcmp(field,"airtime")) plr->airtime = (tic_t)luaL_checkinteger(L, 3); + else if (fastcmp(field,"driftInput")) + plr->driftInput = luaL_checkboolean(L, 3); + else if (fastcmp(field,"airFailsafe")) + plr->airFailsafe = luaL_checkboolean(L, 3); else if (fastcmp(field,"tumbleBounces")) plr->tumbleBounces = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"tumbleHeight")) @@ -529,6 +545,8 @@ static int player_set(lua_State *L) plr->tumbleLastBounce = luaL_checkboolean(L, 3); else if (fastcmp(field,"tumbleSound")) plr->tumbleSound = luaL_checkboolean(L, 3); + else if (fastcmp(field,"glanceDir")) + plr->glanceDir = (SINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"trickpanel")) plr->trickpanel = luaL_checkinteger(L, 3); else if (fastcmp(field,"trickdelay")) @@ -848,13 +866,48 @@ static int kartstuff_set(lua_State *L) return 0; } -// #kartstuff -> NUMKARTSTUFF +// #karthud -> NUMKARTSTUFF static int kartstuff_len(lua_State *L) { lua_pushinteger(L, NUMKARTSTUFF); return 1; } +// karthud, ks -> karthud[ks] +static int karthud_get(lua_State *L) +{ + INT32 *karthud = *((INT32 **)luaL_checkudata(L, 1, META_KARTHUD)); + karthudtype_t ks = luaL_checkinteger(L, 2); + if (ks >= NUMKARTHUD) + return luaL_error(L, LUA_QL("karthudtype_t") " cannot be %u", ks); + lua_pushinteger(L, karthud[ks]); + return 1; +} + +// karthud, ks, value -> kartstuff[ks] = value +static int karthud_set(lua_State *L) +{ + INT32 *karthud = *((INT32 **)luaL_checkudata(L, 1, META_KARTHUD)); + karthudtype_t ks = luaL_checkinteger(L, 2); + INT32 i = (INT32)luaL_checkinteger(L, 3); + if (ks >= NUMKARTHUD) + return luaL_error(L, LUA_QL("karthudtype_t") " cannot be %u", ks); + if (hud_running) + return luaL_error(L, "Do not alter player_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter player_t in BuildCMD code!"); + karthud[ks] = i; + return 0; +} + +// #karthud -> NUMKARTHUD +static int karthud_len(lua_State *L) +{ + lua_pushinteger(L, NUMKARTHUD); + return 1; +} + +// player.cmd get/set #define NOFIELD luaL_error(L, LUA_QL("ticcmd_t") " has no field named " LUA_QS, field) #define NOSET luaL_error(L, LUA_QL("ticcmd_t") " field " LUA_QS " cannot be set.", field) @@ -913,6 +966,82 @@ static int ticcmd_set(lua_State *L) #undef NOFIELD +// Same shit for player.respawn variable... Why is everything in different sub-variables again now??? +#define RNOFIELD luaL_error(L, LUA_QL("respawnvars_t") " has no field named " LUA_QS, field) +#define RUNIMPLEMENTED luaL_error(L, LUA_QL("respawnvars_t") " unimplemented field " LUA_QS " cannot be read or set.", field) +// @TODO: Waypoints in Lua possibly maybe? No don't count on me to do it... + +static int respawn_get(lua_State *L) +{ + respawnvars_t *rsp = *((respawnvars_t **)luaL_checkudata(L, 1, META_RESPAWN)); + const char *field = luaL_checkstring(L, 2); + if (!rsp) + return LUA_ErrInvalid(L, "player_t"); + + if (fastcmp(field,"state")) + lua_pushinteger(L, rsp->state); + else if (fastcmp(field,"waypoint")) + return RUNIMPLEMENTED; + else if (fastcmp(field,"pointx")) + lua_pushfixed(L, rsp->pointx); + else if (fastcmp(field,"pointy")) + lua_pushfixed(L, rsp->pointy); + else if (fastcmp(field,"pointz")) + lua_pushfixed(L, rsp->pointz); + else if (fastcmp(field,"flip")) + lua_pushboolean(L, rsp->flip); + else if (fastcmp(field,"timer")) + lua_pushinteger(L, rsp->timer); + else if (fastcmp(field,"distanceleft")) + lua_pushinteger(L, rsp->distanceleft); // Can't possibly foresee any problem when pushing UINT32 to Lua's INT32 hahahahaha, get ready for dumb hacky shit on high distances. + else if (fastcmp(field,"dropdash")) + lua_pushinteger(L, rsp->dropdash); + else + return RNOFIELD; + + return 1; +} + +static int respawn_set(lua_State *L) +{ + respawnvars_t *rsp = *((respawnvars_t **)luaL_checkudata(L, 1, META_RESPAWN)); + const char *field = luaL_checkstring(L, 2); + if (!rsp) + return LUA_ErrInvalid(L, "respawnvars_t"); + + if (hud_running) + return luaL_error(L, "Do not alter player_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter player_t in CMD building code!"); + + if (fastcmp(field,"state")) + rsp->state = (UINT8)luaL_checkinteger(L, 3); + else if (fastcmp(field,"waypoint")) + return RUNIMPLEMENTED; + else if (fastcmp(field,"pointx")) + rsp->pointx = luaL_checkfixed(L, 3); + else if (fastcmp(field,"pointy")) + rsp->pointy = luaL_checkfixed(L, 3); + else if (fastcmp(field,"pointz")) + rsp->pointz = luaL_checkfixed(L, 3); + else if (fastcmp(field,"flip")) + rsp->flip = luaL_checkboolean(L, 3); + else if (fastcmp(field,"timer")) + rsp->timer = (tic_t)luaL_checkinteger(L, 3); + else if (fastcmp(field,"distanceleft")) + rsp->distanceleft = (UINT32)luaL_checkinteger(L, 3); + else if (fastcmp(field,"dropdash")) + rsp->dropdash = (tic_t)luaL_checkinteger(L, 3); + else + return RNOFIELD; + + return 0; +} + +#undef RNOFIELD +#undef RUNIMPLEMENTED + + int LUA_PlayerLib(lua_State *L) { luaL_newmetatable(L, META_PLAYER); @@ -948,6 +1077,25 @@ int LUA_PlayerLib(lua_State *L) lua_setfield(L, -2, "__len"); lua_pop(L,1); + luaL_newmetatable(L, META_KARTHUD); + lua_pushcfunction(L, karthud_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, karthud_set); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, karthud_len); + lua_setfield(L, -2, "__len"); + lua_pop(L,1); + + luaL_newmetatable(L, META_RESPAWN); + lua_pushcfunction(L, respawn_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, respawn_set); + lua_setfield(L, -2, "__newindex"); + lua_pop(L,1); + luaL_newmetatable(L, META_TICCMD); lua_pushcfunction(L, ticcmd_get); lua_setfield(L, -2, "__index"); diff --git a/src/p_enemy.c b/src/p_enemy.c index 01f8f6bb7..a2c719609 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -4229,7 +4229,7 @@ void A_AttractChase(mobj_t *actor) else actor->drawflags &= ~MFD_DONTDRAW; - // spilled rings have ghost trails and get capped to a certain speed + // spilled rings get capped to a certain speed if (actor->type == (mobjtype_t)actor->info->reactiontime) { const fixed_t maxspeed = 4<momx = FixedMul(FixedDiv(actor->momx, oldspeed), newspeed); actor->momy = FixedMul(FixedDiv(actor->momy, oldspeed), newspeed); } - - if (!P_IsObjectOnGround(actor)) - P_SpawnGhostMobj(actor)->tics = 3; } if (actor->tracer && actor->tracer->player && actor->tracer->health diff --git a/src/p_inter.c b/src/p_inter.c index 5be3cc736..4371752c8 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1371,13 +1371,47 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget case MT_PLAYER: { + angle_t flingAngle; + mobj_t *kart; + target->fuse = TICRATE*3; // timer before mobj disappears from view (even if not an actual player) target->momx = target->momy = target->momz = 0; - if (target->player && target->player->pflags & PF_GAMETYPEOVER) - break; + kart = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_KART_LEFTOVER); + if (kart && !P_MobjWasRemoved(kart)) + { + kart->angle = target->angle; + kart->color = target->color; + kart->hitlag = target->hitlag; + P_SetObjectMomZ(kart, 6*FRACUNIT, false); + kart->extravalue1 = target->player->kartweight; + } + + if (source && !P_MobjWasRemoved(source)) + { + flingAngle = R_PointToAngle2( + source->x - source->momx, source->y - source->momy, + target->x, target->y + ); + } + else + { + flingAngle = target->angle + ANGLE_180; + + if (P_RandomByte() & 1) + { + flingAngle -= ANGLE_45; + } + else + { + flingAngle += ANGLE_45; + } + } + + P_InstaThrust(target, flingAngle, 14 * target->scale); P_SetObjectMomZ(target, 14*FRACUNIT, false); + P_PlayDeathSound(target); } break; @@ -2012,10 +2046,11 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da break; case DMG_TUMBLE: K_TumblePlayer(player, inflictor, source); + ringburst = 10; break; case DMG_EXPLODE: case DMG_KARMA: - K_ExplodePlayer(player, inflictor, source); + ringburst = K_ExplodePlayer(player, inflictor, source); break; case DMG_WIPEOUT: if (P_IsDisplayPlayer(player)) @@ -2100,6 +2135,56 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da return true; } +static void P_FlingBurst +( player_t *player, + angle_t fa, + fixed_t z, + mobjtype_t objType, + tic_t objFuse, + fixed_t objScale, + INT32 i) +{ + mobj_t *mo; + fixed_t ns; + fixed_t momxy = 5<> 1; + + z = player->mo->z; + if (player->mo->eflags & MFE_VERTICALFLIP) + z += player->mo->height - mobjinfo[objType].height; + + mo = P_SpawnMobj(player->mo->x, player->mo->y, z, objType); + + mo->threshold = 10; // not useful for spikes + mo->fuse = objFuse; + P_SetTarget(&mo->target, player->mo); + + mo->destscale = objScale; + P_SetScale(mo, objScale); + + /* + 0: 0 + 1: 1 = (1+1)/2 = 1 + 2: 1 = (2+1)/2 = 1 + 3: 2 = (3+1)/2 = 2 + 4: 2 = (4+1)/2 = 2 + 5: 3 = (4+1)/2 = 2 + */ + // Angle / height offset changes every other ring + momxy -= mx * FRACUNIT; + momz += mx * (2<mo->scale); + mo->momx = (mo->target->momx/2) + FixedMul(FINECOSINE(fa>>ANGLETOFINESHIFT), ns); + mo->momy = (mo->target->momy/2) + FixedMul(FINESINE(fa>>ANGLETOFINESHIFT), ns); + + ns = FixedMul(momz, player->mo->scale); + mo->momz = (mo->target->momz/2) + ((ns) * P_MobjFlip(mo)); +} + /** Spills an injured player's rings. * * \param player The player who is losing rings. @@ -2109,12 +2194,10 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da */ void P_PlayerRingBurst(player_t *player, INT32 num_rings) { + INT32 num_fling_rings; INT32 i; - mobj_t *mo; angle_t fa; - fixed_t ns; fixed_t z; - fixed_t momxy = 5<rings <= 0 && num_rings > 2) - num_rings = 2; + num_fling_rings = min(num_rings, player->rings); P_GivePlayerRings(player, -num_rings); // determine first angle fa = player->mo->angle + ((P_RandomByte() & 1) ? -ANGLE_90 : ANGLE_90); - for (i = 0; i < num_rings; i++) + z = player->mo->z; + if (player->mo->eflags & MFE_VERTICALFLIP) + z += player->mo->height - mobjinfo[MT_RING].height; + + for (i = 0; i < num_fling_rings; i++) { - INT32 objType = mobjinfo[MT_RING].reactiontime; + P_FlingBurst(player, fa, z, + MT_FLINGRING, 60*TICRATE, player->mo->scale, i); + } - z = player->mo->z; - if (player->mo->eflags & MFE_VERTICALFLIP) - z += player->mo->height - mobjinfo[objType].height; - - mo = P_SpawnMobj(player->mo->x, player->mo->y, z, objType); - - mo->threshold = 10; - mo->fuse = 60*TICRATE; - P_SetTarget(&mo->target, player->mo); - - mo->destscale = player->mo->scale; - P_SetScale(mo, player->mo->scale); - - // Angle / height offset changes every other ring - if (i != 0) - { - if (i & 1) - { - momxy -= FRACUNIT; - momz += 2<scale); - mo->momx = (mo->target->momx/2) + FixedMul(FINECOSINE(fa>>ANGLETOFINESHIFT), ns); - mo->momy = (mo->target->momy/2) + FixedMul(FINESINE(fa>>ANGLETOFINESHIFT), ns); - - ns = FixedMul(momz, mo->scale); - P_SetObjectMomZ(mo, (mo->target->momz/2) + ns, false); - - if (player->mo->eflags & MFE_VERTICALFLIP) - mo->momz *= -1; + while (i < num_rings) + { + P_FlingBurst(player, fa, z, + MT_DEBTSPIKE, 0, 3 * player->mo->scale / 2, i++); } } diff --git a/src/p_map.c b/src/p_map.c index 8c9db9ce4..9f8d14100 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -213,6 +213,7 @@ static boolean P_SpecialIsLinedefCrossType(UINT16 ldspecial) switch (ldspecial) { case 2001: // Finish line + case 2003: // Respawn line { linedefcrossspecial = true; } @@ -1398,6 +1399,21 @@ static boolean PIT_CheckThing(mobj_t *thing) return false; } + else if (thing->type == MT_KART_LEFTOVER) + { + // 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 (P_IsObjectOnGround(thing) && tmthing->momz < 0) + K_KartBouncing(tmthing, thing, true, false); + else + K_KartBouncing(tmthing, thing, false, false); + + return false; + } else if (thing->flags & MF_SOLID) { // see if it went over / under diff --git a/src/p_mobj.c b/src/p_mobj.c index 2cbabfd9a..96a10458f 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -221,17 +221,29 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state) case S_KART_STILL: case S_KART_STILL_L: case S_KART_STILL_R: - player->panim = PA_IDLE; + case S_KART_STILL_GLANCE_L: + case S_KART_STILL_GLANCE_R: + case S_KART_STILL_LOOK_L: + case S_KART_STILL_LOOK_R: + player->panim = PA_STILL; break; case S_KART_SLOW: case S_KART_SLOW_L: case S_KART_SLOW_R: - player->panim = PA_WALK; + case S_KART_SLOW_GLANCE_L: + case S_KART_SLOW_GLANCE_R: + case S_KART_SLOW_LOOK_L: + case S_KART_SLOW_LOOK_R: + player->panim = PA_SLOW; break; case S_KART_FAST: case S_KART_FAST_L: case S_KART_FAST_R: - player->panim = PA_RUN; + case S_KART_FAST_GLANCE_L: + case S_KART_FAST_GLANCE_R: + case S_KART_FAST_LOOK_L: + case S_KART_FAST_LOOK_R: + player->panim = PA_FAST; break; case S_KART_DRIFT_L: case S_KART_DRIFT_L_OUT: @@ -239,11 +251,11 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state) case S_KART_DRIFT_R: case S_KART_DRIFT_R_OUT: case S_KART_DRIFT_R_IN: - player->panim = PA_DASH; + player->panim = PA_DRIFT; break; case S_KART_SPINOUT: - case S_KART_SQUISH: - player->panim = PA_PAIN; + case S_KART_DEAD: + player->panim = PA_HURT; break; default: player->panim = PA_ETC; @@ -1300,7 +1312,7 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy) && !(player->mo->standingslope && (!(player->mo->standingslope->flags & SL_NOPHYSICS)) /*&& (abs(player->mo->standingslope->zdelta) >= FRACUNIT/2)*/)) { // if in a walking frame, stop moving - if (player->panim == PA_WALK) + if (player->panim == PA_SLOW) { P_SetPlayerMobjState(mo, S_KART_STILL); } @@ -2280,6 +2292,16 @@ boolean P_ZMovement(mobj_t *mo) else mo->flags2 ^= MFD_DONTDRAW; } + else if (mo->type == MT_DEBTSPIKE) + { + mom.x = mom.y = 0; + mom.z = -mom.z/2; + + if (mo->fuse == 0) + { + mo->fuse = 90; + } + } else if (mo->flags & MF_MISSILE) { if (!(mo->flags & MF_NOCLIP)) @@ -2334,6 +2356,68 @@ boolean P_ZMovement(mobj_t *mo) if (mo->flags2 & MF2_SKULLFLY) // the skull slammed into something mom.z = -mom.z; + else if (mo->type == MT_KART_LEFTOVER) + { + if (mo->health > 1) + { + const fixed_t tireOffset = 32; + const angle_t aOffset = ANGLE_22h; + + UINT8 i; + angle_t tireAngle; + mobj_t *tire; + + // Spawn tires! + mo->health = 1; + P_SetMobjState(mo, S_KART_LEFTOVER_NOTIRES); + + // Front tires + tireAngle = mo->angle - aOffset; + for (i = 0; i < 2; i++) + { + tire = P_SpawnMobjFromMobj( + mo, + tireOffset * FINECOSINE(tireAngle >> ANGLETOFINESHIFT), + tireOffset * FINESINE(tireAngle >> ANGLETOFINESHIFT), + 0, + MT_KART_TIRE + ); + + tire->angle = mo->angle; + tire->fuse = 3*TICRATE; + P_InstaThrust(tire, tireAngle, 4 * mo->scale); + P_SetObjectMomZ(tire, 4*FRACUNIT, false); + + tireAngle += (aOffset * 2); + } + + // Back tires + tireAngle = (mo->angle + ANGLE_180) - aOffset; + for (i = 0; i < 2; i++) + { + tire = P_SpawnMobjFromMobj( + mo, + tireOffset * FINECOSINE(tireAngle >> ANGLETOFINESHIFT), + tireOffset * FINESINE(tireAngle >> ANGLETOFINESHIFT), + 0, + MT_KART_TIRE + ); + + tire->angle = mo->angle; + tire->fuse = 3*TICRATE; + P_InstaThrust(tire, tireAngle, 4 * mo->scale); + P_SetObjectMomZ(tire, 4*FRACUNIT, false); + + P_SetMobjState(tire, S_KART_TIRE2); + + tireAngle += (aOffset * 2); + } + } + } + else if (mo->type == MT_KART_TIRE) + { + mom.z = -mom.z; + } else if (mo->type == MT_BIGTUMBLEWEED || mo->type == MT_LITTLETUMBLEWEED || mo->type == MT_CANNONBALLDECOR @@ -2633,7 +2717,7 @@ void P_PlayerZMovement(mobj_t *mo) mo->z = mo->floorz; // Get up if you fell. - if (mo->player->panim == PA_PAIN && mo->player->kartstuff[k_spinouttimer] == 0 && mo->player->tumbleBounces == 0) + if (mo->player->panim == PA_HURT && mo->player->kartstuff[k_spinouttimer] == 0 && mo->player->tumbleBounces == 0) P_SetPlayerMobjState(mo, S_KART_STILL); if (!mo->standingslope && (mo->eflags & MFE_VERTICALFLIP ? tmceilingslope : tmfloorslope)) { @@ -5867,8 +5951,11 @@ static boolean P_MobjDeadThink(mobj_t *mobj) { // Go away. /// \todo Actually go ahead and remove mobj completely, and fix any bugs and crashes doing this creates. Chasecam should stop moving, and F12 should never return to it. mobj->momz = 0; + if (mobj->player) + { mobj->drawflags |= MFD_DONTDRAW; + } else // safe to remove, nobody's going to complain! { P_RemoveMobj(mobj); @@ -5877,16 +5964,6 @@ static boolean P_MobjDeadThink(mobj_t *mobj) } else // Apply gravity to fall downwards. { - if (mobj->player && !(mobj->fuse % 8) && (mobj->player->charflags & SF_MACHINE)) - { - fixed_t r = mobj->radius >> FRACBITS; - mobj_t *explosion = P_SpawnMobj( - mobj->x + (P_RandomRange(r, -r) << FRACBITS), - mobj->y + (P_RandomRange(r, -r) << FRACBITS), - mobj->z + (P_RandomKey(mobj->height >> FRACBITS) << FRACBITS), - MT_SONIC3KBOSSEXPLODE); - S_StartSound(explosion, sfx_s3kb4); - } P_SetObjectMomZ(mobj, -2*FRACUNIT/3, true); } break; @@ -6671,6 +6748,10 @@ static boolean P_MobjRegularThink(mobj_t *mobj) if (mobj->fuse == 16)/* to red*/ K_SpawnDriftBoostClip(mobj->target->player); break; + + case 0:/* air failsafe boost */ + mobj->color = SKINCOLOR_SILVER; // force white + break; } { @@ -9079,6 +9160,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) switch (thing->type) { case MT_PLAYER: + case MT_KART_LEFTOVER: case MT_SMALLMACE: case MT_BIGMACE: case MT_PUMA: @@ -9124,6 +9206,8 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) thing->shadowscale = FRACUNIT; break; case MT_RING: + case MT_FLINGRING: + case MT_DEBTSPIKE: case MT_FLOATINGITEM: case MT_BLUESPHERE: case MT_EMERALD: @@ -9371,6 +9455,19 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->color = (P_RandomChance(FRACUNIT/2) ? SKINCOLOR_RED : SKINCOLOR_AQUAMARINE); break; case MT_BALLOON: + { + static const UINT8 BALLOONCOLORS[] = { + // Carnival Night balloon colors + SKINCOLOR_KETCHUP, + SKINCOLOR_SAPPHIRE, + SKINCOLOR_TANGERINE, + SKINCOLOR_JET + }; + + mobj->color = BALLOONCOLORS[P_RandomKey(sizeof(BALLOONCOLORS))]; + } + break; + case MT_KART_LEFTOVER: mobj->color = SKINCOLOR_RED; break; case MT_EGGROBO1: diff --git a/src/p_saveg.c b/src/p_saveg.c index 79b99e98f..e24e3d2f7 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -255,7 +255,11 @@ static void P_NetArchivePlayers(void) WRITEUINT32(save_p, players[i].distancetofinish); WRITEUINT32(save_p, K_GetWaypointHeapIndex(players[i].nextwaypoint)); + WRITEUINT32(save_p, players[i].airtime); + WRITEUINT8(save_p, players[i].driftInput); + WRITEUINT8(save_p, players[i].airFailsafe); + WRITEUINT8(save_p, players[i].trickpanel); WRITEUINT8(save_p, players[i].trickdelay); WRITEUINT32(save_p, players[i].trickmomx); @@ -271,6 +275,8 @@ static void P_NetArchivePlayers(void) WRITEUINT8(save_p, players[i].tumbleLastBounce); WRITEUINT8(save_p, players[i].tumbleSound); + WRITESINT8(save_p, players[i].glanceDir); + // respawnvars_t WRITEUINT8(save_p, players[i].respawn.state); WRITEUINT32(save_p, K_GetWaypointHeapIndex(players[i].respawn.wp)); @@ -455,7 +461,11 @@ static void P_NetUnArchivePlayers(void) players[i].distancetofinish = READUINT32(save_p); players[i].nextwaypoint = (waypoint_t *)(size_t)READUINT32(save_p); + players[i].airtime = READUINT32(save_p); + players[i].driftInput = (boolean)READUINT8(save_p); + players[i].airFailsafe = (boolean)READUINT8(save_p); + players[i].trickpanel = READUINT8(save_p); players[i].trickdelay = READUINT8(save_p); players[i].trickmomx = READUINT32(save_p); @@ -471,6 +481,8 @@ static void P_NetUnArchivePlayers(void) players[i].tumbleLastBounce = (boolean)READUINT8(save_p); players[i].tumbleSound = (boolean)READUINT8(save_p); + players[i].glanceDir = READSINT8(save_p); + // respawnvars_t players[i].respawn.state = READUINT8(save_p); players[i].respawn.wp = (waypoint_t *)(size_t)READUINT32(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index 13c0170a8..6cd4ffd72 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -83,6 +83,7 @@ // SRB2Kart #include "k_kart.h" +#include "k_race.h" #include "k_battle.h" // K_SpawnBattleCapsules #include "k_pwrlv.h" #include "k_waypoint.h" @@ -4059,6 +4060,8 @@ boolean P_LoadLevel(boolean fromnetsave) // The waypoint data that's in PU_LEVEL needs to be reset back to 0/NULL now since PU_LEVEL was cleared K_ClearWaypoints(); + K_ClearFinishBeamLine(); + // Load the waypoints please! if (gametyperules & GTR_CIRCUIT) { @@ -4066,6 +4069,11 @@ boolean P_LoadLevel(boolean fromnetsave) { CONS_Alert(CONS_ERROR, "Waypoints were not able to be setup! Player positions will not work correctly.\n"); } + + if (K_GenerateFinishBeamLine() == false) + { + CONS_Alert(CONS_ERROR, "No valid finish line beam setup could be found.\n"); + } } #ifdef HWRENDER // not win32 only 19990829 by Kin diff --git a/src/p_spec.c b/src/p_spec.c index 1973e8708..f810529ce 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2182,6 +2182,20 @@ void P_CrossSpecialLine(line_t *line, INT32 side, mobj_t *thing) } break; + case 2003: // Respawn Line + { + /* No Climb: only trigger from front side */ + if + ( + player->respawn.state == RESPAWNST_NONE && + (!(line->flags & ML_NOCLIMB) || side == 0) + ) + { + P_DamageMobj(player->mo, NULL, NULL, 1, DMG_DEATHPIT); + } + } + break; + default: { // Do nothing @@ -6973,6 +6987,11 @@ void P_SpawnSpecials(boolean fromnetsave) break; case 2002: // Linedef Trigger: Race Lap break; + case 2003: // Respawn Line + break; + case 2004: // Bot controller + break; + case 499: // Linedef Executor: Enable/Disable Waypoints break; diff --git a/src/p_tick.c b/src/p_tick.c index c7e6a946a..71ded772c 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -30,6 +30,7 @@ // SRB2Kart #include "k_kart.h" +#include "k_race.h" #include "k_battle.h" #include "k_waypoint.h" @@ -332,6 +333,9 @@ static inline void P_RunThinkers(void) ps_thlist_times[i] = I_GetTimeMicros() - ps_thlist_times[i]; } + if (gametyperules & GTR_CIRCUIT) + K_RunFinishLineBeam(); + if (gametyperules & GTR_PAPERITEMS) K_RunPaperItemSpawners(); diff --git a/src/p_user.c b/src/p_user.c index e7a94ef11..e4785b249 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1851,7 +1851,7 @@ static void P_3dMovement(player_t *player) totalthrust.x = totalthrust.y = 0; // I forget if this is needed totalthrust.z = FRACUNIT*P_MobjFlip(player->mo)/3; // A bit of extra push-back on slopes - if (player->kartstuff[k_sneakertimer] > 0) + if (K_SlopeResistance(player) == true) { totalthrust.z = -(totalthrust.z); } @@ -2632,9 +2632,6 @@ static void P_DeathThink(player_t *player) if (player->deadtimer < INT32_MAX) player->deadtimer++; - 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_GAMETYPEOVER) && (gametyperules & GTR_CIRCUIT)) { player->karthud[khud_timeovercam]++; @@ -2677,8 +2674,6 @@ static void P_DeathThink(player_t *player) } } -notrealplayer: - if (!player->mo) return; @@ -3075,6 +3070,13 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall mo = player->mo; + if (mo->hitlag > 0 || player->playerstate == PST_DEAD) + { + // Do not move the camera while in hitlag! + // The camera zooming out after you got hit makes it hard to focus on the vibration. + return true; + } + #ifndef NOCLIPCAM cameranoclip = ((player->pflags & PF_NOCLIP) || (mo->flags & (MF_NOCLIP|MF_NOCLIPHEIGHT)) // Noclipping player camera noclips too!! diff --git a/src/r_data.c b/src/r_data.c index fcc316362..39c41d8bf 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -623,13 +623,13 @@ extracolormap_t *R_ColormapForName(char *name) // custom colormaps at runtime. NOTE: For GL mode, we only need to color // data and not the colormap data. // -static double deltas[256][3], map[256][3]; +static double brightChange[256], map[256][3]; static int RoundUp(double number); lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap) { - double cmaskr, cmaskg, cmaskb, cdestr, cdestg, cdestb; + double cmaskr, cmaskg, cmaskb, cdestr, cdestg, cdestb, cdestbright; double maskamt = 0, othermask = 0; UINT8 cr = R_GetRgbaR(extra_colormap->rgba), @@ -668,6 +668,7 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap) cdestr = cfr; cdestg = cfg; cdestb = cfb; + cdestbright = sqrt((cfr*cfr) + (cfg*cfg) + (cfb*cfb)); // fade alpha unused in software // maskamt = (double)(cfa/24.0l); @@ -682,15 +683,15 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap) // This code creates the colormap array used by software renderer ///////////////////// { - double r, g, b, cbrightness; + double r, g, b, cbrightness, cbest, cdist; int p; lighttable_t *colormap_p; // Initialise the map and delta arrays // map[i] stores an RGB color (as double) for index i, // which is then converted to SRB2's palette later - // deltas[i] stores a corresponding fade delta between the RGB color and the final fade color; - // map[i]'s values are decremented by after each use + // brightChange[i] is the value added/subtracted every step for the fade; + // map[i]'s values are in/decremented by it after each use for (i = 0; i < 256; i++) { r = pMasterPalette[i].s.red; @@ -701,17 +702,31 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap) map[i][0] = (cbrightness * cmaskr) + (r * othermask); if (map[i][0] > 255.0l) map[i][0] = 255.0l; - deltas[i][0] = (map[i][0] - cdestr) / (double)fadedist; map[i][1] = (cbrightness * cmaskg) + (g * othermask); if (map[i][1] > 255.0l) map[i][1] = 255.0l; - deltas[i][1] = (map[i][1] - cdestg) / (double)fadedist; map[i][2] = (cbrightness * cmaskb) + (b * othermask); if (map[i][2] > 255.0l) map[i][2] = 255.0l; - deltas[i][2] = (map[i][2] - cdestb) / (double)fadedist; + + // Get the "best" color. + // Our brightest color's value, if we're fading to a darker color, + // or our (inverted) darkest color's value, if we're fading to a brighter color. + if (cbrightness < cdestbright) + { + cbest = 255.0l - min(r, min(g, b)); + cdist = 255.0l - cdestbright; + } + else + { + cbest = max(r, max(g, b)); + cdist = cdestbright; + } + + // Add/subtract this value during fading. + brightChange[i] = fabs(cbest - cdist) / (double)fadedist; } // Now allocate memory for the actual colormap array itself! @@ -732,22 +747,28 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap) if ((UINT32)p < fadestart) continue; -#define ABS2(x) ((x) < 0 ? -(x) : (x)) - if (ABS2(map[i][0] - cdestr) > ABS2(deltas[i][0])) - map[i][0] -= deltas[i][0]; - else + + // Add/subtract towards the destination color. + if (fabs(map[i][0] - cdestr) <= brightChange[i]) map[i][0] = cdestr; - - if (ABS2(map[i][1] - cdestg) > ABS2(deltas[i][1])) - map[i][1] -= deltas[i][1]; + else if (map[i][0] > cdestr) + map[i][0] -= brightChange[i]; else + map[i][0] += brightChange[i]; + + if (fabs(map[i][1] - cdestg) <= brightChange[i]) map[i][1] = cdestg; - - if (ABS2(map[i][2] - cdestb) > ABS2(deltas[i][1])) - map[i][2] -= deltas[i][2]; + else if (map[i][1] > cdestg) + map[i][1] -= brightChange[i]; else + map[i][1] += brightChange[i]; + + if (fabs(map[i][2] - cdestb) <= brightChange[i]) map[i][2] = cdestb; -#undef ABS2 + else if (map[i][2] > cdestb) + map[i][2] -= brightChange[i]; + else + map[i][2] += brightChange[i]; } } diff --git a/src/r_things.c b/src/r_things.c index e1e38041b..c4e76303d 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1236,28 +1236,24 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, patch_t *patch; fixed_t xscale, yscale, shadowxscale, shadowyscale, shadowskew, x1, x2; INT32 light = 0; - fixed_t scalemul; UINT8 trans; - fixed_t floordiff; + UINT8 trans = tr_transsub; fixed_t groundz; pslope_t *groundslope; - boolean isflipped = thing->eflags & MFE_VERTICALFLIP; groundz = R_GetShadowZ(thing, &groundslope); if (abs(groundz-viewz)/tz > 4) return; // Prevent stretchy shadows and possible crashes - floordiff = abs((isflipped ? thing->height : 0) + thing->z - groundz); + if (thing->whiteshadow == true) + { + trans = tr_transadd; + } - trans = floordiff / (100*FRACUNIT) + 3; - if (trans >= 9) return; - - scalemul = FixedMul(FRACUNIT - floordiff/640, scale); - - patch = W_CachePatchName((thing->whiteshadow == true ? "LSHADOW" : "DSHADOW"), PU_CACHE); + patch = W_CachePatchName("DSHADOW", PU_CACHE); xscale = FixedDiv(projection[viewssnum], tz); yscale = FixedDiv(projectiony[viewssnum], tz); - shadowxscale = FixedMul(thing->radius*2, scalemul); - shadowyscale = FixedMul(FixedMul(thing->radius*2, scalemul), FixedDiv(abs(groundz - viewz), tz)); + shadowxscale = FixedMul(thing->radius*2, scale); + shadowyscale = FixedMul(FixedMul(thing->radius*2, scale), FixedDiv(abs(groundz - viewz), tz)); shadowyscale = min(shadowyscale, shadowxscale) / SHORT(patch->height); shadowxscale /= SHORT(patch->width); shadowskew = 0; @@ -1274,9 +1270,9 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, //CONS_Printf("Shadow is sloped by %d %d\n", xslope, zslope); if (viewz < groundz) - shadowyscale += FixedMul(FixedMul(thing->radius*2 / SHORT(patch->height), scalemul), zslope); + shadowyscale += FixedMul(FixedMul(thing->radius*2 / SHORT(patch->height), scale), zslope); else - shadowyscale -= FixedMul(FixedMul(thing->radius*2 / SHORT(patch->height), scalemul), zslope); + shadowyscale -= FixedMul(FixedMul(thing->radius*2 / SHORT(patch->height), scale), zslope); shadowyscale = abs(shadowyscale); @@ -1354,16 +1350,9 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, else shadow->extra_colormap = thing->subsector->sector->extra_colormap; - shadow->transmap = transtables + (trans<transmap = transtables + ((trans-1) << FF_TRANSSHIFT); - if (thing->whiteshadow == true) - { - shadow->colormap = scalelight[LIGHTLEVELS - 1][0]; // full bright! - } - else - { - shadow->colormap = scalelight[0][0]; // full dark! - } + shadow->colormap = colormaps; objectsdrawn++; } diff --git a/src/v_video.c b/src/v_video.c index 89020eae8..3e2935a46 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -551,7 +551,7 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca else if (alphalevel == 15) alphalevel = hudplusalpha[st_translucency]; - if (alphalevel >= 10) + if (alphalevel >= 12) return; // invis if (alphalevel) @@ -744,7 +744,7 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ else if (alphalevel == 15) alphalevel = hudplusalpha[st_translucency]; - if (alphalevel >= 10) + if (alphalevel >= 12) return; // invis if (alphalevel) @@ -997,7 +997,7 @@ void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) else if (alphalevel == 15) alphalevel = hudplusalpha[st_translucency]; - if (alphalevel >= 10) + if (alphalevel >= 12) return; // invis } diff --git a/src/v_video.h b/src/v_video.h index 49ab33028..7dd44a460 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -120,6 +120,8 @@ void V_CubeApply(UINT8 *red, UINT8 *green, UINT8 *blue); #define V_70TRANS 0x00070000 #define V_80TRANS 0x00080000 // used to be V_8020TRANS #define V_90TRANS 0x00090000 +#define V_ADDTRANS 0x000A0000 +#define V_SUBTRANS 0x000B0000 #define V_HUDTRANSHALF 0x000D0000 #define V_HUDTRANS 0x000E0000 // draw the hud translucent #define V_HUDTRANSDOUBLE 0x000F0000