From 07c30a20c222a84b92f0686e37c8be54508c8c71 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 28 Nov 2022 21:46:43 -0800 Subject: [PATCH 01/46] Move all sources into CMakeLists.txt Changing Sourcefile did not automatically update the CMake cache. --- CMakeLists.txt | 7 -- src/CMakeLists.txt | 137 ++++++++++++++++++++++++++++++++++-- src/Sourcefile | 127 --------------------------------- src/blua/CMakeLists.txt | 28 +++++++- src/blua/Sourcefile | 25 ------- src/hardware/CMakeLists.txt | 16 ++++- src/hardware/Sourcefile | 13 ---- src/objects/CMakeLists.txt | 12 +++- src/objects/Sourcefile | 9 --- src/sdl/CMakeLists.txt | 19 +++-- src/sdl/Sourcefile | 7 -- 11 files changed, 197 insertions(+), 203 deletions(-) delete mode 100644 src/Sourcefile delete mode 100644 src/blua/Sourcefile delete mode 100644 src/hardware/Sourcefile delete mode 100644 src/objects/Sourcefile delete mode 100644 src/sdl/Sourcefile diff --git a/CMakeLists.txt b/CMakeLists.txt index c6cd6c3d4..c816ddc06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,13 +125,6 @@ if ((${SRB2_USE_CCACHE}) AND (${CMAKE_C_COMPILER} MATCHES "clang")) message(WARNING "Using clang and CCache: You may want to set environment variable CCACHE_CPP2=yes to prevent include errors during compile.") endif() -# Add sources from Sourcefile -function(target_sourcefile type) - file(STRINGS Sourcefile list - REGEX "[-0-9A-Za-z_]+\.${type}") - target_sources(SRB2SDL2 PRIVATE ${list}) -endfunction() - # bitness check set(SRB2_SYSTEM_BITS 0) if(CMAKE_SIZEOF_VOID_P EQUAL 8) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5b681d042..cc04e2cba 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,135 @@ -add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32) +add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 + comptime.c + md5.c + config.h.in + string.c + d_main.c + d_clisrv.c + d_net.c + d_netfil.c + d_netcmd.c + dehacked.c + deh_soc.c + deh_lua.c + deh_tables.c + z_zone.c + f_finale.c + f_wipe.c + g_demo.c + g_game.c + g_input.c + g_splitscreen.c + am_map.c + command.c + console.c + font.c + hu_stuff.c + i_time.c + y_inter.c + st_stuff.c + m_aatree.c + m_anigif.c + m_argv.c + m_bbox.c + m_cheat.c + m_cond.c + m_easing.c + m_fixed.c + m_misc.c + m_perfstats.c + m_random.c + m_queue.c + info.c + p_ceilng.c + p_enemy.c + p_floor.c + p_inter.c + p_lights.c + p_map.c + p_maputl.c + p_mobj.c + p_polyobj.c + p_saveg.c + p_setup.c + p_sight.c + p_spec.c + p_telept.c + p_tick.c + p_user.c + p_slopes.c + tables.c + r_bsp.c + r_data.c + r_draw.c + r_fps.c + r_main.c + r_plane.c + r_segs.c + r_skins.c + r_sky.c + r_splats.c + r_things.c + r_bbox.c + r_textures.c + r_patch.c + r_patchrotation.c + r_picformats.c + r_portal.c + screen.c + taglist.c + v_video.c + s_sound.c + sounds.c + w_wad.c + filesrch.c + mserv.c + http-mserv.c + i_tcp.c + lzf.c + vid_copy.s + lua_script.c + lua_baselib.c + lua_mathlib.c + lua_hooklib.c + lua_consolelib.c + lua_infolib.c + lua_mobjlib.c + lua_playerlib.c + lua_skinlib.c + lua_thinkerlib.c + lua_maplib.c + lua_taglib.c + lua_polyobjlib.c + lua_blockmaplib.c + lua_hudlib.c + lua_hudlib_drawlist.c + k_kart.c + k_respawn.c + k_collide.c + k_color.c + k_race.c + k_battle.c + k_pwrlv.c + k_waypoint.c + k_pathfind.c + k_bheap.c + k_bot.c + k_botitem.c + k_botsearch.c + k_grandprix.c + k_boss.c + k_hud.c + k_menudef.c + k_menufunc.c + k_menudraw.c + k_terrain.c + k_brightmap.c + k_terrain.c + k_director.c + k_follower.c + k_profiles.c + k_specialstage.c +) if("${CMAKE_COMPILER_IS_GNUCC}" AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows" AND NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}" AND NOT "${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}") # On MinGW with internal libraries, link the standard library statically @@ -7,10 +138,6 @@ endif() set_property(TARGET SRB2SDL2 PROPERTY C_STANDARD 11) -# Core sources -target_sourcefile(c) -target_sources(SRB2SDL2 PRIVATE comptime.c md5.c config.h.in) - set(SRB2_ASM_SOURCES vid_copy.s) set(SRB2_NASM_SOURCES tmap_mmx.nas tmap.nas) diff --git a/src/Sourcefile b/src/Sourcefile deleted file mode 100644 index f2aa652b1..000000000 --- a/src/Sourcefile +++ /dev/null @@ -1,127 +0,0 @@ -string.c -d_main.c -d_clisrv.c -d_net.c -d_netfil.c -d_netcmd.c -dehacked.c -deh_soc.c -deh_lua.c -deh_tables.c -z_zone.c -f_finale.c -f_wipe.c -g_demo.c -g_game.c -g_input.c -g_splitscreen.c -am_map.c -command.c -console.c -font.c -hu_stuff.c -i_time.c -y_inter.c -st_stuff.c -m_aatree.c -m_anigif.c -m_argv.c -m_bbox.c -m_cheat.c -m_cond.c -m_easing.c -m_fixed.c -m_misc.c -m_perfstats.c -m_random.c -m_queue.c -info.c -p_ceilng.c -p_enemy.c -p_floor.c -p_inter.c -p_lights.c -p_map.c -p_maputl.c -p_mobj.c -p_polyobj.c -p_saveg.c -p_setup.c -p_sight.c -p_spec.c -p_telept.c -p_tick.c -p_user.c -p_slopes.c -tables.c -r_bsp.c -r_data.c -r_draw.c -r_fps.c -r_main.c -r_plane.c -r_segs.c -r_skins.c -r_sky.c -r_splats.c -r_things.c -r_bbox.c -r_textures.c -r_patch.c -r_patchrotation.c -r_picformats.c -r_portal.c -screen.c -taglist.c -v_video.c -s_sound.c -sounds.c -w_wad.c -filesrch.c -mserv.c -http-mserv.c -i_tcp.c -lzf.c -vid_copy.s -lua_script.c -lua_baselib.c -lua_mathlib.c -lua_hooklib.c -lua_consolelib.c -lua_infolib.c -lua_mobjlib.c -lua_playerlib.c -lua_skinlib.c -lua_thinkerlib.c -lua_maplib.c -lua_taglib.c -lua_polyobjlib.c -lua_blockmaplib.c -lua_hudlib.c -lua_hudlib_drawlist.c -k_kart.c -k_respawn.c -k_collide.c -k_color.c -k_race.c -k_battle.c -k_pwrlv.c -k_waypoint.c -k_pathfind.c -k_bheap.c -k_bot.c -k_botitem.c -k_botsearch.c -k_grandprix.c -k_boss.c -k_hud.c -k_menudef.c -k_menufunc.c -k_menudraw.c -k_terrain.c -k_brightmap.c -k_terrain.c -k_director.c -k_follower.c -k_profiles.c -k_specialstage.c diff --git a/src/blua/CMakeLists.txt b/src/blua/CMakeLists.txt index 4e9c67d2f..afe03fdc5 100644 --- a/src/blua/CMakeLists.txt +++ b/src/blua/CMakeLists.txt @@ -1 +1,27 @@ -target_sourcefile(c) +target_sources(SRB2SDL2 PRIVATE + lapi.c + lbaselib.c + ldo.c + lfunc.c + linit.c + liolib.c + llex.c + lmem.c + lobject.c + lstate.c + lstrlib.c + ltablib.c + lundump.c + lzio.c + lauxlib.c + lcode.c + ldebug.c + ldump.c + lgc.c + lopcodes.c + lparser.c + lstring.c + ltable.c + ltm.c + lvm.c +) diff --git a/src/blua/Sourcefile b/src/blua/Sourcefile deleted file mode 100644 index f99c89c8d..000000000 --- a/src/blua/Sourcefile +++ /dev/null @@ -1,25 +0,0 @@ -lapi.c -lbaselib.c -ldo.c -lfunc.c -linit.c -liolib.c -llex.c -lmem.c -lobject.c -lstate.c -lstrlib.c -ltablib.c -lundump.c -lzio.c -lauxlib.c -lcode.c -ldebug.c -ldump.c -lgc.c -lopcodes.c -lparser.c -lstring.c -ltable.c -ltm.c -lvm.c diff --git a/src/hardware/CMakeLists.txt b/src/hardware/CMakeLists.txt index 4e9c67d2f..a0a0f280c 100644 --- a/src/hardware/CMakeLists.txt +++ b/src/hardware/CMakeLists.txt @@ -1 +1,15 @@ -target_sourcefile(c) +target_sources(SRB2SDL2 PRIVATE + hw_bsp.c + hw_draw.c + hw_light.c + hw_main.c + hw_clip.c + hw_md2.c + hw_cache.c + hw_md2load.c + hw_md3load.c + hw_model.c + u_list.c + hw_batching.c + r_opengl/r_opengl.c +) diff --git a/src/hardware/Sourcefile b/src/hardware/Sourcefile deleted file mode 100644 index 1c05de76c..000000000 --- a/src/hardware/Sourcefile +++ /dev/null @@ -1,13 +0,0 @@ -hw_bsp.c -hw_draw.c -hw_light.c -hw_main.c -hw_clip.c -hw_md2.c -hw_cache.c -hw_md2load.c -hw_md3load.c -hw_model.c -u_list.c -hw_batching.c -r_opengl/r_opengl.c diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index 4e9c67d2f..f02428382 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -1 +1,11 @@ -target_sourcefile(c) +target_sources(SRB2SDL2 PRIVATE + hyudoro.c + gardentop.c + shrink.c + item-debris.c + spb.c + manta-ring.c + orbinaut.c + jawz.c + duel-bomb.c +) diff --git a/src/objects/Sourcefile b/src/objects/Sourcefile deleted file mode 100644 index b8cb63b1f..000000000 --- a/src/objects/Sourcefile +++ /dev/null @@ -1,9 +0,0 @@ -hyudoro.c -gardentop.c -shrink.c -item-debris.c -spb.c -manta-ring.c -orbinaut.c -jawz.c -duel-bomb.c diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt index dd8d304a4..ee9c4cddc 100644 --- a/src/sdl/CMakeLists.txt +++ b/src/sdl/CMakeLists.txt @@ -1,12 +1,17 @@ # Declare SDL2 interface sources -target_sources(SRB2SDL2 PRIVATE mixer_sound.c) - -target_sourcefile(c) - -target_sources(SRB2SDL2 PRIVATE ogl_sdl.c) - -target_sources(SRB2SDL2 PRIVATE i_threads.c) +target_sources(SRB2SDL2 PRIVATE + mixer_sound.c + ogl_sdl.c + i_threads.c + i_net.c + i_system.c + i_main.c + i_video.c + dosstr.c + endtxt.c + hwsym_sdl.c +) if(${SRB2_USEASM}) set_source_files_properties(${SRB2_ASM_SOURCES} PROPERTIES LANGUAGE C) diff --git a/src/sdl/Sourcefile b/src/sdl/Sourcefile deleted file mode 100644 index 82d5ce073..000000000 --- a/src/sdl/Sourcefile +++ /dev/null @@ -1,7 +0,0 @@ -i_net.c -i_system.c -i_main.c -i_video.c -dosstr.c -endtxt.c -hwsym_sdl.c From a481e4b34b3e237362d7018ff57baf759ece16fc Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 11 Dec 2022 23:58:11 -0500 Subject: [PATCH 02/46] Deterministic roulette The roulette contains NO (non-seeded) RNG anymore. You manually stop it at any time. Still needs the visual of the items scrolling, to make it not blind. --- src/Sourcefile | 1 + src/d_netcmd.c | 61 +-- src/d_netcmd.h | 33 +- src/d_player.h | 23 +- src/g_game.c | 13 +- src/k_botitem.c | 7 +- src/k_collide.c | 6 +- src/k_hud.c | 21 +- src/k_kart.c | 998 +----------------------------------------- src/k_kart.h | 8 - src/k_menudraw.c | 4 +- src/k_menufunc.c | 8 +- src/k_roulette.c | 1001 +++++++++++++++++++++++++++++++++++++++++++ src/k_roulette.h | 35 ++ src/lua_playerlib.c | 8 +- src/p_enemy.c | 7 +- src/p_inter.c | 11 +- src/p_mobj.c | 8 +- src/p_saveg.c | 52 ++- src/typedef.h | 1 + 20 files changed, 1199 insertions(+), 1107 deletions(-) create mode 100644 src/k_roulette.c create mode 100644 src/k_roulette.h diff --git a/src/Sourcefile b/src/Sourcefile index f2aa652b1..4da925828 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -125,3 +125,4 @@ k_director.c k_follower.c k_profiles.c k_specialstage.c +k_roulette.c diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 086c4cbfa..9820eeed3 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -360,35 +360,36 @@ consvar_t cv_joyscale[MAXSPLITSCREENPLAYERS] = { //Alam: Dummy for save #endif // SRB2kart -consvar_t cv_sneaker = CVAR_INIT ("sneaker", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_rocketsneaker = CVAR_INIT ("rocketsneaker", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_invincibility = CVAR_INIT ("invincibility", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_banana = CVAR_INIT ("banana", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_eggmanmonitor = CVAR_INIT ("eggmanmonitor", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_orbinaut = CVAR_INIT ("orbinaut", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_jawz = CVAR_INIT ("jawz", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_mine = CVAR_INIT ("mine", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_landmine = CVAR_INIT ("landmine", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_ballhog = CVAR_INIT ("ballhog", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_selfpropelledbomb = CVAR_INIT ("selfpropelledbomb", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_grow = CVAR_INIT ("grow", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_shrink = CVAR_INIT ("shrink", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_lightningshield = CVAR_INIT ("lightningshield", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_bubbleshield = CVAR_INIT ("bubbleshield", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_flameshield = CVAR_INIT ("flameshield", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_hyudoro = CVAR_INIT ("hyudoro", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_pogospring = CVAR_INIT ("pogospring", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_superring = CVAR_INIT ("superring", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_kitchensink = CVAR_INIT ("kitchensink", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_droptarget = CVAR_INIT ("droptarget", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_gardentop = CVAR_INIT ("gardentop", "On", CV_NETVAR, CV_OnOff, NULL); - -consvar_t cv_dualsneaker = CVAR_INIT ("dualsneaker", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_triplesneaker = CVAR_INIT ("triplesneaker", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_triplebanana = CVAR_INIT ("triplebanana", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_tripleorbinaut = CVAR_INIT ("tripleorbinaut", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_quadorbinaut = CVAR_INIT ("quadorbinaut", "On", CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_dualjawz = CVAR_INIT ("dualjawz", "On", CV_NETVAR, CV_OnOff, NULL); +consvar_t cv_items[NUMKARTRESULTS-1] = { + CVAR_INIT ("sneaker", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("rocketsneaker", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("invincibility", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("banana", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("eggmanmonitor", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("orbinaut", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("jawz", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("mine", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("landmine", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("ballhog", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("selfpropelledbomb", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("grow", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("shrink", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("lightningshield", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("bubbleshield", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("flameshield", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("hyudoro", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("pogospring", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("superring", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("kitchensink", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("droptarget", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("gardentop", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("dualsneaker", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("triplesneaker", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("triplebanana", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("tripleorbinaut", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("quadorbinaut", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("dualjawz", "On", CV_NETVAR, CV_OnOff, NULL) +}; consvar_t cv_kartspeed = CVAR_INIT ("gamespeed", "Auto", CV_NETVAR|CV_CALL|CV_NOINIT, kartspeed_cons_t, KartSpeed_OnChange); static CV_PossibleValue_t kartbumpers_cons_t[] = {{1, "MIN"}, {12, "MAX"}, {0, NULL}}; @@ -5659,7 +5660,7 @@ static void Got_Cheat(UINT8 **cp, INT32 playernum) K_StripItems(player); // Cancel roulette if rolling - player->itemroulette = 0; + player->itemRoulette.active = false; player->itemtype = item; player->itemamount = amt; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 3f4d00645..e0b969ffd 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -16,6 +16,7 @@ #define __D_NETCMD__ #include "command.h" +#include "d_player.h" // console vars extern consvar_t cv_playername[MAXSPLITSCREENPLAYERS]; @@ -72,37 +73,7 @@ extern consvar_t cv_pause; extern consvar_t cv_restrictskinchange, cv_allowteamchange, cv_maxplayers, cv_respawntime; // SRB2kart items -extern consvar_t - cv_sneaker, - cv_rocketsneaker, - cv_invincibility, - cv_banana, - cv_eggmanmonitor, - cv_orbinaut, - cv_jawz, - cv_mine, - cv_landmine, - cv_ballhog, - cv_selfpropelledbomb, - cv_grow, - cv_shrink, - cv_lightningshield, - cv_bubbleshield, - cv_flameshield, - cv_hyudoro, - cv_pogospring, - cv_superring, - cv_kitchensink, - cv_droptarget, - cv_gardentop; - -extern consvar_t - cv_dualsneaker, - cv_triplesneaker, - cv_triplebanana, - cv_tripleorbinaut, - cv_quadorbinaut, - cv_dualjawz; +extern consvar_t cv_items[NUMKARTRESULTS-1]; extern consvar_t cv_kartspeed; extern consvar_t cv_kartbumpers; diff --git a/src/d_player.h b/src/d_player.h index a65f0235b..fdcb5bdc4 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -131,6 +131,7 @@ Do with it whatever you want. Run this macro, then #undef FOREACH afterward */ #define KART_ITEM_ITERATOR \ + FOREACH (EGGEXPLODE, -2),\ FOREACH (SAD, -1),\ FOREACH (NONE, 0),\ FOREACH (SNEAKER, 1),\ @@ -329,6 +330,25 @@ struct skybox_t { mobj_t * centerpoint; }; +// player_t struct for item roulette variables +struct itemroulette_t +{ + boolean active; + + size_t itemListCap; + size_t itemListLen; + SINT8 *itemList; + + size_t index; + UINT8 sound; + + tic_t speed; + tic_t tics; + tic_t elapsed; + + boolean eggman; +}; + // ======================================================================== // PLAYER STRUCTURE // ======================================================================== @@ -477,8 +497,7 @@ struct player_t UINT8 tripwirePass; // see tripwirepass_t UINT16 tripwireLeniency; // When reaching a state that lets you go thru tripwire, you get an extra second leniency after it ends to still go through it. - UINT16 itemroulette; // Used for the roulette when deciding what item to give you (was "pw_kartitem") - UINT8 roulettetype; // Used for the roulette, for deciding type (0 = normal, 1 = better, 2 = eggman mark) + itemroulette_t itemRoulette; // Item roulette data // Item held stuff SINT8 itemtype; // KITEM_ constant for item number diff --git a/src/g_game.c b/src/g_game.c index 5cde2d270..91d309638 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2260,11 +2260,10 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) SINT8 xtralife; // SRB2kart + itemroulette_t itemRoulette; respawnvars_t respawn; INT32 itemtype; INT32 itemamount; - INT32 itemroulette; - INT32 roulettetype; INT32 growshrinktimer; INT32 bumper; boolean songcredit = false; @@ -2325,8 +2324,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) // SRB2kart if (betweenmaps || leveltime < introtime) { - itemroulette = 0; - roulettetype = 0; itemtype = 0; itemamount = 0; growshrinktimer = 0; @@ -2348,9 +2345,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) } else { - itemroulette = (players[player].itemroulette > 0 ? 1 : 0); - roulettetype = players[player].roulettetype; - if (players[player].pflags & PF_ITEMOUT) { itemtype = 0; @@ -2406,6 +2400,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) P_SetTarget(&players[player].follower, NULL); } + memcpy(&itemRoulette, &players[player].itemRoulette, sizeof (itemRoulette)); memcpy(&respawn, &players[player].respawn, sizeof (respawn)); p = &players[player]; @@ -2453,8 +2448,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->xtralife = xtralife; // SRB2kart - p->itemroulette = itemroulette; - p->roulettetype = roulettetype; p->itemtype = itemtype; p->itemamount = itemamount; p->growshrinktimer = growshrinktimer; @@ -2470,6 +2463,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->botvars.rubberband = FRACUNIT; p->botvars.controller = UINT16_MAX; + memcpy(&p->itemRoulette, &itemRoulette, sizeof (p->itemRoulette)); memcpy(&p->respawn, &respawn, sizeof (p->respawn)); if (follower) @@ -2481,7 +2475,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) //p->follower = NULL; // respawn a new one with you, it looks better. // ^ Not necessary anyway since it will be respawned regardless considering it doesn't exist anymore. - p->playerstate = PST_LIVE; p->panim = PA_STILL; // standing animation diff --git a/src/k_botitem.c b/src/k_botitem.c index 6c8b9ece4..e3054aa69 100644 --- a/src/k_botitem.c +++ b/src/k_botitem.c @@ -26,6 +26,7 @@ #include "d_ticcmd.h" #include "m_random.h" #include "r_things.h" // numskins +#include "k_roulette.h" /*-------------------------------------------------- static inline boolean K_ItemButtonWasDown(player_t *player) @@ -739,7 +740,7 @@ static void K_BotItemEggman(player_t *player, ticcmd_t *cmd) tryLookback = true; } - if (stealth > 1 || player->itemroulette > 0) + if (stealth > 1 || player->itemRoulette.active == true) { player->botvars.itemconfirm += player->botvars.difficulty * 4; throwdir = -1; @@ -1400,7 +1401,7 @@ static void K_BotItemRouletteMash(player_t *player, ticcmd_t *cmd) return; } - if (player->rings < 0 && cv_superring.value) + if (player->rings < 0 && K_ItemEnabled(KITEM_SUPERRING) == true) { // Uh oh, we need a loan! // It'll be better in the long run for bots to lose an item set for 10 free rings. @@ -1441,7 +1442,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) return; } - if (player->itemroulette) + if (player->itemRoulette.active == true) { // Mashing behaviors K_BotItemRouletteMash(player, cmd); diff --git a/src/k_collide.c b/src/k_collide.c index dac44b5f7..d9ab58e1c 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -12,6 +12,7 @@ #include "doomdef.h" // Sink snipe print #include "g_game.h" // Sink snipe print #include "k_objects.h" +#include "k_roulette.h" angle_t K_GetCollideAngle(mobj_t *t1, mobj_t *t2) { @@ -158,10 +159,7 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) } else { - K_DropItems(t2->player); //K_StripItems(t2->player); - //K_StripOther(t2->player); - t2->player->itemroulette = 1; - t2->player->roulettetype = 2; + K_StartEggmanRoulette(t2->player); } if (t2->player->flamedash && t2->player->itemtype == KITEM_FLAMESHIELD) diff --git a/src/k_hud.c b/src/k_hud.c index 5325f07eb..25a869c13 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -36,6 +36,7 @@ #include "r_things.h" #include "r_fps.h" #include "m_random.h" +#include "k_roulette.h" //{ Patch Definitions static patch_t *kp_nodraw; @@ -1146,12 +1147,14 @@ static void K_drawKartItem(void) UINT8 *colmap = NULL; boolean flipamount = false; // Used for 3P/4P splitscreen to flip item amount stuff - if (stplyr->itemroulette) + if (stplyr->itemRoulette.active == true) { - const INT32 item = K_GetRollingRouletteItem(stplyr); + const SINT8 item = K_ItemResultToType(stplyr->itemRoulette.itemList[ stplyr->itemRoulette.index ]); if (stplyr->skincolor) + { localcolor = stplyr->skincolor; + } switch (item) { @@ -1165,6 +1168,7 @@ static void K_drawKartItem(void) default: localpatch = K_GetCachedItemPatch(item, offset); + break; } } else @@ -1223,7 +1227,7 @@ static void K_drawKartItem(void) if (stplyr->itemamount <= 0) return; - switch(stplyr->itemtype) + switch (stplyr->itemtype) { case KITEM_INVINCIBILITY: localpatch = localinv; @@ -1302,7 +1306,7 @@ static void K_drawKartItem(void) V_DrawScaledPatch(fx, fy, V_HUDTRANS|V_SLIDEIN|fflags, localbg); // Then, the numbers: - if (stplyr->itemamount >= numberdisplaymin && !stplyr->itemroulette) + if (stplyr->itemamount >= numberdisplaymin && stplyr->itemRoulette.active == false) { V_DrawScaledPatch(fx + (flipamount ? 48 : 0), fy, V_HUDTRANS|V_SLIDEIN|fflags|(flipamount ? V_FLIP : 0), kp_itemmulsticker[offset]); // flip this graphic for p2 and p4 in split and shift it. V_DrawFixedPatch(fx<botvars.itemdelay = TICRATE; - player->botvars.itemconfirm = 0; - - player->itemtype = K_ItemResultToType(getitem); - player->itemamount = K_ItemResultToAmount(getitem); -} - -fixed_t K_ItemOddsScale(UINT8 playerCount) -{ - const UINT8 basePlayer = 8; // The player count we design most of the game around. - fixed_t playerScaling = 0; - - if (playerCount < 2) - { - // Cap to 1v1 scaling - playerCount = 2; - } - - // Then, it multiplies it further if the player count isn't equal to basePlayer. - // This is done to make low player count races more interesting and high player count rates more fair. - if (playerCount < basePlayer) - { - // Less than basePlayer: increase odds significantly. - // 2P: x2.5 - playerScaling = (basePlayer - playerCount) * (FRACUNIT / 4); - } - else if (playerCount > basePlayer) - { - // More than basePlayer: reduce odds slightly. - // 16P: x0.75 - playerScaling = (basePlayer - playerCount) * (FRACUNIT / 32); - } - - return playerScaling; -} - -UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers) -{ - if (mapobjectscale != FRACUNIT) - { - // Bring back to normal scale. - distance = FixedDiv(distance, mapobjectscale); - } - - if (franticitems == true) - { - // Frantic items pretends everyone's farther apart, for crazier items. - distance = (15 * distance) / 14; - } - - // Items get crazier with the fewer players that you have. - distance = FixedMul( - distance, - FRACUNIT + (K_ItemOddsScale(numPlayers) / 2) - ); - - return distance; -} - -/** \brief Item Roulette for Kart - - \param player player object passed from P_KartPlayerThink - - \return void -*/ - -INT32 K_KartGetItemOdds( - UINT8 pos, SINT8 item, - UINT32 ourDist, - fixed_t mashed, - boolean bot, boolean rival) -{ - INT32 newodds; - INT32 i; - - UINT8 pingame = 0, pexiting = 0; - - player_t *first = NULL; - player_t *second = NULL; - - UINT32 firstDist = UINT32_MAX; - UINT32 secondDist = UINT32_MAX; - UINT32 secondToFirst = 0; - boolean isFirst = false; - - boolean powerItem = false; - boolean cooldownOnStart = false; - boolean notNearEnd = false; - - INT32 shieldtype = KSHIELD_NONE; - - I_Assert(item > KITEM_NONE); // too many off by one scenarioes. - I_Assert(KartItemCVars[NUMKARTRESULTS-2] != NULL); // Make sure this exists - - if (!KartItemCVars[item-1]->value && !modeattacking) - { - return 0; - } - - if (K_GetItemCooldown(item) > 0) - { - // Cooldown is still running, don't give another. - return 0; - } - - /* - if (bot) - { - // TODO: Item use on bots should all be passed-in functions. - // Instead of manually inserting these, it should return 0 - // for any items without an item use function supplied - - switch (item) - { - case KITEM_SNEAKER: - break; - default: - return 0; - } - } - */ - (void)bot; - - if (gametype == GT_BATTLE) - { - I_Assert(pos < 2); // DO NOT allow positions past the bounds of the table - newodds = K_KartItemOddsBattle[item-1][pos]; - } - else - { - I_Assert(pos < 8); // Ditto - newodds = K_KartItemOddsRace[item-1][pos]; - } - - // Base multiplication to ALL item odds to simulate fractional precision - newodds *= 4; - - shieldtype = K_GetShieldFromItem(item); - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - - if (!(gametyperules & GTR_BUMPERS) || players[i].bumpers) - pingame++; - - if (players[i].exiting) - pexiting++; - - switch (shieldtype) - { - case KSHIELD_NONE: - /* Marble Garden Top is not REALLY - a Sonic 3 shield */ - case KSHIELD_TOP: - break; - - default: - if (shieldtype == K_GetShieldFromItem(players[i].itemtype)) - { - // Don't allow more than one of each shield type at a time - return 0; - } - } - - if (players[i].position == 1) - { - first = &players[i]; - } - - if (players[i].position == 2) - { - second = &players[i]; - } - } - - if (first != NULL) // calculate 2nd's distance from 1st, for SPB - { - firstDist = first->distancetofinish; - isFirst = (ourDist <= firstDist); - } - - if (second != NULL) - { - secondDist = second->distancetofinish; - } - - if (first != NULL && second != NULL) - { - secondToFirst = secondDist - firstDist; - secondToFirst = K_ScaleItemDistance(secondToFirst, 16 - pingame); // Reversed scaling, so 16P is like 1v1, and 1v1 is like 16P - } - - switch (item) - { - case KITEM_BANANA: - case KITEM_EGGMAN: - case KITEM_SUPERRING: - notNearEnd = true; - break; - - case KITEM_ROCKETSNEAKER: - case KITEM_JAWZ: - case KITEM_LANDMINE: - case KITEM_DROPTARGET: - case KITEM_BALLHOG: - case KITEM_HYUDORO: - case KRITEM_TRIPLESNEAKER: - case KRITEM_TRIPLEORBINAUT: - case KRITEM_QUADORBINAUT: - case KRITEM_DUALJAWZ: - powerItem = true; - break; - - case KRITEM_TRIPLEBANANA: - powerItem = true; - notNearEnd = true; - break; - - case KITEM_INVINCIBILITY: - case KITEM_MINE: - case KITEM_GROW: - case KITEM_BUBBLESHIELD: - case KITEM_FLAMESHIELD: - cooldownOnStart = true; - powerItem = true; - break; - - case KITEM_SPB: - cooldownOnStart = true; - notNearEnd = true; - - if (firstDist < ENDDIST*2 // No SPB when 1st is almost done - || isFirst == true) // No SPB for 1st ever - { - newodds = 0; - } - else - { - const UINT32 dist = max(0, ((signed)secondToFirst) - SPBSTARTDIST); - const UINT32 distRange = SPBFORCEDIST - SPBSTARTDIST; - const UINT8 maxOdds = 20; - fixed_t multiplier = (dist * FRACUNIT) / distRange; - - if (multiplier < 0) - { - multiplier = 0; - } - - if (multiplier > FRACUNIT) - { - multiplier = FRACUNIT; - } - - newodds = FixedMul(maxOdds * 4, multiplier); - } - break; - - case KITEM_SHRINK: - cooldownOnStart = true; - powerItem = true; - notNearEnd = true; - - if (pingame-1 <= pexiting) - newodds = 0; - break; - - case KITEM_LIGHTNINGSHIELD: - cooldownOnStart = true; - powerItem = true; - - if (spbplace != -1) - newodds = 0; - break; - - default: - break; - } - - if (newodds == 0) - { - // Nothing else we want to do with odds matters at this point :p - return newodds; - } - - - if ((cooldownOnStart == true) && (leveltime < (30*TICRATE)+starttime)) - { - // This item should not appear at the beginning of a race. (Usually really powerful crowd-breaking items) - newodds = 0; - } - else if ((notNearEnd == true) && (ourDist < ENDDIST)) - { - // This item should not appear at the end of a race. (Usually trap items that lose their effectiveness) - newodds = 0; - } - else if (powerItem == true) - { - // This item is a "power item". This activates "frantic item" toggle related functionality. - fixed_t fracOdds = newodds * FRACUNIT; - - if (franticitems == true) - { - // First, power items multiply their odds by 2 if frantic items are on; easy-peasy. - fracOdds *= 2; - } - - if (rival == true) - { - // The Rival bot gets frantic-like items, also :p - fracOdds *= 2; - } - - fracOdds = FixedMul(fracOdds, FRACUNIT + K_ItemOddsScale(pingame)); - - if (mashed > 0) - { - // Lastly, it *divides* it based on your mashed value, so that power items are less likely when you mash. - fracOdds = FixedDiv(fracOdds, FRACUNIT + mashed); - } - - newodds = fracOdds / FRACUNIT; - } - - return newodds; -} - -//{ SRB2kart Roulette Code - Distance Based, yes waypoints - -UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper) -{ - UINT8 i; - UINT8 useodds = 0; - UINT8 disttable[14]; - UINT8 distlen = 0; - boolean oddsvalid[8]; - - // Unused now, oops :V - (void)bestbumper; - - for (i = 0; i < 8; i++) - { - UINT8 j; - boolean available = false; - - if (gametype == GT_BATTLE && i > 1) - { - oddsvalid[i] = false; - break; - } - - for (j = 1; j < NUMKARTRESULTS; j++) - { - if (K_KartGetItemOdds( - i, j, - player->distancetofinish, - mashed, - player->bot, (player->bot && player->botvars.rival) - ) > 0) - { - available = true; - break; - } - } - - oddsvalid[i] = available; - } - -#define SETUPDISTTABLE(odds, num) \ - if (oddsvalid[odds]) \ - for (i = num; i; --i) \ - disttable[distlen++] = odds; - - if (gametype == GT_BATTLE) // Battle Mode - { - if (player->roulettetype == 1 && oddsvalid[1] == true) - { - // 1 is the extreme odds of player-controlled "Karma" items - useodds = 1; - } - else - { - useodds = 0; - - if (oddsvalid[0] == false && oddsvalid[1] == true) - { - // try to use karma odds as a fallback - useodds = 1; - } - } - } - else - { - SETUPDISTTABLE(0,1); - SETUPDISTTABLE(1,1); - SETUPDISTTABLE(2,1); - SETUPDISTTABLE(3,2); - SETUPDISTTABLE(4,2); - SETUPDISTTABLE(5,3); - SETUPDISTTABLE(6,3); - SETUPDISTTABLE(7,1); - - if (pdis == 0) - useodds = disttable[0]; - else if (pdis > DISTVAR * ((12 * distlen) / 14)) - useodds = disttable[distlen-1]; - else - { - for (i = 1; i < 13; i++) - { - if (pdis <= DISTVAR * ((i * distlen) / 14)) - { - useodds = disttable[((i * distlen) / 14)]; - break; - } - } - } - } - -#undef SETUPDISTTABLE - - return useodds; -} - -INT32 K_GetRollingRouletteItem(player_t *player) -{ - static UINT8 translation[NUMKARTITEMS-1]; - static UINT16 roulette_size; - - static INT16 odds_cached = -1; - - // Race odds have more columns than Battle - const UINT8 EMPTYODDS[sizeof K_KartItemOddsRace[0]] = {0}; - - if (odds_cached != gametype) - { - UINT8 *odds_row; - size_t odds_row_size; - - UINT8 i; - - roulette_size = 0; - - if (gametype == GT_BATTLE) - { - odds_row = K_KartItemOddsBattle[0]; - odds_row_size = sizeof K_KartItemOddsBattle[0]; - } - else - { - odds_row = K_KartItemOddsRace[0]; - odds_row_size = sizeof K_KartItemOddsRace[0]; - } - - for (i = 1; i < NUMKARTITEMS; ++i) - { - if (memcmp(odds_row, EMPTYODDS, odds_row_size)) - { - translation[roulette_size] = i; - roulette_size++; - } - - odds_row += odds_row_size; - } - - roulette_size *= 3; - odds_cached = gametype; - } - - return translation[(player->itemroulette % roulette_size) / 3]; -} - -boolean K_ForcedSPB(player_t *player) -{ - player_t *first = NULL; - player_t *second = NULL; - UINT32 secondToFirst = UINT32_MAX; - UINT8 pingame = 0; - UINT8 i; - - if (!cv_selfpropelledbomb.value) - { - return false; - } - - if (!(gametyperules & GTR_CIRCUIT)) - { - return false; - } - - if (player->position <= 1) - { - return false; - } - - if (spbplace != -1) - { - return false; - } - - if (itemCooldowns[KITEM_SPB - 1] > 0) - { - return false; - } - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - { - continue; - } - - if (players[i].exiting) - { - return false; - } - - pingame++; - - if (players[i].position == 1) - { - first = &players[i]; - } - - if (players[i].position == 2) - { - second = &players[i]; - } - } - -#if 0 - if (pingame <= 2) - { - return false; - } -#endif - - if (first != NULL && second != NULL) - { - secondToFirst = second->distancetofinish - first->distancetofinish; - secondToFirst = K_ScaleItemDistance(secondToFirst, 16 - pingame); - } - - return (secondToFirst >= SPBFORCEDIST); -} - -static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) -{ - INT32 i; - UINT8 pingame = 0; - UINT8 roulettestop; - UINT32 pdis = 0; - UINT8 useodds = 0; - INT32 spawnchance[NUMKARTRESULTS]; - INT32 totalspawnchance = 0; - UINT8 bestbumper = 0; - fixed_t mashed = 0; - - // This makes the roulette cycle through items - if this is 0, you shouldn't be here. - if (!player->itemroulette) - return; - player->itemroulette++; - - // Gotta check how many players are active at this moment. - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - - pingame++; - - if (players[i].bumpers > bestbumper) - bestbumper = players[i].bumpers; - } - - // This makes the roulette produce the random noises. - if ((player->itemroulette % 3) == 1 && P_IsDisplayPlayer(player) && !demo.freecam) - { -#define PLAYROULETTESND S_StartSound(NULL, sfx_itrol1 + ((player->itemroulette / 3) % 8)) - for (i = 0; i <= r_splitscreen; i++) - { - if (player == &players[displayplayers[i]] && players[displayplayers[i]].itemroulette) - PLAYROULETTESND; - } -#undef PLAYROULETTESND - } - - roulettestop = TICRATE + (3*(pingame - player->position)); - - // If the roulette finishes or the player presses BT_ATTACK, stop the roulette and calculate the item. - // I'm returning via the exact opposite, however, to forgo having another bracket embed. Same result either way, I think. - // Finally, if you get past this check, now you can actually start calculating what item you get. - if ((cmd->buttons & BT_ATTACK) && (player->itemroulette >= roulettestop) - && !(player->pflags & (PF_ITEMOUT|PF_EGGMANOUT|PF_USERINGS))) - { - // Mashing reduces your chances for the good items - mashed = FixedDiv((player->itemroulette)*FRACUNIT, ((TICRATE*3)+roulettestop)*FRACUNIT) - FRACUNIT; - } - else if (!(player->itemroulette >= (TICRATE*3))) - return; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator - && players[i].position == 1) - { - // This player is first! Yay! - - if (player->distancetofinish <= players[i].distancetofinish) - { - // Guess you're in first / tied for first? - pdis = 0; - } - else - { - // Subtract 1st's distance from your distance, to get your distance from 1st! - pdis = player->distancetofinish - players[i].distancetofinish; - } - break; - } - } - - pdis = K_ScaleItemDistance(pdis, pingame); - - if (player->bot && player->botvars.rival) - { - // Rival has better odds :) - pdis = (15 * pdis) / 14; - } - - // SPECIAL CASE No. 1: - // Fake Eggman items - if (player->roulettetype == 2) - { - player->eggmanexplode = 4*TICRATE; - //player->karthud[khud_itemblink] = TICRATE; - //player->karthud[khud_itemblinkmode] = 1; - player->itemroulette = 0; - player->roulettetype = 0; - if (P_IsDisplayPlayer(player) && !demo.freecam) - S_StartSound(NULL, sfx_itrole); - return; - } - - // SPECIAL CASE No. 2: - // Give a debug item instead if specified - if (cv_kartdebugitem.value != 0 && !modeattacking) - { - K_KartGetItemResult(player, cv_kartdebugitem.value); - player->itemamount = cv_kartdebugamount.value; - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = 2; - player->itemroulette = 0; - player->roulettetype = 0; - if (P_IsDisplayPlayer(player) && !demo.freecam) - S_StartSound(NULL, sfx_dbgsal); - return; - } - - // SPECIAL CASE No. 3: - // Record Attack / alone mashing behavior - if (modeattacking || pingame == 1) - { - if (gametype == GT_RACE) - { - if (mashed && (modeattacking || cv_superring.value)) // ANY mashed value? You get rings. - { - K_KartGetItemResult(player, KITEM_SUPERRING); - player->karthud[khud_itemblinkmode] = 1; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolm); - } - else - { - if (modeattacking || cv_sneaker.value) // Waited patiently? You get a sneaker! - K_KartGetItemResult(player, KITEM_SNEAKER); - else // Default to sad if nothing's enabled... - K_KartGetItemResult(player, KITEM_SAD); - player->karthud[khud_itemblinkmode] = 0; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolf); - } - } - else if (gametype == GT_BATTLE) - { - if (mashed && (modeattacking || bossinfo.boss || cv_banana.value)) // ANY mashed value? You get a banana. - { - K_KartGetItemResult(player, KITEM_BANANA); - player->karthud[khud_itemblinkmode] = 1; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolm); - } - else if (bossinfo.boss) - { - K_KartGetItemResult(player, KITEM_ORBINAUT); - player->karthud[khud_itemblinkmode] = 0; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolf); - } - else - { - if (modeattacking || cv_tripleorbinaut.value) // Waited patiently? You get Orbinaut x3! - K_KartGetItemResult(player, KRITEM_TRIPLEORBINAUT); - else // Default to sad if nothing's enabled... - K_KartGetItemResult(player, KITEM_SAD); - player->karthud[khud_itemblinkmode] = 0; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolf); - } - } - - player->karthud[khud_itemblink] = TICRATE; - player->itemroulette = 0; - player->roulettetype = 0; - return; - } - - // SPECIAL CASE No. 4: - // Being in ring debt occasionally forces Super Ring on you if you mashed - if (!(gametyperules & GTR_SPHERES) && mashed && player->rings < 0 && cv_superring.value) - { - INT32 debtamount = min(20, abs(player->rings)); - if (P_RandomChance(PR_ITEM_ROULETTE, (debtamount*FRACUNIT)/20)) - { - K_KartGetItemResult(player, KITEM_SUPERRING); - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = 1; - player->itemroulette = 0; - player->roulettetype = 0; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolm); - return; - } - } - - // SPECIAL CASE No. 5: - // Force SPB if 2nd is way too far behind - if (K_ForcedSPB(player) == true) - { - K_KartGetItemResult(player, KITEM_SPB); - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = 2; - player->itemroulette = 0; - player->roulettetype = 0; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolk); - return; - } - - // NOW that we're done with all of those specialized cases, we can move onto the REAL item roulette tables. - // Initializes existing spawnchance values - for (i = 0; i < NUMKARTRESULTS; i++) - spawnchance[i] = 0; - - // Split into another function for a debug function below - useodds = K_FindUseodds(player, mashed, pdis, bestbumper); - - for (i = 1; i < NUMKARTRESULTS; i++) - { - spawnchance[i] = (totalspawnchance += K_KartGetItemOdds( - useodds, i, - player->distancetofinish, - mashed, - player->bot, (player->bot && player->botvars.rival)) - ); - } - - // Award the player whatever power is rolled - if (totalspawnchance > 0) - { - totalspawnchance = P_RandomKey(PR_ITEM_ROULETTE, totalspawnchance); - for (i = 0; i < NUMKARTRESULTS && spawnchance[i] <= totalspawnchance; i++); - - K_KartGetItemResult(player, i); - } - else - { - player->itemtype = KITEM_SAD; - player->itemamount = 1; - } - - if (P_IsDisplayPlayer(player) && !demo.freecam) - S_StartSound(NULL, ((player->roulettetype == 1) ? sfx_itrolk : (mashed ? sfx_itrolm : sfx_itrolf))); - - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = ((player->roulettetype == 1) ? 2 : (mashed ? 1 : 0)); - - player->itemroulette = 0; // Since we're done, clear the roulette number - player->roulettetype = 0; // This too -} - //} //{ SRB2kart p_user.c Stuff @@ -7040,7 +6067,6 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 spawnchance[i] = (totalspawnchance += K_KartGetItemOdds( useodds, i, UINT32_MAX, - 0, false, false) ); } @@ -10046,10 +9072,9 @@ void K_StripItems(player_t *player) player->itemamount = 0; player->pflags &= ~(PF_ITEMOUT|PF_EGGMANOUT); - if (!player->itemroulette || player->roulettetype != 2) + if (player->itemRoulette.eggman == false) { - player->itemroulette = 0; - player->roulettetype = 0; + player->itemRoulette.active = false; } player->hyudorotimer = 0; @@ -10065,8 +9090,7 @@ void K_StripItems(player_t *player) void K_StripOther(player_t *player) { - player->itemroulette = 0; - player->roulettetype = 0; + player->itemRoulette.active = false; player->invincibilitytimer = 0; if (player->growshrinktimer) @@ -10086,7 +9110,7 @@ void K_StripOther(player_t *player) static INT32 K_FlameShieldMax(player_t *player) { UINT32 disttofinish = 0; - UINT32 distv = DISTVAR; + UINT32 distv = 2048; UINT8 numplayers = 0; UINT8 i; @@ -10758,7 +9782,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (player->itemtype == KITEM_NONE && NO_HYUDORO && !(HOLDING_ITEM || player->itemamount - || player->itemroulette + || player->itemRoulette.active == true || player->rocketsneakertimer || player->eggmanexplode)) player->pflags |= PF_USERINGS; diff --git a/src/k_kart.h b/src/k_kart.h index cddf3d503..83d54b898 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -50,14 +50,6 @@ void K_ReduceVFX(mobj_t *mo, player_t *owner); boolean K_IsPlayerLosing(player_t *player); fixed_t K_GetKartGameSpeedScalar(SINT8 value); -extern consvar_t *KartItemCVars[NUMKARTRESULTS-1]; - -UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper); -fixed_t K_ItemOddsScale(UINT8 numPlayers); -UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers); -INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, fixed_t mashed, boolean bot, boolean rival); -INT32 K_GetRollingRouletteItem(player_t *player); -boolean K_ForcedSPB(player_t *player); INT32 K_GetShieldFromItem(INT32 item); SINT8 K_ItemResultToType(SINT8 getitem); UINT8 K_ItemResultToAmount(SINT8 getitem); diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 8bfec4716..71f8b4242 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -3455,7 +3455,7 @@ void M_DrawItemToggles(void) continue; } - cv = KartItemCVars[currentMenu->menuitems[thisitem].mvar1-1]; + cv = &cv_items[currentMenu->menuitems[thisitem].mvar1-1]; translucent = (cv->value ? 0 : V_TRANSLUCENT); drawnum = K_ItemResultToAmount(currentMenu->menuitems[thisitem].mvar1); @@ -3502,7 +3502,7 @@ void M_DrawItemToggles(void) } else { - cv = KartItemCVars[currentMenu->menuitems[itemOn].mvar1-1]; + cv = &cv_items[currentMenu->menuitems[itemOn].mvar1-1]; translucent = (cv->value ? 0 : V_TRANSLUCENT); drawnum = K_ItemResultToAmount(currentMenu->menuitems[itemOn].mvar1); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index ab43217ff..384905f71 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -5632,12 +5632,12 @@ void M_HandleItemToggles(INT32 choice) else if (currentMenu->menuitems[itemOn].mvar1 == 0) { - INT32 v = cv_sneaker.value; + INT32 v = cv_items[0].value; S_StartSound(NULL, sfx_s1b4); for (i = 0; i < NUMKARTRESULTS-1; i++) { - if (KartItemCVars[i]->value == v) - CV_AddValue(KartItemCVars[i], 1); + if (cv_items[i].value == v) + CV_AddValue(&cv_items[i], 1); } } else @@ -5650,7 +5650,7 @@ void M_HandleItemToggles(INT32 choice) { S_StartSound(NULL, sfx_s1ba); } - CV_AddValue(KartItemCVars[currentMenu->menuitems[itemOn].mvar1-1], 1); + CV_AddValue(&cv_items[currentMenu->menuitems[itemOn].mvar1-1], 1); } } diff --git a/src/k_roulette.c b/src/k_roulette.c new file mode 100644 index 000000000..df16eba5f --- /dev/null +++ b/src/k_roulette.c @@ -0,0 +1,1001 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2022 by Kart Krew +// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour +// +// 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_roulette.c +/// \brief Item roulette code. + +#include "k_roulette.h" + +#include "d_player.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 "r_things.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_kart.h" +#include "k_battle.h" +#include "k_boss.h" +#include "k_pwrlv.h" +#include "k_color.h" +#include "k_respawn.h" +#include "k_waypoint.h" +#include "k_bot.h" +#include "k_hud.h" +#include "k_terrain.h" +#include "k_director.h" +#include "k_collide.h" +#include "k_follower.h" +#include "k_objects.h" +#include "k_grandprix.h" +#include "k_specialstage.h" + +// Magic number distance for use with item roulette tiers +#define DISTVAR (2048) + +// Distance when SPB can start appearing +#define SPBSTARTDIST (8*DISTVAR) + +// Distance when SPB is forced onto the next person who rolls an item +#define SPBFORCEDIST (14*DISTVAR) + +// Distance when the game stops giving you bananas +#define ENDDIST (12*DISTVAR) + +// Consistent seed used for item reels +#define ITEM_REEL_SEED (0x22D5FAA8) + +static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = +{ + { 0, 0, 2, 3, 4, 0, 0, 0 }, // Sneaker + { 0, 0, 0, 0, 0, 3, 4, 5 }, // Rocket Sneaker + { 0, 0, 0, 0, 2, 5, 5, 7 }, // Invincibility + { 2, 3, 1, 0, 0, 0, 0, 0 }, // Banana + { 1, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor + { 5, 5, 2, 2, 0, 0, 0, 0 }, // Orbinaut + { 0, 4, 2, 1, 0, 0, 0, 0 }, // Jawz + { 0, 3, 3, 2, 0, 0, 0, 0 }, // Mine + { 3, 0, 0, 0, 0, 0, 0, 0 }, // Land Mine + { 0, 0, 2, 2, 0, 0, 0, 0 }, // Ballhog + { 0, 0, 0, 0, 0, 2, 4, 0 }, // Self-Propelled Bomb + { 0, 0, 0, 0, 2, 5, 0, 0 }, // Grow + { 0, 0, 0, 0, 0, 2, 4, 2 }, // Shrink + { 1, 0, 0, 0, 0, 0, 0, 0 }, // Lightning Shield + { 0, 1, 2, 1, 0, 0, 0, 0 }, // Bubble Shield + { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield + { 3, 0, 0, 0, 0, 0, 0, 0 }, // Hyudoro + { 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring + { 2, 1, 1, 0, 0, 0, 0, 0 }, // Super Ring + { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink + { 3, 0, 0, 0, 0, 0, 0, 0 }, // Drop Target + { 0, 0, 0, 3, 5, 0, 0, 0 }, // Garden Top + { 0, 0, 2, 2, 2, 0, 0, 0 }, // Sneaker x2 + { 0, 0, 0, 0, 4, 4, 4, 0 }, // Sneaker x3 + { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3 + { 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3 + { 0, 0, 0, 2, 0, 0, 0, 0 }, // Orbinaut x4 + { 0, 0, 1, 2, 1, 0, 0, 0 } // Jawz x2 +}; + +static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS][2] = +{ + //K L + { 2, 1 }, // Sneaker + { 0, 0 }, // Rocket Sneaker + { 4, 1 }, // Invincibility + { 0, 0 }, // Banana + { 1, 0 }, // Eggman Monitor + { 8, 0 }, // Orbinaut + { 8, 1 }, // Jawz + { 6, 1 }, // Mine + { 2, 0 }, // Land Mine + { 2, 1 }, // Ballhog + { 0, 0 }, // Self-Propelled Bomb + { 2, 1 }, // Grow + { 0, 0 }, // Shrink + { 4, 0 }, // Lightning Shield + { 1, 0 }, // Bubble Shield + { 1, 0 }, // Flame Shield + { 2, 0 }, // Hyudoro + { 3, 0 }, // Pogo Spring + { 0, 0 }, // Super Ring + { 0, 0 }, // Kitchen Sink + { 2, 0 }, // Drop Target + { 4, 0 }, // Garden Top + { 0, 0 }, // Sneaker x2 + { 0, 1 }, // Sneaker x3 + { 0, 0 }, // Banana x3 + { 2, 0 }, // Orbinaut x3 + { 1, 1 }, // Orbinaut x4 + { 5, 1 } // Jawz x2 +}; + +static kartitems_t K_KartItemReelTimeAttack[] = +{ + KITEM_SNEAKER, + KITEM_SUPERRING, + KITEM_NONE +}; + +static kartitems_t K_KartItemReelBreakTheCapsules[] = +{ + KRITEM_TRIPLEORBINAUT, + KITEM_BANANA, + KITEM_NONE +}; + +#if 0 +static kartitems_t K_KartItemReelBoss[] = +{ + KITEM_ORBINAUT, + KITEM_BANANA, + KITEM_NONE +}; +#endif + +boolean K_ItemEnabled(SINT8 item) +{ + if (item < 1 || item >= NUMKARTRESULTS) + { + // Not a real item. + return false; + } + + if (K_CanChangeRules(true) == false) + { + // Force all items to be enabled. + return true; + } + + // Allow the user preference. + return cv_items[item - 1].value; +} + +fixed_t K_ItemOddsScale(UINT8 playerCount) +{ + const UINT8 basePlayer = 8; // The player count we design most of the game around. + fixed_t playerScaling = 0; + + if (playerCount < 2) + { + // Cap to 1v1 scaling + playerCount = 2; + } + + // Then, it multiplies it further if the player count isn't equal to basePlayer. + // This is done to make low player count races more interesting and high player count rates more fair. + if (playerCount < basePlayer) + { + // Less than basePlayer: increase odds significantly. + // 2P: x2.5 + playerScaling = (basePlayer - playerCount) * (FRACUNIT / 4); + } + else if (playerCount > basePlayer) + { + // More than basePlayer: reduce odds slightly. + // 16P: x0.75 + playerScaling = (basePlayer - playerCount) * (FRACUNIT / 32); + } + + return playerScaling; +} + +UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers) +{ + if (mapobjectscale != FRACUNIT) + { + // Bring back to normal scale. + distance = FixedDiv(distance, mapobjectscale); + } + + if (franticitems == true) + { + // Frantic items pretends everyone's farther apart, for crazier items. + distance = (15 * distance) / 14; + } + + // Items get crazier with the fewer players that you have. + distance = FixedMul( + distance, + FRACUNIT + (K_ItemOddsScale(numPlayers) / 2) + ); + + return distance; +} + +UINT32 K_GetItemRouletteDistance(player_t *const player, UINT8 pingame) +{ + UINT32 pdis = 0; + +#if 0 + if (specialStage.active == true) + { + UINT32 ufoDis = K_GetSpecialUFODistance(); + + if (player->distancetofinish <= ufoDis) + { + // You're ahead of the UFO. + pdis = 0; + } + else + { + // Subtract the UFO's distance from your distance! + pdis = player->distancetofinish - ufoDis; + } + } + else +#endif + { + UINT8 i; + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator + && players[i].position == 1) + { + // This player is first! Yay! + + if (player->distancetofinish <= players[i].distancetofinish) + { + // Guess you're in first / tied for first? + pdis = 0; + } + else + { + // Subtract 1st's distance from your distance, to get your distance from 1st! + pdis = player->distancetofinish - players[i].distancetofinish; + } + break; + } + } + } + + pdis = K_ScaleItemDistance(pdis, pingame); + + if (player->bot && player->botvars.rival) + { + // Rival has better odds :) + pdis = (15 * pdis) / 14; + } + + return pdis; +} + +/** \brief Item Roulette for Kart + + \param player player object passed from P_KartPlayerThink + + \return void +*/ + +INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, boolean bot, boolean rival) +{ + fixed_t newOdds; + INT32 i; + + UINT8 pingame = 0, pexiting = 0; + + player_t *first = NULL; + player_t *second = NULL; + + UINT32 firstDist = UINT32_MAX; + UINT32 secondDist = UINT32_MAX; + UINT32 secondToFirst = 0; + boolean isFirst = false; + + boolean powerItem = false; + boolean cooldownOnStart = false; + boolean notNearEnd = false; + + INT32 shieldType = KSHIELD_NONE; + + I_Assert(item > KITEM_NONE); // too many off by one scenarioes. + I_Assert(cv_items[NUMKARTRESULTS-2] != NULL); // Make sure this exists + + if (K_ItemEnabled(item) == false) + { + return 0; + } + + if (K_GetItemCooldown(item) > 0) + { + // Cooldown is still running, don't give another. + return 0; + } + + /* + if (bot) + { + // TODO: Item use on bots should all be passed-in functions. + // Instead of manually inserting these, it should return 0 + // for any items without an item use function supplied + + switch (item) + { + case KITEM_SNEAKER: + break; + default: + return 0; + } + } + */ + (void)bot; + + if (gametype == GT_BATTLE) + { + I_Assert(pos < 2); // DO NOT allow positions past the bounds of the table + newOdds = K_KartItemOddsBattle[item-1][pos]; + } + else + { + I_Assert(pos < 8); // Ditto + newOdds = K_KartItemOddsRace[item-1][pos]; + } + + newOdds <<= FRACBITS; + + shieldType = K_GetShieldFromItem(item); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + { + continue; + } + + if (!(gametyperules & GTR_BUMPERS) || players[i].bumpers) + { + pingame++; + } + + if (players[i].exiting) + { + pexiting++; + } + + switch (shieldType) + { + case KSHIELD_NONE: + /* Marble Garden Top is not REALLY + a Sonic 3 shield */ + case KSHIELD_TOP: + { + break; + } + + default: + { + if (shieldType == K_GetShieldFromItem(players[i].itemtype)) + { + // Don't allow more than one of each shield type at a time + return 0; + } + } + } + + if (players[i].position == 1) + { + first = &players[i]; + } + + if (players[i].position == 2) + { + second = &players[i]; + } + } + + if (first != NULL) // calculate 2nd's distance from 1st, for SPB + { + firstDist = first->distancetofinish; + isFirst = (ourDist <= firstDist); + } + + if (second != NULL) + { + secondDist = second->distancetofinish; + } + + if (first != NULL && second != NULL) + { + secondToFirst = secondDist - firstDist; + secondToFirst = K_ScaleItemDistance(secondToFirst, 16 - pingame); // Reversed scaling, so 16P is like 1v1, and 1v1 is like 16P + } + + switch (item) + { + case KITEM_BANANA: + case KITEM_EGGMAN: + case KITEM_SUPERRING: + { + notNearEnd = true; + break; + } + + case KITEM_ROCKETSNEAKER: + case KITEM_JAWZ: + case KITEM_LANDMINE: + case KITEM_DROPTARGET: + case KITEM_BALLHOG: + case KITEM_HYUDORO: + case KRITEM_TRIPLESNEAKER: + case KRITEM_TRIPLEORBINAUT: + case KRITEM_QUADORBINAUT: + case KRITEM_DUALJAWZ: + { + powerItem = true; + break; + } + + case KRITEM_TRIPLEBANANA: + { + powerItem = true; + notNearEnd = true; + break; + } + + case KITEM_INVINCIBILITY: + case KITEM_MINE: + case KITEM_GROW: + case KITEM_BUBBLESHIELD: + case KITEM_FLAMESHIELD: + { + cooldownOnStart = true; + powerItem = true; + break; + } + + case KITEM_SPB: + { + cooldownOnStart = true; + notNearEnd = true; + + if (firstDist < ENDDIST*2 // No SPB when 1st is almost done + || isFirst == true) // No SPB for 1st ever + { + newOdds = 0; + } + else + { + const UINT32 dist = max(0, ((signed)secondToFirst) - SPBSTARTDIST); + const UINT32 distRange = SPBFORCEDIST - SPBSTARTDIST; + const UINT8 maxOdds = 20; + fixed_t multiplier = (dist * FRACUNIT) / distRange; + + if (multiplier < 0) + { + multiplier = 0; + } + + if (multiplier > FRACUNIT) + { + multiplier = FRACUNIT; + } + + newOdds = FixedMul(maxOdds << FRACBITS, multiplier); + } + break; + } + + case KITEM_SHRINK: + cooldownOnStart = true; + powerItem = true; + notNearEnd = true; + + if (pingame-1 <= pexiting) + { + newOdds = 0; + } + break; + + case KITEM_LIGHTNINGSHIELD: + cooldownOnStart = true; + powerItem = true; + + if (spbplace != -1) + { + newOdds = 0; + } + break; + + default: + break; + } + + if (newOdds == 0) + { + // Nothing else we want to do with odds matters at this point :p + return newOdds; + } + + if ((cooldownOnStart == true) && (leveltime < (30*TICRATE)+starttime)) + { + // This item should not appear at the beginning of a race. (Usually really powerful crowd-breaking items) + newOdds = 0; + } + else if ((notNearEnd == true) && (ourDist < ENDDIST)) + { + // This item should not appear at the end of a race. (Usually trap items that lose their effectiveness) + newOdds = 0; + } + else if (powerItem == true) + { + // This item is a "power item". This activates "frantic item" toggle related functionality. + if (franticitems == true) + { + // First, power items multiply their odds by 2 if frantic items are on; easy-peasy. + newOdds *= 2; + } + + if (rival == true) + { + // The Rival bot gets frantic-like items, also :p + newOdds *= 2; + } + + newOdds = FixedMul(newOdds, FRACUNIT + K_ItemOddsScale(pingame)); + } + + return FixedInt(FixedRound(newOdds)); +} + +//{ SRB2kart Roulette Code - Distance Based, yes waypoints + +UINT8 K_FindUseodds(player_t *const player, UINT32 playerDist) +{ + UINT8 i; + UINT8 useOdds = 0; + UINT8 distTable[14]; + UINT8 distLen = 0; + UINT8 totalSize = 0; + boolean oddsValid[8]; + + for (i = 0; i < 8; i++) + { + UINT8 j; + + if (gametype == GT_BATTLE && i > 1) + { + oddsValid[i] = false; + break; + } + + for (j = 1; j < NUMKARTRESULTS; j++) + { + if (K_KartGetItemOdds( + i, j, + player->distancetofinish, + player->bot, (player->bot && player->botvars.rival) + ) > 0) + { + break; + } + } + + oddsValid[i] = (j < NUMKARTRESULTS); + } + +#define SETUPDISTTABLE(odds, num) \ + totalSize += num; \ + if (oddsValid[odds]) \ + for (i = num; i; --i) \ + distTable[distLen++] = odds; + + if (gametype == GT_BATTLE) // Battle Mode + { + useOdds = 0; + } + else + { + SETUPDISTTABLE(0,1); + SETUPDISTTABLE(1,1); + SETUPDISTTABLE(2,1); + SETUPDISTTABLE(3,2); + SETUPDISTTABLE(4,2); + SETUPDISTTABLE(5,3); + SETUPDISTTABLE(6,3); + SETUPDISTTABLE(7,1); + + for (i = 0; i < totalSize; i++) + { + fixed_t pos = 0; + fixed_t dist = 0; + UINT8 index = 0; + + if (i == totalSize-1) + { + useOdds = distTable[distLen - 1]; + break; + } + + pos = ((i << FRACBITS) * distLen) / totalSize; + dist = FixedMul(DISTVAR << FRACBITS, pos) >> FRACBITS; + index = FixedInt(FixedRound(pos)); + + if (playerDist <= (unsigned)dist) + { + useOdds = distTable[index]; + break; + } + } + } + +#undef SETUPDISTTABLE + + return useOdds; +} + +boolean K_ForcedSPB(player_t *const player) +{ + player_t *first = NULL; + player_t *second = NULL; + UINT32 secondToFirst = UINT32_MAX; + UINT8 pingame = 0; + UINT8 i; + + if (K_ItemEnabled(KITEM_SPB) == false) + { + return false; + } + + if (!(gametyperules & GTR_CIRCUIT)) + { + return false; + } + + if (player->position <= 1) + { + return false; + } + + if (spbplace != -1) + { + return false; + } + + if (itemCooldowns[KITEM_SPB - 1] > 0) + { + return false; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + { + continue; + } + + if (players[i].exiting) + { + return false; + } + + pingame++; + + if (players[i].position == 1) + { + first = &players[i]; + } + + if (players[i].position == 2) + { + second = &players[i]; + } + } + +#if 0 + if (pingame <= 2) + { + return false; + } +#endif + + if (first != NULL && second != NULL) + { + secondToFirst = second->distancetofinish - first->distancetofinish; + secondToFirst = K_ScaleItemDistance(secondToFirst, 16 - pingame); + } + + return (secondToFirst >= SPBFORCEDIST); +} + +static void K_InitRoulette(itemroulette_t *const roulette) +{ + if (roulette->itemList == NULL) + { + roulette->itemListCap = 8; + roulette->itemList = Z_Calloc( + sizeof(SINT8) * roulette->itemListCap, + PU_LEVEL, + &roulette->itemList + ); + } + + memset(roulette->itemList, KITEM_NONE, sizeof(SINT8) * roulette->itemListCap); + roulette->itemListLen = 0; + + roulette->index = 0; + roulette->elapsed = 0; + roulette->tics = roulette->speed = 3; // Some default speed + roulette->active = true; +} + +static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t item) +{ + I_Assert(roulette->itemList != NULL); + + if (roulette->itemListLen >= roulette->itemListCap) + { + roulette->itemListCap *= 2; + roulette->itemList = Z_Realloc( + roulette->itemList, + sizeof(SINT8) * roulette->itemListCap, + PU_LEVEL, + &roulette->itemList + ); + } + + roulette->itemList[ roulette->itemListLen ] = item; + roulette->itemListLen++; +} + +static void K_CalculateRouletteSpeed(player_t *const player, itemroulette_t *const roulette, UINT8 playing) +{ + // TODO: Change speed based on two factors: + // - Get faster when your distancetofinish is closer to 1st place's distancetofinish. (winning) + // - Get faster based on overall distancetofinish (race progress) + // Slowest speed should be 12 tics, fastest should be 3 tics. + + (void)player; + (void)playing; + + roulette->tics = roulette->speed = 7; +} + +void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) +{ + UINT8 playing = 0; + UINT32 playerDist = UINT32_MAX; + + UINT8 useOdds = 0; + UINT32 spawnChance[NUMKARTRESULTS] = {0}; + UINT32 totalSpawnChance = 0; + size_t rngRoll = 0; + + size_t i; + + K_InitRoulette(roulette); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] == false || players[i].spectator == true) + { + continue; + } + + playing++; + } + + K_CalculateRouletteSpeed(player, roulette, playing); + + // SPECIAL CASE No. 1: + // Give only the debug item if specified + if (cv_kartdebugitem.value != KITEM_NONE) + { + K_PushToRouletteItemList(roulette, cv_kartdebugitem.value); + return; + } + + // SPECIAL CASE No. 2: + // Use a special, pre-determined item reel for Time Attack / Free Play + if (modeattacking || playing <= 1) + { + switch (gametype) + { + case GT_RACE: + default: + { + for (i = 0; K_KartItemReelTimeAttack[i] != KITEM_NONE; i++) + { + K_PushToRouletteItemList(roulette, K_KartItemReelTimeAttack[i]); + CONS_Printf("Added %d\n", K_KartItemReelTimeAttack[i]); + } + break; + } + case GT_BATTLE: + { + for (i = 0; K_KartItemReelBreakTheCapsules[i] != KITEM_NONE; i++) + { + K_PushToRouletteItemList(roulette, K_KartItemReelBreakTheCapsules[i]); + CONS_Printf("Added %d\n", K_KartItemReelBreakTheCapsules[i]); + } + break; + } + } + + return; + } + + // SPECIAL CASE No. 3: + // Only give the SPB if conditions are right + if (K_ForcedSPB(player) == true) + { + K_PushToRouletteItemList(roulette, KITEM_SPB); + return; + } + + playerDist = K_GetItemRouletteDistance(player, playing); + + useOdds = K_FindUseodds(player, playerDist); + + for (i = 1; i < NUMKARTRESULTS; i++) + { + UINT8 thisItemsOdds = K_KartGetItemOdds( + useOdds, i, + player->distancetofinish, + player->bot, (player->bot && player->botvars.rival) + ); + + spawnChance[i] = (totalSpawnChance += thisItemsOdds); + } + + // SPECIAL CASE No. 4: + // All items are off, so give a placeholder item + if (totalSpawnChance == 0) + { + K_PushToRouletteItemList(roulette, KITEM_SAD); + return; + } + + // We always want the same result when making the same item reel. + P_SetRandSeed(PR_ITEM_ROULETTE, ITEM_REEL_SEED); + + while (totalSpawnChance > 0) + { + rngRoll = P_RandomKey(PR_ITEM_ROULETTE, totalSpawnChance); + + for (i = 0; i < NUMKARTRESULTS && spawnChance[i] <= rngRoll; i++) + { + continue; + } + + K_PushToRouletteItemList(roulette, i); + + // If we're in ring debt, pad out the reel with + // a BUNCH of Super Rings. + if (K_ItemEnabled(KITEM_SUPERRING) + && player->rings < 0 + && !(gametyperules & GTR_SPHERES)) + { + K_PushToRouletteItemList(roulette, KITEM_SUPERRING); + } + + spawnChance[i]--; + totalSpawnChance--; + } +} + +void K_StartEggmanRoulette(player_t *const player) +{ + itemroulette_t *const roulette = &player->itemRoulette; + + K_InitRoulette(roulette); + K_PushToRouletteItemList(roulette, KITEM_EGGEXPLODE); + roulette->eggman = true; +} + +/** \brief Item Roulette for Kart + + \param player player + \param getitem what item we're looking for + + \return void +*/ +static void K_KartGetItemResult(player_t *const player, kartitems_t getitem) +{ + if (getitem == KITEM_SPB || getitem == KITEM_SHRINK) + { + K_SetItemCooldown(getitem, 20*TICRATE); + } + + player->botvars.itemdelay = TICRATE; + player->botvars.itemconfirm = 0; + + player->itemtype = K_ItemResultToType(getitem); + player->itemamount = K_ItemResultToAmount(getitem); +} + +void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) +{ + itemroulette_t *const roulette = &player->itemRoulette; + boolean confirmItem = false; + + // This makes the roulette cycle through items. + // If this isn't active, you shouldn't be here. + if (roulette->active == false) + { + return; + } + + if (roulette->itemList == NULL || roulette->itemListLen == 0) + { + // Invalid roulette setup. + // Escape before we run into issues. + roulette->active = false; + return; + } + + if (roulette->elapsed > TICRATE>>1) // Prevent accidental immediate item confirm + { + if (roulette->elapsed > TICRATE<<4) + { + // Waited way too long, forcefully confirm the item. + confirmItem = true; + } + else + { + // We can stop our item when we choose. + confirmItem = !!(cmd->buttons & BT_ATTACK); + } + } + + // If the roulette finishes or the player presses BT_ATTACK, stop the roulette and calculate the item. + // I'm returning via the exact opposite, however, to forgo having another bracket embed. Same result either way, I think. + // Finally, if you get past this check, now you can actually start calculating what item you get. + if (confirmItem == true && (player->pflags & (PF_ITEMOUT|PF_EGGMANOUT|PF_USERINGS)) == 0) + { + kartitems_t finalItem = roulette->itemList[ roulette->index ]; + + K_KartGetItemResult(player, finalItem); + player->karthud[khud_itemblink] = TICRATE; + + if (P_IsDisplayPlayer(player) && !demo.freecam) + { + S_StartSound(NULL, sfx_itrolf); + } + + // We're done, disable the roulette + roulette->active = false; + return; + } + + roulette->elapsed++; + + if (roulette->tics == 0) + { + roulette->index = (roulette->index + 1) % roulette->itemListLen; + roulette->tics = roulette->speed; + + // This makes the roulette produce the random noises. + roulette->sound = (roulette->sound + 1) % 8; + + if (P_IsDisplayPlayer(player) && !demo.freecam) + { + S_StartSound(NULL, sfx_itrol1 + roulette->sound); + } + } + else + { + roulette->tics--; + } +} diff --git a/src/k_roulette.h b/src/k_roulette.h new file mode 100644 index 000000000..8fd302abf --- /dev/null +++ b/src/k_roulette.h @@ -0,0 +1,35 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2022 by Kart Krew +// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour +// +// 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_roulette.h +/// \brief Item roulette code. + +#ifndef __K_ROULETTE_H__ +#define __K_ROULETTE_H__ + +#include "doomtype.h" +#include "d_player.h" + +boolean K_ItemEnabled(SINT8 item); + +fixed_t K_ItemOddsScale(UINT8 playerCount); +UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers); +UINT32 K_GetItemRouletteDistance(player_t *const player, UINT8 pingame); + +INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, boolean bot, boolean rival); +UINT8 K_FindUseodds(player_t *const player, UINT32 playerDist); + +boolean K_ForcedSPB(player_t *const player); + +void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette); +void K_StartEggmanRoulette(player_t *const player); + +void K_KartItemRoulette(player_t *const player, ticcmd_t *cmd); + +#endif // __K_ROULETTE_H__ diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 2a3b9e46a..b69449ba2 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -304,10 +304,10 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->tripwirePass); else if (fastcmp(field,"tripwireLeniency")) lua_pushinteger(L, plr->tripwireLeniency); + /* else if (fastcmp(field,"itemroulette")) lua_pushinteger(L, plr->itemroulette); - else if (fastcmp(field,"roulettetype")) - lua_pushinteger(L, plr->roulettetype); + */ else if (fastcmp(field,"itemtype")) lua_pushinteger(L, plr->itemtype); else if (fastcmp(field,"itemamount")) @@ -680,10 +680,10 @@ static int player_set(lua_State *L) plr->tripwirePass = luaL_checkinteger(L, 3); else if (fastcmp(field,"tripwireLeniency")) plr->tripwireLeniency = luaL_checkinteger(L, 3); + /* else if (fastcmp(field,"itemroulette")) plr->itemroulette = luaL_checkinteger(L, 3); - else if (fastcmp(field,"roulettetype")) - plr->roulettetype = luaL_checkinteger(L, 3); + */ else if (fastcmp(field,"itemtype")) plr->itemtype = luaL_checkinteger(L, 3); else if (fastcmp(field,"itemamount")) diff --git a/src/p_enemy.c b/src/p_enemy.c index 131e92bf7..636eb500a 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -34,6 +34,7 @@ #include "k_respawn.h" #include "k_collide.h" #include "k_objects.h" +#include "k_roulette.h" #ifdef HW3SOUND #include "hardware/hw3sound.h" @@ -13042,9 +13043,13 @@ void A_ItemPop(mobj_t *actor) Obj_SpawnItemDebrisEffects(actor, actor->target); if (locvar1 == 1) + { P_GivePlayerSpheres(actor->target->player, actor->extravalue1); + } else if (locvar1 == 0) - actor->target->player->itemroulette = 1; + { + K_StartItemRoulette(actor->target->player, &actor->target->player->itemRoulette); + } // Here at mapload in battle? if ((gametyperules & GTR_BUMPERS) && (actor->flags2 & MF2_BOSSNOTRAP)) diff --git a/src/p_inter.c b/src/p_inter.c index 76757f193..2c05f5732 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -38,6 +38,7 @@ #include "k_respawn.h" #include "p_spec.h" #include "k_objects.h" +#include "k_roulette.h" // CTF player names #define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : "" @@ -130,7 +131,7 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) return false; // Already have fake - if (player->roulettetype == 2 + if (player->itemRoulette.eggman == true || player->eggmanexplode) return false; } @@ -143,7 +144,7 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) return false; // Item slot already taken up - if (player->itemroulette + if (player->itemRoulette.active == true || (weapon != 3 && player->itemamount) || (player->pflags & PF_ITEMOUT)) return false; @@ -411,8 +412,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->fuse || !P_CanPickupItem(player, 1) || ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0)) return; - player->itemroulette = 1; - player->roulettetype = 1; + K_StartItemRoulette(player, &player->itemRoulette); // Karma fireworks for (i = 0; i < 5; i++) @@ -1449,8 +1449,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget } player->karthud[khud_itemblink] = TICRATE; player->karthud[khud_itemblinkmode] = 0; - player->itemroulette = 0; - player->roulettetype = 0; + player->itemRoulette.active = false; if (P_IsDisplayPlayer(player)) S_StartSound(NULL, sfx_itrolf); } diff --git a/src/p_mobj.c b/src/p_mobj.c index 340e40e52..d48fa6de8 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6124,7 +6124,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) break; // see also K_drawKartItem in k_hud.c - case MT_PLAYERARROW: + case MT_PLAYERARROW: // FIXME: Delete this object, attach to name tags instead. if (mobj->target && mobj->target->health && mobj->target->player && !mobj->target->player->spectator && mobj->target->health && mobj->target->player->playerstate != PST_DEAD @@ -6178,7 +6178,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) } // Do this in an easy way - if (mobj->target->player->itemroulette) + if (mobj->target->player->itemRoulette.active) { mobj->tracer->color = mobj->target->player->skincolor; mobj->tracer->colorized = true; @@ -6194,11 +6194,11 @@ static void P_MobjSceneryThink(mobj_t *mobj) const INT32 numberdisplaymin = ((mobj->target->player->itemtype == KITEM_ORBINAUT) ? 5 : 2); // Set it to use the correct states for its condition - if (mobj->target->player->itemroulette) + if (mobj->target->player->itemRoulette.active) { P_SetMobjState(mobj, S_PLAYERARROW_BOX); mobj->tracer->sprite = SPR_ITEM; - mobj->tracer->frame = K_GetRollingRouletteItem(mobj->target->player) | FF_FULLBRIGHT; + mobj->tracer->frame = 1 | FF_FULLBRIGHT; mobj->tracer->renderflags &= ~RF_DONTDRAW; } else if (mobj->target->player->stealingtimer < 0) diff --git a/src/p_saveg.c b/src/p_saveg.c index 73d4a7d1f..fee092df9 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -310,9 +310,6 @@ static void P_NetArchivePlayers(void) WRITEUINT8(save_p, players[i].tripwirePass); WRITEUINT16(save_p, players[i].tripwireLeniency); - WRITEUINT16(save_p, players[i].itemroulette); - WRITEUINT8(save_p, players[i].roulettetype); - WRITESINT8(save_p, players[i].itemtype); WRITEUINT8(save_p, players[i].itemamount); WRITESINT8(save_p, players[i].throwdir); @@ -410,6 +407,19 @@ static void P_NetArchivePlayers(void) WRITEUINT32(save_p, players[i].botvars.itemconfirm); WRITESINT8(save_p, players[i].botvars.turnconfirm); WRITEUINT32(save_p, players[i].botvars.spindashconfirm); + + // itemroulette_t + WRITEUINT8(save_p, players[i].itemRoulette.active); + WRITEUINT32(save_p, players[i].itemRoulette.itemListCap); + WRITEUINT32(save_p, players[i].itemRoulette.itemListLen); + for (j = 0; (unsigned)j < players[i].itemRoulette.itemListLen; j++) + { + WRITESINT8(save_p, players[i].itemRoulette.itemList[j]); + } + WRITEUINT32(save_p, players[i].itemRoulette.index); + WRITEUINT32(save_p, players[i].itemRoulette.speed); + WRITEUINT32(save_p, players[i].itemRoulette.tics); + WRITEUINT32(save_p, players[i].itemRoulette.elapsed); } } @@ -612,9 +622,6 @@ static void P_NetUnArchivePlayers(void) players[i].tripwirePass = READUINT8(save_p); players[i].tripwireLeniency = READUINT16(save_p); - players[i].itemroulette = READUINT16(save_p); - players[i].roulettetype = READUINT8(save_p); - players[i].itemtype = READSINT8(save_p); players[i].itemamount = READUINT8(save_p); players[i].throwdir = READSINT8(save_p); @@ -713,6 +720,39 @@ static void P_NetUnArchivePlayers(void) players[i].botvars.turnconfirm = READSINT8(save_p); players[i].botvars.spindashconfirm = READUINT32(save_p); + // itemroulette_t + players[i].itemRoulette.active = READUINT8(save_p); + players[i].itemRoulette.itemListCap = READUINT32(save_p); + players[i].itemRoulette.itemListLen = READUINT32(save_p); + + if (players[i].itemRoulette.itemList == NULL) + { + players[i].itemRoulette.itemList = Z_Calloc( + sizeof(SINT8) * players[i].itemRoulette.itemListCap, + PU_LEVEL, + &players[i].itemRoulette.itemList + ); + } + else + { + players[i].itemRoulette.itemList = Z_Realloc( + players[i].itemRoulette.itemList, + sizeof(SINT8) * players[i].itemRoulette.itemListCap, + PU_LEVEL, + &players[i].itemRoulette.itemList + ); + } + + for (j = 0; (unsigned)j < players[i].itemRoulette.itemListLen; j++) + { + players[i].itemRoulette.itemList[j] = READSINT8(save_p); + } + + players[i].itemRoulette.index = READUINT32(save_p); + players[i].itemRoulette.speed = READUINT32(save_p); + players[i].itemRoulette.tics = READUINT32(save_p); + players[i].itemRoulette.elapsed = READUINT32(save_p); + //players[i].viewheight = P_GetPlayerViewHeight(players[i]); // scale cannot be factored in at this point } } diff --git a/src/typedef.h b/src/typedef.h index db751340c..0c19fa2ac 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -37,6 +37,7 @@ TYPEDEF (discordRequest_t); TYPEDEF (respawnvars_t); TYPEDEF (botvars_t); TYPEDEF (skybox_t); +TYPEDEF (itemroulette_t); TYPEDEF (player_t); // d_clisrv.h From 534026764c01ff9153b12c0ec4065426a9e3e398 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 12 Dec 2022 00:22:14 -0500 Subject: [PATCH 03/46] Make Eggman Mark actually work --- src/d_player.h | 1 - src/k_roulette.c | 37 +++++++++++++++++++++++++++---------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index fdcb5bdc4..a8b70f1a7 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -131,7 +131,6 @@ Do with it whatever you want. Run this macro, then #undef FOREACH afterward */ #define KART_ITEM_ITERATOR \ - FOREACH (EGGEXPLODE, -2),\ FOREACH (SAD, -1),\ FOREACH (NONE, 0),\ FOREACH (SNEAKER, 1),\ diff --git a/src/k_roulette.c b/src/k_roulette.c index df16eba5f..c5a493365 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -738,6 +738,7 @@ static void K_InitRoulette(itemroulette_t *const roulette) roulette->elapsed = 0; roulette->tics = roulette->speed = 3; // Some default speed roulette->active = true; + roulette->eggman = false; } static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t item) @@ -898,9 +899,7 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) void K_StartEggmanRoulette(player_t *const player) { itemroulette_t *const roulette = &player->itemRoulette; - - K_InitRoulette(roulette); - K_PushToRouletteItemList(roulette, KITEM_EGGEXPLODE); + K_StartItemRoulette(player, roulette); roulette->eggman = true; } @@ -964,14 +963,32 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) // Finally, if you get past this check, now you can actually start calculating what item you get. if (confirmItem == true && (player->pflags & (PF_ITEMOUT|PF_EGGMANOUT|PF_USERINGS)) == 0) { - kartitems_t finalItem = roulette->itemList[ roulette->index ]; - - K_KartGetItemResult(player, finalItem); - player->karthud[khud_itemblink] = TICRATE; - - if (P_IsDisplayPlayer(player) && !demo.freecam) + if (roulette->eggman == true) { - S_StartSound(NULL, sfx_itrolf); + // FATASS JUMPSCARE instead of your actual item + player->eggmanexplode = 4*TICRATE; + + //player->karthud[khud_itemblink] = TICRATE; + //player->karthud[khud_itemblinkmode] = 1; + + if (P_IsDisplayPlayer(player) && !demo.freecam) + { + S_StartSound(NULL, sfx_itrole); + } + } + else + { + kartitems_t finalItem = roulette->itemList[ roulette->index ]; + + K_KartGetItemResult(player, finalItem); + + player->karthud[khud_itemblink] = TICRATE; + player->karthud[khud_itemblinkmode] = 0; + + if (P_IsDisplayPlayer(player) && !demo.freecam) + { + S_StartSound(NULL, sfx_itrolf); + } } // We're done, disable the roulette From 202c5056648240cc61429dd831eea185d058129a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 12 Dec 2022 02:18:18 -0500 Subject: [PATCH 04/46] Implement roulette visuals --- src/d_player.h | 1 + src/k_hud.c | 104 ++++++++++++++++++++++++++++++----------------- src/k_kart.c | 5 +++ src/k_roulette.c | 27 +++++++++--- src/k_roulette.h | 3 ++ 5 files changed, 97 insertions(+), 43 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index a8b70f1a7..842edbc0e 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -225,6 +225,7 @@ typedef enum // Item box khud_itemblink, // Item flashing after roulette, serves as a mashing indicator khud_itemblinkmode, // Type of flashing: 0 = white (normal), 1 = red (mashing), 2 = rainbow (enhanced items) + khud_rouletteoffset,// Roulette stop height // Rings khud_ringframe, // Ring spin frame diff --git a/src/k_hud.c b/src/k_hud.c index f92c94a6f..5ba874913 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1023,7 +1023,7 @@ static void K_drawKartItem(void) // Why write V_DrawScaledPatch calls over and over when they're all the same? // Set to 'no item' just in case. const UINT8 offset = ((r_splitscreen > 1) ? 1 : 0); - patch_t *localpatch = kp_nodraw; + patch_t *localpatch[3] = {kp_nodraw}; patch_t *localbg = ((offset) ? kp_itembg[2] : kp_itembg[0]); patch_t *localinv = ((offset) ? kp_invincibility[((leveltime % (6*3)) / 3) + 7] : kp_invincibility[(leveltime % (7*3)) / 3]); INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags... @@ -1036,28 +1036,42 @@ static void K_drawKartItem(void) UINT8 *colmap = NULL; boolean flipamount = false; // Used for 3P/4P splitscreen to flip item amount stuff + fixed_t rouletteOffset = 0; + const INT32 rouletteBox = 36; + INT32 i; + if (stplyr->itemRoulette.active == true) { - const SINT8 item = K_ItemResultToType(stplyr->itemRoulette.itemList[ stplyr->itemRoulette.index ]); + rouletteOffset = K_GetRouletteOffset(&stplyr->itemRoulette, rendertimefrac); if (stplyr->skincolor) { localcolor = stplyr->skincolor; } - switch (item) + for (i = 0; i < 3; i++) { - case KITEM_INVINCIBILITY: - localpatch = localinv; - break; + const SINT8 indexOfs = i-1; + const size_t index = (stplyr->itemRoulette.index + indexOfs) % stplyr->itemRoulette.itemListLen; - case KITEM_ORBINAUT: - localpatch = kp_orbinaut[3 + offset]; - break; + const SINT8 result = stplyr->itemRoulette.itemList[index]; + const SINT8 item = K_ItemResultToType(result); + const UINT8 amt = K_ItemResultToAmount(result); - default: - localpatch = K_GetCachedItemPatch(item, offset); - break; + switch (item) + { + case KITEM_INVINCIBILITY: + localpatch[i] = localinv; + break; + + case KITEM_ORBINAUT: + localpatch[i] = kp_orbinaut[(offset ? 4 : min(amt-1, 3))]; + break; + + default: + localpatch[i] = K_GetCachedItemPatch(item, offset); + break; + } } } else @@ -1066,23 +1080,26 @@ static void K_drawKartItem(void) // The only actual reason is to make sneakers line up this way in the code below // This shouldn't have any actual baring over how it functions // Hyudoro is first, because we're drawing it on top of the player's current item + + rouletteOffset = stplyr->karthud[khud_rouletteoffset]; + if (stplyr->stealingtimer < 0) { if (leveltime & 2) - localpatch = kp_hyudoro[offset]; + localpatch[0] = kp_hyudoro[offset]; else - localpatch = kp_nodraw; + localpatch[0] = kp_nodraw; } else if ((stplyr->stealingtimer > 0) && (leveltime & 2)) { - localpatch = kp_hyudoro[offset]; + localpatch[0] = kp_hyudoro[offset]; } else if (stplyr->eggmanexplode > 1) { if (leveltime & 1) - localpatch = kp_eggman[offset]; + localpatch[0] = kp_eggman[offset]; else - localpatch = kp_nodraw; + localpatch[0] = kp_nodraw; } else if (stplyr->ballhogcharge > 0) { @@ -1090,9 +1107,9 @@ static void K_drawKartItem(void) maxl = (((stplyr->itemamount-1) * BALLHOGINCREMENT) + 1); if (leveltime & 1) - localpatch = kp_ballhog[offset]; + localpatch[0] = kp_ballhog[offset]; else - localpatch = kp_nodraw; + localpatch[0] = kp_nodraw; } else if (stplyr->rocketsneakertimer > 1) { @@ -1100,16 +1117,16 @@ static void K_drawKartItem(void) maxl = (itemtime*3) - barlength; if (leveltime & 1) - localpatch = kp_rocketsneaker[offset]; + localpatch[0] = kp_rocketsneaker[offset]; else - localpatch = kp_nodraw; + localpatch[0] = kp_nodraw; } else if (stplyr->sadtimer > 0) { if (leveltime & 2) - localpatch = kp_sadface[offset]; + localpatch[0] = kp_sadface[offset]; else - localpatch = kp_nodraw; + localpatch[0] = kp_nodraw; } else { @@ -1119,12 +1136,12 @@ static void K_drawKartItem(void) switch (stplyr->itemtype) { case KITEM_INVINCIBILITY: - localpatch = localinv; + localpatch[0] = localinv; localbg = kp_itembg[offset+1]; break; case KITEM_ORBINAUT: - localpatch = kp_orbinaut[(offset ? 4 : min(stplyr->itemamount-1, 3))]; + localpatch[0] = kp_orbinaut[(offset ? 4 : min(stplyr->itemamount-1, 3))]; break; case KITEM_SPB: @@ -1135,15 +1152,15 @@ static void K_drawKartItem(void) /*FALLTHRU*/ default: - localpatch = K_GetCachedItemPatch(stplyr->itemtype, offset); + localpatch[0] = K_GetCachedItemPatch(stplyr->itemtype, offset); - if (localpatch == NULL) - localpatch = kp_nodraw; // diagnose underflows + if (localpatch[0] == NULL) + localpatch[0] = kp_nodraw; // diagnose underflows break; } if ((stplyr->pflags & PF_ITEMOUT) && !(leveltime & 1)) - localpatch = kp_nodraw; + localpatch[0] = kp_nodraw; } if (stplyr->karthud[khud_itemblink] && (leveltime & 1)) @@ -1194,18 +1211,31 @@ static void K_drawKartItem(void) V_DrawScaledPatch(fx, fy, V_HUDTRANS|V_SLIDEIN|fflags, localbg); - //V_SetClipRect((fx + 10) << FRACBITS, (fy + 10) << FRACBITS, 30 << FRACBITS, 30 << FRACBITS, V_HUDTRANS|V_SLIDEIN|fflags); - - // Then, the numbers: - if (stplyr->itemamount >= numberdisplaymin && stplyr->itemRoulette.active == false) + if (stplyr->itemRoulette.active == true) { + V_SetClipRect((fx + 7) << FRACBITS, (fy + 7) << FRACBITS, rouletteBox << FRACBITS, rouletteBox << FRACBITS, V_HUDTRANS|V_SLIDEIN|fflags); + + // Need to draw these in a particular order, for sorting. + V_DrawFixedPatch(fx<itemamount >= numberdisplaymin && stplyr->itemRoulette.active == false) + { + // Then, the numbers: V_DrawScaledPatch(fx + (flipamount ? 48 : 0), fy, V_HUDTRANS|V_SLIDEIN|fflags|(flipamount ? V_FLIP : 0), kp_itemmulsticker[offset]); // flip this graphic for p2 and p4 in split and shift it. - V_DrawFixedPatch(fx<itemamount)); else V_DrawString(fx+24, fy+31, V_ALLOWLOWERCASE|V_HUDTRANS|V_SLIDEIN|fflags, va("x%d", stplyr->itemamount)); + } else { V_DrawScaledPatch(fy+28, fy+41, V_HUDTRANS|V_SLIDEIN|fflags, kp_itemx); @@ -1213,9 +1243,9 @@ static void K_drawKartItem(void) } } else - V_DrawFixedPatch(fx<karthud[khud_itemblink] = 0; } + if (player->karthud[khud_rouletteoffset] != 0) + { + player->karthud[khud_rouletteoffset] = FixedMul(player->karthud[khud_rouletteoffset], FRACUNIT*3/4); + } + if (!(gametyperules & GTR_SPHERES)) { if (player->mo && player->mo->hitlag <= 0) diff --git a/src/k_roulette.c b/src/k_roulette.c index c5a493365..7e3d7a3b7 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -146,14 +146,12 @@ static kartitems_t K_KartItemReelBreakTheCapsules[] = KITEM_NONE }; -#if 0 static kartitems_t K_KartItemReelBoss[] = { KITEM_ORBINAUT, KITEM_BANANA, KITEM_NONE }; -#endif boolean K_ItemEnabled(SINT8 item) { @@ -809,7 +807,16 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) // SPECIAL CASE No. 2: // Use a special, pre-determined item reel for Time Attack / Free Play - if (modeattacking || playing <= 1) + if (bossinfo.boss == true) + { + for (i = 0; K_KartItemReelBoss[i] != KITEM_NONE; i++) + { + K_PushToRouletteItemList(roulette, K_KartItemReelBoss[i]); + } + + return; + } + else if (modeattacking || playing <= 1) { switch (gametype) { @@ -819,7 +826,6 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) for (i = 0; K_KartItemReelTimeAttack[i] != KITEM_NONE; i++) { K_PushToRouletteItemList(roulette, K_KartItemReelTimeAttack[i]); - CONS_Printf("Added %d\n", K_KartItemReelTimeAttack[i]); } break; } @@ -828,7 +834,6 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) for (i = 0; K_KartItemReelBreakTheCapsules[i] != KITEM_NONE; i++) { K_PushToRouletteItemList(roulette, K_KartItemReelBreakTheCapsules[i]); - CONS_Printf("Added %d\n", K_KartItemReelBreakTheCapsules[i]); } break; } @@ -875,7 +880,7 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) { rngRoll = P_RandomKey(PR_ITEM_ROULETTE, totalSpawnChance); - for (i = 0; i < NUMKARTRESULTS && spawnChance[i] <= rngRoll; i++) + for (i = 1; i < NUMKARTRESULTS && spawnChance[i] <= rngRoll; i++) { continue; } @@ -924,6 +929,14 @@ static void K_KartGetItemResult(player_t *const player, kartitems_t getitem) player->itemamount = K_ItemResultToAmount(getitem); } +fixed_t K_GetRouletteOffset(itemroulette_t *const roulette, fixed_t renderDelta) +{ + const fixed_t curTic = (roulette->tics << FRACBITS) - renderDelta; + const fixed_t midTic = roulette->speed * (FRACUNIT >> 1); + + return FixedMul(FixedDiv(midTic - curTic, ((roulette->speed + 1) << FRACBITS)), ROULETTE_SPACING); +} + void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) { itemroulette_t *const roulette = &player->itemRoulette; @@ -970,6 +983,7 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) //player->karthud[khud_itemblink] = TICRATE; //player->karthud[khud_itemblinkmode] = 1; + //player->karthud[khud_rouletteoffset] = K_GetRouletteOffset(roulette, FRACUNIT); if (P_IsDisplayPlayer(player) && !demo.freecam) { @@ -984,6 +998,7 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) player->karthud[khud_itemblink] = TICRATE; player->karthud[khud_itemblinkmode] = 0; + player->karthud[khud_rouletteoffset] = K_GetRouletteOffset(roulette, FRACUNIT); if (P_IsDisplayPlayer(player) && !demo.freecam) { diff --git a/src/k_roulette.h b/src/k_roulette.h index 8fd302abf..81e12aae3 100644 --- a/src/k_roulette.h +++ b/src/k_roulette.h @@ -30,6 +30,9 @@ boolean K_ForcedSPB(player_t *const player); void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette); void K_StartEggmanRoulette(player_t *const player); +#define ROULETTE_SPACING (36 << FRACBITS) +fixed_t K_GetRouletteOffset(itemroulette_t *const roulette, fixed_t renderDelta); + void K_KartItemRoulette(player_t *const player, ticcmd_t *cmd); #endif // __K_ROULETTE_H__ From 10145b75d1dc4c10c7f4ad4caf6088490e06e798 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 12 Dec 2022 02:59:58 -0500 Subject: [PATCH 05/46] Fix incorrect item reel in actual races --- src/k_roulette.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/k_roulette.c b/src/k_roulette.c index 7e3d7a3b7..09bd671d7 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -99,7 +99,7 @@ static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = { 0, 0, 1, 2, 1, 0, 0, 0 } // Jawz x2 }; -static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS][2] = +static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS-1][2] = { //K L { 2, 1 }, // Sneaker @@ -309,7 +309,7 @@ INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, boolean bot, bool INT32 shieldType = KSHIELD_NONE; I_Assert(item > KITEM_NONE); // too many off by one scenarioes. - I_Assert(cv_items[NUMKARTRESULTS-2] != NULL); // Make sure this exists + I_Assert(item < NUMKARTRESULTS); if (K_ItemEnabled(item) == false) { @@ -554,7 +554,8 @@ INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, boolean bot, bool newOdds = FixedMul(newOdds, FRACUNIT + K_ItemOddsScale(pingame)); } - return FixedInt(FixedRound(newOdds)); + newOdds = FixedInt(FixedRound(newOdds)); + return newOdds; } //{ SRB2kart Roulette Code - Distance Based, yes waypoints @@ -575,7 +576,7 @@ UINT8 K_FindUseodds(player_t *const player, UINT32 playerDist) if (gametype == GT_BATTLE && i > 1) { oddsValid[i] = false; - break; + continue; } for (j = 1; j < NUMKARTRESULTS; j++) @@ -856,7 +857,7 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) for (i = 1; i < NUMKARTRESULTS; i++) { - UINT8 thisItemsOdds = K_KartGetItemOdds( + INT32 thisItemsOdds = K_KartGetItemOdds( useOdds, i, player->distancetofinish, player->bot, (player->bot && player->botvars.rival) @@ -879,7 +880,6 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) while (totalSpawnChance > 0) { rngRoll = P_RandomKey(PR_ITEM_ROULETTE, totalSpawnChance); - for (i = 1; i < NUMKARTRESULTS && spawnChance[i] <= rngRoll; i++) { continue; @@ -896,7 +896,15 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) K_PushToRouletteItemList(roulette, KITEM_SUPERRING); } - spawnChance[i]--; + for (; i < NUMKARTRESULTS; i++) + { + // Be sure to fix the remaining items' odds too. + if (spawnChance[i] > 0) + { + spawnChance[i]--; + } + } + totalSpawnChance--; } } From c8f3533b00a6df3dcdf324a14287845c5506f122 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 12 Dec 2022 04:01:09 -0500 Subject: [PATCH 06/46] Roulette speed adjustments - Speeds up the farther in the course you are - Speeds up the further in the front you are - Slows down before the 20 second mark --- src/k_roulette.c | 86 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 17 deletions(-) diff --git a/src/k_roulette.c b/src/k_roulette.c index 09bd671d7..62d8e7fd1 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -62,11 +62,15 @@ #define SPBFORCEDIST (14*DISTVAR) // Distance when the game stops giving you bananas -#define ENDDIST (12*DISTVAR) +#define ENDDIST (18*DISTVAR) // Consistent seed used for item reels #define ITEM_REEL_SEED (0x22D5FAA8) +#define ROULETTE_SPEED_SLOWEST (12) +#define ROULETTE_SPEED_FASTEST (2) +#define ROULETTE_SPEED_DIST (224*DISTVAR) + static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = { { 0, 0, 2, 3, 4, 0, 0, 0 }, // Sneaker @@ -759,17 +763,66 @@ static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t roulette->itemListLen++; } -static void K_CalculateRouletteSpeed(player_t *const player, itemroulette_t *const roulette, UINT8 playing) +static void K_CalculateRouletteSpeed(player_t *const player, itemroulette_t *const roulette) { // TODO: Change speed based on two factors: // - Get faster when your distancetofinish is closer to 1st place's distancetofinish. (winning) // - Get faster based on overall distancetofinish (race progress) // Slowest speed should be 12 tics, fastest should be 3 tics. - (void)player; - (void)playing; + fixed_t frontRun = 0; + fixed_t progress = 0; + fixed_t total = 0; - roulette->tics = roulette->speed = 7; + UINT8 playing = 0; + + UINT32 firstDist = UINT32_MAX; + UINT32 ourDist = UINT32_MAX; + + size_t i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] == false || players[i].spectator == true) + { + continue; + } + + playing++; + + if (players[i].position == 1) + { + firstDist = players[i].distancetofinish; + } + } + + ourDist = K_ScaleItemDistance(player->distancetofinish, playing); + firstDist = K_ScaleItemDistance(firstDist, playing); + + if (ourDist > ENDDIST) + { + // Being farther in the course makes your roulette faster. + progress = min(FRACUNIT, FixedDiv(ourDist - ENDDIST, ROULETTE_SPEED_DIST)); + } + + if (ourDist > firstDist) + { + // Frontrunning makes your roulette faster. + frontRun = min(FRACUNIT, FixedDiv(ourDist - firstDist, ENDDIST)); + } + + // Combine our two factors together. + total = min(FRACUNIT, (frontRun / 2) + (progress / 2)); + + if (leveltime < starttime + 20*TICRATE) + { + // Don't impact as much at the start. + // This makes it so that everyone gets to enjoy the lowest speed at the start. + fixed_t lerp = FRACUNIT - FixedDiv(max(0, leveltime - starttime), 10*TICRATE); + total += FixedMul(lerp, FRACUNIT - total); + } + + roulette->tics = roulette->speed = ROULETTE_SPEED_FASTEST + FixedMul(ROULETTE_SPEED_SLOWEST - ROULETTE_SPEED_FASTEST, total); } void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) @@ -785,18 +838,7 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) size_t i; K_InitRoulette(roulette); - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] == false || players[i].spectator == true) - { - continue; - } - - playing++; - } - - K_CalculateRouletteSpeed(player, roulette, playing); + K_CalculateRouletteSpeed(player, roulette); // SPECIAL CASE No. 1: // Give only the debug item if specified @@ -808,6 +850,16 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) // SPECIAL CASE No. 2: // Use a special, pre-determined item reel for Time Attack / Free Play + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] == false || players[i].spectator == true) + { + continue; + } + + playing++; + } + if (bossinfo.boss == true) { for (i = 0; K_KartItemReelBoss[i] != KITEM_NONE; i++) From e45cef44df8b05d823991e513a58eb285f4ed63b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 12 Dec 2022 04:35:51 -0500 Subject: [PATCH 07/46] Make bots stop roulette instead of waiting it out Done in kind of a lazy way, might revisit later. --- src/k_botitem.c | 18 +++--------------- src/k_roulette.c | 4 ++++ 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/k_botitem.c b/src/k_botitem.c index e3054aa69..fe0837c84 100644 --- a/src/k_botitem.c +++ b/src/k_botitem.c @@ -1394,27 +1394,15 @@ static void K_BotItemRings(player_t *player, ticcmd_t *cmd) --------------------------------------------------*/ static void K_BotItemRouletteMash(player_t *player, ticcmd_t *cmd) { - boolean mash = false; - if (K_ItemButtonWasDown(player) == true) { return; } - if (player->rings < 0 && K_ItemEnabled(KITEM_SUPERRING) == true) - { - // Uh oh, we need a loan! - // It'll be better in the long run for bots to lose an item set for 10 free rings. - mash = true; - } + // TODO: Would be nice to implement smarter behavior + // for selecting items. - // TODO: Mash based on how far behind you are, when items are - // almost garantueed to be in your favor. - - if (mash == true) - { - cmd->buttons |= BT_ATTACK; - } + cmd->buttons |= BT_ATTACK; } /*-------------------------------------------------- diff --git a/src/k_roulette.c b/src/k_roulette.c index 62d8e7fd1..5a0d73ebb 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -823,6 +823,10 @@ static void K_CalculateRouletteSpeed(player_t *const player, itemroulette_t *con } roulette->tics = roulette->speed = ROULETTE_SPEED_FASTEST + FixedMul(ROULETTE_SPEED_SLOWEST - ROULETTE_SPEED_FASTEST, total); + + // Make them select their item after a little while. + // One of the few instances of bot RNG, would be nice to remove it. + player->botvars.itemdelay = P_RandomRange(PR_UNDEFINED, TICRATE, TICRATE*3); } void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) From 13d7c791f554d52613eb94861f90bff1bf3bb568 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 12 Dec 2022 04:36:32 -0500 Subject: [PATCH 08/46] Make the slowest speed slower Was designed for Snap's tiny item sprites, for the huge ones it still feels pretty fast. The fastest speed is unchanged, though :) --- src/k_roulette.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_roulette.c b/src/k_roulette.c index 5a0d73ebb..124d0a67c 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -67,7 +67,7 @@ // Consistent seed used for item reels #define ITEM_REEL_SEED (0x22D5FAA8) -#define ROULETTE_SPEED_SLOWEST (12) +#define ROULETTE_SPEED_SLOWEST (20) #define ROULETTE_SPEED_FASTEST (2) #define ROULETTE_SPEED_DIST (224*DISTVAR) From 8d2eb9220d4905a10db92c35467c669cbd53ed10 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 12 Dec 2022 11:46:03 -0500 Subject: [PATCH 09/46] Make Super Ring flood happen at 0 rings too --- src/k_roulette.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_roulette.c b/src/k_roulette.c index 124d0a67c..f154e261e 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -945,9 +945,9 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) // If we're in ring debt, pad out the reel with // a BUNCH of Super Rings. - if (K_ItemEnabled(KITEM_SUPERRING) - && player->rings < 0 - && !(gametyperules & GTR_SPHERES)) + if (K_ItemEnabled(KITEM_SUPERRING) == true + && player->rings <= 0 + && (gametyperules & GTR_SPHERES) == 0) { K_PushToRouletteItemList(roulette, KITEM_SUPERRING); } From 63f6b18d59c9f9e719710a319a3a63dd6f3bc4f8 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 12 Dec 2022 11:55:28 -0500 Subject: [PATCH 10/46] No user for roulette itemlist Fixes the occasional Z_Free complaint --- src/k_roulette.c | 9 ++++----- src/p_saveg.c | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/k_roulette.c b/src/k_roulette.c index f154e261e..2a62bf7df 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -730,16 +730,15 @@ static void K_InitRoulette(itemroulette_t *const roulette) roulette->itemList = Z_Calloc( sizeof(SINT8) * roulette->itemListCap, PU_LEVEL, - &roulette->itemList + NULL ); } - memset(roulette->itemList, KITEM_NONE, sizeof(SINT8) * roulette->itemListCap); roulette->itemListLen = 0; - roulette->index = 0; + roulette->elapsed = 0; - roulette->tics = roulette->speed = 3; // Some default speed + roulette->tics = roulette->speed = ROULETTE_SPEED_FASTEST; // Some default speed roulette->active = true; roulette->eggman = false; } @@ -755,7 +754,7 @@ static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t roulette->itemList, sizeof(SINT8) * roulette->itemListCap, PU_LEVEL, - &roulette->itemList + NULL ); } diff --git a/src/p_saveg.c b/src/p_saveg.c index fee092df9..0587b96f8 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -730,7 +730,7 @@ static void P_NetUnArchivePlayers(void) players[i].itemRoulette.itemList = Z_Calloc( sizeof(SINT8) * players[i].itemRoulette.itemListCap, PU_LEVEL, - &players[i].itemRoulette.itemList + NULL ); } else @@ -739,7 +739,7 @@ static void P_NetUnArchivePlayers(void) players[i].itemRoulette.itemList, sizeof(SINT8) * players[i].itemRoulette.itemListCap, PU_LEVEL, - &players[i].itemRoulette.itemList + NULL ); } From 9e3ded610d33d55663efbeeb742b49a595c7e046 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 12 Dec 2022 16:19:45 -0500 Subject: [PATCH 11/46] Get rid of my TODO notes I implemented it, lol --- src/k_roulette.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/k_roulette.c b/src/k_roulette.c index 2a62bf7df..2357cde62 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -764,11 +764,6 @@ static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t static void K_CalculateRouletteSpeed(player_t *const player, itemroulette_t *const roulette) { - // TODO: Change speed based on two factors: - // - Get faster when your distancetofinish is closer to 1st place's distancetofinish. (winning) - // - Get faster based on overall distancetofinish (race progress) - // Slowest speed should be 12 tics, fastest should be 3 tics. - fixed_t frontRun = 0; fixed_t progress = 0; fixed_t total = 0; From b0537de79e41de2c31495948be8e1b41acfecfcb Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 12 Dec 2022 16:44:24 -0500 Subject: [PATCH 12/46] Re-implement debugitemodds --- src/d_player.h | 1 + src/k_hud.c | 92 ++++++++++++------------------------------------ src/k_roulette.c | 7 ++-- src/p_saveg.c | 20 +++++++---- 4 files changed, 41 insertions(+), 79 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 842edbc0e..2249d9f46 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -339,6 +339,7 @@ struct itemroulette_t size_t itemListLen; SINT8 *itemList; + UINT8 useOdds; size_t index; UINT8 sound; diff --git a/src/k_hud.c b/src/k_hud.c index 5ba874913..2a78c09de 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -4468,13 +4468,7 @@ K_drawMiniPing (void) static void K_drawDistributionDebugger(void) { -#if 1 - // TODO: Create a roulette struct and initialize it, - // and use that to draw the data, instead of recalculating - // most of it. - return; -#else - patch_t *items[NUMKARTRESULTS] = { + patch_t *patches[NUMKARTRESULTS] = { kp_sadface[1], kp_sneaker[1], kp_rocketsneaker[1], @@ -4506,87 +4500,47 @@ static void K_drawDistributionDebugger(void) kp_orbinaut[4], kp_jawz[1] }; - UINT8 useodds = 0; - UINT8 pingame = 0, bestbumper = 0; - UINT32 pdis = 0; - INT32 i; - INT32 x = -9, y = -9; + + itemroulette_t rouletteData = {0}; + + const INT32 space = 32; + const INT32 pad = 9; + + INT32 x = -pad, y = -pad; + size_t i; if (stplyr != &players[displayplayers[0]]) // only for p1 - return; - - if (K_ForcedSPB(stplyr) == true) { - V_DrawScaledPatch(x, y, V_SNAPTOTOP, items[KITEM_SPB]); - V_DrawThinString(x+11, y+31, V_ALLOWLOWERCASE|V_SNAPTOTOP, "EX"); return; } - // The only code duplication from the Kart, just to avoid the actual item function from calculating pingame twice - for (i = 0; i < MAXPLAYERS; i++) + K_StartItemRoulette(stplyr, &rouletteData); + + for (i = 0; i < rouletteData.itemListLen; i++) { - if (!playeringame[i] || players[i].spectator) - continue; - pingame++; - if (players[i].bumpers > bestbumper) - bestbumper = players[i].bumpers; - } + const kartitems_t item = rouletteData.itemList[i]; + UINT8 amount = 1; - // lovely double loop...... - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator - && players[i].position == 1) - { - // This player is first! Yay! - pdis = stplyr->distancetofinish - players[i].distancetofinish; - break; - } - } - - pdis = K_ScaleItemDistance(pdis, pingame); - - if (stplyr->bot && stplyr->botvars.rival) - { - // Rival has better odds :) - pdis = (15 * pdis) / 14; - } - - useodds = K_FindUseodds(stplyr, pdis); - - for (i = 1; i < NUMKARTRESULTS; i++) - { - INT32 itemodds = K_KartGetItemOdds( - useodds, i, - stplyr->distancetofinish, - 0, - stplyr->bot, (stplyr->bot && stplyr->botvars.rival) - ); - INT32 amount = 1; - - if (itemodds <= 0) - continue; - - V_DrawScaledPatch(x, y, V_SNAPTOTOP, items[i]); - V_DrawThinString(x+11, y+31, V_SNAPTOTOP, va("%d", itemodds)); + V_DrawScaledPatch(x, y, V_SNAPTOTOP, patches[item]); // Display amount for multi-items - amount = K_ItemResultToAmount(i); + amount = K_ItemResultToAmount(item); if (amount > 1) { V_DrawString(x+24, y+31, V_ALLOWLOWERCASE|V_SNAPTOTOP, va("x%d", amount)); } - x += 32; - if (x >= 297) + y += space; + if (y > BASEVIDHEIGHT - space - pad) { - x = -9; - y += 32; + x += space; + y = -pad; } } - V_DrawString(0, 0, V_SNAPTOTOP, va("USEODDS %d", useodds)); -#endif + V_DrawString(0, 0, V_ALLOWLOWERCASE|V_SNAPTOTOP, va("Table #%d", rouletteData.useOdds)); + + Z_Free(rouletteData.itemList); } static void K_DrawWaypointDebugger(void) diff --git a/src/k_roulette.c b/src/k_roulette.c index 2357cde62..5951b4f42 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -736,9 +736,11 @@ static void K_InitRoulette(itemroulette_t *const roulette) roulette->itemListLen = 0; roulette->index = 0; + roulette->useOdds = UINT8_MAX; roulette->elapsed = 0; roulette->tics = roulette->speed = ROULETTE_SPEED_FASTEST; // Some default speed + roulette->active = true; roulette->eggman = false; } @@ -828,7 +830,6 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) UINT8 playing = 0; UINT32 playerDist = UINT32_MAX; - UINT8 useOdds = 0; UINT32 spawnChance[NUMKARTRESULTS] = {0}; UINT32 totalSpawnChance = 0; size_t rngRoll = 0; @@ -903,12 +904,12 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) playerDist = K_GetItemRouletteDistance(player, playing); - useOdds = K_FindUseodds(player, playerDist); + roulette->useOdds = K_FindUseodds(player, playerDist); for (i = 1; i < NUMKARTRESULTS; i++) { INT32 thisItemsOdds = K_KartGetItemOdds( - useOdds, i, + roulette->useOdds, i, player->distancetofinish, player->bot, (player->bot && player->botvars.rival) ); diff --git a/src/p_saveg.c b/src/p_saveg.c index 0587b96f8..99a06782d 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -416,10 +416,13 @@ static void P_NetArchivePlayers(void) { WRITESINT8(save_p, players[i].itemRoulette.itemList[j]); } + WRITEUINT8(save_p, players[i].itemRoulette.useOdds); WRITEUINT32(save_p, players[i].itemRoulette.index); + WRITEUINT8(save_p, players[i].itemRoulette.sound); WRITEUINT32(save_p, players[i].itemRoulette.speed); WRITEUINT32(save_p, players[i].itemRoulette.tics); WRITEUINT32(save_p, players[i].itemRoulette.elapsed); + WRITEUINT8(save_p, players[i].itemRoulette.eggman); } } @@ -721,9 +724,9 @@ static void P_NetUnArchivePlayers(void) players[i].botvars.spindashconfirm = READUINT32(save_p); // itemroulette_t - players[i].itemRoulette.active = READUINT8(save_p); - players[i].itemRoulette.itemListCap = READUINT32(save_p); - players[i].itemRoulette.itemListLen = READUINT32(save_p); + players[i].itemRoulette.active = (boolean)READUINT8(save_p); + players[i].itemRoulette.itemListCap = (size_t)READUINT32(save_p); + players[i].itemRoulette.itemListLen = (size_t)READUINT32(save_p); if (players[i].itemRoulette.itemList == NULL) { @@ -748,10 +751,13 @@ static void P_NetUnArchivePlayers(void) players[i].itemRoulette.itemList[j] = READSINT8(save_p); } - players[i].itemRoulette.index = READUINT32(save_p); - players[i].itemRoulette.speed = READUINT32(save_p); - players[i].itemRoulette.tics = READUINT32(save_p); - players[i].itemRoulette.elapsed = READUINT32(save_p); + players[i].itemRoulette.useOdds = READUINT8(save_p); + players[i].itemRoulette.index = (size_t)READUINT32(save_p); + players[i].itemRoulette.sound = READUINT8(save_p); + players[i].itemRoulette.speed = (tic_t)READUINT32(save_p); + players[i].itemRoulette.tics = (tic_t)READUINT32(save_p); + players[i].itemRoulette.elapsed = (tic_t)READUINT32(save_p); + players[i].itemRoulette.eggman = (boolean)READUINT8(save_p); //players[i].viewheight = P_GetPlayerViewHeight(players[i]); // scale cannot be factored in at this point } From a73e62c163fe94b7a90d9805923de6d8f6c23b41 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 12 Dec 2022 16:59:29 -0500 Subject: [PATCH 13/46] Re-add roulette itemList user Turns out the Z_Free error is something else in r_patch.c, seems unrelated to what I made. --- src/k_roulette.c | 4 ++-- src/p_saveg.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/k_roulette.c b/src/k_roulette.c index 5951b4f42..ad0c754e4 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -730,7 +730,7 @@ static void K_InitRoulette(itemroulette_t *const roulette) roulette->itemList = Z_Calloc( sizeof(SINT8) * roulette->itemListCap, PU_LEVEL, - NULL + &roulette->itemList ); } @@ -756,7 +756,7 @@ static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t roulette->itemList, sizeof(SINT8) * roulette->itemListCap, PU_LEVEL, - NULL + &roulette->itemList ); } diff --git a/src/p_saveg.c b/src/p_saveg.c index 99a06782d..cb4aa7577 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -733,7 +733,7 @@ static void P_NetUnArchivePlayers(void) players[i].itemRoulette.itemList = Z_Calloc( sizeof(SINT8) * players[i].itemRoulette.itemListCap, PU_LEVEL, - NULL + &players[i].itemRoulette.itemList ); } else @@ -742,7 +742,7 @@ static void P_NetUnArchivePlayers(void) players[i].itemRoulette.itemList, sizeof(SINT8) * players[i].itemRoulette.itemListCap, PU_LEVEL, - NULL + &players[i].itemRoulette.itemList ); } From 7bdcb5883dafceb2c12e370ec54edaa979b915c4 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Sat, 5 Nov 2022 20:50:57 -0500 Subject: [PATCH 14/46] cmake: Enable C++ 17 and C11 --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5b681d042..b1099fd1d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,7 +5,7 @@ if("${CMAKE_COMPILER_IS_GNUCC}" AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows" AND target_link_options(SRB2SDL2 PRIVATE "-static") endif() -set_property(TARGET SRB2SDL2 PROPERTY C_STANDARD 11) +target_compile_features(SRB2SDL2 PRIVATE c_std_11 cxx_std_17) # Core sources target_sourcefile(c) From 5139d1276b941153305e6d536e591a0ccb76a121 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Sun, 6 Nov 2022 01:08:43 -0600 Subject: [PATCH 15/46] cmake: Add Catch2 unit testing --- CMakeLists.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 10c65a167..2f65f4d40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,7 @@ option( "Link dependencies using CMake's find_package and do not use internal builds" ${SRB2_CONFIG_SYSTEM_LIBRARIES_DEFAULT} ) +option(SRB2_CONFIG_ENABLE_TESTS "Build the test suite" ON) # This option isn't recommended for distribution builds and probably won't work (yet). cmake_dependent_option( SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES @@ -81,6 +82,25 @@ option(SRB2_CONFIG_ZDEBUG "Compile with ZDEBUG defined." OFF) option(SRB2_CONFIG_PROFILEMODE "Compile for profiling (GCC only)." OFF) set(SRB2_CONFIG_ASSET_DIRECTORY "" CACHE PATH "Path to directory that contains all asset files for the installer. If set, assets will be part of installation and cpack.") +if(SRB2_CONFIG_ENABLE_TESTS) + # https://github.com/catchorg/Catch2 + CPMAddPackage( + NAME Catch2 + VERSION 3.1.1 + GITHUB_REPOSITORY catchorg/Catch2 + OPTIONS + "CATCH_INSTALL_DOCS OFF" + ) + list(APPEND CMAKE_MODULE_PATH "${Catch2_SOURCE_DIR}/extras") + include(CTest) + include(Catch) + add_executable(srb2tests) + # To add tests, use target_sources to add individual test files to the target in subdirs. + target_link_libraries(srb2tests PRIVATE Catch2::Catch2 Catch2::Catch2WithMain) + target_compile_features(srb2tests PRIVATE c_std_11 cxx_std_17) + catch_discover_tests(srb2tests) +endif() + # Enable CCache # (Set USE_CCACHE=ON to use, CCACHE_OPTIONS for options) if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL Windows) From 7423b05f46bbc9420ccda8dd7677de38701bf748 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Sun, 13 Nov 2022 20:06:15 -0600 Subject: [PATCH 16/46] Redefine boolean for C++ compatibility --- src/CMakeLists.txt | 1 + src/doomtype.h | 58 +++++++++++++++++----------------------- src/tests/CMakeLists.txt | 3 +++ src/tests/boolcompat.cpp | 8 ++++++ 4 files changed, 37 insertions(+), 33 deletions(-) create mode 100644 src/tests/CMakeLists.txt create mode 100644 src/tests/boolcompat.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b1099fd1d..97690d812 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -370,6 +370,7 @@ endif() add_subdirectory(sdl) add_subdirectory(objects) +add_subdirectory(tests) # strip debug symbols into separate file when using gcc. # to be consistent with Makefile, don't generate for OS X. diff --git a/src/doomtype.h b/src/doomtype.h index 95f060008..15de30e21 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -17,6 +17,10 @@ #ifndef __DOOMTYPE__ #define __DOOMTYPE__ +#ifdef __cplusplus +extern "C" { +#endif + #ifdef _WIN32 //#define WIN32_LEAN_AND_MEAN #define RPC_NO_WINDOWS_H @@ -107,24 +111,6 @@ typedef long ssize_t; char *strcasestr(const char *in, const char *what); #define stristr strcasestr -#if defined (macintosh) //|| defined (__APPLE__) //skip all boolean/Boolean crap - #define true 1 - #define false 0 - #define min(x,y) (((x)<(y)) ? (x) : (y)) - #define max(x,y) (((x)>(y)) ? (x) : (y)) - -#ifdef macintosh - #define stricmp strcmp - #define strnicmp strncmp -#endif - - #define boolean INT32 - - #ifndef O_BINARY - #define O_BINARY 0 - #endif -#endif //macintosh - #if defined (PC_DOS) || defined (_WIN32) || defined (__HAIKU__) #define HAVE_DOSSTR_FUNCS #endif @@ -151,22 +137,24 @@ size_t strlcpy(char *dst, const char *src, size_t siz); /* Boolean type definition */ -// \note __BYTEBOOL__ used to be set above if "macintosh" was defined, -// if macintosh's version of boolean type isn't needed anymore, then isn't this macro pointless now? -#ifndef __BYTEBOOL__ - #define __BYTEBOOL__ +// Note: C++ bool and C99/C11 _Bool are NOT compatible. +// Historically, boolean was win32 BOOL on Windows. For equivalence, it's now +// int32_t. "true" and "false" are only declared for C code; in C++, conversion +// between "bool" and "int32_t" takes over. +#ifndef _WIN32 +typedef int32_t boolean; +#else +#define BOOL boolean +#endif - //faB: clean that up !! - #if defined( _MSC_VER) && (_MSC_VER >= 1800) // MSVC 2013 and forward - #include "stdbool.h" - #elif defined (_WIN32) - #define false FALSE // use windows types - #define true TRUE - #define boolean BOOL - #else - typedef enum {false, true} boolean; - #endif -#endif // __BYTEBOOL__ +#ifndef __cplusplus +#ifndef _WIN32 +enum {false = 0, true = 1}; +#else +#define false FALSE +#define true TRUE +#endif +#endif /* 7.18.2.1 Limits of exact-width integer types */ @@ -408,4 +396,8 @@ typedef UINT64 precise_t; #include "typedef.h" +#ifdef __cplusplus +} // extern "C" +#endif + #endif //__DOOMTYPE__ diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt new file mode 100644 index 000000000..28c4ce492 --- /dev/null +++ b/src/tests/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources(srb2tests PRIVATE + boolcompat.cpp +) diff --git a/src/tests/boolcompat.cpp b/src/tests/boolcompat.cpp new file mode 100644 index 000000000..fee40cd36 --- /dev/null +++ b/src/tests/boolcompat.cpp @@ -0,0 +1,8 @@ +#include + +#include "../doomtype.h" + +TEST_CASE("C++ bool is convertible to doomtype.h boolean") { + REQUIRE(static_cast(true) == 1); + REQUIRE(static_cast(false) == 0); +} From dceeadd3aa2e381f5632085dcddd7ef49dbf0527 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Sun, 13 Nov 2022 20:07:36 -0600 Subject: [PATCH 17/46] Don't preproc. define `inline` in C++ --- src/doomtype.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/doomtype.h b/src/doomtype.h index 15de30e21..fd5e6c2c4 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -92,7 +92,9 @@ typedef long ssize_t; #endif #define strncasecmp strnicmp #define strcasecmp stricmp +#ifndef __cplusplus #define inline __inline +#endif #elif defined (__WATCOMC__) #include #include From 15acefcc33dcfc117e98d4e2c62b208c01019cf3 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Mon, 12 Dec 2022 16:38:25 -0600 Subject: [PATCH 18/46] c++: Make some defines C++-valid --- src/doomdef.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index f54916599..669b90c71 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -126,10 +126,10 @@ extern char logfilename[1024]; // VERSIONSTRING_RC is for the resource-definition script used by windows builds #else #ifdef BETAVERSION -#define VERSIONSTRING "v"SRB2VERSION" "BETAVERSION +#define VERSIONSTRING "v" SRB2VERSION " " BETAVERSION #define VERSIONSTRING_RC SRB2VERSION " " BETAVERSION "\0" #else -#define VERSIONSTRING "v"SRB2VERSION +#define VERSIONSTRING "v" SRB2VERSION #define VERSIONSTRING_RC SRB2VERSION "\0" #endif // Hey! If you change this, add 1 to the MODVERSION below! @@ -614,12 +614,14 @@ UINT32 quickncasehash (const char *p, size_t n) return x; } +#ifndef __cplusplus #ifndef min // Double-Check with WATTCP-32's cdefs.h #define min(x, y) (((x) < (y)) ? (x) : (y)) #endif #ifndef max // Double-Check with WATTCP-32's cdefs.h #define max(x, y) (((x) > (y)) ? (x) : (y)) #endif +#endif // Max gamepad/joysticks that can be detected/used. #define MAX_JOYSTICKS 4 From 51e0bed20ad915401898306d409ff29b7e8015ad Mon Sep 17 00:00:00 2001 From: Eidolon Date: Mon, 12 Dec 2022 16:40:56 -0600 Subject: [PATCH 19/46] c++: add finally RAII utility This mirrors the `finally` utility in Guidelines Support Library for ensuring that a cleanup function is always called when the utility object leaves scope, even when unwinding the stack from an exception. This is to aid in transitioning malloc/free pairs in function bodies to be more exception-safe. --- src/cxxutil.hpp | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/cxxutil.hpp diff --git a/src/cxxutil.hpp b/src/cxxutil.hpp new file mode 100644 index 000000000..befe9c20b --- /dev/null +++ b/src/cxxutil.hpp @@ -0,0 +1,37 @@ +#ifndef __SRB2_CXXUTIL_HPP__ +#define __SRB2_CXXUTIL_HPP__ + +#include +#include + +namespace srb2 { + +template +class Finally { +public: + explicit Finally(const F& f) noexcept : f_(f) {} + explicit Finally(F&& f) noexcept : f_(f) {} + + Finally(Finally&& from) noexcept : f_(std::move(from.f_)), call_(std::exchange(from.call_, false)) {} + + Finally(const Finally& from) = delete; + void operator=(const Finally& from) = delete; + void operator=(Finally&& from) = delete; + + ~Finally() noexcept { + f_(); + } + +private: + F f_; + bool call_ = true; +}; + +template +Finally> finally(F&& f) noexcept { + return Finally {std::forward(f)}; +} + +} + +#endif // __SRB2_CXXUTIL_HPP__ From 8534703de6278a350e7f507e0861f3678eabaf58 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 12 Dec 2022 17:43:32 -0500 Subject: [PATCH 20/46] Further improve item debugger visibility - Half the scale of the item graphics, and reduce the spacing, so more of the reel can fit in one column. - Move the item table text over to the right, depending on how many columns were drawn. - Display the item roulette speed, as well. --- src/k_hud.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index 2a78c09de..6dc556f80 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -4503,10 +4503,12 @@ static void K_drawDistributionDebugger(void) itemroulette_t rouletteData = {0}; - const INT32 space = 32; - const INT32 pad = 9; + const fixed_t scale = (FRACUNIT >> 1); + const fixed_t space = 24 * scale; + const fixed_t pad = 9 * scale; - INT32 x = -pad, y = -pad; + fixed_t x = -pad; + fixed_t y = -pad; size_t i; if (stplyr != &players[displayplayers[0]]) // only for p1 @@ -4521,24 +4523,33 @@ static void K_drawDistributionDebugger(void) const kartitems_t item = rouletteData.itemList[i]; UINT8 amount = 1; - V_DrawScaledPatch(x, y, V_SNAPTOTOP, patches[item]); + if (y > (BASEVIDHEIGHT << FRACBITS) - space - pad) + { + x += space; + y = -pad; + } + + V_DrawFixedPatch(x, y, scale, V_SNAPTOTOP, patches[item], NULL); // Display amount for multi-items amount = K_ItemResultToAmount(item); if (amount > 1) { - V_DrawString(x+24, y+31, V_ALLOWLOWERCASE|V_SNAPTOTOP, va("x%d", amount)); + V_DrawStringScaled( + x + (18 * scale), + y + (23 * scale), + scale, FRACUNIT, FRACUNIT, + V_ALLOWLOWERCASE|V_SNAPTOTOP, + NULL, HU_FONT, + va("x%d", amount) + ); } y += space; - if (y > BASEVIDHEIGHT - space - pad) - { - x += space; - y = -pad; - } } - V_DrawString(0, 0, V_ALLOWLOWERCASE|V_SNAPTOTOP, va("Table #%d", rouletteData.useOdds)); + V_DrawString((x >> FRACBITS) + 20, 2, V_ALLOWLOWERCASE|V_SNAPTOTOP, va("useOdds[%u]", rouletteData.useOdds)); + V_DrawString((x >> FRACBITS) + 20, 10, V_ALLOWLOWERCASE|V_SNAPTOTOP, va("speed = %u", rouletteData.speed)); Z_Free(rouletteData.itemList); } From 7713ce0ebbbd34fdda274f04c2a2e59807894d83 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 12 Dec 2022 19:41:33 -0500 Subject: [PATCH 21/46] Fix speed being messed up near level start Used the wrong constant, leftover from an earlier experiment that was only partially reverted. Fixes "ghost" roulettes. --- src/k_roulette.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_roulette.c b/src/k_roulette.c index ad0c754e4..dc0ae76c7 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -814,7 +814,7 @@ static void K_CalculateRouletteSpeed(player_t *const player, itemroulette_t *con { // Don't impact as much at the start. // This makes it so that everyone gets to enjoy the lowest speed at the start. - fixed_t lerp = FRACUNIT - FixedDiv(max(0, leveltime - starttime), 10*TICRATE); + fixed_t lerp = FRACUNIT - FixedDiv(max(0, leveltime - starttime), 20*TICRATE); total += FixedMul(lerp, FRACUNIT - total); } From 59ee8177d7faa9b6d269d96b8fcd553083c411d9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 13 Dec 2022 00:00:37 -0500 Subject: [PATCH 22/46] Try some things for roulette sync - PU_STATIC instead of PU_LEVEL, since player_t is always kept around anyway. - Don't alloc itemList when cap is 0 - Read/write 0 when itemList is NULL --- src/k_roulette.c | 4 ++-- src/p_saveg.c | 52 ++++++++++++++++++++++++++++++------------------ 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/k_roulette.c b/src/k_roulette.c index dc0ae76c7..d07246a66 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -729,7 +729,7 @@ static void K_InitRoulette(itemroulette_t *const roulette) roulette->itemListCap = 8; roulette->itemList = Z_Calloc( sizeof(SINT8) * roulette->itemListCap, - PU_LEVEL, + PU_STATIC, &roulette->itemList ); } @@ -755,7 +755,7 @@ static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t roulette->itemList = Z_Realloc( roulette->itemList, sizeof(SINT8) * roulette->itemListCap, - PU_LEVEL, + PU_STATIC, &roulette->itemList ); } diff --git a/src/p_saveg.c b/src/p_saveg.c index cb4aa7577..b69c63a5b 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -412,9 +412,16 @@ static void P_NetArchivePlayers(void) WRITEUINT8(save_p, players[i].itemRoulette.active); WRITEUINT32(save_p, players[i].itemRoulette.itemListCap); WRITEUINT32(save_p, players[i].itemRoulette.itemListLen); - for (j = 0; (unsigned)j < players[i].itemRoulette.itemListLen; j++) + for (j = 0; j < (signed)players[i].itemRoulette.itemListLen; j++) { - WRITESINT8(save_p, players[i].itemRoulette.itemList[j]); + if (players[i].itemRoulette.itemList == NULL) + { + WRITESINT8(save_p, KITEM_NONE); + } + else + { + WRITESINT8(save_p, players[i].itemRoulette.itemList[j]); + } } WRITEUINT8(save_p, players[i].itemRoulette.useOdds); WRITEUINT32(save_p, players[i].itemRoulette.index); @@ -728,27 +735,34 @@ static void P_NetUnArchivePlayers(void) players[i].itemRoulette.itemListCap = (size_t)READUINT32(save_p); players[i].itemRoulette.itemListLen = (size_t)READUINT32(save_p); - if (players[i].itemRoulette.itemList == NULL) + if (players[i].itemRoulette.itemListCap > 0) { - players[i].itemRoulette.itemList = Z_Calloc( - sizeof(SINT8) * players[i].itemRoulette.itemListCap, - PU_LEVEL, - &players[i].itemRoulette.itemList - ); - } - else - { - players[i].itemRoulette.itemList = Z_Realloc( - players[i].itemRoulette.itemList, - sizeof(SINT8) * players[i].itemRoulette.itemListCap, - PU_LEVEL, - &players[i].itemRoulette.itemList - ); + if (players[i].itemRoulette.itemList == NULL) + { + players[i].itemRoulette.itemList = Z_Calloc( + sizeof(SINT8) * players[i].itemRoulette.itemListCap, + PU_STATIC, + &players[i].itemRoulette.itemList + ); + } + else + { + players[i].itemRoulette.itemList = Z_Realloc( + players[i].itemRoulette.itemList, + sizeof(SINT8) * players[i].itemRoulette.itemListCap, + PU_STATIC, + &players[i].itemRoulette.itemList + ); + } } - for (j = 0; (unsigned)j < players[i].itemRoulette.itemListLen; j++) + for (j = 0; j < (signed)players[i].itemRoulette.itemListLen; j++) { - players[i].itemRoulette.itemList[j] = READSINT8(save_p); + SINT8 item = READSINT8(save_p); + if (players[i].itemRoulette.itemList != NULL) + { + players[i].itemRoulette.itemList[j] = item; + } } players[i].itemRoulette.useOdds = READUINT8(save_p); From c1845673615cb1e5878950a03072848bca285ea7 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 13 Dec 2022 12:06:52 -0500 Subject: [PATCH 23/46] No longer dynamically allocate roulette list Done with the issues trying to sync this memory over the wire, so now it's just a long static array... --- src/d_player.h | 10 +++++++++ src/k_roulette.c | 26 ++++++++++++++++++++++- src/p_saveg.c | 54 ++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 2249d9f46..fc45174d2 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -331,13 +331,23 @@ struct skybox_t { }; // player_t struct for item roulette variables + +// Doing this the right way is causing problems. +// so FINE, it's a static length now. +#define ITEM_LIST_SIZE (NUMKARTRESULTS * 20) + struct itemroulette_t { boolean active; +#ifdef ITEM_LIST_SIZE + size_t itemListLen; + SINT8 itemList[ITEM_LIST_SIZE]; +#else size_t itemListCap; size_t itemListLen; SINT8 *itemList; +#endif UINT8 useOdds; size_t index; diff --git a/src/k_roulette.c b/src/k_roulette.c index d07246a66..b136113b6 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -724,6 +724,7 @@ boolean K_ForcedSPB(player_t *const player) static void K_InitRoulette(itemroulette_t *const roulette) { +#ifndef ITEM_LIST_SIZE if (roulette->itemList == NULL) { roulette->itemListCap = 8; @@ -732,7 +733,13 @@ static void K_InitRoulette(itemroulette_t *const roulette) PU_STATIC, &roulette->itemList ); + + if (roulette->itemList == NULL) + { + I_Error("Not enough memory for item roulette list\n"); + } } +#endif roulette->itemListLen = 0; roulette->index = 0; @@ -747,6 +754,13 @@ static void K_InitRoulette(itemroulette_t *const roulette) static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t item) { +#ifdef ITEM_LIST_SIZE + if (roulette->itemListLen >= ITEM_LIST_SIZE) + { + I_Error("Out of space for item reel! Go and make ITEM_LIST_SIZE bigger I guess?\n"); + return; + } +#else I_Assert(roulette->itemList != NULL); if (roulette->itemListLen >= roulette->itemListCap) @@ -758,7 +772,13 @@ static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t PU_STATIC, &roulette->itemList ); + + if (roulette->itemList == NULL) + { + I_Error("Not enough memory for item roulette list\n"); + } } +#endif roulette->itemList[ roulette->itemListLen ] = item; roulette->itemListLen++; @@ -1008,7 +1028,11 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) return; } - if (roulette->itemList == NULL || roulette->itemListLen == 0) + if (roulette->itemListLen == 0 +#ifndef ITEM_LIST_SIZE + || roulette->itemList == NULL +#endif + ) { // Invalid roulette setup. // Escape before we run into issues. diff --git a/src/p_saveg.c b/src/p_saveg.c index b69c63a5b..46aa64147 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -96,7 +96,7 @@ static void P_NetArchivePlayers(void) { INT32 i, j; UINT16 flags; -// size_t q; + size_t q; WRITEUINT32(save_p, ARCHIVEBLOCK_PLAYERS); @@ -410,19 +410,39 @@ static void P_NetArchivePlayers(void) // itemroulette_t WRITEUINT8(save_p, players[i].itemRoulette.active); - WRITEUINT32(save_p, players[i].itemRoulette.itemListCap); + +#ifdef ITEM_LIST_SIZE WRITEUINT32(save_p, players[i].itemRoulette.itemListLen); - for (j = 0; j < (signed)players[i].itemRoulette.itemListLen; j++) + + for (q = 0; q < ITEM_LIST_SIZE; q++) { - if (players[i].itemRoulette.itemList == NULL) + if (q >= players[i].itemRoulette.itemListLen) { WRITESINT8(save_p, KITEM_NONE); } else { - WRITESINT8(save_p, players[i].itemRoulette.itemList[j]); + WRITESINT8(save_p, players[i].itemRoulette.itemList[q]); } } +#else + if (players[i].itemRoulette.itemList == NULL) + { + WRITEUINT32(save_p, 0); + WRITEUINT32(save_p, 0); + } + else + { + WRITEUINT32(save_p, players[i].itemRoulette.itemListCap); + WRITEUINT32(save_p, players[i].itemRoulette.itemListLen); + + for (q = 0; q < players[i].itemRoulette.itemListLen; q++) + { + WRITESINT8(save_p, players[i].itemRoulette.itemList[q]); + } + } +#endif + WRITEUINT8(save_p, players[i].itemRoulette.useOdds); WRITEUINT32(save_p, players[i].itemRoulette.index); WRITEUINT8(save_p, players[i].itemRoulette.sound); @@ -437,6 +457,7 @@ static void P_NetUnArchivePlayers(void) { INT32 i, j; UINT16 flags; + size_t q; if (READUINT32(save_p) != ARCHIVEBLOCK_PLAYERS) I_Error("Bad $$$.sav at archive block Players"); @@ -732,6 +753,15 @@ static void P_NetUnArchivePlayers(void) // itemroulette_t players[i].itemRoulette.active = (boolean)READUINT8(save_p); + +#ifdef ITEM_LIST_SIZE + players[i].itemRoulette.itemListLen = (size_t)READUINT32(save_p); + + for (q = 0; q < ITEM_LIST_SIZE; q++) + { + players[i].itemRoulette.itemList[q] = READSINT8(save_p); + } +#else players[i].itemRoulette.itemListCap = (size_t)READUINT32(save_p); players[i].itemRoulette.itemListLen = (size_t)READUINT32(save_p); @@ -754,16 +784,18 @@ static void P_NetUnArchivePlayers(void) &players[i].itemRoulette.itemList ); } - } - for (j = 0; j < (signed)players[i].itemRoulette.itemListLen; j++) - { - SINT8 item = READSINT8(save_p); - if (players[i].itemRoulette.itemList != NULL) + if (players[i].itemRoulette.itemList == NULL) { - players[i].itemRoulette.itemList[j] = item; + I_Error("Not enough memory for item roulette list\n"); + } + + for (q = 0; q < players[i].itemRoulette.itemListLen; q++) + { + players[i].itemRoulette.itemList[q] = READSINT8(save_p); } } +#endif players[i].itemRoulette.useOdds = READUINT8(save_p); players[i].itemRoulette.index = (size_t)READUINT32(save_p); From 2a546df3fbc7caa3012b2dba92215769cec3eaa7 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 13 Dec 2022 12:44:11 -0500 Subject: [PATCH 24/46] Fix debugger breaking, reduce size --- src/d_player.h | 2 +- src/k_hud.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/d_player.h b/src/d_player.h index fc45174d2..1a447d8de 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -334,7 +334,7 @@ struct skybox_t { // Doing this the right way is causing problems. // so FINE, it's a static length now. -#define ITEM_LIST_SIZE (NUMKARTRESULTS * 20) +#define ITEM_LIST_SIZE (NUMKARTRESULTS << 2) struct itemroulette_t { diff --git a/src/k_hud.c b/src/k_hud.c index 6dc556f80..4e73b85a0 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -4551,7 +4551,9 @@ static void K_drawDistributionDebugger(void) V_DrawString((x >> FRACBITS) + 20, 2, V_ALLOWLOWERCASE|V_SNAPTOTOP, va("useOdds[%u]", rouletteData.useOdds)); V_DrawString((x >> FRACBITS) + 20, 10, V_ALLOWLOWERCASE|V_SNAPTOTOP, va("speed = %u", rouletteData.speed)); +#ifndef ITEM_LIST_SIZE Z_Free(rouletteData.itemList); +#endif } static void K_DrawWaypointDebugger(void) From 6b8a011aa1561137e8997f552236067e456ab8b3 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 13 Dec 2022 18:02:21 -0500 Subject: [PATCH 25/46] Flicker other items when selecting Makes the lerp back to the middle less jarring, since the items that were just there won't disappear. --- src/k_hud.c | 166 +++++++++++++++++++++++++++++++-------------------- src/k_kart.c | 11 +++- 2 files changed, 112 insertions(+), 65 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index 4e73b85a0..30c41e227 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1023,7 +1023,7 @@ static void K_drawKartItem(void) // Why write V_DrawScaledPatch calls over and over when they're all the same? // Set to 'no item' just in case. const UINT8 offset = ((r_splitscreen > 1) ? 1 : 0); - patch_t *localpatch[3] = {kp_nodraw}; + patch_t *localpatch[3] = { kp_nodraw }; patch_t *localbg = ((offset) ? kp_itembg[2] : kp_itembg[0]); patch_t *localinv = ((offset) ? kp_invincibility[((leveltime % (6*3)) / 3) + 7] : kp_invincibility[(leveltime % (7*3)) / 3]); INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags... @@ -1031,24 +1031,17 @@ static void K_drawKartItem(void) INT32 itembar = 0; INT32 maxl = 0; // itembar's normal highest value const INT32 barlength = (r_splitscreen > 1 ? 12 : 26); - UINT16 localcolor = SKINCOLOR_NONE; - SINT8 colormode = TC_RAINBOW; - UINT8 *colmap = NULL; + UINT16 localcolor[3] = { stplyr->skincolor }; + SINT8 colormode[3] = { TC_RAINBOW }; boolean flipamount = false; // Used for 3P/4P splitscreen to flip item amount stuff fixed_t rouletteOffset = 0; const INT32 rouletteBox = 36; INT32 i; - if (stplyr->itemRoulette.active == true) + if (stplyr->itemRoulette.itemListLen > 0) { - rouletteOffset = K_GetRouletteOffset(&stplyr->itemRoulette, rendertimefrac); - - if (stplyr->skincolor) - { - localcolor = stplyr->skincolor; - } - + // Init with item roulette stuff. for (i = 0; i < 3; i++) { const SINT8 indexOfs = i-1; @@ -1074,6 +1067,11 @@ static void K_drawKartItem(void) } } } + + if (stplyr->itemRoulette.active == true) + { + rouletteOffset = K_GetRouletteOffset(&stplyr->itemRoulette, rendertimefrac); + } else { // I'm doing this a little weird and drawing mostly in reverse order @@ -1081,25 +1079,26 @@ static void K_drawKartItem(void) // This shouldn't have any actual baring over how it functions // Hyudoro is first, because we're drawing it on top of the player's current item + localcolor[1] = SKINCOLOR_NONE; rouletteOffset = stplyr->karthud[khud_rouletteoffset]; if (stplyr->stealingtimer < 0) { if (leveltime & 2) - localpatch[0] = kp_hyudoro[offset]; + localpatch[1] = kp_hyudoro[offset]; else - localpatch[0] = kp_nodraw; + localpatch[1] = kp_nodraw; } else if ((stplyr->stealingtimer > 0) && (leveltime & 2)) { - localpatch[0] = kp_hyudoro[offset]; + localpatch[1] = kp_hyudoro[offset]; } else if (stplyr->eggmanexplode > 1) { if (leveltime & 1) - localpatch[0] = kp_eggman[offset]; + localpatch[1] = kp_eggman[offset]; else - localpatch[0] = kp_nodraw; + localpatch[1] = kp_nodraw; } else if (stplyr->ballhogcharge > 0) { @@ -1107,9 +1106,9 @@ static void K_drawKartItem(void) maxl = (((stplyr->itemamount-1) * BALLHOGINCREMENT) + 1); if (leveltime & 1) - localpatch[0] = kp_ballhog[offset]; + localpatch[1] = kp_ballhog[offset]; else - localpatch[0] = kp_nodraw; + localpatch[1] = kp_nodraw; } else if (stplyr->rocketsneakertimer > 1) { @@ -1117,16 +1116,16 @@ static void K_drawKartItem(void) maxl = (itemtime*3) - barlength; if (leveltime & 1) - localpatch[0] = kp_rocketsneaker[offset]; + localpatch[1] = kp_rocketsneaker[offset]; else - localpatch[0] = kp_nodraw; + localpatch[1] = kp_nodraw; } else if (stplyr->sadtimer > 0) { if (leveltime & 2) - localpatch[0] = kp_sadface[offset]; + localpatch[1] = kp_sadface[offset]; else - localpatch[0] = kp_nodraw; + localpatch[1] = kp_nodraw; } else { @@ -1136,12 +1135,12 @@ static void K_drawKartItem(void) switch (stplyr->itemtype) { case KITEM_INVINCIBILITY: - localpatch[0] = localinv; + localpatch[1] = localinv; localbg = kp_itembg[offset+1]; break; case KITEM_ORBINAUT: - localpatch[0] = kp_orbinaut[(offset ? 4 : min(stplyr->itemamount-1, 3))]; + localpatch[1] = kp_orbinaut[(offset ? 4 : min(stplyr->itemamount-1, 3))]; break; case KITEM_SPB: @@ -1152,34 +1151,41 @@ static void K_drawKartItem(void) /*FALLTHRU*/ default: - localpatch[0] = K_GetCachedItemPatch(stplyr->itemtype, offset); + localpatch[1] = K_GetCachedItemPatch(stplyr->itemtype, offset); - if (localpatch[0] == NULL) - localpatch[0] = kp_nodraw; // diagnose underflows + if (localpatch[1] == NULL) + localpatch[1] = kp_nodraw; // diagnose underflows break; } if ((stplyr->pflags & PF_ITEMOUT) && !(leveltime & 1)) - localpatch[0] = kp_nodraw; + localpatch[1] = kp_nodraw; } if (stplyr->karthud[khud_itemblink] && (leveltime & 1)) { - colormode = TC_BLINK; + colormode[1] = TC_BLINK; switch (stplyr->karthud[khud_itemblinkmode]) { case 2: - localcolor = K_RainbowColor(leveltime); + localcolor[1] = K_RainbowColor(leveltime); break; case 1: - localcolor = SKINCOLOR_RED; + localcolor[1] = SKINCOLOR_RED; break; default: - localcolor = SKINCOLOR_WHITE; + localcolor[1] = SKINCOLOR_WHITE; break; } } + else + { + // Hide the other items. + // Effectively lets the other roulette items + // show flicker away after you select. + localpatch[0] = localpatch[2] = kp_nodraw; + } } // pain and suffering defined below @@ -1206,45 +1212,77 @@ static void K_drawKartItem(void) } } - if (localcolor != SKINCOLOR_NONE) - colmap = R_GetTranslationColormap(colormode, localcolor, GTC_CACHE); - V_DrawScaledPatch(fx, fy, V_HUDTRANS|V_SLIDEIN|fflags, localbg); + // Need to draw these in a particular order, for sorting. + V_SetClipRect( + (fx + 7) << FRACBITS, (fy + 7) << FRACBITS, + rouletteBox << FRACBITS, rouletteBox << FRACBITS, + V_SLIDEIN|fflags + ); + + V_DrawFixedPatch( + fx<itemRoulette.active == true) { - V_SetClipRect((fx + 7) << FRACBITS, (fy + 7) << FRACBITS, rouletteBox << FRACBITS, rouletteBox << FRACBITS, V_HUDTRANS|V_SLIDEIN|fflags); - - // Need to draw these in a particular order, for sorting. - V_DrawFixedPatch(fx<itemamount >= numberdisplaymin && stplyr->itemRoulette.active == false) - { - // Then, the numbers: - V_DrawScaledPatch(fx + (flipamount ? 48 : 0), fy, V_HUDTRANS|V_SLIDEIN|fflags|(flipamount ? V_FLIP : 0), kp_itemmulsticker[offset]); // flip this graphic for p2 and p4 in split and shift it. - - V_DrawFixedPatch(fx<itemamount)); - else - V_DrawString(fx+24, fy+31, V_ALLOWLOWERCASE|V_HUDTRANS|V_SLIDEIN|fflags, va("x%d", stplyr->itemamount)); - } - else - { - V_DrawScaledPatch(fy+28, fy+41, V_HUDTRANS|V_SLIDEIN|fflags, kp_itemx); - V_DrawKartString(fx+38, fy+36, V_HUDTRANS|V_SLIDEIN|fflags, va("%d", stplyr->itemamount)); - } - } else { - V_DrawFixedPatch(fx<itemamount >= numberdisplaymin && stplyr->itemRoulette.active == false) + { + // Then, the numbers: + V_DrawScaledPatch( + fx + (flipamount ? 48 : 0), fy, + V_HUDTRANS|V_SLIDEIN|fflags|(flipamount ? V_FLIP : 0), + kp_itemmulsticker[offset] + ); // flip this graphic for p2 and p4 in split and shift it. + + V_DrawFixedPatch( + fx<itemamount)); + else + V_DrawString(fx+24, fy+31, V_ALLOWLOWERCASE|V_HUDTRANS|V_SLIDEIN|fflags, va("x%d", stplyr->itemamount)); + } + else + { + V_DrawScaledPatch(fy+28, fy+41, V_HUDTRANS|V_SLIDEIN|fflags, kp_itemx); + V_DrawKartString(fx+38, fy+36, V_HUDTRANS|V_SLIDEIN|fflags, va("%d", stplyr->itemamount)); + } + } + else + { + V_DrawFixedPatch( + fx<karthud[khud_rouletteoffset] != 0) { - player->karthud[khud_rouletteoffset] = FixedMul(player->karthud[khud_rouletteoffset], FRACUNIT*3/4); + if (abs(player->karthud[khud_rouletteoffset]) < (FRACUNIT >> 1)) + { + // Snap to 0, since the change is getting very small. + player->karthud[khud_rouletteoffset] = 0; + } + else + { + // Lerp to 0. + player->karthud[khud_rouletteoffset] = FixedMul(player->karthud[khud_rouletteoffset], FRACUNIT*3/4); + } } if (!(gametyperules & GTR_SPHERES)) From 8d43acccb7ac9fb5b7038b2993f4cf9920a4ed67 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 13 Dec 2022 18:03:44 -0500 Subject: [PATCH 26/46] Use predetermined roulette speed for Time Attack --- src/k_roulette.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/k_roulette.c b/src/k_roulette.c index b136113b6..07633a7e0 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -70,6 +70,7 @@ #define ROULETTE_SPEED_SLOWEST (20) #define ROULETTE_SPEED_FASTEST (2) #define ROULETTE_SPEED_DIST (224*DISTVAR) +#define ROULETTE_SPEED_TIMEATTACK (9) static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = { @@ -797,6 +798,10 @@ static void K_CalculateRouletteSpeed(player_t *const player, itemroulette_t *con size_t i; + // Make them select their item after a little while. + // One of the few instances of bot RNG, would be nice to remove it. + player->botvars.itemdelay = P_RandomRange(PR_UNDEFINED, TICRATE, TICRATE*3); + for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] == false || players[i].spectator == true) @@ -812,6 +817,13 @@ static void K_CalculateRouletteSpeed(player_t *const player, itemroulette_t *con } } + if (modeattacking || playing <= 1) + { + // Time Attack rules; use a consistent speed. + roulette->tics = roulette->speed = ROULETTE_SPEED_TIMEATTACK; + return; + } + ourDist = K_ScaleItemDistance(player->distancetofinish, playing); firstDist = K_ScaleItemDistance(firstDist, playing); @@ -834,15 +846,18 @@ static void K_CalculateRouletteSpeed(player_t *const player, itemroulette_t *con { // Don't impact as much at the start. // This makes it so that everyone gets to enjoy the lowest speed at the start. - fixed_t lerp = FRACUNIT - FixedDiv(max(0, leveltime - starttime), 20*TICRATE); - total += FixedMul(lerp, FRACUNIT - total); + if (leveltime < starttime) + { + total = FRACUNIT; + } + else + { + const fixed_t lerp = FixedDiv(leveltime - starttime, 20*TICRATE); + total = FRACUNIT + FixedMul(lerp, total - FRACUNIT); + } } roulette->tics = roulette->speed = ROULETTE_SPEED_FASTEST + FixedMul(ROULETTE_SPEED_SLOWEST - ROULETTE_SPEED_FASTEST, total); - - // Make them select their item after a little while. - // One of the few instances of bot RNG, would be nice to remove it. - player->botvars.itemdelay = P_RandomRange(PR_UNDEFINED, TICRATE, TICRATE*3); } void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) From f7e53ec81128a16fe0b15652cd4d07c4885cb7cd Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Dec 2022 01:23:23 -0500 Subject: [PATCH 27/46] Cleanup lots of the older roulette code - Most of it now requires a reference to itemroulette_t, rather than copying all of the variables over. - Increased the effect of Frantic Items, as the old scaling was made extremely low because item distance was exponential back then. - A lot of variables are pre-calculated in itemroulette_t instead of redoing the calculations every time (player count, first place's dist, etc) - Make most of these functions only accessible by k_roulette.c - Even when single items get forced into your roulette, the Super Ring Debt effect can happen - Make the game support setting single items from other gametypes (Pogo Spring-only races work now) - Fix some item distances not accounting for scale - Prevent multiple of certain items from rolling at once; Shrinks (not a big deal) and SPBs (OH GOD NO) --- src/d_player.h | 7 +- src/k_hud.c | 7 + src/k_kart.c | 12 +- src/k_roulette.c | 471 ++++++++++++++++++++++++----------------------- src/k_roulette.h | 10 +- src/p_saveg.c | 2 + 6 files changed, 266 insertions(+), 243 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 1a447d8de..81a80d641 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -334,7 +334,7 @@ struct skybox_t { // Doing this the right way is causing problems. // so FINE, it's a static length now. -#define ITEM_LIST_SIZE (NUMKARTRESULTS << 2) +#define ITEM_LIST_SIZE (NUMKARTRESULTS << 3) struct itemroulette_t { @@ -350,6 +350,11 @@ struct itemroulette_t #endif UINT8 useOdds; + UINT8 playing, exiting; + UINT32 dist, baseDist; + UINT32 firstDist, secondDist; + UINT32 secondToFirst; + size_t index; UINT8 sound; diff --git a/src/k_hud.c b/src/k_hud.c index 30c41e227..32904beaa 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -4589,6 +4589,13 @@ static void K_drawDistributionDebugger(void) V_DrawString((x >> FRACBITS) + 20, 2, V_ALLOWLOWERCASE|V_SNAPTOTOP, va("useOdds[%u]", rouletteData.useOdds)); V_DrawString((x >> FRACBITS) + 20, 10, V_ALLOWLOWERCASE|V_SNAPTOTOP, va("speed = %u", rouletteData.speed)); + V_DrawString((x >> FRACBITS) + 20, 22, V_ALLOWLOWERCASE|V_SNAPTOTOP, va("baseDist = %u", rouletteData.baseDist)); + V_DrawString((x >> FRACBITS) + 20, 30, V_ALLOWLOWERCASE|V_SNAPTOTOP, va("dist = %u", rouletteData.dist)); + + V_DrawString((x >> FRACBITS) + 20, 42, V_ALLOWLOWERCASE|V_SNAPTOTOP, va("firstDist = %u", rouletteData.firstDist)); + V_DrawString((x >> FRACBITS) + 20, 50, V_ALLOWLOWERCASE|V_SNAPTOTOP, va("secondDist = %u", rouletteData.secondDist)); + V_DrawString((x >> FRACBITS) + 20, 58, V_ALLOWLOWERCASE|V_SNAPTOTOP, va("secondToFirst = %u", rouletteData.secondToFirst)); + #ifndef ITEM_LIST_SIZE Z_Free(rouletteData.itemList); #endif diff --git a/src/k_kart.c b/src/k_kart.c index 235f838f8..b512a64c3 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6053,6 +6053,7 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 if (type == 0) { + itemroulette_t rouletteData = {0}; UINT8 useodds = 0; INT32 spawnchance[NUMKARTRESULTS]; INT32 totalspawnchance = 0; @@ -6062,12 +6063,12 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 useodds = amount; + K_StartItemRoulette(stplyr, &rouletteData); + for (i = 1; i < NUMKARTRESULTS; i++) { - spawnchance[i] = (totalspawnchance += K_KartGetItemOdds( - useodds, i, - UINT32_MAX, - false, false) + spawnchance[i] = ( + totalspawnchance += K_KartGetItemOdds(NULL, &rouletteData, useodds, i) ); } @@ -10498,8 +10499,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (spbplace == -1 || player->position != spbplace) player->pflags &= ~PF_RINGLOCK; // reset ring lock - if (player->itemtype == KITEM_SPB - || player->itemtype == KITEM_SHRINK) + if (K_ItemSingularity(player->itemtype) == true) { K_SetItemCooldown(player->itemtype, 20*TICRATE); } diff --git a/src/k_roulette.c b/src/k_roulette.c index 07633a7e0..b77659be6 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -59,17 +59,19 @@ #define SPBSTARTDIST (8*DISTVAR) // Distance when SPB is forced onto the next person who rolls an item -#define SPBFORCEDIST (14*DISTVAR) +#define SPBFORCEDIST (16*DISTVAR) // Distance when the game stops giving you bananas -#define ENDDIST (18*DISTVAR) +#define ENDDIST (14*DISTVAR) // Consistent seed used for item reels #define ITEM_REEL_SEED (0x22D5FAA8) +#define FRANTIC_ITEM_SCALE (FRACUNIT*6/5) + #define ROULETTE_SPEED_SLOWEST (20) #define ROULETTE_SPEED_FASTEST (2) -#define ROULETTE_SPEED_DIST (224*DISTVAR) +#define ROULETTE_SPEED_DIST (150*DISTVAR) #define ROULETTE_SPEED_TIMEATTACK (9) static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = @@ -176,7 +178,23 @@ boolean K_ItemEnabled(SINT8 item) return cv_items[item - 1].value; } -fixed_t K_ItemOddsScale(UINT8 playerCount) +boolean K_ItemSingularity(kartitems_t item) +{ + switch (item) + { + case KITEM_SPB: + case KITEM_SHRINK: + { + return true; + } + default: + { + return false; + } + } +} + +static fixed_t K_ItemOddsScale(UINT8 playerCount) { const UINT8 basePlayer = 8; // The player count we design most of the game around. fixed_t playerScaling = 0; @@ -205,18 +223,23 @@ fixed_t K_ItemOddsScale(UINT8 playerCount) return playerScaling; } -UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers) +static UINT32 K_UndoMapScaling(UINT32 distance) { if (mapobjectscale != FRACUNIT) { // Bring back to normal scale. - distance = FixedDiv(distance, mapobjectscale); + return FixedDiv(distance, mapobjectscale); } + return distance; +} + +static UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers) +{ if (franticitems == true) { // Frantic items pretends everyone's farther apart, for crazier items. - distance = (15 * distance) / 14; + distance = FixedMul(distance, FRANTIC_ITEM_SCALE); } // Items get crazier with the fewer players that you have. @@ -228,7 +251,7 @@ UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers) return distance; } -UINT32 K_GetItemRouletteDistance(player_t *const player, UINT8 pingame) +static UINT32 K_GetItemRouletteDistance(player_t *const player, UINT8 numPlayers) { UINT32 pdis = 0; @@ -274,12 +297,13 @@ UINT32 K_GetItemRouletteDistance(player_t *const player, UINT8 pingame) } } - pdis = K_ScaleItemDistance(pdis, pingame); + pdis = K_UndoMapScaling(pdis); + pdis = K_ScaleItemDistance(pdis, numPlayers); if (player->bot && player->botvars.rival) { // Rival has better odds :) - pdis = (15 * pdis) / 14; + pdis = FixedMul(pdis, FRANTIC_ITEM_SCALE); } return pdis; @@ -292,30 +316,33 @@ UINT32 K_GetItemRouletteDistance(player_t *const player, UINT8 pingame) \return void */ -INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, boolean bot, boolean rival) +INT32 K_KartGetItemOdds(player_t *const player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item) { - fixed_t newOdds; - INT32 i; + boolean bot = false; + boolean rival = false; + UINT8 position = 0; - UINT8 pingame = 0, pexiting = 0; - - player_t *first = NULL; - player_t *second = NULL; - - UINT32 firstDist = UINT32_MAX; - UINT32 secondDist = UINT32_MAX; - UINT32 secondToFirst = 0; - boolean isFirst = false; + INT32 shieldType = KSHIELD_NONE; boolean powerItem = false; boolean cooldownOnStart = false; boolean notNearEnd = false; - INT32 shieldType = KSHIELD_NONE; + fixed_t newOdds = 0; + size_t i, j; + + I_Assert(roulette != NULL); I_Assert(item > KITEM_NONE); // too many off by one scenarioes. I_Assert(item < NUMKARTRESULTS); + if (player != NULL) + { + bot = player->bot; + rival = (bot == true && player->botvars.rival == true); + position = player->position; + } + if (K_ItemEnabled(item) == false) { return 0; @@ -345,6 +372,58 @@ INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, boolean bot, bool */ (void)bot; + shieldType = K_GetShieldFromItem(item); + switch (shieldType) + { + case KSHIELD_NONE: + /* Marble Garden Top is not REALLY + a Sonic 3 shield */ + case KSHIELD_TOP: + { + break; + } + + default: + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] == false || players[i].spectator == true) + { + continue; + } + + if (shieldType == K_GetShieldFromItem(players[i].itemtype)) + { + // Don't allow more than one of each shield type at a time + return 0; + } + } + } + } + + if (K_ItemSingularity(item) == true) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] == false || players[i].spectator == true) + { + continue; + } + + if (players[i].itemRoulette.active == true) + { + for (j = 0; j < players[i].itemRoulette.itemListLen; j++) + { + if (players[i].itemRoulette.itemList[j] == item) + { + // Don't add if someone is already rolling for it. + return 0; + } + } + } + } + } + if (gametype == GT_BATTLE) { I_Assert(pos < 2); // DO NOT allow positions past the bounds of the table @@ -358,73 +437,6 @@ INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, boolean bot, bool newOdds <<= FRACBITS; - shieldType = K_GetShieldFromItem(item); - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - { - continue; - } - - if (!(gametyperules & GTR_BUMPERS) || players[i].bumpers) - { - pingame++; - } - - if (players[i].exiting) - { - pexiting++; - } - - switch (shieldType) - { - case KSHIELD_NONE: - /* Marble Garden Top is not REALLY - a Sonic 3 shield */ - case KSHIELD_TOP: - { - break; - } - - default: - { - if (shieldType == K_GetShieldFromItem(players[i].itemtype)) - { - // Don't allow more than one of each shield type at a time - return 0; - } - } - } - - if (players[i].position == 1) - { - first = &players[i]; - } - - if (players[i].position == 2) - { - second = &players[i]; - } - } - - if (first != NULL) // calculate 2nd's distance from 1st, for SPB - { - firstDist = first->distancetofinish; - isFirst = (ourDist <= firstDist); - } - - if (second != NULL) - { - secondDist = second->distancetofinish; - } - - if (first != NULL && second != NULL) - { - secondToFirst = secondDist - firstDist; - secondToFirst = K_ScaleItemDistance(secondToFirst, 16 - pingame); // Reversed scaling, so 16P is like 1v1, and 1v1 is like 16P - } - switch (item) { case KITEM_BANANA: @@ -440,7 +452,6 @@ INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, boolean bot, bool case KITEM_LANDMINE: case KITEM_DROPTARGET: case KITEM_BALLHOG: - case KITEM_HYUDORO: case KRITEM_TRIPLESNEAKER: case KRITEM_TRIPLEORBINAUT: case KRITEM_QUADORBINAUT: @@ -450,6 +461,7 @@ INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, boolean bot, bool break; } + case KITEM_HYUDORO: case KRITEM_TRIPLEBANANA: { powerItem = true; @@ -473,17 +485,23 @@ INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, boolean bot, bool cooldownOnStart = true; notNearEnd = true; - if (firstDist < ENDDIST*2 // No SPB when 1st is almost done - || isFirst == true) // No SPB for 1st ever + if ((gametyperules & GTR_CIRCUIT) == 0) { - newOdds = 0; + // Needs to be a race. + return 0; + } + + if (roulette->firstDist < ENDDIST*2 // No SPB when 1st is almost done + || position == 1) // No SPB for 1st ever + { + return 0; } else { - const UINT32 dist = max(0, ((signed)secondToFirst) - SPBSTARTDIST); + const UINT32 dist = max(0, ((signed)roulette->secondToFirst) - SPBSTARTDIST); const UINT32 distRange = SPBFORCEDIST - SPBSTARTDIST; - const UINT8 maxOdds = 20; - fixed_t multiplier = (dist * FRACUNIT) / distRange; + const fixed_t maxOdds = 20 << FRACBITS; + fixed_t multiplier = FixedDiv(dist, distRange); if (multiplier < 0) { @@ -495,34 +513,40 @@ INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, boolean bot, bool multiplier = FRACUNIT; } - newOdds = FixedMul(maxOdds << FRACBITS, multiplier); + newOdds = FixedMul(maxOdds, multiplier); } break; } case KITEM_SHRINK: + { cooldownOnStart = true; powerItem = true; notNearEnd = true; - if (pingame-1 <= pexiting) + if (roulette->playing - 1 <= roulette->exiting) { - newOdds = 0; + return 0; } break; + } case KITEM_LIGHTNINGSHIELD: + { cooldownOnStart = true; powerItem = true; if (spbplace != -1) { - newOdds = 0; + return 0; } break; + } default: + { break; + } } if (newOdds == 0) @@ -536,7 +560,7 @@ INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, boolean bot, bool // This item should not appear at the beginning of a race. (Usually really powerful crowd-breaking items) newOdds = 0; } - else if ((notNearEnd == true) && (ourDist < ENDDIST)) + else if ((notNearEnd == true) && (roulette->baseDist < ENDDIST)) { // This item should not appear at the end of a race. (Usually trap items that lose their effectiveness) newOdds = 0; @@ -556,16 +580,14 @@ INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, boolean bot, bool newOdds *= 2; } - newOdds = FixedMul(newOdds, FRACUNIT + K_ItemOddsScale(pingame)); + newOdds = FixedMul(newOdds, FRACUNIT + K_ItemOddsScale(roulette->playing)); } newOdds = FixedInt(FixedRound(newOdds)); return newOdds; } -//{ SRB2kart Roulette Code - Distance Based, yes waypoints - -UINT8 K_FindUseodds(player_t *const player, UINT32 playerDist) +static UINT8 K_FindUseodds(player_t *const player, itemroulette_t *const roulette) { UINT8 i; UINT8 useOdds = 0; @@ -586,11 +608,7 @@ UINT8 K_FindUseodds(player_t *const player, UINT32 playerDist) for (j = 1; j < NUMKARTRESULTS; j++) { - if (K_KartGetItemOdds( - i, j, - player->distancetofinish, - player->bot, (player->bot && player->botvars.rival) - ) > 0) + if (K_KartGetItemOdds(player, roulette, i, j) > 0) { break; } @@ -636,7 +654,7 @@ UINT8 K_FindUseodds(player_t *const player, UINT32 playerDist) dist = FixedMul(DISTVAR << FRACBITS, pos) >> FRACBITS; index = FixedInt(FixedRound(pos)); - if (playerDist <= (unsigned)dist) + if (roulette->dist <= (unsigned)dist) { useOdds = distTable[index]; break; @@ -649,14 +667,8 @@ UINT8 K_FindUseodds(player_t *const player, UINT32 playerDist) return useOdds; } -boolean K_ForcedSPB(player_t *const player) +static boolean K_ForcedSPB(player_t *const player, itemroulette_t *const roulette) { - player_t *first = NULL; - player_t *second = NULL; - UINT32 secondToFirst = UINT32_MAX; - UINT8 pingame = 0; - UINT8 i; - if (K_ItemEnabled(KITEM_SPB) == false) { return false; @@ -682,49 +694,20 @@ boolean K_ForcedSPB(player_t *const player) return false; } - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - { - continue; - } - - if (players[i].exiting) - { - return false; - } - - pingame++; - - if (players[i].position == 1) - { - first = &players[i]; - } - - if (players[i].position == 2) - { - second = &players[i]; - } - } - #if 0 - if (pingame <= 2) + if (roulette->playing <= 2) { return false; } #endif - if (first != NULL && second != NULL) - { - secondToFirst = second->distancetofinish - first->distancetofinish; - secondToFirst = K_ScaleItemDistance(secondToFirst, 16 - pingame); - } - - return (secondToFirst >= SPBFORCEDIST); + return (roulette->secondToFirst >= SPBFORCEDIST); } static void K_InitRoulette(itemroulette_t *const roulette) { + size_t i; + #ifndef ITEM_LIST_SIZE if (roulette->itemList == NULL) { @@ -744,13 +727,50 @@ static void K_InitRoulette(itemroulette_t *const roulette) roulette->itemListLen = 0; roulette->index = 0; + roulette->useOdds = UINT8_MAX; + roulette->baseDist = roulette->dist = 0; + roulette->playing = roulette->exiting = 0; + roulette->firstDist = roulette->secondDist = UINT32_MAX; + roulette->secondToFirst = 0; roulette->elapsed = 0; roulette->tics = roulette->speed = ROULETTE_SPEED_FASTEST; // Some default speed roulette->active = true; roulette->eggman = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] == false || players[i].spectator == true) + { + continue; + } + + roulette->playing++; + + if (players[i].exiting) + { + roulette->exiting++; + } + + if (players[i].position == 1) + { + roulette->firstDist = K_UndoMapScaling(players[i].distancetofinish); + } + + if (players[i].position == 2) + { + roulette->secondDist = K_UndoMapScaling(players[i].distancetofinish); + } + } + + // Calculate 2nd's distance from 1st, for SPB + if (roulette->firstDist != UINT32_MAX && roulette->secondDist != UINT32_MAX) + { + roulette->secondToFirst = roulette->secondDist - roulette->firstDist; + roulette->secondToFirst = K_ScaleItemDistance(roulette->secondToFirst, 16 - roulette->playing); // Reversed scaling + } } static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t item) @@ -785,58 +805,47 @@ static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t roulette->itemListLen++; } +static void K_AddItemToReel(player_t *const player, itemroulette_t *const roulette, kartitems_t item) +{ + K_PushToRouletteItemList(roulette, item); + + // If we're in ring debt, pad out the reel with + // a BUNCH of Super Rings. + if (K_ItemEnabled(KITEM_SUPERRING) == true + && player->rings <= 0 + && (gametyperules & GTR_SPHERES) == 0) + { + K_PushToRouletteItemList(roulette, KITEM_SUPERRING); + } +} + static void K_CalculateRouletteSpeed(player_t *const player, itemroulette_t *const roulette) { fixed_t frontRun = 0; fixed_t progress = 0; fixed_t total = 0; - UINT8 playing = 0; - - UINT32 firstDist = UINT32_MAX; - UINT32 ourDist = UINT32_MAX; - - size_t i; - // Make them select their item after a little while. // One of the few instances of bot RNG, would be nice to remove it. player->botvars.itemdelay = P_RandomRange(PR_UNDEFINED, TICRATE, TICRATE*3); - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] == false || players[i].spectator == true) - { - continue; - } - - playing++; - - if (players[i].position == 1) - { - firstDist = players[i].distancetofinish; - } - } - - if (modeattacking || playing <= 1) + if (modeattacking || roulette->playing <= 1) { // Time Attack rules; use a consistent speed. roulette->tics = roulette->speed = ROULETTE_SPEED_TIMEATTACK; return; } - ourDist = K_ScaleItemDistance(player->distancetofinish, playing); - firstDist = K_ScaleItemDistance(firstDist, playing); - - if (ourDist > ENDDIST) + if (roulette->baseDist > ENDDIST) { // Being farther in the course makes your roulette faster. - progress = min(FRACUNIT, FixedDiv(ourDist - ENDDIST, ROULETTE_SPEED_DIST)); + progress = min(FRACUNIT, FixedDiv(roulette->baseDist - ENDDIST, ROULETTE_SPEED_DIST)); } - if (ourDist > firstDist) + if (roulette->baseDist > roulette->firstDist) { // Frontrunning makes your roulette faster. - frontRun = min(FRACUNIT, FixedDiv(ourDist - firstDist, ENDDIST)); + frontRun = min(FRACUNIT, FixedDiv(roulette->baseDist - roulette->firstDist, ENDDIST)); } // Combine our two factors together. @@ -862,16 +871,19 @@ static void K_CalculateRouletteSpeed(player_t *const player, itemroulette_t *con void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) { - UINT8 playing = 0; - UINT32 playerDist = UINT32_MAX; - UINT32 spawnChance[NUMKARTRESULTS] = {0}; UINT32 totalSpawnChance = 0; size_t rngRoll = 0; + UINT8 numItems = 0; + kartitems_t singleItem = KITEM_SAD; + size_t i; K_InitRoulette(roulette); + + roulette->baseDist = K_UndoMapScaling(player->distancetofinish); + K_CalculateRouletteSpeed(player, roulette); // SPECIAL CASE No. 1: @@ -884,16 +896,6 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) // SPECIAL CASE No. 2: // Use a special, pre-determined item reel for Time Attack / Free Play - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] == false || players[i].spectator == true) - { - continue; - } - - playing++; - } - if (bossinfo.boss == true) { for (i = 0; K_KartItemReelBoss[i] != KITEM_NONE; i++) @@ -903,7 +905,7 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) return; } - else if (modeattacking || playing <= 1) + else if (modeattacking || roulette->playing <= 1) { switch (gametype) { @@ -931,36 +933,58 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) // SPECIAL CASE No. 3: // Only give the SPB if conditions are right - if (K_ForcedSPB(player) == true) + if (K_ForcedSPB(player, roulette) == true) { - K_PushToRouletteItemList(roulette, KITEM_SPB); + K_AddItemToReel(player, roulette, KITEM_SPB); return; } - playerDist = K_GetItemRouletteDistance(player, playing); - - roulette->useOdds = K_FindUseodds(player, playerDist); - - for (i = 1; i < NUMKARTRESULTS; i++) - { - INT32 thisItemsOdds = K_KartGetItemOdds( - roulette->useOdds, i, - player->distancetofinish, - player->bot, (player->bot && player->botvars.rival) - ); - - spawnChance[i] = (totalSpawnChance += thisItemsOdds); - } - // SPECIAL CASE No. 4: - // All items are off, so give a placeholder item - if (totalSpawnChance == 0) + // If only one item is enabled, always use it + for (i = 1; i < NUMKARTRESULTS; i++) { - K_PushToRouletteItemList(roulette, KITEM_SAD); + if (K_ItemEnabled(i) == true) + { + numItems++; + if (numItems > 1) + { + break; + } + + singleItem = i; + } + } + + if (numItems < 2) + { + // singleItem = KITEM_SAD by default, + // so it will be used when all items are turned off. + K_AddItemToReel(player, roulette, singleItem); return; } - // We always want the same result when making the same item reel. + // Special cases are all handled, we can now + // actually calculate actual item reels. + roulette->dist = K_GetItemRouletteDistance(player, roulette->playing); + roulette->useOdds = K_FindUseodds(player, roulette); + + for (i = 1; i < NUMKARTRESULTS; i++) + { + spawnChance[i] = ( + totalSpawnChance += K_KartGetItemOdds(player, roulette, roulette->useOdds, i) + ); + } + + if (totalSpawnChance == 0) + { + // This shouldn't happen, but if it does, early exit. + // Maybe can happen if you enable multiple items for + // another gametype, so we give the singleItem as a fallback. + K_AddItemToReel(player, roulette, singleItem); + return; + } + + // Create the same item reel given the same inputs. P_SetRandSeed(PR_ITEM_ROULETTE, ITEM_REEL_SEED); while (totalSpawnChance > 0) @@ -971,16 +995,7 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) continue; } - K_PushToRouletteItemList(roulette, i); - - // If we're in ring debt, pad out the reel with - // a BUNCH of Super Rings. - if (K_ItemEnabled(KITEM_SUPERRING) == true - && player->rings <= 0 - && (gametyperules & GTR_SPHERES) == 0) - { - K_PushToRouletteItemList(roulette, KITEM_SUPERRING); - } + K_AddItemToReel(player, roulette, i); for (; i < NUMKARTRESULTS; i++) { @@ -1011,7 +1026,7 @@ void K_StartEggmanRoulette(player_t *const player) */ static void K_KartGetItemResult(player_t *const player, kartitems_t getitem) { - if (getitem == KITEM_SPB || getitem == KITEM_SHRINK) + if (K_ItemSingularity(getitem) == true) { K_SetItemCooldown(getitem, 20*TICRATE); } diff --git a/src/k_roulette.h b/src/k_roulette.h index 81e12aae3..3d52123f9 100644 --- a/src/k_roulette.h +++ b/src/k_roulette.h @@ -17,15 +17,9 @@ #include "d_player.h" boolean K_ItemEnabled(SINT8 item); +boolean K_ItemSingularity(kartitems_t item); -fixed_t K_ItemOddsScale(UINT8 playerCount); -UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers); -UINT32 K_GetItemRouletteDistance(player_t *const player, UINT8 pingame); - -INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, boolean bot, boolean rival); -UINT8 K_FindUseodds(player_t *const player, UINT32 playerDist); - -boolean K_ForcedSPB(player_t *const player); +INT32 K_KartGetItemOdds(player_t *const player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item); void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette); void K_StartEggmanRoulette(player_t *const player); diff --git a/src/p_saveg.c b/src/p_saveg.c index 46aa64147..3ce18aa5f 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -444,6 +444,7 @@ static void P_NetArchivePlayers(void) #endif WRITEUINT8(save_p, players[i].itemRoulette.useOdds); + WRITEUINT32(save_p, players[i].itemRoulette.dist); WRITEUINT32(save_p, players[i].itemRoulette.index); WRITEUINT8(save_p, players[i].itemRoulette.sound); WRITEUINT32(save_p, players[i].itemRoulette.speed); @@ -798,6 +799,7 @@ static void P_NetUnArchivePlayers(void) #endif players[i].itemRoulette.useOdds = READUINT8(save_p); + players[i].itemRoulette.dist = READUINT32(save_p); players[i].itemRoulette.index = (size_t)READUINT32(save_p); players[i].itemRoulette.sound = READUINT8(save_p); players[i].itemRoulette.speed = (tic_t)READUINT32(save_p); From ffe9d5cd1d20d7dee3f89c9270554d0c34c6b254 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Dec 2022 01:41:34 -0500 Subject: [PATCH 28/46] Properly support small splitscreen itembox --- src/k_hud.c | 32 +++++++++++++++++++------------- src/k_roulette.h | 1 + 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index 32904beaa..49bb6aa07 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1030,13 +1030,14 @@ static void K_drawKartItem(void) const INT32 numberdisplaymin = ((!offset && stplyr->itemtype == KITEM_ORBINAUT) ? 5 : 2); INT32 itembar = 0; INT32 maxl = 0; // itembar's normal highest value - const INT32 barlength = (r_splitscreen > 1 ? 12 : 26); + const INT32 barlength = (offset ? 12 : 26); UINT16 localcolor[3] = { stplyr->skincolor }; SINT8 colormode[3] = { TC_RAINBOW }; boolean flipamount = false; // Used for 3P/4P splitscreen to flip item amount stuff fixed_t rouletteOffset = 0; - const INT32 rouletteBox = 36; + fixed_t rouletteSpace = ROULETTE_SPACING; + vector2_t rouletteCrop = {7, 7}; INT32 i; if (stplyr->itemRoulette.itemListLen > 0) @@ -1189,13 +1190,7 @@ static void K_drawKartItem(void) } // pain and suffering defined below - if (r_splitscreen < 2) // don't change shit for THIS splitscreen. - { - fx = ITEM_X; - fy = ITEM_Y; - fflags = V_SNAPTOTOP|V_SNAPTOLEFT|V_SPLITSCREEN; - } - else // now we're having a fun game. + if (offset) { if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... { @@ -1210,24 +1205,35 @@ static void K_drawKartItem(void) fflags = V_SNAPTORIGHT|V_SNAPTOTOP|V_SPLITSCREEN; flipamount = true; } + + rouletteSpace = ROULETTE_SPACING_SPLITSCREEN; + rouletteOffset = FixedMul(rouletteOffset, FixedDiv(ROULETTE_SPACING_SPLITSCREEN, ROULETTE_SPACING)); + rouletteCrop.x = 16; + rouletteCrop.y = 15; + } + else + { + fx = ITEM_X; + fy = ITEM_Y; + fflags = V_SNAPTOTOP|V_SNAPTOLEFT|V_SPLITSCREEN; } V_DrawScaledPatch(fx, fy, V_HUDTRANS|V_SLIDEIN|fflags, localbg); // Need to draw these in a particular order, for sorting. V_SetClipRect( - (fx + 7) << FRACBITS, (fy + 7) << FRACBITS, - rouletteBox << FRACBITS, rouletteBox << FRACBITS, + (fx + rouletteCrop.x) << FRACBITS, (fy + rouletteCrop.y) << FRACBITS, + rouletteSpace, rouletteSpace, V_SLIDEIN|fflags ); V_DrawFixedPatch( - fx< Date: Thu, 15 Dec 2022 01:44:09 -0500 Subject: [PATCH 29/46] Remove item roulette between maps --- src/g_game.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 91d309638..ab4b6bc9c 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2322,8 +2322,13 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE)); // SRB2kart + memcpy(&itemRoulette, &players[player].itemRoulette, sizeof (itemRoulette)); + memcpy(&respawn, &players[player].respawn, sizeof (respawn)); + if (betweenmaps || leveltime < introtime) { + itemRoulette.active = false; + itemtype = 0; itemamount = 0; growshrinktimer = 0; @@ -2400,9 +2405,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) P_SetTarget(&players[player].follower, NULL); } - memcpy(&itemRoulette, &players[player].itemRoulette, sizeof (itemRoulette)); - memcpy(&respawn, &players[player].respawn, sizeof (respawn)); - p = &players[player]; memset(p, 0, sizeof (*p)); From 6554c8bfb1f7e2746a867713b20cbe9d106ddf62 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Dec 2022 02:13:46 -0500 Subject: [PATCH 30/46] Improve scope handling of the roulette All of the player_t references are now full-const instead of const pointer after a certain point. This is because I've made two mistakes so far of modifying the player with this, when it's supposed to be safe to call for HUD as well. Also uses this split to add a more efficient way to prevent multi-Shrink/SPB. Also handles NULL player better, for the sake of Battle's K_CreatePaperItem. --- src/k_hud.c | 2 +- src/k_kart.c | 2 +- src/k_roulette.c | 91 +++++++++++++++++++++++++++--------------------- src/k_roulette.h | 5 +-- src/p_enemy.c | 2 +- src/p_inter.c | 2 +- 6 files changed, 59 insertions(+), 45 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index 49bb6aa07..7c1d94e42 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -4560,7 +4560,7 @@ static void K_drawDistributionDebugger(void) return; } - K_StartItemRoulette(stplyr, &rouletteData); + K_FillItemRouletteData(stplyr, &rouletteData); for (i = 0; i < rouletteData.itemListLen; i++) { diff --git a/src/k_kart.c b/src/k_kart.c index b512a64c3..3bce71c54 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6063,7 +6063,7 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 useodds = amount; - K_StartItemRoulette(stplyr, &rouletteData); + K_FillItemRouletteData(NULL, &rouletteData); for (i = 1; i < NUMKARTRESULTS; i++) { diff --git a/src/k_roulette.c b/src/k_roulette.c index b77659be6..6d59f1ec3 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -251,10 +251,15 @@ static UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers) return distance; } -static UINT32 K_GetItemRouletteDistance(player_t *const player, UINT8 numPlayers) +static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers) { UINT32 pdis = 0; + if (player == NULL) + { + return 0; + } + #if 0 if (specialStage.active == true) { @@ -316,7 +321,7 @@ static UINT32 K_GetItemRouletteDistance(player_t *const player, UINT8 numPlayers \return void */ -INT32 K_KartGetItemOdds(player_t *const player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item) +INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item) { boolean bot = false; boolean rival = false; @@ -329,7 +334,7 @@ INT32 K_KartGetItemOdds(player_t *const player, itemroulette_t *const roulette, boolean notNearEnd = false; fixed_t newOdds = 0; - size_t i, j; + size_t i; I_Assert(roulette != NULL); @@ -401,29 +406,6 @@ INT32 K_KartGetItemOdds(player_t *const player, itemroulette_t *const roulette, } } - if (K_ItemSingularity(item) == true) - { - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] == false || players[i].spectator == true) - { - continue; - } - - if (players[i].itemRoulette.active == true) - { - for (j = 0; j < players[i].itemRoulette.itemListLen; j++) - { - if (players[i].itemRoulette.itemList[j] == item) - { - // Don't add if someone is already rolling for it. - return 0; - } - } - } - } - } - if (gametype == GT_BATTLE) { I_Assert(pos < 2); // DO NOT allow positions past the bounds of the table @@ -587,7 +569,7 @@ INT32 K_KartGetItemOdds(player_t *const player, itemroulette_t *const roulette, return newOdds; } -static UINT8 K_FindUseodds(player_t *const player, itemroulette_t *const roulette) +static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulette) { UINT8 i; UINT8 useOdds = 0; @@ -667,7 +649,7 @@ static UINT8 K_FindUseodds(player_t *const player, itemroulette_t *const roulett return useOdds; } -static boolean K_ForcedSPB(player_t *const player, itemroulette_t *const roulette) +static boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulette) { if (K_ItemEnabled(KITEM_SPB) == false) { @@ -679,6 +661,11 @@ static boolean K_ForcedSPB(player_t *const player, itemroulette_t *const roulett return false; } + if (player == NULL) + { + return false; + } + if (player->position <= 1) { return false; @@ -805,10 +792,15 @@ static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t roulette->itemListLen++; } -static void K_AddItemToReel(player_t *const player, itemroulette_t *const roulette, kartitems_t item) +static void K_AddItemToReel(const player_t *player, itemroulette_t *const roulette, kartitems_t item) { K_PushToRouletteItemList(roulette, item); + if (player == NULL) + { + return; + } + // If we're in ring debt, pad out the reel with // a BUNCH of Super Rings. if (K_ItemEnabled(KITEM_SUPERRING) == true @@ -819,16 +811,12 @@ static void K_AddItemToReel(player_t *const player, itemroulette_t *const roulet } } -static void K_CalculateRouletteSpeed(player_t *const player, itemroulette_t *const roulette) +static void K_CalculateRouletteSpeed(itemroulette_t *const roulette) { fixed_t frontRun = 0; fixed_t progress = 0; fixed_t total = 0; - // Make them select their item after a little while. - // One of the few instances of bot RNG, would be nice to remove it. - player->botvars.itemdelay = P_RandomRange(PR_UNDEFINED, TICRATE, TICRATE*3); - if (modeattacking || roulette->playing <= 1) { // Time Attack rules; use a consistent speed. @@ -869,7 +857,7 @@ static void K_CalculateRouletteSpeed(player_t *const player, itemroulette_t *con roulette->tics = roulette->speed = ROULETTE_SPEED_FASTEST + FixedMul(ROULETTE_SPEED_SLOWEST - ROULETTE_SPEED_FASTEST, total); } -void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) +void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette) { UINT32 spawnChance[NUMKARTRESULTS] = {0}; UINT32 totalSpawnChance = 0; @@ -882,9 +870,11 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) K_InitRoulette(roulette); - roulette->baseDist = K_UndoMapScaling(player->distancetofinish); - - K_CalculateRouletteSpeed(player, roulette); + if (player != NULL) + { + roulette->baseDist = K_UndoMapScaling(player->distancetofinish); + K_CalculateRouletteSpeed(roulette); + } // SPECIAL CASE No. 1: // Give only the debug item if specified @@ -1010,10 +1000,33 @@ void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette) } } +void K_StartItemRoulette(player_t *const player) +{ + itemroulette_t *const roulette = &player->itemRoulette; + size_t i; + + K_FillItemRouletteData(player, roulette); + + // Make the bots select their item after a little while. + // One of the few instances of bot RNG, would be nice to remove it. + player->botvars.itemdelay = P_RandomRange(PR_UNDEFINED, TICRATE, TICRATE*3); + + // Prevent further duplicates of items that + // are intended to only have one out at a time. + for (i = 0; i < roulette->itemListLen; i++) + { + kartitems_t item = roulette->itemList[i]; + if (K_ItemSingularity(item) == true) + { + K_SetItemCooldown(item, TICRATE<<4); + } + } +} + void K_StartEggmanRoulette(player_t *const player) { itemroulette_t *const roulette = &player->itemRoulette; - K_StartItemRoulette(player, roulette); + K_StartItemRoulette(player); roulette->eggman = true; } diff --git a/src/k_roulette.h b/src/k_roulette.h index b1664643c..fbc6d4a4b 100644 --- a/src/k_roulette.h +++ b/src/k_roulette.h @@ -19,9 +19,10 @@ boolean K_ItemEnabled(SINT8 item); boolean K_ItemSingularity(kartitems_t item); -INT32 K_KartGetItemOdds(player_t *const player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item); +INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item); +void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette); -void K_StartItemRoulette(player_t *const player, itemroulette_t *const roulette); +void K_StartItemRoulette(player_t *const player); void K_StartEggmanRoulette(player_t *const player); #define ROULETTE_SPACING (36 << FRACBITS) diff --git a/src/p_enemy.c b/src/p_enemy.c index 636eb500a..3c45a1d2e 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -13048,7 +13048,7 @@ void A_ItemPop(mobj_t *actor) } else if (locvar1 == 0) { - K_StartItemRoulette(actor->target->player, &actor->target->player->itemRoulette); + K_StartItemRoulette(actor->target->player); } // Here at mapload in battle? diff --git a/src/p_inter.c b/src/p_inter.c index 2c05f5732..6400b9d35 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -412,7 +412,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->fuse || !P_CanPickupItem(player, 1) || ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0)) return; - K_StartItemRoulette(player, &player->itemRoulette); + K_StartItemRoulette(player); // Karma fireworks for (i = 0; i < 5; i++) From 7f569fe1688886a8f72871f46fdb454c8e8043ca Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 15 Dec 2022 04:14:38 -0800 Subject: [PATCH 31/46] Rename K_SpawnBrolyKi to Obj_SpawnBrolyKi, move to objects/broly.c, spawn MT_BROLY --- src/deh_tables.c | 2 ++ src/info.c | 27 +++++++++++++++++++++++++++ src/info.h | 2 ++ src/k_collide.c | 2 +- src/k_kart.c | 33 --------------------------------- src/k_kart.h | 1 - src/k_objects.h | 3 +++ src/objects/Sourcefile | 1 + src/objects/broly.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/p_enemy.c | 2 +- 10 files changed, 78 insertions(+), 36 deletions(-) create mode 100644 src/objects/broly.c diff --git a/src/deh_tables.c b/src/deh_tables.c index 5f026fe39..9abc8f3f2 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5627,6 +5627,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_PAPERITEMSPOT", "MT_BEAMPOINT", + + "MT_BROLY", }; const char *const MOBJFLAG_LIST[] = { diff --git a/src/info.c b/src/info.c index 5a1cefa1f..17d930a90 100644 --- a/src/info.c +++ b/src/info.c @@ -29030,6 +29030,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIPTHING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, + + { // MT_BROLY + -1, // doomednum + S_BROLY1, // 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 + 0, // radius + 0, // height + 0, // display offset + 100, // mass + 1, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, }; skincolor_t skincolors[MAXSKINCOLORS] = { diff --git a/src/info.h b/src/info.h index e4c96915c..bc074a0b5 100644 --- a/src/info.h +++ b/src/info.h @@ -6682,6 +6682,8 @@ typedef enum mobj_type MT_BEAMPOINT, + MT_BROLY, + MT_FIRSTFREESLOT, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, NUMMOBJTYPES diff --git a/src/k_collide.c b/src/k_collide.c index 64f460a61..11ac0b745 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -319,7 +319,7 @@ tic_t K_MineExplodeAttack(mobj_t *actor, fixed_t size, boolean spin) if (!spin) { - K_SpawnBrolyKi(actor, minehitlag); + Obj_SpawnBrolyKi(actor, minehitlag); return minehitlag; } diff --git a/src/k_kart.c b/src/k_kart.c index 1c8213998..12598f831 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5210,39 +5210,6 @@ void K_SpawnMineExplosion(mobj_t *source, UINT8 color, tic_t delay) } } -void K_SpawnBrolyKi(mobj_t *source, tic_t duration) -{ - mobj_t *x; - - if (duration == 0) - { - return; - } - - x = P_SpawnMobjFromMobj(source, 0, 0, 0, MT_THOK); - - // Shrink into center of source object. - x->z = (source->z + source->height / 2); - x->height = 0; - - P_SetMobjState(x, S_BROLY1); - x->colorized = true; - x->color = source->color; - x->hitlag = 0; // do not copy source hitlag - - P_SetScale(x, 64 * mapobjectscale); - x->scalespeed = x->scale / duration; - - // The last tic doesn't actually get rendered so in order - // to show scale = destscale, add one buffer tic. - x->tics = (duration + 1); - x->destscale = 1; // 0 also doesn't work - - K_ReduceVFX(x, NULL); - - S_StartSound(x, sfx_cdfm74); -} - #undef MINEQUAKEDIST fixed_t K_ItemScaleForPlayer(player_t *player) diff --git a/src/k_kart.h b/src/k_kart.h index 87f3e1998..6827c77bc 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -107,7 +107,6 @@ void K_DestroyBumpers(player_t *player, UINT8 amount); void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount); void K_MineFlashScreen(mobj_t *source); void K_SpawnMineExplosion(mobj_t *source, UINT8 color, tic_t delay); -void K_SpawnBrolyKi(mobj_t *source, tic_t duration); void K_RunFinishLineBeam(void); UINT16 K_DriftSparkColor(player_t *player, INT32 charge); void K_SpawnBoostTrail(player_t *player); diff --git a/src/k_objects.h b/src/k_objects.h index 96e0fa2b5..fc15a2153 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -54,4 +54,7 @@ void Obj_DuelBombReverse(mobj_t *bomb); void Obj_DuelBombTouch(mobj_t *bomb, mobj_t *toucher); void Obj_DuelBombInit(mobj_t *bomb); +/* Broly Ki */ +mobj_t *Obj_SpawnBrolyKi(mobj_t *source, tic_t duration); + #endif/*k_objects_H*/ diff --git a/src/objects/Sourcefile b/src/objects/Sourcefile index b8cb63b1f..099f0d203 100644 --- a/src/objects/Sourcefile +++ b/src/objects/Sourcefile @@ -7,3 +7,4 @@ manta-ring.c orbinaut.c jawz.c duel-bomb.c +broly.c diff --git a/src/objects/broly.c b/src/objects/broly.c new file mode 100644 index 000000000..8c743a64a --- /dev/null +++ b/src/objects/broly.c @@ -0,0 +1,41 @@ +#include "../doomdef.h" +#include "../info.h" +#include "../k_kart.h" +#include "../k_objects.h" +#include "../p_local.h" +#include "../s_sound.h" + +mobj_t * +Obj_SpawnBrolyKi +( mobj_t * source, + tic_t duration) +{ + mobj_t *x = P_SpawnMobjFromMobj( + source, 0, 0, 0, MT_BROLY); + + if (duration == 0) + { + return x; + } + + // Shrink into center of source object. + x->z = (source->z + source->height / 2); + + x->colorized = true; + x->color = source->color; + x->hitlag = 0; // do not copy source hitlag + + P_SetScale(x, 64 * mapobjectscale); + x->scalespeed = x->scale / duration; + + // The last tic doesn't actually get rendered so in order + // to show scale = destscale, add one buffer tic. + x->tics = (duration + 1); + x->destscale = 1; // 0 also doesn't work + + K_ReduceVFX(x, NULL); + + S_StartSound(x, sfx_cdfm74); + + return x; +} diff --git a/src/p_enemy.c b/src/p_enemy.c index afe17b5bc..61762cce8 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -13186,7 +13186,7 @@ void A_LandMineExplode(mobj_t *actor) expl->momz = ((i+1)*actor->scale*5/2)*P_MobjFlip(expl); } - K_SpawnBrolyKi(actor, delay); + Obj_SpawnBrolyKi(actor, delay); } void A_BallhogExplode(mobj_t *actor) From 01f8fb323320856b2de029ddd614f363a93804af Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 15 Dec 2022 05:16:19 -0800 Subject: [PATCH 32/46] Use sinusoidial interpolation for Broly ki --- src/k_objects.h | 1 + src/objects/broly.c | 43 +++++++++++++++++++++++++++++++++++++------ src/p_mobj.c | 3 +++ 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/k_objects.h b/src/k_objects.h index fc15a2153..4cc4ed1c3 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -56,5 +56,6 @@ void Obj_DuelBombInit(mobj_t *bomb); /* Broly Ki */ mobj_t *Obj_SpawnBrolyKi(mobj_t *source, tic_t duration); +void Obj_BrolyKiThink(mobj_t *ki); #endif/*k_objects_H*/ diff --git a/src/objects/broly.c b/src/objects/broly.c index 8c743a64a..d041c23b7 100644 --- a/src/objects/broly.c +++ b/src/objects/broly.c @@ -2,9 +2,33 @@ #include "../info.h" #include "../k_kart.h" #include "../k_objects.h" +#include "../m_easing.h" #include "../p_local.h" #include "../s_sound.h" +// TODO: generic function +static void P_InstaScale(mobj_t *thing, fixed_t scale) +{ + P_SetScale(thing, scale); + thing->destscale = scale; +} + +/* An object may not be visible on the same tic: + 1) that it spawned + 2) that it cycles to the next state */ +#define BUFFER_TICS (2) + +#define broly_duration(o) ((o)->extravalue1) +#define broly_maxscale(o) ((o)->extravalue2) + +static inline fixed_t +get_unit_linear (const mobj_t *x) +{ + const tic_t t = (x->tics - BUFFER_TICS); + + return t * FRACUNIT / broly_duration(x); +} + mobj_t * Obj_SpawnBrolyKi ( mobj_t * source, @@ -25,13 +49,10 @@ Obj_SpawnBrolyKi x->color = source->color; x->hitlag = 0; // do not copy source hitlag - P_SetScale(x, 64 * mapobjectscale); - x->scalespeed = x->scale / duration; + broly_maxscale(x) = 64 * mapobjectscale; + broly_duration(x) = duration; - // The last tic doesn't actually get rendered so in order - // to show scale = destscale, add one buffer tic. - x->tics = (duration + 1); - x->destscale = 1; // 0 also doesn't work + x->tics = (duration + BUFFER_TICS); K_ReduceVFX(x, NULL); @@ -39,3 +60,13 @@ Obj_SpawnBrolyKi return x; } + +void +Obj_BrolyKiThink (mobj_t *x) +{ + const fixed_t + t = get_unit_linear(x), + n = Easing_OutSine(t, 0, broly_maxscale(x)); + + P_InstaScale(x, n); +} diff --git a/src/p_mobj.c b/src/p_mobj.c index ce526659d..19041e3e4 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6508,6 +6508,9 @@ static void P_MobjSceneryThink(mobj_t *mobj) case MT_DRIFTELECTRICSPARK: mobj->renderflags ^= RF_DONTDRAW; break; + case MT_BROLY: + Obj_BrolyKiThink(mobj); + break; case MT_VWREF: case MT_VWREB: { From 05413eeb2d93d5cb0d0725016ab95d8158fd4cd8 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Dec 2022 16:19:06 -0500 Subject: [PATCH 33/46] Add proper comments for each function (I think this is the only code style I want to actively enforce :p) --- src/k_roulette.c | 211 ++++++++++++++++++++++++++++++++++++++++++----- src/k_roulette.h | 153 ++++++++++++++++++++++++++++++++-- 2 files changed, 336 insertions(+), 28 deletions(-) diff --git a/src/k_roulette.c b/src/k_roulette.c index 6d59f1ec3..ec15f4e9b 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -160,7 +160,12 @@ static kartitems_t K_KartItemReelBoss[] = KITEM_NONE }; -boolean K_ItemEnabled(SINT8 item) +/*-------------------------------------------------- + boolean K_ItemEnabled(kartitems_t item) + + See header file for description. +--------------------------------------------------*/ +boolean K_ItemEnabled(kartitems_t item) { if (item < 1 || item >= NUMKARTRESULTS) { @@ -178,6 +183,11 @@ boolean K_ItemEnabled(SINT8 item) return cv_items[item - 1].value; } +/*-------------------------------------------------- + boolean K_ItemSingularity(kartitems_t item) + + See header file for description. +--------------------------------------------------*/ boolean K_ItemSingularity(kartitems_t item) { switch (item) @@ -194,6 +204,19 @@ boolean K_ItemSingularity(kartitems_t item) } } +/*-------------------------------------------------- + static fixed_t K_ItemOddsScale(UINT8 playerCount) + + A multiplier for odds and distances to scale + them with the player count. + + Input Arguments:- + playerCount - Number of players in the game. + + Return:- + Fixed point number, to multiply odds or + distances by. +--------------------------------------------------*/ static fixed_t K_ItemOddsScale(UINT8 playerCount) { const UINT8 basePlayer = 8; // The player count we design most of the game around. @@ -223,6 +246,18 @@ static fixed_t K_ItemOddsScale(UINT8 playerCount) return playerScaling; } +/*-------------------------------------------------- + static UINT32 K_UndoMapScaling(UINT32 distance) + + Takes a raw map distance and adjusts it to + be in x1 scale. + + Input Arguments:- + distance - Original distance. + + Return:- + Distance unscaled by mapobjectscale. +--------------------------------------------------*/ static UINT32 K_UndoMapScaling(UINT32 distance) { if (mapobjectscale != FRACUNIT) @@ -234,6 +269,19 @@ static UINT32 K_UndoMapScaling(UINT32 distance) return distance; } +/*-------------------------------------------------- + static UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers) + + Adjust item distance for lobby-size scaling + as well as Frantic Items. + + Input Arguments:- + distance - Original distance. + numPlayers - Number of players in the game. + + Return:- + New distance after scaling. +--------------------------------------------------*/ static UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers) { if (franticitems == true) @@ -251,6 +299,19 @@ static UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers) return distance; } +/*-------------------------------------------------- + static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers) + + Gets a player's distance used for the item + roulette, including all scaling factors. + + Input Arguments:- + player - The player to get the distance of. + numPlayers - Number of players in the game. + + Return:- + The player's finalized item distance. +--------------------------------------------------*/ static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers) { UINT32 pdis = 0; @@ -314,13 +375,11 @@ static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers return pdis; } -/** \brief Item Roulette for Kart - - \param player player object passed from P_KartPlayerThink - - \return void -*/ +/*-------------------------------------------------- + INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item) + See header file for description. +--------------------------------------------------*/ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item) { boolean bot = false; @@ -569,6 +628,21 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, return newOdds; } +/*-------------------------------------------------- + static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulette) + + Gets which item bracket the player is in. + This can be adjusted depending on which + items being turned off. + + Input Arguments:- + player - The player the roulette is for. + roulette - The item roulette data. + + Return:- + The item bracket the player is in, as an + index to the array. +--------------------------------------------------*/ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulette) { UINT8 i; @@ -649,6 +723,20 @@ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulett return useOdds; } +/*-------------------------------------------------- + static boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulette) + + Determines special conditions where we want + to forcefully give the player an SPB. + + Input Arguments:- + player - The player the roulette is for. + roulette - The item roulette data. + + Return:- + true if we want to give the player a forced SPB, + otherwise false. +--------------------------------------------------*/ static boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulette) { if (K_ItemEnabled(KITEM_SPB) == false) @@ -691,6 +779,17 @@ static boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulett return (roulette->secondToFirst >= SPBFORCEDIST); } +/*-------------------------------------------------- + static void K_InitRoulette(itemroulette_t *const roulette) + + Initializes the data for a new item roulette. + + Input Arguments:- + roulette - The item roulette data to initialize. + + Return:- + N/A +--------------------------------------------------*/ static void K_InitRoulette(itemroulette_t *const roulette) { size_t i; @@ -760,6 +859,19 @@ static void K_InitRoulette(itemroulette_t *const roulette) } } +/*-------------------------------------------------- + static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t item) + + Pushes a new item to the end of the item + roulette's item list. + + Input Arguments:- + roulette - The item roulette data to modify. + item - The item to push to the list. + + Return:- + N/A +--------------------------------------------------*/ static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t item) { #ifdef ITEM_LIST_SIZE @@ -792,6 +904,23 @@ static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t roulette->itemListLen++; } +/*-------------------------------------------------- + static void K_AddItemToReel(const player_t *player, itemroulette_t *const roulette, kartitems_t item) + + Adds an item to a player's item reel. Unlike + pushing directly with K_PushToRouletteItemList, + this function handles special behaviors (like + padding with extra Super Rings). + + Input Arguments:- + player - The player to add to the item roulette. + This is valid to be NULL. + roulette - The player's item roulette data. + item - The item to push to the list. + + Return:- + N/A +--------------------------------------------------*/ static void K_AddItemToReel(const player_t *player, itemroulette_t *const roulette, kartitems_t item) { K_PushToRouletteItemList(roulette, item); @@ -811,6 +940,19 @@ static void K_AddItemToReel(const player_t *player, itemroulette_t *const roulet } } +/*-------------------------------------------------- + static void K_CalculateRouletteSpeed(itemroulette_t *const roulette) + + Determines the speed for the item roulette, + adjusted for progress in the race and front + running. + + Input Arguments:- + roulette - The item roulette data to modify. + + Return:- + N/A +--------------------------------------------------*/ static void K_CalculateRouletteSpeed(itemroulette_t *const roulette) { fixed_t frontRun = 0; @@ -857,6 +999,11 @@ static void K_CalculateRouletteSpeed(itemroulette_t *const roulette) roulette->tics = roulette->speed = ROULETTE_SPEED_FASTEST + FixedMul(ROULETTE_SPEED_SLOWEST - ROULETTE_SPEED_FASTEST, total); } +/*-------------------------------------------------- + void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette) + + See header file for description. +--------------------------------------------------*/ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette) { UINT32 spawnChance[NUMKARTRESULTS] = {0}; @@ -1000,6 +1147,11 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet } } +/*-------------------------------------------------- + void K_StartItemRoulette(player_t *const player) + + See header file for description. +--------------------------------------------------*/ void K_StartItemRoulette(player_t *const player) { itemroulette_t *const roulette = &player->itemRoulette; @@ -1023,6 +1175,11 @@ void K_StartItemRoulette(player_t *const player) } } +/*-------------------------------------------------- + void K_StartEggmanRoulette(player_t *const player) + + See header file for description. +--------------------------------------------------*/ void K_StartEggmanRoulette(player_t *const player) { itemroulette_t *const roulette = &player->itemRoulette; @@ -1030,13 +1187,32 @@ void K_StartEggmanRoulette(player_t *const player) roulette->eggman = true; } -/** \brief Item Roulette for Kart +/*-------------------------------------------------- + fixed_t K_GetRouletteOffset(itemroulette_t *const roulette, fixed_t renderDelta) - \param player player - \param getitem what item we're looking for + See header file for description. +--------------------------------------------------*/ +fixed_t K_GetRouletteOffset(itemroulette_t *const roulette, fixed_t renderDelta) +{ + const fixed_t curTic = (roulette->tics << FRACBITS) - renderDelta; + const fixed_t midTic = roulette->speed * (FRACUNIT >> 1); - \return void -*/ + return FixedMul(FixedDiv(midTic - curTic, ((roulette->speed + 1) << FRACBITS)), ROULETTE_SPACING); +} + +/*-------------------------------------------------- + static void K_KartGetItemResult(player_t *const player, kartitems_t getitem) + + Initializes a player's item to what was + received from the roulette. + + Input Arguments:- + player - The player receiving the item. + getitem - The item to give to the player. + + Return:- + N/A +--------------------------------------------------*/ static void K_KartGetItemResult(player_t *const player, kartitems_t getitem) { if (K_ItemSingularity(getitem) == true) @@ -1051,14 +1227,11 @@ static void K_KartGetItemResult(player_t *const player, kartitems_t getitem) player->itemamount = K_ItemResultToAmount(getitem); } -fixed_t K_GetRouletteOffset(itemroulette_t *const roulette, fixed_t renderDelta) -{ - const fixed_t curTic = (roulette->tics << FRACBITS) - renderDelta; - const fixed_t midTic = roulette->speed * (FRACUNIT >> 1); - - return FixedMul(FixedDiv(midTic - curTic, ((roulette->speed + 1) << FRACBITS)), ROULETTE_SPACING); -} +/*-------------------------------------------------- + void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) + See header file for description. +--------------------------------------------------*/ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) { itemroulette_t *const roulette = &player->itemRoulette; diff --git a/src/k_roulette.h b/src/k_roulette.h index fbc6d4a4b..0cef89f7a 100644 --- a/src/k_roulette.h +++ b/src/k_roulette.h @@ -16,19 +16,154 @@ #include "doomtype.h" #include "d_player.h" -boolean K_ItemEnabled(SINT8 item); -boolean K_ItemSingularity(kartitems_t item); - -INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item); -void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette); - -void K_StartItemRoulette(player_t *const player); -void K_StartEggmanRoulette(player_t *const player); - #define ROULETTE_SPACING (36 << FRACBITS) #define ROULETTE_SPACING_SPLITSCREEN (16 << FRACBITS) + +/*-------------------------------------------------- + boolean K_ItemEnabled(kartitems_t item); + + Determines whenever or not an item should + be enabled. Accounts for situations where + rules should not be able to be changed. + + Input Arguments:- + item - The item to check. + + Return:- + true if the item is enabled, otherwise false. +--------------------------------------------------*/ + +boolean K_ItemEnabled(kartitems_t item); + + +/*-------------------------------------------------- + boolean K_ItemSingularity(kartitems_t item); + + Determines whenever or not this item should + be using special cases to prevent more than + one existing at a time. + + Input Arguments:- + item - The item to check. + + Return:- + true to use the special rules, otherwise false. +--------------------------------------------------*/ + +boolean K_ItemSingularity(kartitems_t item); + + +/*-------------------------------------------------- + INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item); + + Gets the frequency an item should show up in + an item bracket, and adjusted for special + factors (such as Frantic Items). + + Input Arguments:- + player - The player we intend to give the item to later. + Can be NULL for generic use. + roulette - The roulette data that we intend to + insert this item into. + pos - The item bracket we are in. + item - The item to give. + + Return:- + The number of items we want to insert + into the roulette. +--------------------------------------------------*/ + +INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item); + + +/*-------------------------------------------------- + void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette); + + Fills out the item roulette struct when it is + initially created. This function needs to be + HUD-safe for the item debugger, so the player + cannot be modified at this stage. + + Input Arguments:- + player - The player this roulette data is for. + Can be NULL for generic use. + roulette - The roulette data struct to fill out. + + Return:- + N/A +--------------------------------------------------*/ + +void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette); + + +/*-------------------------------------------------- + void K_StartItemRoulette(player_t *const player); + + Starts the item roulette sequence for a player. + This stage can only be used by gameplay, thus + this handles gameplay modifications as well. + + Input Arguments:- + player - The player to start the item roulette for. + + Return:- + N/A +--------------------------------------------------*/ + +void K_StartItemRoulette(player_t *const player); + + +/*-------------------------------------------------- + void K_StartEggmanRoulette(player_t *const player); + + Starts the Eggman Mark roulette sequence for + a player. Looks identical to a regular item + roulette, but gives you the Eggman explosion + countdown instead when confirming it. + + Input Arguments:- + player - The player to start the Eggman roulette for. + + Return:- + N/A +--------------------------------------------------*/ + +void K_StartEggmanRoulette(player_t *const player); + + +/*-------------------------------------------------- + fixed_t K_GetRouletteOffset(itemroulette_t *const roulette, fixed_t renderDelta); + + Gets the Y offset, for use in the roulette HUD. + A separate function since it is used both by the + HUD itself, as well as when confirming an item. + + Input Arguments:- + roulette - The roulette we are drawing for. + renderDelta - Fractional tic delta, when used for HUD. + + Return:- + The Y offset when drawing the item. +--------------------------------------------------*/ + fixed_t K_GetRouletteOffset(itemroulette_t *const roulette, fixed_t renderDelta); + +/*-------------------------------------------------- + void K_KartItemRoulette(player_t *const player, ticcmd_t *cmd); + + Handles ticking a player's item roulette, + and player input for stopping it. + + Input Arguments:- + player - The player to run the item roulette for. + cmd - The player's controls. + + Return:- + N/A +--------------------------------------------------*/ + void K_KartItemRoulette(player_t *const player, ticcmd_t *cmd); + #endif // __K_ROULETTE_H__ From 3f2209b853b90d12e6ccfa4d7c5f4090df4bd5be Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Dec 2022 19:55:29 -0500 Subject: [PATCH 34/46] Newly added files to CMakeLists.txt --- src/CMakeLists.txt | 1 + src/objects/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bed2d5532..61e5ff86f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -129,6 +129,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 k_follower.c k_profiles.c k_specialstage.c + k_roulette.c ) if("${CMAKE_COMPILER_IS_GNUCC}" AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows" AND NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}" AND NOT "${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}") diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index f02428382..b3b5ff2c0 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -8,4 +8,5 @@ target_sources(SRB2SDL2 PRIVATE orbinaut.c jawz.c duel-bomb.c + broly.c ) From 5ffcfb558f331575aa84c64843d827137a2cfd07 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Dec 2022 20:54:15 -0500 Subject: [PATCH 35/46] #define BOOL boolean -> #define boolean BOOL --- src/doomtype.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doomtype.h b/src/doomtype.h index fd5e6c2c4..b1269eaff 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -146,7 +146,7 @@ size_t strlcpy(char *dst, const char *src, size_t siz); #ifndef _WIN32 typedef int32_t boolean; #else -#define BOOL boolean +#define boolean BOOL #endif #ifndef __cplusplus From 4feba883276502f5603c158c541bb7fabc6a01aa Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Dec 2022 22:08:29 -0500 Subject: [PATCH 36/46] Slightly improve colorize's luminance method Before I was using coefficents from another formula I forget the source of, but over time I've stopped liking how it looked -- just considers greens far too bright. Here I'm trying out BT.601 coefficents instead (https://en.wikipedia.org/wiki/Rec._601). These are also the coefficients Doom itself used for invulnerability's invert effect (if you ignore the typo Carmack made :p), so this checks out. --- src/k_color.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/k_color.c b/src/k_color.c index ac1a2e6c8..5f58441e0 100644 --- a/src/k_color.c +++ b/src/k_color.c @@ -24,11 +24,12 @@ --------------------------------------------------*/ UINT8 K_ColorRelativeLuminance(UINT8 r, UINT8 g, UINT8 b) { - UINT32 redweight = 1063 * r; - UINT32 greenweight = 3576 * g; - UINT32 blueweight = 361 * b; - UINT32 brightness = (redweight + greenweight + blueweight) / 5000; - return min(brightness, UINT8_MAX); + // These are the BT.601 coefficents + // See also: https://en.wikipedia.org/wiki/Rec._601 + UINT32 redweight = 299 * r; + UINT32 greenweight = 587 * g; + UINT32 blueweight = 114 * b; + return min((redweight + greenweight + blueweight) / 1000, UINT8_MAX); } /*-------------------------------------------------- From 107ec2784631a556f6033d64162504d3cbd37657 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Dec 2022 22:29:38 -0500 Subject: [PATCH 37/46] Go back to the old one, but gamma correct it Did more research into the subject, the formula I used before was fine and was just expecting gamma correction. --- src/k_color.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/k_color.c b/src/k_color.c index 5f58441e0..d73a938e7 100644 --- a/src/k_color.c +++ b/src/k_color.c @@ -24,12 +24,18 @@ --------------------------------------------------*/ UINT8 K_ColorRelativeLuminance(UINT8 r, UINT8 g, UINT8 b) { - // These are the BT.601 coefficents - // See also: https://en.wikipedia.org/wiki/Rec._601 - UINT32 redweight = 299 * r; - UINT32 greenweight = 587 * g; - UINT32 blueweight = 114 * b; - return min((redweight + greenweight + blueweight) / 1000, UINT8_MAX); + double redWeight = ((r * 1.0) / UINT8_MAX); + double greenWeight = ((g * 1.0) / UINT8_MAX); + double blueWeight = ((b * 1.0) / UINT8_MAX); + double brightness = 0.5; + + redWeight = pow(redWeight, 2.2) * 0.2126; + greenWeight = pow(greenWeight, 2.2) * 0.7152; + blueWeight = pow(greenWeight, 2.2) * 0.0722; + + brightness = pow(redWeight + greenWeight + blueWeight, 1.0 / 2.2); + + return (UINT8)(brightness * UINT8_MAX); } /*-------------------------------------------------- From 45dad16e4cf96ce61553f646fa5ce63ec2bf82dc Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 17 Dec 2022 00:09:56 -0800 Subject: [PATCH 38/46] Kill mine when running into it during explosion state Fixes punting a mine if you run straight into it while invincible. --- src/k_collide.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_collide.c b/src/k_collide.c index 867efba03..db7be93f0 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -343,7 +343,7 @@ boolean K_MineCollide(mobj_t *t1, mobj_t *t2) // Bomb punting if ((t1->state >= &states[S_SSMINE1] && t1->state <= &states[S_SSMINE4]) - || (t1->state >= &states[S_SSMINE_DEPLOY8] && t1->state <= &states[S_SSMINE_DEPLOY13])) + || (t1->state >= &states[S_SSMINE_DEPLOY8] && t1->state <= &states[S_SSMINE_EXPLODE2])) { P_KillMobj(t1, t2, t2, DMG_NORMAL); } From 81fec17bb4d13c6d942215fd3a95feff13350758 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 17 Dec 2022 16:39:10 +0000 Subject: [PATCH 39/46] First Musicdef changes in preparation for music test - Add `title` field, to permit seperation from `source` - Store current displayed string as zone memory, to prevent repeated recalculation --- src/hu_stuff.c | 18 ++++++++---------- src/p_setup.c | 1 + src/s_sound.c | 14 +++++++------- src/s_sound.h | 3 ++- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index e30a725e9..0f861f642 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -938,8 +938,7 @@ static void HU_TickSongCredits(void) if (cursongcredit.anim > 0) { - char *str = va("\x1F"" %s", cursongcredit.def->source); - INT32 len = V_ThinStringWidth(str, V_ALLOWLOWERCASE|V_6WIDTHSPACE); + INT32 len = V_ThinStringWidth(cursongcredit.text, V_ALLOWLOWERCASE|V_6WIDTHSPACE); fixed_t destx = (len+7) * FRACUNIT; if (cursongcredit.trans > 0) @@ -2045,29 +2044,28 @@ static void HU_DrawDemoInfo(void) // void HU_DrawSongCredits(void) { - char *str; fixed_t x; fixed_t y = (r_splitscreen ? (BASEVIDHEIGHT/2)-4 : 32) * FRACUNIT; INT32 bgt; - if (!cursongcredit.def) // No def + if (!cursongcredit.def || cursongcredit.trans >= NUMTRANSMAPS) // No def { return; } - str = va("\x1F"" %s", cursongcredit.def->source); bgt = (NUMTRANSMAPS/2) + (cursongcredit.trans / 2); x = R_InterpolateFixed(cursongcredit.old_x, cursongcredit.x); if (bgt < NUMTRANSMAPS) { - V_DrawFixedPatch(x, y - (2 * FRACUNIT), FRACUNIT, V_SNAPTOLEFT|(bgt<usage, textline); -#endif + if (!stricmp(stoken, "title")) { + STRBUFCPY(def->title, textline); } else if (!stricmp(stoken, "source")) { STRBUFCPY(def->source, textline); } else if (!stricmp(stoken, "volume")) { @@ -1609,13 +1607,15 @@ void S_ShowMusicCredit(void) if (!stricmp(def->name, music_name)) { cursongcredit.def = def; + Z_Free(cursongcredit.text); + cursongcredit.text = Z_StrDup(va("\x1F"" %s - %s", def->title, def->source)); cursongcredit.anim = 5*TICRATE; - cursongcredit.x = cursongcredit.old_x =0; + cursongcredit.x = cursongcredit.old_x = 0; cursongcredit.trans = NUMTRANSMAPS; return; } - else - def = def->next; + + def = def->next; } } diff --git a/src/s_sound.h b/src/s_sound.h index adbf92533..0b1148964 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -173,7 +173,7 @@ boolean S_SpeedMusic(float speed); struct musicdef_t { char name[7]; - //char usage[256]; + char title[256]; char source[256]; int volume; musicdef_t *next; @@ -182,6 +182,7 @@ struct musicdef_t extern struct cursongcredit { musicdef_t *def; + char *text; UINT16 anim; UINT8 trans; fixed_t x; From ac423b3461c03f7d1d7ff2fe422fa1714155560b Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 17 Dec 2022 17:36:44 +0000 Subject: [PATCH 40/46] Further changes to musicdef based on discussion with Gunla - Add "author" and "originalcomposers" fields - "author" is for remixes and original compositions, can be ommitted - "originalcomposers" will not be visible mid-game, but will be visible in music test. Stores original sound team info - Store all strings as Zone memory instead of static arrays, since not every field will be relevant for every track --- src/s_sound.c | 71 +++++++++++++++++++++++++++++++++++++++++++++------ src/s_sound.h | 6 +++-- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/s_sound.c b/src/s_sound.c index 29c3bb6d4..96211d653 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -1476,14 +1476,32 @@ ReadMusicDefFields textline = value; - /* based ignored lumps */ - if (!stricmp(stoken, "title")) { - STRBUFCPY(def->title, textline); - } else if (!stricmp(stoken, "source")) { - STRBUFCPY(def->source, textline); - } else if (!stricmp(stoken, "volume")) { + if (!stricmp(stoken, "title")) + { + Z_Free(def->title); + def->title = Z_StrDup(textline); + } + else if (!stricmp(stoken, "author")) + { + Z_Free(def->author); + def->author = Z_StrDup(textline); + } + else if (!stricmp(stoken, "source")) + { + Z_Free(def->source); + def->source = Z_StrDup(textline); + } + else if (!stricmp(stoken, "originalcomposers")) + { + Z_Free(def->composers); + def->composers = Z_StrDup(textline); + } + else if (!stricmp(stoken, "volume")) + { def->volume = atoi(textline); - } else { + } + else + { MusicDefError(CONS_WARNING, "Unknown field '%s'.", stoken, lumpnum, line); @@ -1606,9 +1624,46 @@ void S_ShowMusicCredit(void) { if (!stricmp(def->name, music_name)) { + char credittext[128] = ""; + char *work = NULL; + size_t len = 128, worklen; + + if (!def->title) + { + return; + } + + work = va("\x1F %s", def->title); + worklen = strlen(work); + if (worklen <= len) + { + strncat(credittext, work, len); + len -= worklen; + +#define MUSICCREDITAPPEND(field)\ + if (field)\ + {\ + work = va(" - %s", field);\ + worklen = strlen(work);\ + if (worklen <= len)\ + {\ + strncat(credittext, work, len);\ + len -= worklen;\ + }\ + } + + MUSICCREDITAPPEND(def->author); + MUSICCREDITAPPEND(def->source); + +#undef MUSICCREDITAPPEND + } + + if (credittext[0] == '\0') + return; + cursongcredit.def = def; Z_Free(cursongcredit.text); - cursongcredit.text = Z_StrDup(va("\x1F"" %s - %s", def->title, def->source)); + cursongcredit.text = Z_StrDup(credittext); cursongcredit.anim = 5*TICRATE; cursongcredit.x = cursongcredit.old_x = 0; cursongcredit.trans = NUMTRANSMAPS; diff --git a/src/s_sound.h b/src/s_sound.h index 0b1148964..c9a86a2f5 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -173,8 +173,10 @@ boolean S_SpeedMusic(float speed); struct musicdef_t { char name[7]; - char title[256]; - char source[256]; + char *title; + char *author; + char *source; + char *composers; int volume; musicdef_t *next; }; From 182c673677ac5ec64b9b6bff1df466a4c542eb0a Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 17 Dec 2022 17:30:55 -0800 Subject: [PATCH 41/46] Fix eggman mark becoming intagible to players who previously hit one Specifically, if a player hit an eggmark and didn't roll another item before hitting another eggmark. --- src/p_inter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_inter.c b/src/p_inter.c index 6400b9d35..3bc592988 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -131,7 +131,7 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) return false; // Already have fake - if (player->itemRoulette.eggman == true + if ((player->itemRoulette.active && player->itemRoulette.eggman) == true || player->eggmanexplode) return false; } From 3a40352679f15fd5e2a61ab9f09573ab3f0d0027 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 17 Dec 2022 18:38:40 -0800 Subject: [PATCH 42/46] opengl: fix bottom and right edge of clip rect --- src/hardware/hw_draw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c index d8eac4a44..a336bd53c 100644 --- a/src/hardware/hw_draw.c +++ b/src/hardware/hw_draw.c @@ -249,7 +249,7 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p if ((cx + fwidth) > clip->right) { - const float n = (clip->right - clip->left); + const float n = (clip->right - cx); s_max = (s_min + ((n / fwidth) * s_max)); fwidth = n; @@ -257,7 +257,7 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p if ((cy + fheight) > clip->bottom) { - const float n = (clip->bottom - clip->top); + const float n = (clip->bottom - cy); t_max = (t_min + ((n / fheight) * t_max)); fheight = n; From 374342358d82a100299a1af265148a4921585a00 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 17 Dec 2022 18:55:36 -0800 Subject: [PATCH 43/46] Fix HUD item flicker crash Fixes item capsule award if a roulette was not rolled sometime before. --- src/k_hud.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_hud.c b/src/k_hud.c index 7c1d94e42..8d9167a4e 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1023,7 +1023,7 @@ static void K_drawKartItem(void) // Why write V_DrawScaledPatch calls over and over when they're all the same? // Set to 'no item' just in case. const UINT8 offset = ((r_splitscreen > 1) ? 1 : 0); - patch_t *localpatch[3] = { kp_nodraw }; + patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw }; patch_t *localbg = ((offset) ? kp_itembg[2] : kp_itembg[0]); patch_t *localinv = ((offset) ? kp_invincibility[((leveltime % (6*3)) / 3) + 7] : kp_invincibility[(leveltime % (7*3)) / 3]); INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags... From 12052a48de530ce9609b26253ab3aaf6af376adb Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 17 Dec 2022 20:09:23 -0800 Subject: [PATCH 44/46] Fix spring stars not spawning for upside down vertical springs --- src/p_map.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_map.c b/src/p_map.c index c7047bcde..9c9a71fdf 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -406,7 +406,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) K_TumbleInterrupt(object->player); P_ResetPlayer(object->player); - object->player->springstars = max(vertispeed, horizspeed) / FRACUNIT / 2; + object->player->springstars = max(abs(vertispeed), horizspeed) / FRACUNIT / 2; object->player->springcolor = starcolor; // Less friction when hitting springs From ab42c473735c22305c49a15269a80d7f61af9e90 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 17 Dec 2022 21:23:07 -0800 Subject: [PATCH 45/46] .gitignore: add CMakeUserPresets.json --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3eca1c57a..6b2702a76 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ Win32_LIB_ASM_Release /make /bin /build +/CMakeUserPresets.json From 659dc8270eeddbff17032b8281bae87a1f1972eb Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 18 Dec 2022 01:10:34 -0500 Subject: [PATCH 46/46] Roulette speed thing - Slightly slower at start - More reasonable default --- src/k_roulette.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_roulette.c b/src/k_roulette.c index ec15f4e9b..40cd68af4 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -821,7 +821,7 @@ static void K_InitRoulette(itemroulette_t *const roulette) roulette->secondToFirst = 0; roulette->elapsed = 0; - roulette->tics = roulette->speed = ROULETTE_SPEED_FASTEST; // Some default speed + roulette->tics = roulette->speed = ROULETTE_SPEED_TIMEATTACK; // Some default speed roulette->active = true; roulette->eggman = false; @@ -981,7 +981,7 @@ static void K_CalculateRouletteSpeed(itemroulette_t *const roulette) // Combine our two factors together. total = min(FRACUNIT, (frontRun / 2) + (progress / 2)); - if (leveltime < starttime + 20*TICRATE) + if (leveltime < starttime + 30*TICRATE) { // Don't impact as much at the start. // This makes it so that everyone gets to enjoy the lowest speed at the start. @@ -991,7 +991,7 @@ static void K_CalculateRouletteSpeed(itemroulette_t *const roulette) } else { - const fixed_t lerp = FixedDiv(leveltime - starttime, 20*TICRATE); + const fixed_t lerp = FixedDiv(leveltime - starttime, 30*TICRATE); total = FRACUNIT + FixedMul(lerp, total - FRACUNIT); } }