mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-04-27 20:41:46 +00:00
Stereo Mode: Add "shf" (Shuffle) option
This basically came to me in a dream, who am I to look the horse in its mouth
- Press to start a shuffled sequence, losing your current position in the autosequence.
- Press again to disable, but keep your current track.
- Adjust horizontal offset of Stereo buttons slightly.
- More judiciously comment Sound Test functionality, to assist future maintainers.
This commit is contained in:
parent
e331c57a32
commit
36f8a64d65
5 changed files with 209 additions and 15 deletions
|
|
@ -1309,6 +1309,7 @@ typedef enum
|
||||||
stereospecial_pause,
|
stereospecial_pause,
|
||||||
stereospecial_play,
|
stereospecial_play,
|
||||||
stereospecial_seq,
|
stereospecial_seq,
|
||||||
|
stereospecial_shf,
|
||||||
stereospecial_vol,
|
stereospecial_vol,
|
||||||
stereospecial_track,
|
stereospecial_track,
|
||||||
} stereospecial_e;
|
} stereospecial_e;
|
||||||
|
|
|
||||||
|
|
@ -7089,6 +7089,11 @@ void M_DrawSoundTest(void)
|
||||||
if (soundtest.autosequence == true)
|
if (soundtest.autosequence == true)
|
||||||
y = currentMenu->y + 6;
|
y = currentMenu->y + 6;
|
||||||
}
|
}
|
||||||
|
else if (currentMenu->menuitems[i].mvar2 == stereospecial_shf) // shf
|
||||||
|
{
|
||||||
|
if (soundtest.shuffle == true)
|
||||||
|
y = currentMenu->y + 6;
|
||||||
|
}
|
||||||
|
|
||||||
// Button is being pressed
|
// Button is being pressed
|
||||||
if (i == itemOn && !soundtest.justopened && M_MenuConfirmHeld(pid))
|
if (i == itemOn && !soundtest.justopened && M_MenuConfirmHeld(pid))
|
||||||
|
|
@ -7199,7 +7204,7 @@ void M_DrawSoundTest(void)
|
||||||
V_DrawCenteredThinString(x + 13, y + 1, 0, currentMenu->menuitems[i].text);
|
V_DrawCenteredThinString(x + 13, y + 1, 0, currentMenu->menuitems[i].text);
|
||||||
}
|
}
|
||||||
|
|
||||||
x += 27;
|
x += 25;
|
||||||
}
|
}
|
||||||
|
|
||||||
V_DrawCharacter(cursorx - 4, currentMenu->y - 8 - (skullAnimCounter/5),
|
V_DrawCharacter(cursorx - 4, currentMenu->y - 8 - (skullAnimCounter/5),
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,26 @@ static void M_SoundTestSeq(INT32 choice)
|
||||||
soundtest.autosequence ^= true;
|
soundtest.autosequence ^= true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void M_SoundTestShf(INT32 choice)
|
||||||
|
{
|
||||||
|
(void)choice;
|
||||||
|
|
||||||
|
if (soundtest.shuffle)
|
||||||
|
{
|
||||||
|
soundtest.shuffle = false;
|
||||||
|
soundtest.sequence.shuffleinfo = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
S_SoundTestStop();
|
||||||
|
|
||||||
|
soundtest.playing = true;
|
||||||
|
soundtest.autosequence = true;
|
||||||
|
soundtest.shuffle = true;
|
||||||
|
S_UpdateSoundTestDef(false, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
consvar_t *M_GetSoundTestVolumeCvar(void)
|
consvar_t *M_GetSoundTestVolumeCvar(void)
|
||||||
{
|
{
|
||||||
if (soundtest.current == NULL)
|
if (soundtest.current == NULL)
|
||||||
|
|
@ -160,18 +180,20 @@ static void M_SoundTestTick(void)
|
||||||
menuitem_t MISC_SoundTest[] =
|
menuitem_t MISC_SoundTest[] =
|
||||||
{
|
{
|
||||||
{IT_STRING | IT_CALL, "Back", "STER_IC0", NULL, {.routine = M_GoBack}, 0, stereospecial_back},
|
{IT_STRING | IT_CALL, "Back", "STER_IC0", NULL, {.routine = M_GoBack}, 0, stereospecial_back},
|
||||||
{IT_SPACE, NULL, NULL, NULL, {NULL}, 11, 0},
|
{IT_SPACE, NULL, NULL, NULL, {NULL}, 6, 0},
|
||||||
{IT_STRING | IT_CALL, "Stop", "STER_IC1", NULL, {.routine = M_SoundTestMainControl}, 0, 0},
|
{IT_STRING | IT_CALL, "Stop", "STER_IC1", NULL, {.routine = M_SoundTestMainControl}, 0, 0},
|
||||||
{IT_SPACE, NULL, NULL, NULL, {NULL}, 8, 0},
|
{IT_SPACE, NULL, NULL, NULL, {NULL}, 6, 0},
|
||||||
{IT_STRING | IT_CALL, "Pause", "STER_IC2", NULL, {.routine = M_SoundTestMainControl}, 2, stereospecial_pause},
|
{IT_STRING | IT_CALL, "Pause", "STER_IC2", NULL, {.routine = M_SoundTestMainControl}, 2, stereospecial_pause},
|
||||||
{IT_STRING | IT_CALL, "Play", "STER_IC3", NULL, {.routine = M_SoundTestMainControl}, 1, stereospecial_play},
|
{IT_STRING | IT_CALL, "Play", "STER_IC3", NULL, {.routine = M_SoundTestMainControl}, 1, stereospecial_play},
|
||||||
{IT_SPACE, NULL, NULL, NULL, {NULL}, 8, 0},
|
{IT_SPACE, NULL, NULL, NULL, {NULL}, 6, 0},
|
||||||
{IT_STRING | IT_CALL, "Prev", "STER_IC4", NULL, {.routine = M_SoundTestNextPrev}, -1, 0},
|
{IT_STRING | IT_CALL, "Prev", "STER_IC4", NULL, {.routine = M_SoundTestNextPrev}, -1, 0},
|
||||||
{IT_STRING | IT_CALL, "Next", "STER_IC5", NULL, {.routine = M_SoundTestNextPrev}, 1, 0},
|
{IT_STRING | IT_CALL, "Next", "STER_IC5", NULL, {.routine = M_SoundTestNextPrev}, 1, 0},
|
||||||
{IT_SPACE, NULL, NULL, NULL, {NULL}, 8, 0},
|
{IT_SPACE, NULL, NULL, NULL, {NULL}, 6, 0},
|
||||||
{IT_STRING | IT_ARROWS, "Seq", "STER_IC6", NULL, {.routine = M_SoundTestSeq}, 0, stereospecial_seq},
|
{IT_STRING | IT_ARROWS, "Seq", "STER_IC6", NULL, {.routine = M_SoundTestSeq}, 0, stereospecial_seq},
|
||||||
|
{IT_STRING | IT_ARROWS, "Shf", "STER_IC7", NULL, {.routine = M_SoundTestShf}, 0, stereospecial_shf},
|
||||||
{IT_SPACE, NULL, NULL, NULL, {NULL}, 0, 244},
|
{IT_SPACE, NULL, NULL, NULL, {NULL}, 0, 244},
|
||||||
{IT_STRING | IT_ARROWS, "Vol", NULL, NULL, {.routine = M_SoundTestVol}, 0, stereospecial_vol},
|
{IT_STRING | IT_ARROWS, "Vol", NULL, NULL, {.routine = M_SoundTestVol}, 0, stereospecial_vol},
|
||||||
|
{IT_SPACE, NULL, NULL, NULL, {NULL}, 2, 0},
|
||||||
{IT_STRING | IT_ARROWS, "Track", NULL, NULL, {.routine = M_SoundTestTrack}, 0, stereospecial_track},
|
{IT_STRING | IT_ARROWS, "Track", NULL, NULL, {.routine = M_SoundTestTrack}, 0, stereospecial_track},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
182
src/s_sound.c
182
src/s_sound.c
|
|
@ -32,7 +32,7 @@
|
||||||
#include "lua_hook.h" // MusicChange hook
|
#include "lua_hook.h" // MusicChange hook
|
||||||
#include "byteptr.h"
|
#include "byteptr.h"
|
||||||
#include "k_menu.h" // M_PlayMenuJam
|
#include "k_menu.h" // M_PlayMenuJam
|
||||||
#include "m_random.h" // P_RandomKey
|
#include "m_random.h" // M_RandomKey
|
||||||
#include "i_time.h"
|
#include "i_time.h"
|
||||||
#include "v_video.h" // V_ThinStringWidth
|
#include "v_video.h" // V_ThinStringWidth
|
||||||
#include "music.h"
|
#include "music.h"
|
||||||
|
|
@ -1366,6 +1366,10 @@ void S_PopulateSoundTestSequence(void)
|
||||||
if (soundtest.sequence.id == 0)
|
if (soundtest.sequence.id == 0)
|
||||||
soundtest.sequence.id = 1;
|
soundtest.sequence.id = 1;
|
||||||
|
|
||||||
|
// Prepare shuffle material.
|
||||||
|
soundtest.sequence.shuffleinfo = 0;
|
||||||
|
soundtest.sequence.shufflenext = NULL;
|
||||||
|
|
||||||
soundtest.sequence.next = NULL;
|
soundtest.sequence.next = NULL;
|
||||||
|
|
||||||
tail = &soundtest.sequence.next;
|
tail = &soundtest.sequence.next;
|
||||||
|
|
@ -1437,6 +1441,11 @@ void S_PopulateSoundTestSequence(void)
|
||||||
|
|
||||||
for (def = musicdefstart; def; def = def->next)
|
for (def = musicdefstart; def; def = def->next)
|
||||||
{
|
{
|
||||||
|
// This is the simplest set of checks,
|
||||||
|
// so let's wipe the shuffle data here.
|
||||||
|
def->sequence.shuffleinfo = 0;
|
||||||
|
def->sequence.shufflenext = NULL;
|
||||||
|
|
||||||
if (def->sequence.id == soundtest.sequence.id)
|
if (def->sequence.id == soundtest.sequence.id)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
@ -1467,12 +1476,11 @@ static boolean S_SoundTestDefLocked(musicdef_t *def)
|
||||||
|
|
||||||
void S_UpdateSoundTestDef(boolean reverse, boolean dotracks, boolean skipnull)
|
void S_UpdateSoundTestDef(boolean reverse, boolean dotracks, boolean skipnull)
|
||||||
{
|
{
|
||||||
musicdef_t *newdef;
|
musicdef_t *newdef = NULL;
|
||||||
|
|
||||||
newdef = NULL;
|
|
||||||
|
|
||||||
if (reverse == false)
|
if (reverse == false)
|
||||||
{
|
{
|
||||||
|
// Track update
|
||||||
if (dotracks == true && soundtest.current != NULL
|
if (dotracks == true && soundtest.current != NULL
|
||||||
&& soundtest.currenttrack < soundtest.current->numtracks-1)
|
&& soundtest.currenttrack < soundtest.current->numtracks-1)
|
||||||
{
|
{
|
||||||
|
|
@ -1480,22 +1488,146 @@ void S_UpdateSoundTestDef(boolean reverse, boolean dotracks, boolean skipnull)
|
||||||
goto updatetrackonly;
|
goto updatetrackonly;
|
||||||
}
|
}
|
||||||
|
|
||||||
newdef = (soundtest.current != NULL)
|
if (soundtest.shuffle == true && soundtest.sequence.shuffleinfo == 0)
|
||||||
? soundtest.current->sequence.next
|
|
||||||
: soundtest.sequence.next;
|
|
||||||
while (newdef != NULL && S_SoundTestDefLocked(newdef))
|
|
||||||
newdef = newdef->sequence.next;
|
|
||||||
if (newdef == NULL && skipnull == true)
|
|
||||||
{
|
{
|
||||||
|
// The shuffle data isn't initialised.
|
||||||
|
// Count the valid set of musicdefs we can randomly select from!
|
||||||
|
// This will later liberally be passed to M_RandomKey.
|
||||||
|
|
||||||
newdef = soundtest.sequence.next;
|
newdef = soundtest.sequence.next;
|
||||||
|
while (newdef != NULL)
|
||||||
|
{
|
||||||
|
if (S_SoundTestDefLocked(newdef) == false)
|
||||||
|
{
|
||||||
|
newdef->sequence.shuffleinfo = 0;
|
||||||
|
soundtest.sequence.shuffleinfo++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Don't permit if it gets unlocked before shuffle count gets reset
|
||||||
|
newdef->sequence.shuffleinfo = (size_t)-1;
|
||||||
|
}
|
||||||
|
newdef->sequence.shufflenext = NULL;
|
||||||
|
|
||||||
|
newdef = newdef->sequence.next;
|
||||||
|
}
|
||||||
|
soundtest.sequence.shufflenext = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (soundtest.shuffle == true)
|
||||||
|
{
|
||||||
|
// Do we have it cached..?
|
||||||
|
newdef = soundtest.current != NULL
|
||||||
|
? soundtest.current->sequence.shufflenext
|
||||||
|
: soundtest.sequence.shufflenext;
|
||||||
|
|
||||||
|
if (newdef != NULL)
|
||||||
|
;
|
||||||
|
else if (soundtest.sequence.shuffleinfo != 0)
|
||||||
|
{
|
||||||
|
// Nope, not cached. Grab a random entry and hunt for it.
|
||||||
|
size_t shuffleseek = M_RandomKey(soundtest.sequence.shuffleinfo);
|
||||||
|
size_t shuffleseekcopy = shuffleseek;
|
||||||
|
|
||||||
|
// Since these are sequential, we can sometimes
|
||||||
|
// get a small benefit by starting partway down the list.
|
||||||
|
if (
|
||||||
|
soundtest.current != NULL
|
||||||
|
&& soundtest.current->sequence.shuffleinfo != 0
|
||||||
|
&& soundtest.current->sequence.shuffleinfo <= shuffleseek
|
||||||
|
)
|
||||||
|
{
|
||||||
|
newdef = soundtest.current;
|
||||||
|
shuffleseek -= (soundtest.current->sequence.shuffleinfo - 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newdef = soundtest.sequence.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...yeah, though, this is basically O(n). I could provide a
|
||||||
|
// great many excuses, but the basic impetus is that I saw
|
||||||
|
// a thread on an open-source software development forum where,
|
||||||
|
// since 2014, a parade of users have been asking for the same
|
||||||
|
// basic QoL feature and been consecutively berated by one developer
|
||||||
|
// extremely against the idea of implmenting something imperfect.
|
||||||
|
// I have enough self-awareness as a programmer to recognise that
|
||||||
|
// that is a chronic case of "PROGRAMMER BRAIN". Sometimes you
|
||||||
|
// just need to do a feature "badly" because it's more important
|
||||||
|
// for it to exist at all than to channel mathematical elegance.
|
||||||
|
// ~toast 220923
|
||||||
|
|
||||||
|
for (; newdef != NULL; newdef = newdef->sequence.next)
|
||||||
|
{
|
||||||
|
if (newdef->sequence.shuffleinfo != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (S_SoundTestDefLocked(newdef) == true)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (shuffleseek != 0)
|
||||||
|
{
|
||||||
|
shuffleseek--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newdef == NULL)
|
||||||
|
{
|
||||||
|
// Fell short!? Try again later
|
||||||
|
soundtest.sequence.shuffleinfo = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Don't select the same entry twice
|
||||||
|
if (soundtest.sequence.shuffleinfo)
|
||||||
|
soundtest.sequence.shuffleinfo--;
|
||||||
|
|
||||||
|
// One-indexed so the first shuffled entry has a valid shuffleinfo
|
||||||
|
newdef->sequence.shuffleinfo = shuffleseekcopy+1;
|
||||||
|
|
||||||
|
// Link it to the end of the chain
|
||||||
|
if (soundtest.current && soundtest.current->sequence.shuffleinfo != 0)
|
||||||
|
{
|
||||||
|
soundtest.current->sequence.shufflenext = newdef;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
soundtest.sequence.shufflenext = newdef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Just blaze through the musicdefs
|
||||||
|
newdef = (soundtest.current != NULL)
|
||||||
|
? soundtest.current->sequence.next
|
||||||
|
: soundtest.sequence.next;
|
||||||
while (newdef != NULL && S_SoundTestDefLocked(newdef))
|
while (newdef != NULL && S_SoundTestDefLocked(newdef))
|
||||||
newdef = newdef->sequence.next;
|
newdef = newdef->sequence.next;
|
||||||
|
|
||||||
|
if (newdef == NULL && skipnull == true)
|
||||||
|
{
|
||||||
|
newdef = soundtest.sequence.next;
|
||||||
|
while (newdef != NULL && S_SoundTestDefLocked(newdef))
|
||||||
|
newdef = newdef->sequence.next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Everything in this case is doing a full-on O(n) search
|
||||||
|
// for the previous entry in one of two singly linked lists.
|
||||||
|
// I know there are better solutions. It basically boils
|
||||||
|
// down to the fact that this code only runs on direct user
|
||||||
|
// input on a menu, never in the background, and therefore
|
||||||
|
// is straight up less important than the forwards direction.
|
||||||
|
|
||||||
musicdef_t *def, *lastdef = NULL;
|
musicdef_t *def, *lastdef = NULL;
|
||||||
|
|
||||||
|
// Track update
|
||||||
if (dotracks == true && soundtest.current != NULL
|
if (dotracks == true && soundtest.current != NULL
|
||||||
&& soundtest.currenttrack > 0)
|
&& soundtest.currenttrack > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -1503,6 +1635,34 @@ void S_UpdateSoundTestDef(boolean reverse, boolean dotracks, boolean skipnull)
|
||||||
goto updatetrackonly;
|
goto updatetrackonly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (soundtest.shuffle && soundtest.current != NULL)
|
||||||
|
{
|
||||||
|
// Basically identical structure to the sequence.next case... templates might be cool one day
|
||||||
|
|
||||||
|
if (soundtest.sequence.shufflenext == soundtest.current)
|
||||||
|
;
|
||||||
|
else for (def = soundtest.sequence.shufflenext; def; def = def->sequence.shufflenext)
|
||||||
|
{
|
||||||
|
if (!S_SoundTestDefLocked(def))
|
||||||
|
{
|
||||||
|
lastdef = def;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (def->sequence.shufflenext != soundtest.current)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
newdef = lastdef;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto updatecurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
soundtest.shuffle = false;
|
||||||
|
soundtest.sequence.shuffleinfo = 0;
|
||||||
|
|
||||||
if (soundtest.current == soundtest.sequence.next
|
if (soundtest.current == soundtest.sequence.next
|
||||||
&& skipnull == false)
|
&& skipnull == false)
|
||||||
{
|
{
|
||||||
|
|
@ -1617,6 +1777,8 @@ void S_SoundTestStop(void)
|
||||||
|
|
||||||
soundtest.playing = false;
|
soundtest.playing = false;
|
||||||
soundtest.autosequence = false;
|
soundtest.autosequence = false;
|
||||||
|
soundtest.shuffle = false;
|
||||||
|
soundtest.sequence.shuffleinfo = 0;
|
||||||
|
|
||||||
Music_Stop("stereo");
|
Music_Stop("stereo");
|
||||||
Music_Stop("stereo_fade");
|
Music_Stop("stereo_fade");
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,9 @@ struct soundtestsequence_t
|
||||||
UINT8 id;
|
UINT8 id;
|
||||||
UINT16 map;
|
UINT16 map;
|
||||||
musicdef_t *next;
|
musicdef_t *next;
|
||||||
|
|
||||||
|
size_t shuffleinfo;
|
||||||
|
musicdef_t *shufflenext;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Music credits
|
// Music credits
|
||||||
|
|
@ -195,6 +198,7 @@ extern struct soundtest
|
||||||
soundtestsequence_t sequence; // Sequence head
|
soundtestsequence_t sequence; // Sequence head
|
||||||
|
|
||||||
boolean autosequence; // In auto sequence mode?
|
boolean autosequence; // In auto sequence mode?
|
||||||
|
boolean shuffle; // In shuffle mode;
|
||||||
} soundtest;
|
} soundtest;
|
||||||
|
|
||||||
void S_PopulateSoundTestSequence(void);
|
void S_PopulateSoundTestSequence(void);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue