#include "global.h"

#define MSCRIPT_CONTINUE 0
#define MSCRIPT_STOP 1

/**
 * Branch forward if the provided week_event_reg flag is set
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(u16) flag
 *  3:(s16) skip
 * Command size: 5
 */
s32 MsgEvent_Cmd00(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    u16 flag = MSCRIPT_GET_16(script, 1);
    s16 skip = MSCRIPT_GET_16(script, 3);

    if (CHECK_WEEKEVENTREG(flag)) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Branch forward if the player is currently in goron form
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) skip
 * Command size: 3
 */
s32 MsgEvent_Cmd01(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    Player* player = GET_PLAYER(play);
    u8* script = *scriptPtr;
    s16 skip = MSCRIPT_GET_16(script, 1);

    if (player->transformation == PLAYER_FORM_GORON) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Branch forward if the player is currently in zora form
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) skip
 * Command size: 3
 */
s32 MsgEvent_Cmd02(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    Player* player = GET_PLAYER(play);
    u8* script = *scriptPtr;
    s16 skip = MSCRIPT_GET_16(script, 1);

    if (player->transformation == PLAYER_FORM_ZORA) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Branch forward if the player is currently in deku form
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) skip
 * Command size: 3
 */
s32 MsgEvent_Cmd03(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    Player* player = GET_PLAYER(play);
    u8* script = *scriptPtr;
    s16 skip = MSCRIPT_GET_16(script, 1);

    if (player->transformation == PLAYER_FORM_DEKU) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Branch forward if the player is currently in human form
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) skip
 * Command size: 3
 */
s32 MsgEvent_Cmd04(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    Player* player = GET_PLAYER(play);
    u8* script = *scriptPtr;
    s16 skip = MSCRIPT_GET_16(script, 1);

    if (player->transformation == PLAYER_FORM_HUMAN) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Branch forward depending on the message choice index
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) skipChoice1
 *  3:(s16) skipChoice2
 *  5:(s16) skipChoice3
 * Command size: 7
 */
s32 MsgEvent_Cmd05(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 skip = 0;

    switch (play->msgCtx.choiceIndex) {
        case 0:
            skip = MSCRIPT_GET_16(script, 1);
            break;
        case 1:
            skip = MSCRIPT_GET_16(script, 3);
            break;
        case 2:
            skip = MSCRIPT_GET_16(script, 5);
            break;
    }
    *scriptPtr += skip;

    return MSCRIPT_CONTINUE;
}

/**
 * Branch forward if the actor has a parent, else get item?
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) getItemId
 *  3:(s16) skip
 * Command size: 5
 */
s32 MsgEvent_Cmd06(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s32 getItemId = MSCRIPT_GET_16(script, 1);
    f32 xzRange = actor->xzDistToPlayer + 1.0f;
    f32 yRange = fabsf(actor->playerHeightRel) + 1.0f;
    s16 skip = MSCRIPT_GET_16(script, 3);

    if (Actor_HasParent(actor, play)) {
        *scriptPtr += skip;
    } else {
        Actor_OfferGetItem(actor, play, getItemId, xzRange, yRange);
        return MSCRIPT_STOP;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Branch forward by 1(s16) if there is a talk request, else get item?
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) skip
 * Command size: 3
 */
s32 MsgEvent_Cmd07(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    f32 xzRange = actor->xzDistToPlayer + 1.0f;
    f32 yRange = fabsf(actor->playerHeightRel) + 1.0f;
    f32 xzDist;
    s16 skip = MSCRIPT_GET_16(script, 1);

    if (Actor_ProcessTalkRequest(actor, &play->state)) {
        *scriptPtr += skip;
    } else {
        actor->flags |= ACTOR_FLAG_10000;
        xzDist = actor->xzDistToPlayer;
        actor->xzDistToPlayer = 0.0f;
        Actor_OfferTalkExchange(actor, play, xzRange, yRange, PLAYER_IA_NONE);
        actor->xzDistToPlayer = xzDist;
        return MSCRIPT_STOP;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Branch forward if the player currently has more rupees than 1(u16)
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) rupees
 *  3:(s16) skip
 * Command size: 5
 */
s32 MsgEvent_Cmd08(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 rupees = MSCRIPT_GET_16(script, 1);
    s16 skip = MSCRIPT_GET_16(script, 3);

    if (gSaveContext.save.saveInfo.playerData.rupees >= rupees) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Branch forward if the callback is null or returns non-zero
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) skip
 * Command size: 3
 */
s32 MsgEvent_Cmd09(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 skip = MSCRIPT_GET_16(script, 1);

    if (callback == NULL || callback(actor, play) != 0) {
        *scriptPtr += skip;
    } else {
        return MSCRIPT_STOP;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Branch forward based on time of day
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) skipDay1
 *  3:(s16) skipNight1
 *  5:(s16) skipDay2
 *  7:(s16) skipNight2
 *  9:(s16) skipDay3
 *  11:(s16) skipNight3
 * Command size: 13
 */
s32 MsgEvent_Cmd10(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 skip = 0;

    switch (gSaveContext.save.day) {
        case 1:
            if (!gSaveContext.save.isNight) {
                skip = MSCRIPT_GET_16(script, 1);
            } else {
                skip = MSCRIPT_GET_16(script, 3);
            }
            break;
        case 2:
            if (!gSaveContext.save.isNight) {
                skip = MSCRIPT_GET_16(script, 5);
            } else {
                skip = MSCRIPT_GET_16(script, 7);
            }
            break;
        case 3:
            if (!gSaveContext.save.isNight) {
                skip = MSCRIPT_GET_16(script, 9);
            } else {
                skip = MSCRIPT_GET_16(script, 11);
            }
            break;
    }
    *scriptPtr += skip;
    return MSCRIPT_CONTINUE;
}

/**
 * Wait on choice text, skip forward when text closes.
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) skip
 * Command size: 3
 */
s32 MsgEvent_Cmd11(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 skip;

    switch (Message_GetState(&play->msgCtx)) {
        case TEXT_STATE_CHOICE:
        case TEXT_STATE_5:
            if (!Message_ShouldAdvance(play)) {
                return MSCRIPT_STOP;
            }
        case TEXT_STATE_CLOSING:
            skip = MSCRIPT_GET_16(script, 1);
            break;
        default:
            return MSCRIPT_STOP;
    }
    *scriptPtr += skip;
    return MSCRIPT_CONTINUE;
}

/**
 * Stop script if textbox active?
 *
 * Command structure:
 *  0:(u8)  cmd
 * Command size: 1
 */
s32 MsgEvent_Cmd12(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    switch (Message_GetState(&play->msgCtx)) {
        case TEXT_STATE_CHOICE:
        case TEXT_STATE_5:
            if (!Message_ShouldAdvance(play)) {
                return MSCRIPT_STOP;
            }
            break;
        case TEXT_STATE_CLOSING:
            break;
        default:
            return MSCRIPT_STOP;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * End script when textbox is done?
 *
 * Command structure:
 *  0:(u8)  cmd
 * Command size: 1
 */
s32 MsgEvent_Cmd13(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    s32 state = Message_GetState(&play->msgCtx);

    *endScript = false;

    switch (state) {
        case TEXT_STATE_CHOICE:
        case TEXT_STATE_5:
            if (!Message_ShouldAdvance(play)) {
                break;
            }
        case TEXT_STATE_CLOSING:
            *endScript = true;
            break;
        default:
            return MSCRIPT_STOP;
    }
    return MSCRIPT_STOP;
}

/**
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(u16) textId
 * Command size: 3
 */
s32 MsgEvent_Cmd14(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;

    Message_StartTextbox(play, MSCRIPT_GET_16(script, 1), NULL);
    return MSCRIPT_CONTINUE;
}

/**
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(u16) textId
 * Command size: 3
 */
s32 MsgEvent_Cmd15(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;

    Message_ContinueTextbox(play, MSCRIPT_GET_16(script, 1));
    return MSCRIPT_CONTINUE;
}

/**
 * Script Terminator
 *
 * Command structure:
 *  0:(u8)  cmd
 * Command size: 1
 */
s32 MsgEvent_Cmd16(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    *endScript = true;
    return MSCRIPT_STOP;
}

/**
 * Sets week_event_reg flags
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(u16) flag
 * Command size: 3
 */
s32 MsgEvent_Cmd17(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    u16 flag = MSCRIPT_GET_16(script, 1);

    SET_WEEKEVENTREG(flag);
    return MSCRIPT_CONTINUE;
}

/**
 *
 * Command structure:
 *  0:(u8)  cmd
 * Command size: 1
 */
s32 MsgEvent_Cmd18(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    Message_CloseTextbox(play);
    return MSCRIPT_CONTINUE;
}

/**
 *
 * Command structure:
 *  0:(u8)  cmd
 *  0:(u16) flag
 * Command size: 3
 */
s32 MsgEvent_Cmd19(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s32 flag = MSCRIPT_GET_16(script, 1);

    if (!Flags_GetCollectible(play, flag)) {
        Flags_SetCollectible(play, flag);
    }
    actor->parent = NULL;
    return MSCRIPT_CONTINUE;
}

/**
 *
 * Command structure:
 *  0:(u8)  cmd
 *  0:(s16) rupees
 * Command size: 3
 */
s32 MsgEvent_Cmd20(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 rupees = MSCRIPT_GET_16(script, 1);

    Rupees_ChangeBy(rupees);
    return MSCRIPT_CONTINUE;
}

/**
 * Pause the message system
 *
 * Command structure:
 *  0:(u8)  cmd
 * Command size: 1
 */
s32 MsgEvent_Cmd21(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    play->msgCtx.msgMode = 0x44;
    return MSCRIPT_CONTINUE;
}

/**
 * Unsets ACTOR_FLAG_10000 for the actor executing the script
 *
 * Command structure:
 *  0:(u8)  cmd
 * Command size: 1
 */
s32 MsgEvent_Cmd22(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    actor->flags &= ~ACTOR_FLAG_10000;
    return MSCRIPT_CONTINUE;
}

/**
 * Sets player focus & talk actor to the child of the actor executing the script
 *
 * Command structure:
 *  0:(u8)  cmd
 * Command size: 1
 */
s32 MsgEvent_Cmd23(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    if (actor->child != NULL) {
        Actor_ChangeFocus(actor, play, actor->child);
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Sets player focus & talk actor to the actor executing the script
 *
 * Command structure:
 *  0:(u8)  cmd
 * Command size: 1
 */
s32 MsgEvent_Cmd24(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    if (actor->child != NULL) {
        Actor_ChangeFocus(actor->child, play, actor);
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Unconditional branch by `skip`
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) skip
 * Command size: 3
 */
s32 MsgEvent_Cmd25(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 skip = MSCRIPT_GET_16(script, 1);

    *scriptPtr += skip;
    return MSCRIPT_CONTINUE;
}

/**
 * Branch forwards if the specified quest item is obtained
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(u16) questItem
 *  3:(s16) skip
 * Command size: 5
 */
s32 MsgEvent_Cmd26(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    u16 questItem = MSCRIPT_GET_16(script, 1);
    s16 skip = MSCRIPT_GET_16(script, 3);

    if (CHECK_QUEST_ITEM(questItem)) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Branch forwards if event_inf flag is set
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(u16) event_inf flag
 *  3:(s16) skip
 * Command size: 5
 */
s32 MsgEvent_Cmd27(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 skip = MSCRIPT_GET_16(script, 3);

    if (MSCRIPT_GET_8(script, 2) & gSaveContext.eventInf[MSCRIPT_GET_8(script, 1)]) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Sets event_inf flag
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(u16) event_inf flag
 * Command size: 3
 */
s32 MsgEvent_Cmd28(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;

    gSaveContext.eventInf[MSCRIPT_GET_8(script, 1)] |= MSCRIPT_GET_8(script, 2);
    return MSCRIPT_CONTINUE;
}

/**
 * Unsets event_inf flag
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(u16) event_inf flag
 * Command size: 3
 */
s32 MsgEvent_Cmd29(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;

    gSaveContext.eventInf[MSCRIPT_GET_8(script, 1)] &= MSCRIPT_GET_8(script, 2) ^ 0xFF;
    return MSCRIPT_CONTINUE;
}

/**
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) input player item-action
 *  3:(s16) skip if player item-action matches input player item-action
 *  5:(s16) skip if player item-action does not match input player item-action
 *  7:(s16) skip if player item-action is negative
 * Command size: 9
 */
s32 MsgEvent_Cmd30(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    PlayerItemAction checkItemAction = MSCRIPT_GET_16(script, 1);
    s16 skip;
    PlayerItemAction curItemAction;

    switch (Message_GetState(&play->msgCtx)) {
        case TEXT_STATE_CHOICE:
        case TEXT_STATE_5:
            if (!Message_ShouldAdvance(play)) {
                return MSCRIPT_STOP;
            }
        case TEXT_STATE_16:
            curItemAction = func_80123810(play);

            if (curItemAction == PLAYER_IA_NONE) {
                return MSCRIPT_STOP;
            } else if (curItemAction < 0) {
                skip = MSCRIPT_GET_16(script, 7);
            } else if (curItemAction == checkItemAction) {
                skip = MSCRIPT_GET_16(script, 3);
            } else {
                skip = MSCRIPT_GET_16(script, 5);
            }
            break;
        default:
            return MSCRIPT_STOP;
    }
    *scriptPtr += skip;
    return MSCRIPT_CONTINUE;
}

/**
 * Branches forward if song is obtained
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(u16) song
 *  3:(s16) skip
 * Command size: 9
 */
s32 MsgEvent_Cmd31(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    u16 song = MSCRIPT_GET_16(script, 1);
    s16 skip = MSCRIPT_GET_16(script, 3);

    if (CHECK_QUEST_ITEM(QUEST_SONG_SONATA + song)) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Branches forward if the player's current mask matches
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) mask
 *  3:(s16) skip
 * Command size: 9
 */
s32 MsgEvent_Cmd32(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s32 mask = MSCRIPT_GET_16(script, 1);
    s16 skip = MSCRIPT_GET_16(script, 3);

    if (Player_GetMask(play) == mask) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

#define MSCRIPT_GET_TIME(hour, minute, dest, temp) \
    (temp) = (hour)*60.0f;                         \
    (temp) += (minute);                            \
    (dest) = (temp) * (0x10000 / 60 / 24.0f);      \
    (dest) = ((dest)-0x10000 / 360 * 90)

#define MSCRIPT_TIME_NOW ((gSaveContext.save.time) - 0x10000 / 360 * 90)

/**
 * Branches forward if the current time is greater than the provided time
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(u8)  hour
 *  1:(u8)  min
 *  3:(s16) skip
 * Command size: 5
 */
s32 MsgEvent_Cmd33(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 skip = MSCRIPT_GET_16(script, 3);
    f32 f;
    u16 time;
    u16 now;

    MSCRIPT_GET_TIME(MSCRIPT_GET_8(script, 1), MSCRIPT_GET_8(script, 2), time, f);
    now = MSCRIPT_TIME_NOW;

    if (time < now) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Branches forward if the current time is less or equal to the provided time
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(u8)  hour
 *  1:(u8)  min
 *  3:(s16) skip
 * Command size: 5
 */
s32 MsgEvent_Cmd34(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 skip = MSCRIPT_GET_16(script, 3);
    f32 f;
    u16 time;
    u16 now;

    MSCRIPT_GET_TIME(MSCRIPT_GET_8(script, 1), MSCRIPT_GET_8(script, 2), time, f);
    now = MSCRIPT_TIME_NOW;

    if (time >= now) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Branches forward if switch flag is set
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) flag
 *  3:(s16) skip
 * Command size: 5
 */
s32 MsgEvent_Cmd35(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 flag = MSCRIPT_GET_16(script, 1);
    s16 skip = MSCRIPT_GET_16(script, 3);

    if (Flags_GetSwitch(play, flag)) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Sets switch flag
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) flag
 * Command size: 3
 */
s32 MsgEvent_Cmd36(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 flag = MSCRIPT_GET_16(script, 1);

    Flags_SetSwitch(play, flag);
    return MSCRIPT_CONTINUE;
}

/**
 * Branches forward if item is in inventory
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(u16) item
 *  3:(s16) skip
 * Command size: 5
 */
s32 MsgEvent_Cmd37(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    u16 item = MSCRIPT_GET_16(script, 1);
    s16 skip = MSCRIPT_GET_16(script, 3);

    if (INV_CONTENT(item) == item) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Branches forward if current time is within the specified interval
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(u16) startTime
 *  3:(u16) endTime
 *  5:(s16) skip
 * Command size: 7
 */
s32 MsgEvent_Cmd38(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 skip = MSCRIPT_GET_16(script, 5);
    f32 f;
    u16 startTime;
    u16 endTime;
    u16 now;

    MSCRIPT_GET_TIME(MSCRIPT_GET_8(script, 1), MSCRIPT_GET_8(script, 2), startTime, f);
    MSCRIPT_GET_TIME(MSCRIPT_GET_8(script, 3), MSCRIPT_GET_8(script, 4), endTime, f);
    endTime--;
    now = MSCRIPT_TIME_NOW;

    if (startTime >= now || now >= endTime) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Branches forward if the current day matches
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) day
 *  3:(s16) skip
 * Command size: 5
 */
s32 MsgEvent_Cmd39(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 day = MSCRIPT_GET_16(script, 1);
    s16 skip = MSCRIPT_GET_16(script, 3);

    if (gSaveContext.save.day == day) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Branches forward if callback exists and returns non-zero
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) skip
 * Command size: 3
 */
s32 MsgEvent_Cmd40(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 skip = MSCRIPT_GET_16(script, 1);

    if (callback != NULL && callback(actor, play) != 0) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Skip forward if a powder keg is in inventory or if a powder keg actor exists
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) skip
 * Command size: 3
 */
s32 MsgEvent_Cmd41(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 skip = MSCRIPT_GET_16(script, 1);

    if (AMMO(ITEM_POWDER_KEG) != 0 || (play->actorCtx.flags & ACTORCTX_FLAG_0)) {
        *scriptPtr += skip;
    }
    return MSCRIPT_CONTINUE;
}

/**
 * Deletes the specified item
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) item
 * Command size: 3
 */
s32 MsgEvent_Cmd42(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 item = MSCRIPT_GET_16(script, 1);

    Inventory_DeleteItem(item, SLOT(item));
    return MSCRIPT_CONTINUE;
}

/**
 * Runs the callback and selects a skip based on the result, default skip is the first one
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) skip
 *  3:(s16) skip
 *  5:(s16) skip
 * Command size: 7
 */
s32 MsgEvent_Cmd43(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s32 ret = 1;
    s16 skip;

    if (callback != NULL) {
        ret = callback(actor, play);
    }
    switch (ret) {
        case 3:
            skip = MSCRIPT_GET_16(script, 5);
            break;
        case 2:
            skip = MSCRIPT_GET_16(script, 3);
            break;
        case 1:
            skip = MSCRIPT_GET_16(script, 1);
            break;
        default:
            return MSCRIPT_STOP;
    }
    *scriptPtr += skip;
    return MSCRIPT_CONTINUE;
}

/**
 * Close the current textbox and set the player actor text id
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(u16) textId
 * Command size: 3
 */
s32 MsgEvent_Cmd44(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    Player* player = GET_PLAYER(play);
    u8* script = *scriptPtr;
    u16 textId = MSCRIPT_GET_16(script, 1);

    player->actor.textId = textId;
    Message_CloseTextbox(play);
    return MSCRIPT_CONTINUE;
}

/**
 * Queue a bomber's notebook event
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(u16) event
 * Command size: 3
 */
s32 MsgEvent_Cmd45(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    u16 event = MSCRIPT_GET_16(script, 1);

    Message_BombersNotebookQueueEvent(play, event);
    return MSCRIPT_CONTINUE;
}

/**
 *
 *
 * Command structure:
 *  0:(u8)  cmd
 * Command size: 1
 */
s32 MsgEvent_Cmd46(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    switch (Message_GetState(&play->msgCtx)) {
        case TEXT_STATE_CLOSING:
            break;
        case TEXT_STATE_CHOICE:
        case TEXT_STATE_5:
            if (!Message_ShouldAdvance(play)) {
                return MSCRIPT_STOP;
            }
        case TEXT_STATE_DONE:
            if (!Message_ShouldAdvance(play)) {
                return MSCRIPT_STOP;
            }
            break;
        default:
            return MSCRIPT_STOP;
    }
    return MSCRIPT_CONTINUE;
}

/**
 *
 * Command structure:
 *  0:(u8)  cmd
 *  1:(s16) skip
 * Command size: 3
 */
s32 MsgEvent_Cmd47(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    u8* script = *scriptPtr;
    s16 skip = MSCRIPT_GET_16(script, 1);

    *scriptPtr += skip;
    *scriptPtr += 3;
    return MSCRIPT_STOP;
}

/**
 * Plays Decide sfx
 *
 * Command structure:
 *  0:(u8)  cmd
 * Command size: 1
 */
s32 MsgEvent_Cmd48(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    Audio_PlaySfx_MessageDecide();
    return MSCRIPT_CONTINUE;
}

/**
 * Plays Cancel sfx
 *
 * Command structure:
 *  0:(u8)  cmd
 * Command size: 1
 */
s32 MsgEvent_Cmd49(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    Audio_PlaySfx_MessageCancel();
    return MSCRIPT_CONTINUE;
}

/**
 * Plays NA_SE_SY_ERROR sfx
 *
 * Command structure:
 *  0:(u8)  cmd
 * Command size: 1
 */
s32 MsgEvent_Cmd50(Actor* actor, PlayState* play, u8** scriptPtr, MsgEventCallback callback, s32* endScript) {
    Audio_PlaySfx(NA_SE_SY_ERROR);
    return MSCRIPT_CONTINUE;
}

MsgEventHandler sMsgEventCmdHandlers[51] = {
    MsgEvent_Cmd00, MsgEvent_Cmd01, MsgEvent_Cmd02, MsgEvent_Cmd03, MsgEvent_Cmd04, MsgEvent_Cmd05, MsgEvent_Cmd06,
    MsgEvent_Cmd07, MsgEvent_Cmd08, MsgEvent_Cmd09, MsgEvent_Cmd10, MsgEvent_Cmd11, MsgEvent_Cmd12, MsgEvent_Cmd13,
    MsgEvent_Cmd14, MsgEvent_Cmd15, MsgEvent_Cmd16, MsgEvent_Cmd17, MsgEvent_Cmd18, MsgEvent_Cmd19, MsgEvent_Cmd20,
    MsgEvent_Cmd21, MsgEvent_Cmd22, MsgEvent_Cmd23, MsgEvent_Cmd24, MsgEvent_Cmd25, MsgEvent_Cmd26, MsgEvent_Cmd27,
    MsgEvent_Cmd28, MsgEvent_Cmd29, MsgEvent_Cmd30, MsgEvent_Cmd31, MsgEvent_Cmd32, MsgEvent_Cmd33, MsgEvent_Cmd34,
    MsgEvent_Cmd35, MsgEvent_Cmd36, MsgEvent_Cmd37, MsgEvent_Cmd38, MsgEvent_Cmd39, MsgEvent_Cmd40, MsgEvent_Cmd41,
    MsgEvent_Cmd42, MsgEvent_Cmd43, MsgEvent_Cmd44, MsgEvent_Cmd45, MsgEvent_Cmd46, MsgEvent_Cmd47, MsgEvent_Cmd48,
    MsgEvent_Cmd49, MsgEvent_Cmd50,
};

u8 sMsgEventCmdLengths[51] = {
    MSCRIPT_BRANCH_ON_WEEK_EVENT_REG_LENGTH,
    3,
    3,
    3,
    3,
    7,
    5,
    3,
    5,
    3,
    13,
    3,
    1,
    1,
    3,
    3,
    1,
    3,
    1,
    3,
    3,
    1,
    1,
    1,
    1,
    3,
    5,
    5,
    3,
    3,
    9,
    5,
    5,
    5,
    5,
    5,
    3,
    5,
    7,
    5,
    3,
    3,
    3,
    7,
    3,
    3,
    1,
    3,
    1,
    1,
    1,
};

/**
 * Runs a message event script
 *
 * @param actor         The actor associated with the script
 * @param play          Play game state
 * @param script        The script to run
 * @param callback      Callback function used by various commands for different purposes
 * @param[in,out] pos   Position to resume the script from, the point at which the script stops executing is also
 *                       written out here
 * @return s32  Whether the script has reached an endpoint
 */
s32 MsgEvent_RunScript(Actor* actor, PlayState* play, MsgScript* script, MsgEventCallback callback, s32* pos) {
    u8* start;
    u8* cur;
    s32 scriptDone = false;
    s32 i;
    s32 cmdLen;
    u8 cmdId;
    s32 pad;
    start = script;
    script += *pos;

    if (sREG(95) != 0) {}

    cmdLen = 0;
    do {
        // Skip data from previous command
        script += cmdLen;

        // Get command id
        cmdId = *script;

        // Get command length
        if (cmdId < ARRAY_COUNTU(sMsgEventCmdLengths)) {
            cmdLen = sMsgEventCmdLengths[cmdId];
        } else {
            cmdLen = -1;
            //! @bug command handler still runs even if cmdId is invalid
        }

        // Debug loop?
        if (sREG(95) != 0) {
            for (i = 0; i < cmdLen; i++) {}
        }

        // Run command handler
    } while (sMsgEventCmdHandlers[cmdId](actor, play, &script, callback, &scriptDone) == MSCRIPT_CONTINUE);

    cur = script;
    if (!scriptDone) {
        *pos = cur - start;
    } else {
        *pos = 0;
    }
    return scriptDone;
}
