Skip to content

Remove 'Select' Sound From Specific Map Events

Mathew Arnold edited this page Feb 5, 2024 · 5 revisions

~ By Skyeward ~

Interacting with almost any event in the Overworld (signs, hidden items, NPCs etc) plays a select sound. There is, however, an existing blacklist of Event Scripts which do not trigger the select sound, for example interacting with Dolls and Cushions in Secret Bases.

Here we're going to extend this list of Event Scripts which bypass the select sound. This can be for an existing Event Script or a new one you've made, but we'll run an example using the Ever Grande City Pokemon League sign.

The first important step is to copy the name of the Event Script which you don't want to make the select sound. Select your Event in Porymap and copy the Script field.

image

Next, go to src\field_control_avatar.c and find TryStartInteractionScript(). We can add our Event Script name to the chain of excluded Event Scripts.

static bool8 TryStartInteractionScript(struct MapPosition *position, u16 metatileBehavior, u8 direction)
{
    const u8 *script = GetInteractionScript(position, metatileBehavior, direction);
    if (script == NULL)
        return FALSE;

    // Don't play interaction sound for certain scripts.
    if (script != LittlerootTown_BrendansHouse_2F_EventScript_PC
     && script != LittlerootTown_MaysHouse_2F_EventScript_PC
     && script != SecretBase_EventScript_PC
     && script != SecretBase_EventScript_RecordMixingPC
     && script != SecretBase_EventScript_DollInteract
     && script != SecretBase_EventScript_CushionInteract
+    && script != EverGrandeCity_EventScript_PokemonLeagueSign
     && script != EventScript_PC)
        PlaySE(SE_SELECT);

    ScriptContext_SetupScript(script);
    return TRUE;
}

The final step is to make sure this Event Script is declared, so that our new reference to it is valid. Head to include/event_scripts.h. If you're using an existing Event Script, it's possible that a declaration already exists, so search this file for your Event Script name. If this is an Event Script you just made, or if no declaration shows up for an existing Event Script, we need to add a declaration. It doesn't matter where, so we'll just add it to the bottom of the file in this example.

extern const u8 VSSeeker_Text_BatteryNotChargedNeedXSteps[];
extern const u8 VSSeeker_Text_NoTrainersWithinRange[];
extern const u8 VSSeeker_Text_TrainersNotReady[];
extern const u8 EventScript_VsSeekerChargingDone[];

+extern const u8 EverGrandeCity_EventScript_PokemonLeagueSign[];

#endif // GUARD_EVENT_SCRIPTS_H

For the simplest version of this change, that's all! Enjoy your Event in sweet silence. If you want to add more complex logic to your check, or change the type of sound that's played rather than just silencing it, I recommend starting with a small change to the TryStartInteractionScript() method. We're going to add a bool that tracks whether the sound should be played:

static bool8 TryStartInteractionScript(struct MapPosition *position, u16 metatileBehavior, u8 direction)
{
    const u8 *script = GetInteractionScript(position, metatileBehavior, direction);
    if (script == NULL)
        return FALSE;

    bool8 shouldPlaySelect = TRUE;

    // Don't play interaction sound for certain scripts.
    if (script == LittlerootTown_BrendansHouse_2F_EventScript_PC
     || script == LittlerootTown_MaysHouse_2F_EventScript_PC
     || script == SecretBase_EventScript_PC
     || script == SecretBase_EventScript_RecordMixingPC
     || script == SecretBase_EventScript_DollInteract
     || script == SecretBase_EventScript_CushionInteract
     || script == EventScript_PC)
        shouldPlaySelect = FALSE;

    if (shouldPlaySelect)
        PlaySE(SE_SELECT);

    ScriptContext_SetupScript(script);
    return TRUE;
}

The long chain of existing checks will start getting nasty if trying to add more complex logic than simply checking for one more Event Script. Adding a bool that can be updated over multiple steps allows us to split things up for readability. You'll notice that that the && and != have been swapped for || and ==. Whereas this chain of checks used to mean "if all of these Event Scripts are not running, play the select sound", it now means "if any of these Event Scripts are running, don't play the select sound".

Let's close out with a simple example of what it might look like if we wanted to silence the select sound for an Event Script called EventScript_MegaFishing, but only when the player has received the Super Rod. Notice that the new check is separated so it's easier to read:

static bool8 TryStartInteractionScript(struct MapPosition *position, u16 metatileBehavior, u8 direction)
{
    const u8 *script = GetInteractionScript(position, metatileBehavior, direction);
    if (script == NULL)
        return FALSE;

    bool8 shouldPlaySelect = TRUE;

    // Don't play interaction sound for certain scripts.
    if (script == LittlerootTown_BrendansHouse_2F_EventScript_PC
     || script == LittlerootTown_MaysHouse_2F_EventScript_PC
     || script == SecretBase_EventScript_PC
     || script == SecretBase_EventScript_RecordMixingPC
     || script == SecretBase_EventScript_DollInteract
     || script == SecretBase_EventScript_CushionInteract
     || script == EventScript_PC)
        shouldPlaySelect = FALSE;
    
    if (script == EventScript_MegaFishing && FlagGet(FLAG_RECEIVED_SUPER_ROD))
        shouldPlaySelect = FALSE;

    if (shouldPlaySelect)
        PlaySE(SE_SELECT);

    ScriptContext_SetupScript(script);
    return TRUE;
}

I'm .skyeward on Discord, feel free to contact me for questions, corrections etc. Happy hacking!

Clone this wiki locally