Polish, Twinrova intro cutscene skip with Kotake/Koume appear on climb (#7001)

This commit is contained in:
djevangelia
2026-08-08 21:04:04 +02:00
committed by GitHub
parent 94cebb35bd
commit 5d1a396668
5 changed files with 671 additions and 8 deletions
+512
View File
@@ -0,0 +1,512 @@
#ifndef SEQCMD_H
#define SEQCMD_H
#include "sequence.h"
// ==== Primary commands ====
#define SEQCMD_OP_MASK 0xF0000000
typedef enum SeqCmdOp {
/* 0x0 */ SEQCMD_OP_PLAY_SEQUENCE,
/* 0x1 */ SEQCMD_OP_STOP_SEQUENCE,
/* 0x2 */ SEQCMD_OP_QUEUE_SEQUENCE,
/* 0x3 */ SEQCMD_OP_UNQUEUE_SEQUENCE,
/* 0x4 */ SEQCMD_OP_SET_SEQPLAYER_VOLUME,
/* 0x5 */ SEQCMD_OP_SET_SEQPLAYER_FREQ,
/* 0x6 */ SEQCMD_OP_SET_CHANNEL_VOLUME,
/* 0x7 */ SEQCMD_OP_SET_SEQPLAYER_IO,
/* 0x8 */ SEQCMD_OP_SET_CHANNEL_IO,
/* 0x9 */ SEQCMD_OP_SET_CHANNEL_IO_DISABLE_MASK,
/* 0xA */ SEQCMD_OP_SET_CHANNEL_DISABLE_MASK,
/* 0xB */ SEQCMD_OP_TEMPO_CMD,
/* 0xC */ SEQCMD_OP_SETUP_CMD,
/* 0xD */ SEQCMD_OP_SET_CHANNEL_FREQ,
/* 0xE */ SEQCMD_OP_GLOBAL_CMD,
/* 0xF */ SEQCMD_OP_RESET_AUDIO_HEAP
} SeqCmdOp;
// ==== Secondary commands ====
// Subset of `SEQCMD_OP_TEMPO_CMD`
typedef enum SeqCmdTempoCmdOp {
/* 0x0 */ SEQCMD_SUB_OP_TEMPO_SET,
/* 0x1 */ SEQCMD_SUB_OP_TEMPO_SPEED_UP,
/* 0x2 */ SEQCMD_SUB_OP_TEMPO_SLOW_DOWN,
/* 0x3 */ SEQCMD_SUB_OP_TEMPO_SCALE,
/* 0x4 */ SEQCMD_SUB_OP_TEMPO_RESET
} SeqCmdTempoCmdOp;
// Subset of `SEQCMD_OP_SETUP_CMD`
typedef enum SeqCmdSetupCmdOp {
/* 0x0 */ SEQCMD_SUB_OP_SETUP_RESTORE_SEQPLAYER_VOLUME,
/* 0x1 */ SEQCMD_SUB_OP_SETUP_SEQ_UNQUEUE,
/* 0x2 */ SEQCMD_SUB_OP_SETUP_RESTART_SEQ,
/* 0x3 */ SEQCMD_SUB_OP_SETUP_TEMPO_SCALE,
/* 0x4 */ SEQCMD_SUB_OP_SETUP_TEMPO_RESET,
/* 0x5 */ SEQCMD_SUB_OP_SETUP_PLAY_SEQ,
/* 0x6 */ SEQCMD_SUB_OP_SETUP_SET_FADE_TIMER,
/* 0x7 */ SEQCMD_SUB_OP_SETUP_RESTORE_SEQPLAYER_VOLUME_IF_QUEUED,
/* 0x8 */ SEQCMD_SUB_OP_SETUP_RESTORE_SEQPLAYER_VOLUME_WITH_SCALE_INDEX,
/* 0x9 */ SEQCMD_SUB_OP_SETUP_SET_CHANNEL_DISABLE_MASK,
/* 0xA */ SEQCMD_SUB_OP_SETUP_SET_SEQPLAYER_FREQ,
/* 0xE */ SEQCMD_SUB_OP_SETUP_POP_PERSISTENT_CACHE = 0xE,
/* 0xF */ SEQCMD_SUB_OP_SETUP_RESET_SETUP_CMDS
} SeqCmdSetupCmdOp;
// Subset of `SEQCMD_OP_GLOBAL_CMD`
typedef enum SeqCmdSubCmdOp {
/* 0x0 */ SEQCMD_SUB_OP_GLOBAL_SET_SOUND_OUTPUT_MODE,
/* 0x1 */ SEQCMD_SUB_OP_GLOBAL_DISABLE_NEW_SEQUENCES
} SeqCmdSubCmdOp;
// ==== Audio Sequence Primary Commands ====
/**
* Play a sequence on a given seqPlayer
*
* @param seqPlayerIndex the index of the seqPlayer to play the sequence
* @param fadeInDuration effect will depend on seqArg. See below
* @param seqArg no effect: < 0x7F, skip ahead: = 0x7F, will not play: >= 0x80 (see note)
* @param seqId the id of the sequence to play, see `SeqId`
*
* @note seqArg will also be stored in gActiveSeqs.seqId, any check against that seqId must also include seqArg.
* seqArg < 0x7F: fade in the sequence over `fadeInDuration` in units of (1/30th) seconds
* seqArg = 0x7F: start the sequence immediately, but begins `fadeInDuration` number of second into the sequence.
* seqArg >= 0x80: no sequence will play. Intended to load a soundFont asynchronously but was only half implemented
* (inferred from MM).
*/
#define SEQCMD_PLAY_SEQUENCE(seqPlayerIndex, fadeInDuration, seqArg, seqId) \
Audio_QueueSeqCmd((SEQCMD_OP_PLAY_SEQUENCE << 28) | ((u8)(seqPlayerIndex) << 24) | ((u8)(fadeInDuration) << 16) | \
((u8)(seqArg) << 8) | (u16)(seqId))
/**
* Stop a sequence on a given seqPlayer
*
* @param seqPlayerIndex the index of the seqPlayer to stop the sequence
* @param fadeOutDuration duration the sequence will fade out over
*
* @note the 0xFF in the command is not read from at all, but is common in all Stop SeqPlayer Commands
*/
#define SEQCMD_STOP_SEQUENCE(seqPlayerIndex, fadeOutDuration) \
Audio_QueueSeqCmd((SEQCMD_OP_STOP_SEQUENCE << 28) | 0xFF | ((u8)(seqPlayerIndex) << 24) | \
((u8)(fadeOutDuration) << 16))
/**
* Add a sequence to a queue of sequences associated with a given seqPlayer.
* If the sequence is first in queue, play the sequence
*
* @param seqPlayerIndex the index of the seqPlayer to queue the sequence
* @param fadeInDuration if the sequence is played, duration the sequence will fade in over
* @param priority priority in the queue. The highest valued priority will be queued to play first. Also used as `seqArg`.
* @param seqId the id of the sequence to play, see `SeqId`
*
* @note for the next sequence in the queue to play, the current sequence must be unqueued with the unqueue command
* @note for a priority >= 0x7F, similar effects happen as `seqArg` in the play sequence command
*/
#define SEQCMD_QUEUE_SEQUENCE(seqPlayerIndex, fadeInDuration, priority, seqId) \
Audio_QueueSeqCmd((SEQCMD_OP_QUEUE_SEQUENCE << 28) | ((u8)(seqPlayerIndex) << 24) | ((u8)(fadeInDuration) << 16) | \
((u8)(priority) << 8) | (u8)(seqId))
/**
* Remove a sequence from a queue of sequences associated with a given seqPlayer.
* If the sequence is first in queue, stop the sequence, and play the next one in queue if any
*
* @param seqPlayerIndex the index of the seqPlayer to queue the sequence
* @param fadeInDuration if the sequence is stopped, duration the sequence will fade out over, and duration the next seq will fade in over
* @param seqId the id of the sequence to remove, see `SeqId`
*
* @note for the next sequence in the queue to play, the current sequence must be unqueued with this command
*/
#define SEQCMD_UNQUEUE_SEQUENCE(seqPlayerIndex, fadeOutInDuration, seqId) \
Audio_QueueSeqCmd((SEQCMD_OP_UNQUEUE_SEQUENCE << 28) | ((u8)(seqPlayerIndex) << 24) | \
((u8)(fadeOutInDuration) << 16) | (seqId))
/**
* Set the volume of an entire sequence on a given seqPlayer. Fade to the volume over a specified duration
*
* @param seqPlayerIndex the index of the seqPlayer to modify
* @param duration duration to transition to the volume
* @param volume the target volume for the sequence. Ranged from 0-0xFF, with 0x7F mapping to 1.0f
*/
#define SEQCMD_SET_SEQPLAYER_VOLUME(seqPlayerIndex, duration, volume) \
Audio_QueueSeqCmd((SEQCMD_OP_SET_SEQPLAYER_VOLUME << 28) | ((u8)(seqPlayerIndex) << 24) | ((duration) << 16) | \
(volume))
/**
* Scale the frequency of every channel on a given seqPlayer over a specified duration.
*
* @param seqPlayerIndex the index of the seqPlayer to modify
* @param duration duration to transition to the frequency
* @param freqScale the scaling factor to shift the pitch. Ranged from 0-0xFFFF, with 1000 mapping to 1.0f
*
* @note 2000 will double the frequency (raise an octave), 500 will halve the frequency (lower an octave).
* Cannot be used with `SEQCMD_SET_CHANNEL_FREQ` as they will overwrite one another.
*/
#define SEQCMD_SET_SEQPLAYER_FREQ(seqPlayerIndex, duration, freqScale) \
Audio_QueueSeqCmd((SEQCMD_OP_SET_SEQPLAYER_FREQ << 28) | ((u8)(seqPlayerIndex) << 24) | ((duration) << 16) | \
(freqScale))
/**
* Scale the frequency of a specific channel on a given seqPlayer over a specified duration
*
* @param seqPlayerIndex the index of the seqPlayer to modify
* @param channelIndex the index of the channel to modify
* @param duration duration to transition to the frequency
* @param freqScale the scaling factor to shift the pitch. Ranged from 0-0xFFF, with 1000 mapping to 1.0f
*
* @note a frequency of 2000 will double the frequency (raise an octave), 500 will halve the frequency (lower an octave).
* Cannot be used with `SEQCMD_SET_SEQPLAYER_FREQ` as they will overwrite one another.
*/
#define SEQCMD_SET_CHANNEL_FREQ(seqPlayerIndex, channelIndex, duration, freqScale) \
Audio_QueueSeqCmd((SEQCMD_OP_SET_CHANNEL_FREQ << 28) | ((u8)(seqPlayerIndex) << 24) | ((duration) << 16) | \
((channelIndex) << 12) | (freqScale))
/**
* Set the volume of a specific channel on a given seqPlayer. Fade to the volume over a specified duration
*
* @param seqPlayerIndex the index of the seqPlayer to modify
* @param channelIndex the index of the channel to modify
* @param duration duration to transition to the volume
* @param volume the target volume for the sequence. Ranged from 0-0xFF, with 0x7F mapping to 1.0f
*/
#define SEQCMD_SET_CHANNEL_VOLUME(seqPlayerIndex, channelIndex, duration, volume) \
Audio_QueueSeqCmd((SEQCMD_OP_SET_CHANNEL_VOLUME << 28) | ((u8)(seqPlayerIndex) << 24) | ((u8)(duration) << 16) | \
((u8)(channelIndex) << 8) | ((u8)volume))
/**
* Write a value that can be read as input directly by the sequence itself. This will be set to the global
* ioPort, which can affect the entire sequence.
*
* @param seqPlayerIndex the index of the seqPlayer to write the input to
* @param ioPort the index of the array to store the input-output value
* @param ioData the value s8 that's written to the input-output array
*
* @note Each seqPlayer has 8 global ioPorts indexed 0-7.
* ioPort 0 and 1 are read-only-once, and will reset after being read by the sequence.
* ioPort 2-7 can be read multiple times.
*/
#define SEQCMD_SET_SEQPLAYER_IO(seqPlayerIndex, ioPort, ioData) \
Audio_QueueSeqCmd((SEQCMD_OP_SET_SEQPLAYER_IO << 28) | ((u8)(seqPlayerIndex) << 24) | ((u8)(ioPort) << 16) | \
(u8)(ioData))
/**
* Write a value that can be read as input directly by the sequence itself. This will be set to the channel
* ioPort, which will only affect a single channel.
*
* @param seqPlayerIndex the index of the seqPlayer to write the input to
* @param channelIndex the index of the channel to write the input to
* @param ioPort the index of the array to store the input-output value
* @param ioData the value s8 that's written to the input-output array
*
* @note Each channel has 8 channel ioPorts indexed 0-7.
* ioPort 0 and 1 are read-only-once, and will reset after being read by the sequence.
* ioPort 2-7 can be read multiple times.
*/
#define SEQCMD_SET_CHANNEL_IO(seqPlayerIndex, channelIndex, ioPort, ioData) \
Audio_QueueSeqCmd((SEQCMD_OP_SET_CHANNEL_IO << 28) | ((u8)(seqPlayerIndex) << 24) | ((u8)(ioPort) << 16) | \
((u8)(channelIndex) << 8) | (u8)(ioData))
/**
* Disable (or reenable) specific channels from receiving input set by `SEQCMD_SET_CHANNEL_IO`
*
* @param seqPlayerIndex the index of the seqPlayer to modify
* @param channelMask a 16 bit mask where each bit maps to a channel. Bitflag on to disable
*
* @note using AUDIOCMD_CHANNEL_SET_IO will bypass this channelMask
*/
#define SEQCMD_SET_CHANNEL_IO_DISABLE_MASK(seqPlayerIndex, channelMask) \
Audio_QueueSeqCmd((SEQCMD_OP_SET_CHANNEL_IO_DISABLE_MASK << 28) | ((u8)(seqPlayerIndex) << 24) | (u16)(channelMask))
/**
* Disable (or reenable) specific channels
*
* @param seqPlayerIndex the index of the seqPlayer to modify
* @param channelMask a 16 bit mask where each bit maps to a channel. Bitflag on to disable
*/
#define SEQCMD_SET_CHANNEL_DISABLE_MASK(seqPlayerIndex, channelMask) \
Audio_QueueSeqCmd((SEQCMD_OP_SET_CHANNEL_DISABLE_MASK << 28) | ((u8)(seqPlayerIndex) << 24) | (u16)(channelMask))
// ==== Audio Sequence Tempo Commands ====
/**
* Set the absolute tempo of a sequence on a given seqPlayer, fading over a specified duration
*
* @param seqPlayerIndex the index of the seqPlayer to modify
* @param duration duration to transition to the tempo
* @param tempoTarget the target tempo for the sequence
*
* @note the absolute tempo is constrained to a maximum of 300
*/
#define SEQCMD_SET_TEMPO(seqPlayerIndex, duration, tempoTarget) \
Audio_QueueSeqCmd((SEQCMD_OP_TEMPO_CMD << 28) | (SEQCMD_SUB_OP_TEMPO_SET << 12) | ((u8)(seqPlayerIndex) << 24) | \
((u8)(duration) << 16) | (u16)(tempoTarget))
/**
* Increase the tempo of a sequence by a relative value on a given seqPlayer over a specified duration
*
* @param seqPlayerIndex the index of the seqPlayer to modify
* @param duration duration to transition to the tempo
* @param tempoIncrease the change to add to the current tempo
*
* @note the absolute tempo is constrained to a maximum of 300
*/
#define SEQCMD_SPEED_UP_TEMPO(seqPlayerIndex, duration, tempoIncrease) \
Audio_QueueSeqCmd((SEQCMD_OP_TEMPO_CMD << 28) | (SEQCMD_SUB_OP_TEMPO_SPEED_UP << 12) | \
((u8)(seqPlayerIndex) << 24) | ((u8)(duration) << 16) | (u16)(tempoIncrease))
/**
* Decrease the tempo of a sequence by a relative value on a given seqPlayer over a specified duration
*
* @param seqPlayerIndex the index of the seqPlayer to modify
* @param duration duration to transition to the tempo
* @param tempoDecrease the change to subtract to the current tempo
*
* @note the absolute tempo is constrained to a maximum of 300
*/
#define SEQCMD_SLOW_DOWN_TEMPO(seqPlayerIndex, duration, tempoDecrease) \
Audio_QueueSeqCmd((SEQCMD_OP_TEMPO_CMD << 28) | (SEQCMD_SUB_OP_TEMPO_SLOW_DOWN << 12) | \
((u8)(seqPlayerIndex) << 24) | ((u8)(duration) << 16) | (u16)(tempoDecrease))
/**
* Scale the tempo of a sequence by a multiplicative value on a given seqPlayer over a specified duration
*
* @param seqPlayerIndex the index of the seqPlayer to modify
* @param duration duration to transition to the tempo
* @param tempoScale the scaling factor of the tempo, relative to 100
*
* @note a tempoScale of 200 will go double-time, 50 will go half-time.
* the absolute tempo is constrained to a maximum of 300
*/
#define SEQCMD_SCALE_TEMPO(seqPlayerIndex, duration, tempoScale) \
Audio_QueueSeqCmd((SEQCMD_OP_TEMPO_CMD << 28) | (SEQCMD_SUB_OP_TEMPO_SCALE << 12) | ((u8)(seqPlayerIndex) << 24) | \
((u8)(duration) << 16) | (u16)(tempoScale))
/**
* Reset the tempo of a sequence to the original tempo on a given seqPlayer over a specified duration
*
* @param seqPlayerIndex the index of the seqPlayer to modify
* @param duration duration to transition to the tempo
*/
#define SEQCMD_RESET_TEMPO(seqPlayerIndex, duration) \
Audio_QueueSeqCmd((SEQCMD_OP_TEMPO_CMD << 28) | (SEQCMD_SUB_OP_TEMPO_RESET << 12) | ((u8)(seqPlayerIndex) << 24) | \
((u8)(duration) << 16))
/**
* ==== Audio Sequence Setup Commands ====
*
* The setup commands are designed to wait to be executed.
* Up to 8 commands can be queued per `seqPlayerIndex`.
* These commands will only execute once that `seqPlayerIndex` is finished playing and is no longer enabled.
* They will often target a different player (`targetSeqPlayerIndex`) but not always.
*/
/**
* Setup a request to restore a volume on target seqPlayer once a setup seqPlayer is finished playing and disabled
*
* @param setupSeqPlayerIndex the index of the seqPlayer to wait for to be disabled
* @param targetSeqPlayerIndex the index of the seqPlayer to modify
* @param duration duration to transition to the volume
*/
#define SEQCMD_SETUP_RESTORE_SEQPLAYER_VOLUME(setupSeqPlayerIndex, targetSeqPlayerIndex, duration) \
Audio_QueueSeqCmd((SEQCMD_OP_SETUP_CMD << 28) | (SEQCMD_SUB_OP_SETUP_RESTORE_SEQPLAYER_VOLUME << 20) | \
((u8)(setupSeqPlayerIndex) << 24) | ((u8)(targetSeqPlayerIndex) << 16) | (u8)(duration))
/**
* Setup a request to unqueue a seqPlayer once that same setup seqPlayer is finished playing and disabled
*
* @param setupSeqPlayerIndex the index of the seqPlayer to unqueue once the same seqPlayer is disabled
*
* @bug this command was misimplemented and fails to unqueue. The command relies on `gActiveSeqs.seqId` for
* unqueueing, but seqId is reset before being used to unqueue. A simple fix is to unqueue based on
* `gActiveSeqs.prevSeqId` instead.
*/
#define SEQCMD_SETUP_UNQUEUE_SEQUENCE(setupSeqPlayerIndex) \
Audio_QueueSeqCmd((SEQCMD_OP_SETUP_CMD << 28) | (SEQCMD_SUB_OP_SETUP_SEQ_UNQUEUE << 20) | \
((u8)(setupSeqPlayerIndex) << 24))
/**
* Setup a request to restart and play an active sequence currently playing on a target seqPlayer
* once a setup seqPlayer is finished playing and disabled
*
* @param setupSeqPlayerIndex the index of the seqPlayer to wait for to be disabled
* @param targetSeqPlayerIndex the index of the seqPlayer to restart its active sequence
*/
#define SEQCMD_SETUP_RESTART_SEQUENCE(setupSeqPlayerIndex, targetSeqPlayerIndex) \
Audio_QueueSeqCmd((SEQCMD_OP_SETUP_CMD << 28) | (SEQCMD_SUB_OP_SETUP_RESTART_SEQ << 20) | \
((u8)(setupSeqPlayerIndex) << 24) | ((u8)(targetSeqPlayerIndex) << 16))
/**
* Setup a request to scale the tempo of a sequence by a multiplicative value on a target seqPlayer
* once a setup seqPlayer is finished playing and disabled
*
* @param setupSeqPlayerIndex the index of the seqPlayer to wait for to be disabled
* @param targetSeqPlayerIndex the index of the seqPlayer to modify
* @param duration duration to transition to the tempo
* @param tempoScale the scaling factor of the tempo, relative to 100
*
* @note a tempoScale of 200 will go double-time, 50 will go half-time.
* the absolute tempo is constrained to a maximum of 300
*/
#define SEQCMD_SETUP_SCALE_TEMPO(setupSeqPlayerIndex, targetSeqPlayerIndex, duration, tempoScale) \
Audio_QueueSeqCmd((SEQCMD_OP_SETUP_CMD << 28) | (SEQCMD_SUB_OP_SETUP_TEMPO_SCALE << 20) | \
((u8)(setupSeqPlayerIndex) << 24) | ((u8)(targetSeqPlayerIndex) << 16) | ((u8)(duration) << 8) | \
(u8)(tempoScale))
/**
* Setup a request to reset the tempo of a sequence to the original tempo on a target seqPlayer
* once a setup seqPlayer is finished playing and disabled
*
* @param setupSeqPlayerIndex the index of the seqPlayer to wait for to be disabled
* @param targetSeqPlayerIndex the index of the seqPlayer to modify
* @param duration duration to transition to the tempo
*/
#define SEQCMD_SETUP_RESET_TEMPO(setupSeqPlayerIndex, targetSeqPlayerIndex, duration) \
Audio_QueueSeqCmd((SEQCMD_OP_SETUP_CMD << 28) | (SEQCMD_SUB_OP_SETUP_TEMPO_RESET << 20) | \
((u8)(setupSeqPlayerIndex) << 24) | ((u8)(targetSeqPlayerIndex) << 16) | (u8)(duration))
/**
* Setup a request to play a sequence on a target seqPlayer once a setup seqPlayer is finished playing and disabled.
* This command is optionally paired with `SEQCMD_SETUP_SET_FADE_IN_TIMER` to set the fade in duration
*
* @param setupSeqPlayerIndex the index of the seqPlayer to wait for to be disabled
* @param targetSeqPlayerIndex the index of the seqPlayer to play the sequence
* @param seqId the id of the sequence to play, see `SeqId`
*/
#define SEQCMD_SETUP_PLAY_SEQUENCE(setupSeqPlayerIndex, targetSeqPlayerIndex, seqId) \
Audio_QueueSeqCmd((SEQCMD_OP_SETUP_CMD << 28) | (SEQCMD_SUB_OP_SETUP_PLAY_SEQ << 20) | \
((u8)(setupSeqPlayerIndex) << 24) | ((u8)(targetSeqPlayerIndex) << 16) | (u16)(seqId))
/**
* This command is an optional command before `SEQCMD_SETUP_PLAY_SEQUENCE` to set the fade in duration
* (see cmd above)
*
* @param targetSeqPlayerIndex the index of the seqPlayer to play the sequence
* @param fadeInDuration duration the sequence will fade in over
*/
#define SEQCMD_SETUP_SET_FADE_IN_TIMER(targetSeqPlayerIndex, fadeInDuration) \
Audio_QueueSeqCmd((SEQCMD_OP_SETUP_CMD << 28) | (SEQCMD_SUB_OP_SETUP_SET_FADE_TIMER << 20) | \
((u8)(targetSeqPlayerIndex) << 24) | ((u8)(fadeInDuration) << 8))
/**
* Setup a request to restore a volume on target seqPlayer once a setup seqPlayer is finished playing and disabled.
* Specifically, it will only restore volume if the number of queued requests on the setup seqPlayer matches
* the number of sequences queued
*
* @param setupSeqPlayerIndex the index of the seqPlayer to wait for to be disabled
* @param targetSeqPlayerIndex the index of the seqPlayer to modify
* @param duration duration to transition to the volume
* @param numSeqRequests the number of sequence requests queued that must match the actual number of sequence requests
*/
#define SEQCMD_SETUP_RESTORE_SEQPLAYER_VOLUME_IF_QUEUED(setupSeqPlayerIndex, targetSeqPlayerIndex, duration, \
numSeqRequests) \
Audio_QueueSeqCmd((SEQCMD_OP_SETUP_CMD << 28) | (SEQCMD_SUB_OP_SETUP_RESTORE_SEQPLAYER_VOLUME_IF_QUEUED << 20) | \
((u8)(setupSeqPlayerIndex) << 24) | ((u8)(targetSeqPlayerIndex) << 16) | ((u8)(duration) << 8) | \
(u8)(numSeqRequests))
/**
* Setup a request to restore a volume on target seqPlayer once a setup seqPlayer is finished playing and disabled.
* Allows `scaleIndex` to be specified.
*
* @param setupSeqPlayerIndex the index of the seqPlayer to wait for to be disabled
* @param targetSeqPlayerIndex the index of the seqPlayer to modify
* @param scaleIndex the scale index of a seqPlayer
* @param duration duration to transition to the volume
*/
#define SEQCMD_SETUP_RESTORE_SEQPLAYER_VOLUME_WITH_SCALE_INDEX(setupSeqPlayerIndex, targetSeqPlayerIndex, scaleIndex, \
duration) \
Audio_QueueSeqCmd((SEQCMD_OP_SETUP_CMD << 28) | \
(SEQCMD_SUB_OP_SETUP_RESTORE_SEQPLAYER_VOLUME_WITH_SCALE_INDEX << 20) | \
((u8)(setupSeqPlayerIndex) << 24) | ((u8)(targetSeqPlayerIndex) << 16) | \
((u8)(scaleIndex) << 8) | (u8)(duration))
/**
* Setup a request to disable (or reenable) specific channels on a target seqPlayer
* once a setup seqPlayer is finished playing and disabled.
*
* @param setupSeqPlayerIndex the index of the seqPlayer to wait for to be disabled
* @param targetSeqPlayerIndex the index of the seqPlayer to modify
* @param channelMask a 16 bit mask where each bit maps to a channel. Bitflag on to disable
*/
#define SEQCMD_SETUP_SET_CHANNEL_DISABLE_MASK(setupSeqPlayerIndex, targetSeqPlayerIndex, channelMask) \
Audio_QueueSeqCmd((SEQCMD_OP_SETUP_CMD << 28) | (SEQCMD_SUB_OP_SETUP_SET_CHANNEL_DISABLE_MASK << 20) | \
((u8)(setupSeqPlayerIndex) << 24) | ((u8)(targetSeqPlayerIndex) << 16) | (u16)(channelMask))
/**
* Queue a request to scale the frequency of an entire sequence on a target seqPlayer
* once a setup seqPlayer is finished playing and disabled.
*
* @param setupSeqPlayerIndex the index of the seqPlayer to wait for to be disabled
* @param targetSeqPlayerIndex the index of the seqPlayer to modify
* @param duration duration to transition to the frequency
* @param freqScale the scaling factor to shift the pitch. Ranged from 0-0xFF, with 100 mapping to 1.0f
*
* @note The base value for frequency, 100, is 10 times smaller than other frequency commands.
* 200 will double the frequency (raise an octave), 50 will halve the frequency (lower an octave).
*/
#define SEQCMD_SETUP_SET_SEQPLAYER_FREQ(setupSeqPlayerIndex, targetSeqPlayerIndex, duration, freqScale) \
Audio_QueueSeqCmd((SEQCMD_OP_SETUP_CMD << 28) | (SEQCMD_SUB_OP_SETUP_SET_SEQPLAYER_FREQ << 20) | \
((u8)(setupSeqPlayerIndex) << 24) | ((u8)(targetSeqPlayerIndex) << 16) | ((u8)(duration) << 8) | \
(u8)(freqScale))
/**
* Queue a request to discard audio data by popping an entry from the persistent caches on the audio heap,
* once a setup seqPlayer is finished playing and disabled.
*
* @param setupSeqPlayerIndex the index of the seqPlayer to wait for to be disabled
* @param tableTypeFlag All tables with the flag `(tableTypeFlag & (1 << tableType))` will be discarded. Specifically:
* `(tableTypeFlag & 1)` will discard the `SEQUENCE_TABLE`
* `(tableTypeFlag & 2)` will discard the `FONT_TABLE`
* `(tableTypeFlag & 4)` will discard the `SAMPLE_TABLE`
*/
#define SEQCMD_SETUP_POP_PERSISTENT_CACHE(setupSeqPlayerIndex, tableTypeFlag) \
Audio_QueueSeqCmd((SEQCMD_OP_SETUP_CMD << 28) | (SEQCMD_SUB_OP_SETUP_POP_PERSISTENT_CACHE << 20) | \
((u8)(setupSeqPlayerIndex) << 24) | ((u8)tableTypeFlag))
/**
* Discard all setup command requests on a seqPlayerIndex by resetting the setup command queue
*
* @param setupSeqPlayerIndex the index of the seqPlayer to disable all setup commands
*/
#define SEQCMD_RESET_SETUP_CMDS(setupSeqPlayerIndex) \
Audio_QueueSeqCmd((SEQCMD_OP_SETUP_CMD << 28) | (SEQCMD_SUB_OP_SETUP_RESET_SETUP_CMDS << 20) | \
((u8)(setupSeqPlayerIndex) << 24))
/**
* ==== Audio Sequence Global Commands ====
*
* The global commands will apply to the entire audio system and all 4 sequence players
*/
/**
* Change the sound output mode
*
* @param soundOutputMode see the `SoundOutputMode` enum
*/
#define SEQCMD_SET_SOUND_OUTPUT_MODE(soundOutputMode) \
Audio_QueueSeqCmd((SEQCMD_OP_GLOBAL_CMD << 28) | (SEQCMD_SUB_OP_GLOBAL_SET_SOUND_OUTPUT_MODE << 8) | (u8)(soundOutputMode))
/**
* Disable (or reenable) new sequences from starting
*
* @param isDisabled true to disable, false to enable
*
* @note this does not disable the sfx player
*/
#define SEQCMD_DISABLE_PLAY_SEQUENCES(isDisabled) \
Audio_QueueSeqCmd((SEQCMD_OP_GLOBAL_CMD << 28) | (SEQCMD_SUB_OP_GLOBAL_DISABLE_NEW_SEQUENCES << 8) | \
(u16)(isDisabled))
/**
* Restart the audio heap with the specified settings
*
* @param sfxChannelLayout index for different mappings between the 7 banks and the 16 channels
* @param specId index for the audio specifications to set high-level audio parameters
*
* @note: For sfxChannelLayout, There are 4 possible layouts indexed by 0-3.
* However, only index 0 is properly implemented. Other indices lead to bugs and softlocks.
*/
#define SEQCMD_RESET_AUDIO_HEAP(sfxChannelLayout, specId) \
Audio_QueueSeqCmd((SEQCMD_OP_RESET_AUDIO_HEAP << 28) | ((u8)(sfxChannelLayout) << 8) | (u8)(specId))
#endif
@@ -0,0 +1,122 @@
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ShipInit.hpp"
extern "C" {
#include "variables.h"
#include "seqcmd.h"
#include "src/overlays/actors/ovl_Boss_Tw/z_boss_tw.h"
#include "assets/objects/object_tw/object_tw.h"
extern BossTw* sTwinrovaPtr;
extern void BossTw_SetupFlyTo(BossTw*, PlayState*);
extern void BossTw_SetupWait(BossTw*, PlayState*);
}
typedef enum {
/* 0x00 */ TW_KOTAKE,
/* 0x01 */ TW_KOUME,
} TwinrovaType;
// Adjust alpha for spawning portals on the platforms
extern "C" void BossTwShip_TwinrovaHandlePortal(BossTw* bossTw, PlayState* play) {
if (bossTw->timers[0]-- > 0) {
Math_StepToF(&bossTw->spawnPortalAlpha, 255.0f, 10.0f);
} else if (Math_StepToF(&bossTw->spawnPortalAlpha, 0.0f, 10.0f)) {
BossTw_SetupWait(bossTw, play);
}
}
extern "C" void BossTwShip_TwinrovaSetupHandlePortal(BossTw* bossTw, PlayState* play) {
bossTw->timers[0] = 80;
bossTw->spawnPortalAlpha = 1.0f;
bossTw->spawnPortalScale = 0.05f;
bossTw->actionFunc = BossTwShip_TwinrovaHandlePortal;
}
// Rise from below platform, then enter normal gameplay
extern "C" void BossTwShip_Rise(BossTw* bossTw, PlayState* play) {
Player* player = GET_PLAYER(play);
Math_SmoothStepToF(&bossTw->actor.world.pos.y, 400.0f, 300.0f, 6.0f, 4.0f);
bossTw->actor.world.rot.y = bossTw->actor.shape.rot.y = Actor_WorldYawTowardActor(&bossTw->actor, &player->actor);
SkelAnime_Update(&bossTw->skelAnime);
Audio_PlayActorSound2(&bossTw->actor, NA_SE_EN_TWINROBA_FLY - SFX_FLAG);
Math_ApproachF(&bossTw->scepterAlpha, 0.0f, 1.0f, 10.0f);
if (bossTw->actor.world.pos.y >= 400.0f) {
BossTw_SetupFlyTo(bossTw, play);
if (bossTw->actor.params == TW_KOUME) {
Audio_PlayActorSound2(&bossTw->actor, NA_SE_EN_TWINROBA_LAUGH2);
}
}
}
extern "C" void BossTwShip_SetupRise(BossTw* bossTw, PlayState* play) {
Player* player = GET_PLAYER(play);
bossTw->actor.flags |= ACTOR_FLAG_ATTENTION_ENABLED;
bossTw->actionFunc = BossTwShip_Rise;
if (bossTw->actor.params == TW_KOTAKE) {
Audio_PlayActorSound2(&bossTw->actor, NA_SE_EN_TWINROBA_LAUGH);
}
}
// Wait for player to climb up to start fight
extern "C" void BossTwShip_WaitHeight(BossTw* bossTw, PlayState* play) {
Player* player = GET_PLAYER(play);
if (player->actor.world.pos.y >= 150.0f) {
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_TWINROVA_BATTLE);
SEQCMD_PLAY_SEQUENCE(SEQ_PLAYER_BGM_MAIN, 0, 0, NA_BGM_BOSS);
BossTwShip_SetupRise(bossTw, play);
// The spawn portal is drawn by the Twinrova type BossTw actor, not Kotake/Koume
BossTwShip_TwinrovaSetupHandlePortal(sTwinrovaPtr, play);
}
}
extern "C" void BossTwShip_SetupWaitHeight(BossTw* bossTw, PlayState* play) {
Player* player = GET_PLAYER(play);
// X and Z are swapped
bossTw->actor.world.pos.x = (bossTw->actor.params == TW_KOTAKE) ? -600.0f : 600.0f;
bossTw->actor.world.pos.y = 60.0f;
bossTw->actor.world.rot.y = bossTw->actor.shape.rot.y = Actor_WorldYawTowardActor(&bossTw->actor, &player->actor);
bossTw->unk_5F8 = true; // Hair activated
bossTw->actionFunc = BossTwShip_WaitHeight;
}
void RegisterTwinrovaSkipIntroCS() {
COND_VB_SHOULD(VB_PLAY_TWINROVA_INTRO_CS,
(CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), IS_RANDO)), {
BossTw* bossTw = va_arg(args, BossTw*);
PlayState* play = va_arg(args, PlayState*);
if (bossTw->actor.params == TW_KOTAKE || bossTw->actor.params == TW_KOUME) {
BossTwShip_SetupWaitHeight(bossTw, play);
} else if (!Flags_GetClear(play, play->roomCtx.curRoom.num)) {
// Twinrova
SEQCMD_PLAY_SEQUENCE(SEQ_PLAYER_BGM_MAIN, 0, 0, NA_BGM_KOTAKE_KOUME);
BossTw_SetupWait(bossTw, play);
bossTw->visible = false;
bossTw->actor.world.pos.y = -2000.0f;
bossTw->actor.flags &= ~ACTOR_FLAG_ATTENTION_ENABLED;
}
*should = false;
});
// Swap portal XZ to match new Kotake/Koume spawn position
COND_VB_SHOULD(VB_TWINROVA_SPAWN_PORTAL_TRANSLATION_KOTAKE,
(CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), IS_RANDO)), {
Matrix_Translate(-600.0f, 232.0f, 0.0f, MTXMODE_NEW); // Ice/Kotake
*should = false;
});
COND_VB_SHOULD(VB_TWINROVA_SPAWN_PORTAL_TRANSLATION_KOUME,
(CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), IS_RANDO)), {
Matrix_Translate(600.0f, 232.0f, 0.0f, MTXMODE_NEW); // Fire/Koume
*should = false;
});
}
static RegisterShipInitFunc initTwinrovaSkipIntroCS(RegisterTwinrovaSkipIntroCS,
{ CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"),
"IS_RANDO" });
@@ -1266,9 +1266,9 @@ void TimeSaverOnSceneInitHandler(int16_t sceneNum) {
}
break;
case SCENE_SPIRIT_TEMPLE_BOSS:
// For Twinrova see Twinrova.cpp
if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.BossIntro"), IS_RANDO)) {
if (!Flags_GetEventChkInf(EVENTCHKINF_BEGAN_TWINROVA_BATTLE)) {
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_TWINROVA_BATTLE);
if (!Flags_GetEventChkInf(EVENTCHKINF_BEGAN_NABOORU_BATTLE)) {
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_NABOORU_BATTLE);
Flags_SetEventChkInf(EVENTCHKINF_NABOORU_ORDERED_TO_FIGHT_BY_TWINROVA);
}
@@ -2005,6 +2005,31 @@ typedef enum {
// - None
VB_PLAY_NABOORU_CAPTURED_CS,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - `*BossTw`
// - `*PlayState`
VB_PLAY_TWINROVA_INTRO_CS,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - None
VB_TWINROVA_SPAWN_PORTAL_TRANSLATION_KOTAKE,
// #### `result`
// ```c
// true
// ```
// #### `args`
// - None
VB_TWINROVA_SPAWN_PORTAL_TRANSLATION_KOUME,
// #### `result`
// ```c
// true
@@ -178,7 +178,7 @@ static s8 sEnvType;
static u8 sGroundBlastType;
static BossTw* sKotakePtr;
static BossTw* sKoumePtr;
static BossTw* sTwinrovaPtr;
BossTw* sTwinrovaPtr; // global for CS skip hook
static u8 sShieldFireCharge;
static u8 sShieldIceCharge;
static f32 D_8094C854;
@@ -495,7 +495,7 @@ void BossTw_Init(Actor* thisx, PlayState* play2) {
this->actor.world.pos.y = 400.0f;
this->actor.world.pos.z = 0.0f;
Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS);
} else {
} else if (GameInteractor_Should(VB_PLAY_TWINROVA_INTRO_CS, true, this, play)) {
BossTw_SetupCSWait(this, play);
}
@@ -512,7 +512,7 @@ void BossTw_Init(Actor* thisx, PlayState* play2) {
this->actor.world.pos.x = 600.0f;
this->actor.world.pos.y = 400.0f;
this->actor.world.pos.z = 0.0f;
} else {
} else if (GameInteractor_Should(VB_PLAY_TWINROVA_INTRO_CS, true, this, play)) {
BossTw_SetupCSWait(this, play);
}
@@ -531,7 +531,7 @@ void BossTw_Init(Actor* thisx, PlayState* play2) {
if (Flags_GetEventChkInf(EVENTCHKINF_BEGAN_TWINROVA_BATTLE)) {
// began twinrova battle
BossTw_SetupWait(this, play);
} else {
} else if (GameInteractor_Should(VB_PLAY_TWINROVA_INTRO_CS, true, this, play)) {
BossTw_TwinrovaSetupIntroCS(this, play);
this->actor.world.pos.x = 0.0f;
this->actor.world.pos.y = 1000.0f;
@@ -3782,7 +3782,9 @@ void BossTw_SpawnPortalDraw(BossTw* this, PlayState* play) {
Matrix_Push();
Matrix_Translate(0.0f, 232.0f, -600.0f, MTXMODE_NEW);
if (GameInteractor_Should(VB_TWINROVA_SPAWN_PORTAL_TRANSLATION_KOTAKE, true)) {
Matrix_Translate(0.0f, 232.0f, -600.0f, MTXMODE_NEW);
}
Matrix_Scale(this->spawnPortalScale, this->spawnPortalScale, this->spawnPortalScale, MTXMODE_APPLY);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, (s16)this->spawnPortalAlpha);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
@@ -3794,7 +3796,9 @@ void BossTw_SpawnPortalDraw(BossTw* this, PlayState* play) {
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);
gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gTwinrovaKotakeMagicSigilDL));
Matrix_Translate(0.0f, 232.0f, 600.0f, MTXMODE_NEW);
if (GameInteractor_Should(VB_TWINROVA_SPAWN_PORTAL_TRANSLATION_KOUME, true)) {
Matrix_Translate(0.0f, 232.0f, 600.0f, MTXMODE_NEW);
}
Matrix_Scale(this->spawnPortalScale, this->spawnPortalScale, this->spawnPortalScale, MTXMODE_APPLY);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, (s16)this->spawnPortalAlpha);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH);