Skip to content

Instantly share code, notes, and snippets.

@PhoenixBound
Created September 26, 2025 22:29
Show Gist options
  • Select an option

  • Save PhoenixBound/6da220aa0eda7a26442ad519d0800b5c to your computer and use it in GitHub Desktop.

Select an option

Save PhoenixBound/6da220aa0eda7a26442ad519d0800b5c to your computer and use it in GitHub Desktop.
Fallible money giving commands for CCExpand
// "Fallible money giving" commands by PhoenixBound
// Last updated: 2025-09-26
// ---
// SETUP INSTRUCTIONS
// 1. In CCExpand.ccs, add cc_try_give_money.CC_TryGiveMoney and
// cc_try_give_money.CC_CanGiveMoney to the "CodeTable" in their
// appropriate spots.
// 2. Adjust the command definitions below, to replace "1A xx"
// and "1A yy" with their appropriate byte values, based on
// where you put them in the CodeTable in step 1.
// 3. (OPTIONAL) Re-export the command definitions here into your
// own file full of useful command definitions:
//
// // See cc_try_give_money.ccs for documentation
// command try_give_money(n) {
// cc_try_give_money.try_give_money(n)
// }
// // See cc_try_give_money.ccs for documentation
// command money_gift_is_too_big(amt) {
// cc_try_give_money.money_gift_is_too_big(amt)
// }
//
// I assume that works anyway. I haven't tried doing that in
// CCScript before. Doing this means you don't have to import
// this ugly file everywhere.
// 4. It's for *your* hack's money. Use it when *you* need it!
// https://www.youtube.com/watch?v=pdPM6j1Q4sg
import asm65816
import ccexpand
// Inputs:
// amt: The amount of money to give to the player
// (0 means use the argument register)
// Outputs:
// result register: 0 if the player fully received `amt`
// dollars; otherwise, the player's current
// cash on hand + `amt` - 99999 (i.e., the
// remainder that would be lost if
// `givemoney(amt)` were used instead).
// Effects:
// If the player's cash on hand + `amt` > 99'999, does
// nothing; otherwise, gives the player `amt` more dollars
// of cash on hand, as if `givemoney(amt)` were used.
command try_give_money(amt) {
"[1A xx {long amt}]"
}
// Inputs:
// amt: The amount of money to give to the player
// (0 means use the argument register)
// Outputs:
// result register: 0 if the player has room for all
// `amt` dollars in their wallet;
// otherwise, the player's current cash on
// hand + `amt` - 99999 (i.e., the
// remainder that would be lost after
// using `givemoney(amt)`).
// Effects:
// Nothing
command money_gift_is_too_big(amt) {
"[1A yy {long amt}]"
}
CC_TryGiveMoney: M_EB_Function_Wrapper({
LDY_i (0x0004)
JSL (R_Read_Parameter_Bytes)
LDA_a (D_cc_argv)
STA_d (0x06)
LDA_a (D_cc_argv_2)
STA_d (0x08)
JSL (R_Set_06_To_Argument_If_Zero)
LDA_d (0x06)
STA_d (0x0A)
STA_d (0x0E)
LDA_d (0x08)
STA_d (0x0C)
STA_d (0x10)
JSL (Can_Give_Money)
LDA_d (0x06)
STA_d (0x0E)
LDA_d (0x08)
STA_d (0x10)
JSL (R_Set_Working_Memory)
LDA_d (0x06)
ORA_d (0x08)
BNE_a (_return)
// If there's nothing that'll vanish out of existence, then
// give the money for real
LDA_d (0x0A)
STA_d (0x0E)
LDA_d (0x0C)
STA_d (0x10)
JSL (0xC22214)
_return:
})
CC_CanGiveMoney: M_EB_Function_Wrapper({
LDY_i (0x0004)
JSL (R_Read_Parameter_Bytes)
LDA_a (D_cc_argv)
STA_d (0x06)
LDA_a (D_cc_argv_2)
STA_d (0x08)
JSL (R_Set_06_To_Argument_If_Zero)
LDA_d (0x06)
STA_d (0x0E)
LDA_d (0x08)
STA_d (0x10)
JSL (Can_Give_Money)
LDA_d (0x06)
STA_d (0x0E)
LDA_d (0x08)
STA_d (0x10)
JSL (R_Set_Working_Memory)
})
Can_Give_Money: M_EB_Function_Wrapper_UserStack_Clobber(-4, {
// $00-$03 = amt + player's cash on hand (GAME_STATE+game_state::money_carried)
LDA_d (0x12)
CLC
ADC_a (0x9831)
STA_d (0x00)
LDA_d (0x14)
ADC_a (0x9833)
STA_d (0x02)
// Subtract 99999
LDA_d (0x00)
SEC
SBC_i (short [0] 99999)
STA_d (0x00)
LDA_d (0x02)
SBC_i (short [1] 99999)
// STA_d (0x02)
// If the result was negative, return 0
// If the result was 0 or something positive, return that
BMI_a (_return_zero)
STA_d (0x0C)
LDA_d (0x00)
STA_d (0x0A)
BRA_a (_return)
_return_zero:
STZ_d (0x0A)
STZ_d (0x0C)
_return:
})
// Test script for the fallible money giving commands
// You don't need this file in your hack! It's only useful for
// debugging and testing the commands.
import cc_try_give_money
command zero_money {
givemoney(50000)
givemoney(49999)
takemoney(50000)
takemoney(49999)
}
// Attach this to an NPC to ensure that the control code is working as specified
// in its doc comment
try_give_money_test: {
zero_money
open_wallet
"@TRY GIVE MONEY TEST" next
// TEST 1: test that giving succeeds under ideal conditions
try_give_money(99999)
rtoarg
"@Ensure that 0 = {number(0)} and the player has $99999"
open_wallet
next
// TEST 2: test that giving can fail when you're at $99999
givemoney(50000)
givemoney(49999)
try_give_money(1)
rtoarg
"@Ensure that 1 = {number(0)} and the player has $99999"
open_wallet
next
try_give_money(420)
rtoarg
"@And that {number(0)} is lit fam, and the player still has $99999"
open_wallet
next
// TEST 3: test that when giving money fails, no money is actually given
zero_money
givemoney(50000)
try_give_money(50000)
rtoarg
"@Ensure again that 1 = {number(0)} and the player has $50000"
open_wallet
next
try_give_money(50419)
rtoarg
"@And that {number(0)} is still an overused joke, and the player has $50000"
open_wallet
next
// TEST 4: same thing but for a difference of >65536
zero_money
givemoney(25000)
try_give_money(75000)
rtoarg
"@Ensure again that 1 = {number(0)} and the player has $25000"
open_wallet
next
try_give_money(75419)
rtoarg
"@And that {number(0)} is discussed on r[5F]trees and the player has $25000"
open_wallet
next
// TEST 5: giving money succeeds when you already have money
zero_money
givemoney(25000)
try_give_money(252)
rtoarg
"@0 = {number(0)} and the player has $25252 dollars"
open_wallet
next
// TEST 6: the amount to give is read from the argument register when 0 is passed in
zero_money
givemoney(33334)
// Set all registers to something other than 33334
counter(69)
ctoarg
swap
rtoarg
store_registers
// Set only the argument register to 33334
"[19 27 06]"
swap
// Give that much money
try_give_money(0)
swap
"@0 = {number(0)} and the player has $66668"
open_wallet
next
// And again
swap
try_give_money(0)
swap
"@3 = {number(0)} and the player has $66668"
open_wallet
next
swap
"@And just to make sure... 33334 = {number(0)}" next
// TEST 7: same thing, but make the high word of the argument register nonzero
zero_money
givemoney(60000)
givemoney(6000)
counter(69)
ctoarg
swap
rtoarg
store_registers
"[19 27 06]"
swap
try_give_money(0)
swap
"@32001 = {number(0)} and the player has $66000"
open_wallet
next
"@If all of those statements are correct, then the control code must be working." next
"@MONEY GIFT IS TOO BIG TEST" next
zero_money
// TEST 1: give various immediate amounts big and small
money_gift_is_too_big(0x7FFF)
rtoarg
"@0 = {number(0)} and player has $0"
open_wallet
next
money_gift_is_too_big(99999)
rtoarg
"@0 = {number(0)} and player has $0"
open_wallet
next
money_gift_is_too_big(9999999)
rtoarg
"@9900000 =" linebreak
" {number(0)} and player has $0"
open_wallet
next
// TEST 2: now with money
zero_money
givemoney(50000)
money_gift_is_too_big(49999)
rtoarg
"@0 = {number(0)} and player has $50000"
open_wallet
next
money_gift_is_too_big(50000)
rtoarg
"@1 = {number(0)} and player has $50000"
open_wallet
next
money_gift_is_too_big(150000)
rtoarg
"@100001 =" linebreak
" {number(0)} and player has $50000"
open_wallet
next
// TEST 3: now with the argument register
zero_money
givemoney(33334)
counter(69)
ctoarg
swap
rtoarg
store_registers
"[19 27 06]"
swap
money_gift_is_too_big(0)
swap
"@0 = {number(0)} and the player has $33334"
open_wallet
next
zero_money
givemoney(33334)
givemoney(33334)
counter(69)
ctoarg
swap
rtoarg
store_registers
"[19 27 06]"
swap
money_gift_is_too_big(0)
swap
"@33337 = {number(0)} and the player has $66668"
open_wallet
next
swap
"@And 66668 = {number(0)}" next
"@...That's probably enough for now. Good night." end
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment