Skip to content

Instantly share code, notes, and snippets.

@charasyn
Last active May 19, 2023 17:55
Show Gist options
  • Select an option

  • Save charasyn/26027b350fd9dd84c0420f7de469c616 to your computer and use it in GitHub Desktop.

Select an option

Save charasyn/26027b350fd9dd84c0420f7de469c616 to your computer and use it in GitHub Desktop.
expand_text_windows.ccs
import asm65816
// expand_text_windows.ccs v2
// cooprocks123e, 2021-2022
// public domain
// v2:
// - fixes weird bug with Escargo Express, ATM, etc.
// thanks ShrineFox for reporting the bug!
// X,Y,W,H are (X-position, Y-position, Width, Height) in 8x8 tiles
// The screen's width and height are 32x28 tiles, but the outer tile probably won't be visible
command WindowDefinition(idx,x,y,w,h) { short x short y short w short h }
command _CustomWindows {
//////////////////////////////////////////////////////////////////////////////////////////////////
////////// PUT YOUR WINDOWS BELOW
//////////////////////////////////////////////////////////////////////////////////////////////////
// Start putting window definitions here, starting with window #53 (#0 to #52 are vanilla)
// The index numbers don't do anything, they're just there so you can read the table easier.
// Reminder: ( #, X, Y, W, H)
WindowDefinition(53, 1, 1, 30, 26) // "Full-screen" window
WindowDefinition(54, 4, 4, 24, 20) // Smaller, centered window
//////////////////////////////////////////////////////////////////////////////////////////////////
////////// PUT YOUR WINDOWS ABOVE
//////////////////////////////////////////////////////////////////////////////////////////////////
}
// Previously, there were 53 window types. Now there's space for 106
// (since each entry in the window existence table is 1/2 the size)
define MAX_WINDOW_TYPES = 106
// The window existence table, which is the reason this script is 300+ lines instead of ~60
define windowExistenceTable = 0x7E88E4
///////////
/// Patches
// CHECK_DEAD_PLAYERS
ROM[0xC2BBD0] = {
// Orig: LDX_a(0x8900) STX_d(0x16)
JSL(Patch_C2BBD0) TAX
}
Patch_C2BBD0: {
LDA_i(14)
JSL(GetExistenceAtA)
STA_d(0x16)
RTL
}
// ENEMY_SELECT_MODE
ROM[0xC1E1C1] = {
// Orig: LDA_a(0x8900) LDY_i(0x0052)
JSL(Patch_C1E1C1) BRA(0)
}
Patch_C1E1C1: {
LDA_i(14)
JMP(GetExistenceAndLoadYWithSizeofStats)
}
// FILE_MENU_LOOP
ROM[0xC1FB70] = {
// Orig: LDA_a(0x8928) LDY_i(0x0052)
JSL(Patch_C1FB70) BRA(0)
}
Patch_C1FB70: {
LDA_i(34)
JMP(GetExistenceAndLoadYWithSizeofStats)
}
ROM[0xC1FC22] = {
// Orig: LDA_a(0x892A) LDY_i(0x0052)
JSL(Patch_C1FC22) BRA(0)
}
Patch_C1FC22: {
LDA_i(35)
JMP(GetExistenceAndLoadYWithSizeofStats)
}
// CLOSE_WINDOW
ROM[0xC3E5B3] = {
// Orig: ASL TAX LDA_i(0xFFFF) STA_x(0x88E4)
JSL(Patch_C3E5B3) BRA(2)
}
Patch_C3E5B3: {
TAX
SEP(0x20)
LDA_8(0xFF)
STA_x(windowExistenceTable)
REP(0x20)
RTL
}
// CREATE_WINDOW
ROM[0xC104EE] = {
// Patch the whole beginning of the Create Window fn.
/* 4ee */ REP(0x31)
/* 4f0 */ PHD
/* 4f1 */ TAY
/* 4f2 */ TDC
/* 4f3 */ ADC_i(0xFFEA)
/* 4f6 */ TCD
/* 4f7 */ STY_d(0x14)
/* 4f9 */ TYX
/* 4fa */ LDA_x(windowExistenceTable)
/* 4fd */ AND_i(0x00FF)
/* 500 */ CMP_i(0x00FF)
/* 503 */ BNE(3)
/* 505 */ JMP(0xC10528) // UNKNOWN0
/* 508 */ STY_a(0x8958)
/* 50b */ JSR(0xC11383)
/* 50e */ LDX_d(0x14)
/* 510 */ LDA_x(windowExistenceTable)
/* 513 */ AND_i(0x00FF)
/* 516 */ NOP
/* 517 */ // Rest of code...
}
ROM[0xC105C1] = {
/* 5c1 */ TAX
/* 5c2 */ LDA_d(0x0E)
/* 5c4 */ JSL(SetExistenceAtXToA)
/* 5c8 */ NOP
/* 5c9 */ // Rest of code...
}
// SELECTION_MENU
ROM[0xC11E94] = {
JSL(Patch_C11E94) BRA(0)
}
Patch_C11E94: {
LDA_i(0)
JSL(GetExistenceAtA)
CMP_a(0x88E2)
RTL
}
ROM[0xC11EA5] = {
JSL(Patch_C11EA5) BRA(0)
}
Patch_C11EA5: {
LDA_i(10)
JSL(GetExistenceAtA)
CMP_i(0xFFFF)
RTL
}
// UNKNOWN_C20ABC
ROM[0xC20AD6] = {
// Replaces:
// ASL CLC ADC_i(0x88e4) TAX LDA_x(0x0000)
JSL(GetExistenceAtA)
BRA(3)
NOP NOP NOP
}
ROM[0xC20AE7] = {
// Replaces:
// LDA_d(0x0E) STA_a(0x8958) LDA_x(0x0000)
LDX_d(0x0E)
STX_a(0x8958)
BRA(1)
NOP
}
// UNKNOWN_C200D9
ROM[0xC2013D] = {
// Replaces:
// ASL TAX LDA_i(0xFFFF) STA_x(0x88E4)
TAX
LDA_i(0xFFFF)
JSL(SetExistenceAtXToA)
}
ROM[0xC2014A] = {
// Replaces: CMP_i(0x0035)
CMP_i(MAX_WINDOW_TYPES)
}
// The programmers did an oopsy here - let's skip past this code
// that will write garbage into memory
ROM[0xC2021F] = BRA(0x21) // Skip computing X pointer to garbage
ROM[0xC20247] = { BRA(1) NOP } // Skip STA_x(...)
// UNKNOWN_C436D7
ROM[0xC436E3] = {
// Replaces:
// ASL TAX LDY_x(0x88E4) STY_d(0x10) TYA
TAX
JSL(GetExistenceAtA)
STA_d(0x10)
NOP
}
// All code that uses the following sequence: 0A AA BD E4 88
// { ASL TAX LDA_x(0x88E4) }
command _CommonPatch(a) { ROM[a]={ JSL(GetExistenceAtA) NOP } }
_CommonPatch(0xC101BC)
_CommonPatch(0xC10313)
_CommonPatch(0xC104C7)
_CommonPatch(0xC104DD)
_CommonPatch(0xC10A97)
_CommonPatch(0xC10BB9)
_CommonPatch(0xC10BDF)
_CommonPatch(0xC10CD1)
_CommonPatch(0xC10CEB)
_CommonPatch(0xC10D68)
_CommonPatch(0xC10E30)
_CommonPatch(0xC10ECB)
_CommonPatch(0xC10F4F)
_CommonPatch(0xC10FD6)
_CommonPatch(0xC11001)
_CommonPatch(0xC11048)
_CommonPatch(0xC113FA)
_CommonPatch(0xC11652)
_CommonPatch(0xC11839)
_CommonPatch(0xC1189B)
_CommonPatch(0xC11989)
_CommonPatch(0xC11F6D)
_CommonPatch(0xC11F9F)
_CommonPatch(0xC12602)
_CommonPatch(0xC12624)
_CommonPatch(0xC12997)
_CommonPatch(0xC129B9)
_CommonPatch(0xC12BDF)
_CommonPatch(0xC154E9)
_CommonPatch(0xC1CA87)
_CommonPatch(0xC1E4DA)
_CommonPatch(0xC1E724)
_CommonPatch(0xC1EFDF)
_CommonPatch(0xC1F4BC)
_CommonPatch(0xC1F63A)
//_CommonPatch(0xC20222)
_CommonPatch(0xC202B8)
_CommonPatch(0xC202FC)
_CommonPatch(0xC20343)
_CommonPatch(0xC208C9)
_CommonPatch(0xC209AA)
_CommonPatch(0xC20A41)
_CommonPatch(0xC20A59)
_CommonPatch(0xC20A71)
_CommonPatch(0xC20A8D)
_CommonPatch(0xC20AA5)
_CommonPatch(0xC20B02)
_CommonPatch(0xC20B1A)
_CommonPatch(0xC20B36)
_CommonPatch(0xC20B4E)
_CommonPatch(0xC20B80)
_CommonPatch(0xC3E537)
_CommonPatch(0xC3E7F2)
_CommonPatch(0xC43749)
_CommonPatch(0xC4379F)
_CommonPatch(0xC437C4)
_CommonPatch(0xC4388B)
_CommonPatch(0xC438C4)
_CommonPatch(0xC43B20)
_CommonPatch(0xC43BD2)
_CommonPatch(0xC43C05)
_CommonPatch(0xC43DA4)
_CommonPatch(0xC43E4E)
_CommonPatch(0xC43F0F)
_CommonPatch(0xC43F91)
_CommonPatch(0xC43FFA)
_CommonPatch(0xC442DF)
_CommonPatch(0xC44490)
_CommonPatch(0xC44619)
_CommonPatch(0xC44813)
_CommonPatch(0xC44CA8)
_CommonPatch(0xC44E99)
_CommonPatch(0xC450A4)
_CommonPatch(0xC45214)
_CommonPatch(0xEF011F)
_CommonPatch(0xEF017A)
_CommonPatch(0xEF01E1)
///////////////////
/// Common ASM code
// We need to patch out some things of the form:
// LDA_a(windowExistenceTable+amount) LDY_i(0x0052)
// This lets those patches load A with a value then jump here :)
GetExistenceAndLoadYWithSizeofStats: {
JSL(GetExistenceAtA)
LDY_i(0x0052)
RTL
}
SetExistenceAtXToA: {
SEP(0x20)
STA_x(windowExistenceTable)
REP(0x20)
RTL
}
command SExt8 { AND_i(0x00FF) BIT_i(0x0080) BEQ(3) ORA_i(0xFF00) }
GetExistenceAtA: {
TAX
LDA_x(windowExistenceTable)
SExt8
RTL
}
//////////////////////////
/// Actual table expansion
// Usually, you just need to relocate the table and change everything that points to it,
// but thanks to the window existence table, this ended up being the easy part
// Literally only one reference to this table.. smh I thought this would be so easy
_asmptr(0xc105c9, NewWindowTable)
NewWindowTable:
WindowDefinition( 0, 0x0001, 0x0001, 0x000D, 0x0008) //
WindowDefinition( 1, 0x000C, 0x0001, 0x0013, 0x0008) // Out-of-battle text
WindowDefinition( 2, 0x0007, 0x0001, 0x0018, 0x0010) // Main inventory window
WindowDefinition( 3, 0x0001, 0x0001, 0x0006, 0x000A) // Inventory menu
WindowDefinition( 4, 0x0001, 0x0003, 0x000B, 0x0006) //
WindowDefinition( 5, 0x0014, 0x0001, 0x000B, 0x0010) // Phone menu
WindowDefinition( 6, 0x0008, 0x0001, 0x0014, 0x000A) // Equip menu
WindowDefinition( 7, 0x0012, 0x0001, 0x000D, 0x0010) // Item list for equip menu
WindowDefinition( 8, 0x0001, 0x0001, 0x001E, 0x0012) // Status menu
WindowDefinition( 9, 0x000C, 0x0001, 0x0013, 0x0012) // Used by status screen?
WindowDefinition(10, 0x0001, 0x000A, 0x0008, 0x0004) // Carried money window
WindowDefinition(11, 0x0001, 0x000F, 0x000B, 0x0004) // Used by status screen?
WindowDefinition(12, 0x000C, 0x0001, 0x0013, 0x0010) //
WindowDefinition(13, 0x0007, 0x0001, 0x0018, 0x0010) //
WindowDefinition(14, 0x0004, 0x0001, 0x0018, 0x0006) // In-battle text
WindowDefinition(15, 0x0001, 0x0001, 0x0015, 0x0006) // Normal battle menu
WindowDefinition(16, 0x0004, 0x0001, 0x0008, 0x0008) //
WindowDefinition(17, 0x000C, 0x0001, 0x000C, 0x0004) //
WindowDefinition(18, 0x0001, 0x0001, 0x000E, 0x0006) // Jeff's Battle menu
WindowDefinition(19, 0x0001, 0x0002, 0x001E, 0x0008) // File Select
WindowDefinition(20, 0x0005, 0x0009, 0x0016, 0x0004) // Overworld Menu
WindowDefinition(21, 0x000A, 0x0010, 0x000C, 0x0008) // Copy Menu (2 choices)
WindowDefinition(22, 0x000A, 0x0010, 0x000C, 0x0006) // Copy Menu (1 choice)
WindowDefinition(23, 0x0006, 0x0011, 0x0015, 0x000A) // Delete confirmation
WindowDefinition(24, 0x0003, 0x000E, 0x0010, 0x000A) // Text Speed
WindowDefinition(25, 0x0008, 0x000F, 0x0012, 0x0008) // Music Mode
WindowDefinition(26, 0x0005, 0x0004, 0x0008, 0x0004) // Naming Box
WindowDefinition(27, 0x000D, 0x0004, 0x0011, 0x0004) // "Name This Friend"
WindowDefinition(28, 0x0001, 0x0009, 0x001E, 0x0010) // Name input box
WindowDefinition(29, 0x0007, 0x0003, 0x0007, 0x0004) // Ness's Name
WindowDefinition(30, 0x0007, 0x0007, 0x0007, 0x0004) // Paula's Name
WindowDefinition(31, 0x0007, 0x000B, 0x0007, 0x0004) // Jeff's Name
WindowDefinition(32, 0x0007, 0x000F, 0x0007, 0x0004) // Poo's Name
WindowDefinition(33, 0x0014, 0x0003, 0x0008, 0x0004) // King's Name
WindowDefinition(34, 0x000F, 0x0007, 0x000D, 0x0006) // Favourite Food
WindowDefinition(35, 0x000F, 0x000D, 0x000D, 0x0006) // Favourite Thing
WindowDefinition(36, 0x0004, 0x0015, 0x0018, 0x0004) // "Are you sure?"
WindowDefinition(37, 0x0012, 0x0006, 0x000D, 0x0008) //
WindowDefinition(38, 0x000C, 0x0001, 0x000C, 0x0004) //
WindowDefinition(39, 0x0003, 0x0003, 0x001A, 0x0006) //
WindowDefinition(40, 0x0001, 0x0001, 0x0007, 0x0004) //
WindowDefinition(41, 0x0010, 0x0008, 0x000F, 0x0004) //
WindowDefinition(42, 0x000A, 0x0008, 0x0015, 0x0004) //
WindowDefinition(43, 0x0004, 0x0008, 0x001B, 0x0004) //
WindowDefinition(44, 0x0008, 0x0002, 0x0018, 0x0010) //
WindowDefinition(45, 0x0003, 0x000B, 0x000F, 0x0006) //
WindowDefinition(46, 0x0004, 0x0001, 0x0008, 0x000A) //
WindowDefinition(47, 0x0001, 0x0009, 0x001E, 0x000A) //
WindowDefinition(48, 0x0001, 0x0001, 0x001C, 0x0006) //
WindowDefinition(49, 0x000A, 0x0004, 0x0014, 0x0004) //
WindowDefinition(50, 0x000E, 0x000B, 0x000F, 0x0010) // File select: flavour selection
WindowDefinition(51, 0x0016, 0x0008, 0x0009, 0x0004) //
WindowDefinition(52, 0x0007, 0x0009, 0x0012, 0x0012) //
CustomWindows:
_CustomWindows
import asm65816
import ccexpand
import cc_asmcall
// ================================================================================================
// Make Tracy say our custom text.
ROM[0xc66aa4] = { goto(test_text) }
test_text: {
"@Make a window?" newline
"[19 02]Yes[02][19 02]No[02][1C 07 02][11]{clearline}[09 02 {long l_yes} {long l_no}]"
l_no:
"@Ok/ maybe later.| Cya" next
eob
l_yes:
window_closeall
window_open(53)
"@W/h/o/a.///// Big Window!" next
window_clear
text_pos(11,0)
"TOP TEXT"
text_pos(10,11)
"BOTTOM TEXT"
next
window_closeall
window_open(1)
"@Meme?" newline
"[19 02]Yes[02][19 02]No[02][1C 07 02][11]{clearline}[09 02 {long meme} {long no_meme}]"
goto(no_meme)
meme:
window_closeall
window_open(54)
gettextspeed
settextspeed(2)
rtoarg
"@The FitnessGram(TM) Pacer Test is a multistage aerobic capacity test "
"that progressively gets more difficult as it continues.||" newline
"@The 20 meter pacer test will begin in 30 seconds.| Line up at the start.||" newline
"@The running speed starts slowly, but gets faster each minute after you hear this signal.||{sound(5)}||" newline
"@A single lap should be completed each time you hear this sound.||{sound(1)}||" newline
"@Remember to run in a straight line, and run as long as possible.||" newline
"@The second time you fail to complete a lap before the sound, your test is over.||" newline
"@The test will begin on the word start.| On your mark||, get ready||, start.{sound(11)}||"
settextspeed(0)
next
no_meme:
window_closeall
window_open(1)
"@Showcase too much text?" newline
"[19 02]Yes[02][19 02]No[02][1C 07 02][11]{clearline}[09 02 {long too_much_text} {long no_too_much_text}]"
no_too_much_text:
eob
too_much_text:
window_open(53)
"@If you put too much text, the game will close your window." next
"@The quick brown fox jumps over the lazy dog." newline
"@The quick brown fox jumps over the lazy dog." newline
"@The quick brown fox jumps over the lazy dog." newline
"@The quick brown fox jumps over the lazy dog." newline
"@The quick brown fox jumps over the lazy dog." newline
"@The quick brown fox jumps over the lazy dog." newline
"@The quick brown fox jumps over the lazy dog." newline
"@The quick brown fox jumps over the lazy dog." newline
"@The quick brown fox jumps over the lazy dog." newline
"@The quick brown fox jumps over the lazy dog."
next
"@That should have done it. You shouldn't see this text."
window_closeall
eob
}
command gettextspeed cc_asmcall(ASM_gettextspeed,RET_RESULT)
command settextspeed(x) { cc_asmcall(ASM_settextspeed,RET_NONE) byte x }
// Get text speed in result.
ASM_gettextspeed: {
LDA_a(0x9625)
INC
RTL
}
// Set text speed. Lower is faster. 0 for arg.
// By jtolmar
ASM_settextspeed: M_EB_Function_Wrapper({
M_Read_Bytes_or_Arg(1)
LDA_d(0x06)
DEC // Fastest speed is actually 0, but 0 means arg, so dec.
STA_a(0x9625) // In-game text speed
})
//setup_giygas: {
// LDA_i(0x0004)
// STA_a(0xA97A)
// RTL
//}
//// Overwrite ATM card battle routine with our code
//ROM[0xD584A8] = long atm_card_text
//ROM[0xD584AC] = long atm_card_asm
//ROM[0xD56B10] = byte 0x35
//atm_card_text: {
// newline
// "@{user}[19 1F]{swap} used the" linebreak
// " secret debug device![03]" eob
//}
////////////////////////////////////////////////////////////////////////////////
////// Setup CCScript stuff: "newgame" commands, hijacks and pointer overwrites
//// "Normal" CCScript commands
// Set Ness' initial position
newgame_location(7720, 360)
// Set the "Startup" text. This normally handles the whole
// Onett night-time flyover sequence.
newgame_startup(newgame_text_in_map)
//// New ASM patch that runs when a new game is created, but
//// before the map is loaded.
ROM[0xc1feb2] = {
// We just call the new text block.
MText(newgame_text_before_map)
BRA(0) // 2-byte no-op
// This skips the following:
// - Set flag 11
// - Disable NPCs
// So if you want those things, make sure to do them yourselves!
// Specifically, if you want to disable/enable NPCs, try this:
// https://github.com/pk-hack/CoilSnake/wiki/CCScript-Library#enabledisable-all-npcs-by-cooprocks123e
}
//// Change a text pointer that is always called after loading a
//// save file but before loading the map.
_asmptr(0xC1FF1C, always_text_before_map)
////////////////////////////////////////////////////////////////////////////////
////// Actual text
newgame_text_before_map:
// This is where you should set all your flags, especially if
// you are adjusting some that affect the map.
call(newgame_set_flags_to_post_meteor)
// Teach Ness teleport
"[1F 71 01 01]"
"[1F 71 01 02]"
// Add Paula
"[1f 11 02]"
// Set levels to 80
"[1e 08 01 50]"
"[1e 08 02 50]"
eob
always_text_before_map:
// Unset flag indicating that you just slept in a hotel.
// If this is set, it will play the Good Morning music,
// and you won't be able to sleep in the hotel again (without leaving).
unset(flag 383)
eob
newgame_text_in_map:
// You probably want to save here!
// This is the equivalent
save
eob
newgame_set_flags_to_post_meteor:
unset(flag 11) // Disable "peaceful mode"
set(flag 20) // Picky joined your party. Never unset
set(flag 34) // Post Starman Jr / Picky left party. Never unset
set(flag 94) // Seems to fix your mom
unset(flag 95) // Makes phones work (might already be unset)
set(flag 98) // Makes Ness's phone specifically work
set(flag 100) // King doesn't want to go outside
set(flag 104) // Pokey/Picky punished, first day events cleaned up
unset(flag 107) // When on, primes sunrise transition exiting Pokey's house
set(flag 199) // Know how to call Ness's dad
set(flag 200) // Know how to call Ness's mom
set(flag 209) // Onett warp - necessary to have correct Mom text
set(flag 375) // Most Onett townsfolk
set(flag 422) // Onett daytime palette
set(flag 469) // Suppresses the day 1 townsfolk
unset(flag 476) // Onett roadblocks
unset(flag 477) // Ness head in his room
set(flag 517) // Ness's house's lights are on
unset(flag 532) // No meteor sound in Ness' house
unset(flag 533) // No meteor sound in Ness' room
unset(flag 749) // No pajamas?
eob
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment