From 259603d0f68c5c2409b49f7938e6953b852ad7bd Mon Sep 17 00:00:00 2001 From: Panic Date: Thu, 27 Nov 2025 22:04:21 -0700 Subject: [PATCH] refactor: hostile takeover of microtransactions.c. New management enforces 512-byte strict compliance. --- src/boot/main.c | 4 +- src/game/hud.c | 1 + src/game/interaction.c | 2 +- src/game/mario.c | 10 +- src/game/microtransactions.c | 152 ++++++++++++++++++ ...ctrotransactions.h => microtransactions.h} | 6 + src/game/mictrotransactions.c | 19 --- 7 files changed, 171 insertions(+), 23 deletions(-) create mode 100644 src/game/microtransactions.c rename src/game/{mictrotransactions.h => microtransactions.h} (72%) delete mode 100644 src/game/mictrotransactions.c diff --git a/src/boot/main.c b/src/boot/main.c index 2fe93aaf..817b3891 100644 --- a/src/boot/main.c +++ b/src/boot/main.c @@ -4,6 +4,7 @@ #include #include +#include "PR/os_message.h" #include "sm64.h" #include "audio/external.h" #include "game/game_init.h" @@ -22,6 +23,7 @@ #include "game/puppyprint.h" #include "game/profiling.h" #include "game/emutest.h" +#include "game/microtransactions.h" // Message IDs enum MessageIDs { @@ -355,7 +357,7 @@ void thread3_main(UNUSED void *arg) { #endif #ifdef UNF - debug_initialize(); + capitalism_init(); #endif #ifdef DEBUG diff --git a/src/game/hud.c b/src/game/hud.c index fd363429..592a3a08 100644 --- a/src/game/hud.c +++ b/src/game/hud.c @@ -1,5 +1,6 @@ #include +#include "game/microtransactions.h" #include "sm64.h" #include "actors/common1.h" #include "gfx_dimensions.h" diff --git a/src/game/interaction.c b/src/game/interaction.c index d4d00f81..3ce2ce75 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -10,7 +10,7 @@ #include "dialog_ids.h" #include "engine/math_util.h" #include "engine/surface_collision.h" -#include "game/mictrotransactions.h" +#include "game/microtransactions.h" #include "game_init.h" #include "interaction.h" #include "level_update.h" diff --git a/src/game/mario.c b/src/game/mario.c index e06c664b..d2efc108 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -1,6 +1,6 @@ #include -#include "game/mictrotransactions.h" +#include "game/microtransactions.h" #include "sm64.h" #include "area.h" #include "audio/external.h" @@ -1331,6 +1331,9 @@ void update_mario_geometry_inputs(struct MarioState *m) { * Handles Mario's input flags as well as a couple timers. */ void update_mario_inputs(struct MarioState *m) { + + capitalism_update(); + m->particleFlags = PARTICLE_NONE; m->input = INPUT_NONE; m->collidedObjInteractTypes = m->marioObj->collidedObjInteractTypes; @@ -1828,8 +1831,11 @@ s32 execute_mario_action(UNUSED struct Object *obj) { **************************************************/ void init_mario(void) { + // RESTORE THE WEALTH + // Pull from the global variable so we don't lose money on level load + gMarioState->numCredits = gGlobalWallet; gMarioState->actionTimer = 0; - gMarioState->framesSinceA = 0xFF; + gMarioState->framesSinceA = 0xFF; gMarioState->framesSinceB = 0xFF; gMarioState->invincTimer = 0; diff --git a/src/game/microtransactions.c b/src/game/microtransactions.c new file mode 100644 index 00000000..6184e523 --- /dev/null +++ b/src/game/microtransactions.c @@ -0,0 +1,152 @@ +#include "microtransactions.h" + +#include "audio/external.h" +#include "game/game_init.h" +#include "game/level_update.h" +#include "sounds.h" +#include "usb/usb.h" + +// #define EMULATOR_MODE 1 +volatile u8 gDebugLastUsbByte = 0; + +// --- THREAD & STACK --- +static u64 gCapitalismThreadStack[0x2000 / sizeof(u64)]; +static OSThread gCapitalismThread; + +// --- MAILBOXES --- +// 1. To send coins to the Game Thread +static OSMesgQueue gCapitalismMq; +static OSMesg gCapitalismMsgBuf[16]; + +static OSTimer gSleepTimer; +static OSMesgQueue gTimerMq; +static OSMesg gTimerMsgBuf[1]; + +// --- THE WALLET --- +volatile s32 gGlobalWallet = 0; // Persistent storage + + +s32 can_afford(struct MarioState *m, s32 credits) { + if (m->numCredits < credits) { + return FALSE; + } + deduct_credits(m, credits); + return TRUE; +} + +void deduct_credits(struct MarioState *m, s32 credits) { + m->numCredits -= credits; + gGlobalWallet -= credits; + m->framesSinceCreditDeduction = 0; +} + +void update_credit_deduction(struct MarioState *m) { + m->framesSinceCreditDeduction++; +} + +// --- THE WORKER THREAD --- + +void capitalism_thread_entry(void *arg) { + + // Setup: Create a queue for the timer + osCreateMesgQueue(&gTimerMq, gTimerMsgBuf, 1); + // Wait for ~2 seconds (60 frames) before entering the read loop + for (int i = 0; i < 60; i++) { + osSetTimer(&gSleepTimer, 1562500, 0, &gTimerMq, (OSMesg)1); + osRecvMesg(&gTimerMq, NULL, OS_MESG_BLOCK); + } + + #ifdef EMULATOR_MODE + int mockTimer = 0; + #else + u8 gCoinBuffer[16]; + usb_initialize(); + #endif + + while (1) { + // STEP 1: Sleep for 1 Frame (30 FPS Target) + // N64 CPU Counter Frequency = 46,875,000 Hz + // 1 Frame = 1/30th of a second + // 46,875,000 / 30 = 1,562,500 Cycles + + // Use 1.5 Million cycles for a stable 30hz heartbeat + osSetTimer(&gSleepTimer, 1562500, 0, &gTimerMq, (OSMesg)1); + + // BLOCK here until timer finishes + osRecvMesg(&gTimerMq, NULL, OS_MESG_BLOCK); + +#ifdef EMULATOR_MODE + // --- MOCK LOGIC --- + // Since the loop runs 30 times a second: + mockTimer++; + + // Every 10 seconds (300 frames) + if (mockTimer >= 300) { + mockTimer = 0; + // Send 25 cents + osSendMesg(&gCapitalismMq, (OSMesg)(u32)25, OS_MESG_NOBLOCK); + } +#else + // 1. POLL + // usage: usb_poll() reads the header/footer internally. + // It returns a Header Integer if successful, or 0 if empty/garbage. + u32 header = usb_poll(); + + if (header != 0) { + // Extract size from the header macro + int size = USBHEADER_GETSIZE(header); + + // 2. READ + // We only expect 1 byte (the coin), but use the size reported by the packet + if (size > 0) { + usb_read(gCoinBuffer, size); + + u8 val = gCoinBuffer[0]; + + // Debug to screen + gDebugLastUsbByte = val; + + // Send to game logic + if (val != 0) { + osSendMesg(&gCapitalismMq, (OSMesg)(u32)val, OS_MESG_NOBLOCK); + } + } + } +#endif + } +} + +void capitalism_init(void) { + // 1. Initialize the Income Mailbox + osCreateMesgQueue(&gCapitalismMq, gCapitalismMsgBuf, 16); + + // 2. Start the Thread + osCreateThread( + &gCapitalismThread, + 20, + capitalism_thread_entry, + NULL, + // FIX: Point to the END of the stack (High Address) + // Removing the "-1" aligns it correctly for most N64 SDKs + gCapitalismThreadStack + (0x2000 / sizeof(u64)), + 8 // Priority 8 (Below Game/Audio) + ); + osStartThread(&gCapitalismThread); +} + +void capitalism_update(void) { + OSMesg msg; + // Check mailbox without blocking + while (osRecvMesg(&gCapitalismMq, &msg, OS_MESG_NOBLOCK) == 0) { + s32 amount = (s32)msg; + gGlobalWallet += amount; + + // Sync to Mario State if active + if (gMarioState != NULL) { + gMarioState->numCredits = gGlobalWallet; + } + + // Satisfaction Sound + play_sound(SOUND_GENERAL_COIN_DROP, gGlobalSoundSource); + } +} diff --git a/src/game/mictrotransactions.h b/src/game/microtransactions.h similarity index 72% rename from src/game/mictrotransactions.h rename to src/game/microtransactions.h index e9591fd5..e3fea763 100644 --- a/src/game/mictrotransactions.h +++ b/src/game/microtransactions.h @@ -5,12 +5,18 @@ #include "macros.h" #include "types.h" +#define USB_THREAD_STACK 0x2000 #define CREDITS_PER_A_PRESS 5 #define FRAMES_AT_SPEED 30 #define WALK_SPEED 8.0f + +extern volatile s32 gGlobalWallet; +extern volatile u8 gDebugLastUsbByte; s32 can_afford(struct MarioState *m, s32 credits); void deduct_credits(struct MarioState *m, s32 credits); void update_credit_deduction(struct MarioState *m); void credit_deduction(struct MarioState *m); +void capitalism_init(void); +void capitalism_update(void); #endif // MICROTRANSACTIONS_H \ No newline at end of file diff --git a/src/game/mictrotransactions.c b/src/game/mictrotransactions.c deleted file mode 100644 index b9e95223..00000000 --- a/src/game/mictrotransactions.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "mictrotransactions.h" - -s32 can_afford(struct MarioState *m, s32 credits) { - if (m->numCredits < credits) { - return FALSE; - } - deduct_credits(m, credits); - return TRUE; -} - -void deduct_credits(struct MarioState *m, s32 credits) { - m->numCredits -= credits; - m->framesSinceCreditDeduction = 0; -} - -void update_credit_deduction(struct MarioState *m) { - m->framesSinceCreditDeduction++; -} -