Skip to content

Instantly share code, notes, and snippets.

@CataLatas
Last active December 31, 2023 20:41
Show Gist options
  • Save CataLatas/76700c2781bcfade26953ef4cc827862 to your computer and use it in GitHub Desktop.
Save CataLatas/76700c2781bcfade26953ef4cc827862 to your computer and use it in GitHub Desktop.
Earthbound movement script expansion patch
import asm65816
/*
* Relocates the movement script pointer table, allowing
* for custom scripts to be inserted into the game
*
* By Catador
* Additional ideas by JTolmar and cooprocks123e
* And maybe others (sorry!)
*/
//*****************************************************************************
// Here, inside this block, you define the pointers your own movement scripts by using `adr24`
// Custom script IDs start at 895
command DEFINE_CUSTOM_POINTERS {
adr24(movscr_examples.M_DvdLogo) // 895
adr24(movscr_examples.M_TouchSound) // 896
adr24(movscr_examples.M_UnitTests) // 897
}
// This part intentionally has lots of newlines to separate the
// "user" section from the "black magic" section
//*****************************************************************************
// EVERYTHING BELOW OPERATES COMPLETELY ON BLACK MAGIC.
// UNLESS YOU REALLY, ABSOLUTELY, SURELY, UNDERSTAND WHAT YOU'RE DOING,
// DO. NOT. TOUCH.
_MOVSCR_PTR:
short 0xC2FFB7
_MOVSCR_PTR_PLUS2:
byte[2] 0xC2FFB7
DEFINE_VANILLA_POINTERS
DEFINE_CUSTOM_POINTERS
ROM[0xC093D5] = LDA_xl(_MOVSCR_PTR_PLUS2)
ROM[0xC093DA] = LDA_xl(_MOVSCR_PTR)
ROM[0xC46194] = ASMLoadAddress06(_MOVSCR_PTR)
ROM[0xC461E4] = ASMLoadAddress06(_MOVSCR_PTR)
ROM[0xC4D87E] = ASMLoadAddress06(_MOVSCR_PTR)
command DEFINE_VANILLA_POINTERS {
adr24(0xC3A043) // 001
adr24(0xC3A05E) // 002
adr24(0xC3A080) // 003
adr24(0xC3A273) // 004
adr24(0xC3A481) // 005
adr24(0xC3A2E4) // 006
adr24(0xC3A287) // 007
adr24(0xC3A2AA) // 008
adr24(0xC3A299) // 009
adr24(0xC3A2D3) // 010
adr24(0xC3A2D3) // 011
adr24(0xC3A2E4) // 012
adr24(0xC3A33B) // 013
adr24(0xC3A349) // 014
adr24(0xC3A357) // 015
adr24(0xC3A365) // 016
adr24(0xC3A373) // 017
adr24(0xC3A381) // 018
adr24(0xC3A48A) // 019
adr24(0xC3A4C9) // 020
adr24(0xC3A549) // 021
adr24(0xC3A5C9) // 022
adr24(0xC3A643) // 023
adr24(0xC3A6C4) // 024
adr24(0xC3A714) // 025
adr24(0xC3A780) // 026
adr24(0xC3A7F8) // 027
adr24(0xC3A874) // 028
adr24(0xC3A8D2) // 029
adr24(0xC3A953) // 030
adr24(0xC3A9DA) // 031
adr24(0xC3DE01) // 032
adr24(0xC3DF72) // 033
adr24(0xC3DF1E) // 034
adr24(0xC3A204) // 035
adr24(0xC3ACAD) // 036
adr24(0xC3AD7A) // 037
adr24(0xC3AE1E) // 038
adr24(0xC3AE78) // 039
adr24(0xC3AEA0) // 040
adr24(0xC3AEAC) // 041
adr24(0xC3AE02) // 042
adr24(0xC3ADE1) // 043
adr24(0xC3AE0F) // 044
adr24(0xC3AF0F) // 045
adr24(0xC3AF4E) // 046
adr24(0xC3AFD8) // 047
adr24(0xC3AFFA) // 048
adr24(0xC3B021) // 049
adr24(0xC3B0EC) // 050
adr24(0xC3B06D) // 051
adr24(0xC3B1A6) // 052
adr24(0xC3B04D) // 053
adr24(0xC3B13E) // 054
adr24(0xC3AFAC) // 055
adr24(0xC3B1E9) // 056
adr24(0xC3B208) // 057
adr24(0xC3B25D) // 058
adr24(0xC3B2B2) // 059
adr24(0xC3B2FA) // 060
adr24(0xC3B35D) // 061
adr24(0xC3B3A2) // 062
adr24(0xC3B3C8) // 063
adr24(0xC3B445) // 064
adr24(0xC3B46F) // 065
adr24(0xC3B4A5) // 066
adr24(0xC3B4FB) // 067
adr24(0xC3B5D6) // 068
adr24(0xC3B538) // 069
adr24(0xC3B633) // 070
adr24(0xC3B69C) // 071
adr24(0xC3B6D4) // 072
adr24(0xC3B757) // 073
adr24(0xC3B784) // 074
adr24(0xC3B7BC) // 075
adr24(0xC3B7EF) // 076
adr24(0xC3B810) // 077
adr24(0xC3B86C) // 078
adr24(0xC3B8A5) // 079
adr24(0xC3B8E8) // 080
adr24(0xC3B902) // 081
adr24(0xC3B926) // 082
adr24(0xC3B95D) // 083
adr24(0xC3B9B6) // 084
adr24(0xC3B9D4) // 085
adr24(0xC3B9F2) // 086
adr24(0xC3BA1C) // 087
adr24(0xC3BA07) // 088
adr24(0xC3BA31) // 089
adr24(0xC3BA4F) // 090
adr24(0xC3BAEA) // 091
adr24(0xC3BB17) // 092
adr24(0xC3BB94) // 093
adr24(0xC3BA64) // 094
adr24(0xC3BA67) // 095
adr24(0xC3BA6A) // 096
adr24(0xC3BA6D) // 097
adr24(0xC3BA70) // 098
adr24(0xC3BB33) // 099
adr24(0xC3BB4C) // 100
adr24(0xC3BD2E) // 101
adr24(0xC3BD0E) // 102
adr24(0xC3BBB7) // 103
adr24(0xC3BC0A) // 104
adr24(0xC3BC5D) // 105
adr24(0xC3BCB0) // 106
adr24(0xC3BD56) // 107
adr24(0xC3BD80) // 108
adr24(0xC3BDA0) // 109
adr24(0xC3BDC3) // 110
adr24(0xC3BE01) // 111
adr24(0xC3BE2C) // 112
adr24(0xC3BE57) // 113
adr24(0xC3BE80) // 114
adr24(0xC3BEEE) // 115
adr24(0xC3BF4E) // 116
adr24(0xC3BFB2) // 117
adr24(0xC3C016) // 118
adr24(0xC3C07C) // 119
adr24(0xC3C236) // 120
adr24(0xC3C258) // 121
adr24(0xC3C2B8) // 122
adr24(0xC3C2C8) // 123
adr24(0xC3C2D1) // 124
adr24(0xC3C2DA) // 125
adr24(0xC3C2E3) // 126
adr24(0xC3C326) // 127
adr24(0xC3C336) // 128
adr24(0xC3C33F) // 129
adr24(0xC3C348) // 130
adr24(0xC3C351) // 131
adr24(0xC3C282) // 132
adr24(0xC3C394) // 133
adr24(0xC3C3ED) // 134
adr24(0xC3C427) // 135
adr24(0xC3C46E) // 136
adr24(0xC3C4CF) // 137
adr24(0xC3C540) // 138
adr24(0xC3C59A) // 139
adr24(0xC3C5C6) // 140
adr24(0xC3C5F0) // 141
adr24(0xC3C60D) // 142
adr24(0xC3C634) // 143
adr24(0xC3C687) // 144
adr24(0xC3C6B5) // 145
adr24(0xC3C6DD) // 146
adr24(0xC3C8A3) // 147
adr24(0xC3C8B2) // 148
adr24(0xC3C8C1) // 149
adr24(0xC3C8D0) // 150
adr24(0xC3C8DF) // 151
adr24(0xC3C8EE) // 152
adr24(0xC3C990) // 153
adr24(0xC3CA3E) // 154
adr24(0xC3C9E0) // 155
adr24(0xC3CA8E) // 156
adr24(0xC3CAEA) // 157
adr24(0xC3CB87) // 158
adr24(0xC3CB38) // 159
adr24(0xC3CBD5) // 160
adr24(0xC3CCB5) // 161
adr24(0xC3C747) // 162
adr24(0xC3CEC7) // 163
adr24(0xC3CDF0) // 164
adr24(0xC3C7AE) // 165
adr24(0xC3CEF5) // 166
adr24(0xC3CF1B) // 167
adr24(0xC3CF3C) // 168
adr24(0xC3CF4B) // 169
adr24(0xC3CF76) // 170
adr24(0xC3D04D) // 171
adr24(0xC3D0C5) // 172
adr24(0xC3D0EE) // 173
adr24(0xC3D10E) // 174
adr24(0xC3D12E) // 175
adr24(0xC3D159) // 176
adr24(0xC3D172) // 177
adr24(0xC3C57A) // 178
adr24(0xC3D196) // 179
adr24(0xC3D1C9) // 180
adr24(0xC3D1F8) // 181
adr24(0xC3D229) // 182
adr24(0xC3D251) // 183
adr24(0xC3D26E) // 184
adr24(0xC3D2F7) // 185
adr24(0xC3D31D) // 186
adr24(0xC3D395) // 187
adr24(0xC3D3C8) // 188
adr24(0xC3D3FD) // 189
adr24(0xC3D423) // 190
adr24(0xC3D454) // 191
adr24(0xC3D464) // 192
adr24(0xC3D486) // 193
adr24(0xC3D4C9) // 194
adr24(0xC3D4EF) // 195
adr24(0xC3D515) // 196
adr24(0xC3D53B) // 197
adr24(0xC3D566) // 198
adr24(0xC3D58C) // 199
adr24(0xC3D5B2) // 200
adr24(0xC3D5D8) // 201
adr24(0xC3D673) // 202
adr24(0xC3D6D6) // 203
adr24(0xC3D732) // 204
adr24(0xC3D758) // 205
adr24(0xC3D7E2) // 206
adr24(0xC3D83C) // 207
adr24(0xC3D898) // 208
adr24(0xC3D8BE) // 209
adr24(0xC3D8E4) // 210
adr24(0xC3D91C) // 211
adr24(0xC3D98C) // 212
adr24(0xC3D9B2) // 213
adr24(0xC3D9D8) // 214
adr24(0xC3D9FE) // 215
adr24(0xC3DA49) // 216
adr24(0xC3DA97) // 217
adr24(0xC3DAF8) // 218
adr24(0xC3DB19) // 219
adr24(0xC3D966) // 220
adr24(0xC30195) // 221
adr24(0xC30235) // 222
adr24(0xC3024A) // 223
adr24(0xC30260) // 224
adr24(0xC302AC) // 225
adr24(0xC302AC) // 226
adr24(0xC302AC) // 227
adr24(0xC302D7) // 228
adr24(0xC302EF) // 229
adr24(0xC30303) // 230
adr24(0xC30317) // 231
adr24(0xC3032B) // 232
adr24(0xC3036F) // 233
adr24(0xC3036F) // 234
adr24(0xC3036F) // 235
adr24(0xC3036F) // 236
adr24(0xC3036F) // 237
adr24(0xC30394) // 238
adr24(0xC303C0) // 239
adr24(0xC303E5) // 240
adr24(0xC30478) // 241
adr24(0xC304FA) // 242
adr24(0xC304FA) // 243
adr24(0xC30520) // 244
adr24(0xC3053A) // 245
adr24(0xC30550) // 246
adr24(0xC3056E) // 247
adr24(0xC3056E) // 248
adr24(0xC30590) // 249
adr24(0xC305EF) // 250
adr24(0xC30671) // 251
adr24(0xC3069F) // 252
adr24(0xC306BA) // 253
adr24(0xC306DA) // 254
adr24(0xC30704) // 255
adr24(0xC30716) // 256
adr24(0xC30776) // 257
adr24(0xC30796) // 258
adr24(0xC307AB) // 259
adr24(0xC30832) // 260
adr24(0xC30869) // 261
adr24(0xC308BB) // 262
adr24(0xC308E5) // 263
adr24(0xC30962) // 264
adr24(0xC30979) // 265
adr24(0xC30993) // 266
adr24(0xC309B0) // 267
adr24(0xC30A32) // 268
adr24(0xC30A76) // 269
adr24(0xC30A91) // 270
adr24(0xC30ACB) // 271
adr24(0xC30AF8) // 272
adr24(0xC30B4E) // 273
adr24(0xC30BEA) // 274
adr24(0xC30BEA) // 275
adr24(0xC30BEA) // 276
adr24(0xC30C09) // 277
adr24(0xC30C20) // 278
adr24(0xC30C37) // 279
adr24(0xC30C3D) // 280
adr24(0xC30C43) // 281
adr24(0xC30C49) // 282
adr24(0xC30C4F) // 283
adr24(0xC30CE2) // 284
adr24(0xC30D1E) // 285
adr24(0xC30DB6) // 286
adr24(0xC30DCD) // 287
adr24(0xC30E21) // 288
adr24(0xC30E52) // 289
adr24(0xC30E7F) // 290
adr24(0xC30E99) // 291
adr24(0xC30EB9) // 292
adr24(0xC30F20) // 293
adr24(0xC30F9C) // 294
adr24(0xC31068) // 295
adr24(0xC310B1) // 296
adr24(0xC31115) // 297
adr24(0xC31140) // 298
adr24(0xC31182) // 299
adr24(0xC311B4) // 300
adr24(0xC311DF) // 301
adr24(0xC31221) // 302
adr24(0xC3126E) // 303
adr24(0xC312AD) // 304
adr24(0xC312C2) // 305
adr24(0xC312E7) // 306
adr24(0xC3131B) // 307
adr24(0xC3133A) // 308
adr24(0xC31359) // 309
adr24(0xC3137E) // 310
adr24(0xC31389) // 311
adr24(0xC313A9) // 312
adr24(0xC313F7) // 313
adr24(0xC313D5) // 314
adr24(0xC3141E) // 315
adr24(0xC31427) // 316
adr24(0xC31452) // 317
adr24(0xC31485) // 318
adr24(0xC31529) // 319
adr24(0xC31556) // 320
adr24(0xC3155C) // 321
adr24(0xC315CC) // 322
adr24(0xC315F8) // 323
adr24(0xC31626) // 324
adr24(0xC31651) // 325
adr24(0xC31689) // 326
adr24(0xC316BC) // 327
adr24(0xC316E4) // 328
adr24(0xC31717) // 329
adr24(0xC31743) // 330
adr24(0xC3176F) // 331
adr24(0xC3179B) // 332
adr24(0xC317C7) // 333
adr24(0xC317FC) // 334
adr24(0xC31831) // 335
adr24(0xC31869) // 336
adr24(0xC3189A) // 337
adr24(0xC318A5) // 338
adr24(0xC318D0) // 339
adr24(0xC318FD) // 340
adr24(0xC31A42) // 341
adr24(0xC31A83) // 342
adr24(0xC31AB1) // 343
adr24(0xC31ABD) // 344
adr24(0xC31B14) // 345
adr24(0xC31B4B) // 346
adr24(0xC31BED) // 347
adr24(0xC31BFD) // 348
adr24(0xC31BFD) // 349
adr24(0xC31C23) // 350
adr24(0xC31C49) // 351
adr24(0xC31CA4) // 352
adr24(0xC31CFB) // 353
adr24(0xC31D15) // 354
adr24(0xC31D61) // 355
adr24(0xC31D6A) // 356
adr24(0xC31D89) // 357
adr24(0xC31E92) // 358
adr24(0xC31EA6) // 359
adr24(0xC31E66) // 360
adr24(0xC31E79) // 361
adr24(0xC31E89) // 362
adr24(0xC31EAF) // 363
adr24(0xC31EB8) // 364
adr24(0xC32149) // 365
adr24(0xC32342) // 366
adr24(0xC322B3) // 367
adr24(0xC31FE8) // 368
adr24(0xC31DB5) // 369
adr24(0xC323D1) // 370
adr24(0xC3240A) // 371
adr24(0xC32454) // 372
adr24(0xC324A8) // 373
adr24(0xC324B1) // 374
adr24(0xC324BA) // 375
adr24(0xC324CE) // 376
adr24(0xC324D7) // 377
adr24(0xC324E0) // 378
adr24(0xC324E9) // 379
adr24(0xC324F2) // 380
adr24(0xC324F9) // 381
adr24(0xC32507) // 382
adr24(0xC32520) // 383
adr24(0xC328A8) // 384
adr24(0xC32534) // 385
adr24(0xC328B1) // 386
adr24(0xC32818) // 387
adr24(0xC32C46) // 388
adr24(0xC32860) // 389
adr24(0xC32C8A) // 390
adr24(0xC32878) // 391
adr24(0xC32CA2) // 392
adr24(0xC32860) // 393
adr24(0xC32C8A) // 394
adr24(0xC32890) // 395
adr24(0xC32CBA) // 396
adr24(0xC327AA) // 397
adr24(0xC32B9B) // 398
adr24(0xC32CF0) // 399
adr24(0xC32486) // 400
adr24(0xC32DFE) // 401
adr24(0xC32E56) // 402
adr24(0xC32E5F) // 403
adr24(0xC324BA) // 404
adr24(0xC324CE) // 405
adr24(0xC324D7) // 406
adr24(0xC324E0) // 407
adr24(0xC324E9) // 408
adr24(0xC32E68) // 409
adr24(0xC33063) // 410
adr24(0xC32E75) // 411
adr24(0xC331ED) // 412
adr24(0xC33205) // 413
adr24(0xC3320E) // 414
adr24(0xC33217) // 415
adr24(0xC33220) // 416
adr24(0xC33229) // 417
adr24(0xC33232) // 418
adr24(0xC3324E) // 419
adr24(0xC3326A) // 420
adr24(0xC332FA) // 421
adr24(0xC33283) // 422
adr24(0xC33310) // 423
adr24(0xC33299) // 424
adr24(0xC3332C) // 425
adr24(0xC332C1) // 426
adr24(0xC33342) // 427
adr24(0xC332D7) // 428
adr24(0xC3335E) // 429
adr24(0xC3337D) // 430
adr24(0xC3338F) // 431
adr24(0xC3338F) // 432
adr24(0xC3338F) // 433
adr24(0xC3338F) // 434
adr24(0xC33424) // 435
adr24(0xC334CF) // 436
adr24(0xC32E34) // 437
adr24(0xC334FF) // 438
adr24(0xC33535) // 439
adr24(0xC33572) // 440
adr24(0xC33595) // 441
adr24(0xC335B5) // 442
adr24(0xC33980) // 443
adr24(0xC339D2) // 444
adr24(0xC33AB5) // 445
adr24(0xC33A88) // 446
adr24(0xC33AED) // 447
adr24(0xC33B0F) // 448
adr24(0xC33B8B) // 449
adr24(0xC33B9E) // 450
adr24(0xC33BB2) // 451
adr24(0xC33BB7) // 452
adr24(0xC3DBA0) // 453
adr24(0xC3DBCC) // 454
adr24(0xC3DBF2) // 455
adr24(0xC3DC57) // 456
adr24(0xC3DC74) // 457
adr24(0xC3DC91) // 458
adr24(0xC3DCAE) // 459
adr24(0xC3DCCB) // 460
adr24(0xC3DD15) // 461
adr24(0xC3DD32) // 462
adr24(0xC3DD4F) // 463
adr24(0xC3DD6C) // 464
adr24(0xC33C6C) // 465
adr24(0xC33CDA) // 466
adr24(0xC33C30) // 467
adr24(0xC3C0F3) // 468
adr24(0xC3C101) // 469
adr24(0xC3C110) // 470
adr24(0xC3C11F) // 471
adr24(0xC3C12E) // 472
adr24(0xC3C167) // 473
adr24(0xC3C17A) // 474
adr24(0xC3C1A8) // 475
adr24(0xC3ABED) // 476
adr24(0xC3AC27) // 477
adr24(0xC3AC61) // 478
adr24(0xC33DD4) // 479
adr24(0xC33F0C) // 480
adr24(0xC33E42) // 481
adr24(0xC33EC4) // 482
adr24(0xC34029) // 483
adr24(0xC34097) // 484
adr24(0xC340CE) // 485
adr24(0xC3410B) // 486
adr24(0xC34114) // 487
adr24(0xC3411D) // 488
adr24(0xC34126) // 489
adr24(0xC3412F) // 490
adr24(0xC34138) // 491
adr24(0xC3415D) // 492
adr24(0xC34182) // 493
adr24(0xC341A7) // 494
adr24(0xC341CC) // 495
adr24(0xC34249) // 496
adr24(0xC3426E) // 497
adr24(0xC342B1) // 498
adr24(0xC3441A) // 499
adr24(0xC34402) // 500
adr24(0xC342F4) // 501
adr24(0xC40F68) // 502
adr24(0xC40F6E) // 503
adr24(0xC40F74) // 504
adr24(0xC40F7A) // 505
adr24(0xC40F80) // 506
adr24(0xC40F86) // 507
adr24(0xC40F8C) // 508
adr24(0xC40F92) // 509
adr24(0xC40F98) // 510
adr24(0xC40F9E) // 511
adr24(0xC40FA4) // 512
adr24(0xC40FAA) // 513
adr24(0xC40FB0) // 514
adr24(0xC40FB6) // 515
adr24(0xC40FBC) // 516
adr24(0xC40FC2) // 517
adr24(0xC40FC8) // 518
adr24(0xC40FCE) // 519
adr24(0xC40FD4) // 520
adr24(0xC40FDA) // 521
adr24(0xC40FE0) // 522
adr24(0xC40FE6) // 523
adr24(0xC40FEC) // 524
adr24(0xC40FF4) // 525
adr24(0xC40FFD) // 526
adr24(0xC41003) // 527
adr24(0xC4100C) // 528
adr24(0xC41012) // 529
adr24(0xC41018) // 530
adr24(0xC34301) // 531
adr24(0xC34378) // 532
adr24(0xC34385) // 533
adr24(0xC41020) // 534
adr24(0xC34E85) // 535
adr24(0xC34EC8) // 536
adr24(0xC34F31) // 537
adr24(0xC34F9B) // 538
adr24(0xC34FC7) // 539
adr24(0xC34FF8) // 540
adr24(0xC3500E) // 541
adr24(0xC35056) // 542
adr24(0xC350B0) // 543
adr24(0xC350F4) // 544
adr24(0xC35154) // 545
adr24(0xC35198) // 546
adr24(0xC34508) // 547
adr24(0xC34572) // 548
adr24(0xC3459E) // 549
adr24(0xC3456F) // 550
adr24(0xC345CA) // 551
adr24(0xC34635) // 552
adr24(0xC34693) // 553
adr24(0xC346F1) // 554
adr24(0xC3474E) // 555
adr24(0xC3486A) // 556
adr24(0xC34810) // 557
adr24(0xC347C1) // 558
adr24(0xC34767) // 559
adr24(0xC34AF6) // 560
adr24(0xC34A6C) // 561
adr24(0xC34975) // 562
adr24(0xC348FC) // 563
adr24(0xC34BAB) // 564
adr24(0xC34BCD) // 565
adr24(0xC34BF7) // 566
adr24(0xC34C3A) // 567
adr24(0xC34C86) // 568
adr24(0xC34CE0) // 569
adr24(0xC34D5C) // 570
adr24(0xC34D65) // 571
adr24(0xC34D6E) // 572
adr24(0xC34D77) // 573
adr24(0xC34D92) // 574
adr24(0xC34D9B) // 575
adr24(0xC34DA4) // 576
adr24(0xC34DA7) // 577
adr24(0xC34DB0) // 578
adr24(0xC34DB9) // 579
adr24(0xC34DC2) // 580
adr24(0xC34DCB) // 581
adr24(0xC34DE0) // 582
adr24(0xC34DEA) // 583
adr24(0xC36A53) // 584
adr24(0xC36A98) // 585
adr24(0xC36ABF) // 586
adr24(0xC36AE6) // 587
adr24(0xC36AFF) // 588
adr24(0xC36B18) // 589
adr24(0xC36B4B) // 590
adr24(0xC36BC6) // 591
adr24(0xC36BEB) // 592
adr24(0xC36C00) // 593
adr24(0xC36C4A) // 594
adr24(0xC36C94) // 595
adr24(0xC36CDB) // 596
adr24(0xC36D29) // 597
adr24(0xC36D40) // 598
adr24(0xC36D53) // 599
adr24(0xC36D5C) // 600
adr24(0xC36D7B) // 601
adr24(0xC36D9F) // 602
adr24(0xC36DBE) // 603
adr24(0xC36DD9) // 604
adr24(0xC36E19) // 605
adr24(0xC36E2D) // 606
adr24(0xC36E52) // 607
adr24(0xC36E5E) // 608
adr24(0xC36E6A) // 609
adr24(0xC36E76) // 610
adr24(0xC36E82) // 611
adr24(0xC36EB7) // 612
adr24(0xC36ED4) // 613
adr24(0xC36F08) // 614
adr24(0xC36F33) // 615
adr24(0xC36F68) // 616
adr24(0xC36F85) // 617
adr24(0xC36FB9) // 618
adr24(0xC36FE4) // 619
adr24(0xC37010) // 620
adr24(0xC37098) // 621
adr24(0xC370FD) // 622
adr24(0xC3715D) // 623
adr24(0xC371F4) // 624
adr24(0xC371FA) // 625
adr24(0xC37245) // 626
adr24(0xC37276) // 627
adr24(0xC37287) // 628
adr24(0xC372B0) // 629
adr24(0xC373C2) // 630
adr24(0xC37409) // 631
adr24(0xC3740F) // 632
adr24(0xC37456) // 633
adr24(0xC37479) // 634
adr24(0xC3749C) // 635
adr24(0xC374B0) // 636
adr24(0xC374E4) // 637
adr24(0xC3756D) // 638
adr24(0xC37596) // 639
adr24(0xC375C5) // 640
adr24(0xC375EE) // 641
adr24(0xC3763B) // 642
adr24(0xC3765F) // 643
adr24(0xC37698) // 644
adr24(0xC376D8) // 645
adr24(0xC37711) // 646
adr24(0xC37751) // 647
adr24(0xC37778) // 648
adr24(0xC377D2) // 649
adr24(0xC377F4) // 650
adr24(0xC37814) // 651
adr24(0xC3786F) // 652
adr24(0xC378B6) // 653
adr24(0xC378FD) // 654
adr24(0xC37891) // 655
adr24(0xC378D8) // 656
adr24(0xC3791D) // 657
adr24(0xC3793F) // 658
adr24(0xC379C0) // 659
adr24(0xC37987) // 660
adr24(0xC37A0B) // 661
adr24(0xC37A55) // 662
adr24(0xC37A5D) // 663
adr24(0xC37A66) // 664
adr24(0xC37A8A) // 665
adr24(0xC37AB5) // 666
adr24(0xC37B0B) // 667
adr24(0xC37B5A) // 668
adr24(0xC37B7E) // 669
adr24(0xC37BFE) // 670
adr24(0xC37CFD) // 671
adr24(0xC37D33) // 672
adr24(0xC37D92) // 673
adr24(0xC37DF1) // 674
adr24(0xC37EC1) // 675
adr24(0xC37F65) // 676
adr24(0xC37F71) // 677
adr24(0xC37F7D) // 678
adr24(0xC37FCD) // 679
adr24(0xC3801C) // 680
adr24(0xC3804C) // 681
adr24(0xC3809C) // 682
adr24(0xC380C0) // 683
adr24(0xC38146) // 684
adr24(0xC381B8) // 685
adr24(0xC381E0) // 686
adr24(0xC38208) // 687
adr24(0xC38230) // 688
adr24(0xC38264) // 689
adr24(0xC38298) // 690
adr24(0xC382CC) // 691
adr24(0xC38309) // 692
adr24(0xC383D2) // 693
adr24(0xC3840A) // 694
adr24(0xC38442) // 695
adr24(0xC384D8) // 696
adr24(0xC384E3) // 697
adr24(0xC384EE) // 698
adr24(0xC38515) // 699
adr24(0xC38544) // 700
adr24(0xC385E2) // 701
adr24(0xC38678) // 702
adr24(0xC3868F) // 703
adr24(0xC386A9) // 704
adr24(0xC386B2) // 705
adr24(0xC386FA) // 706
adr24(0xC38771) // 707
adr24(0xC3877A) // 708
adr24(0xC38783) // 709
adr24(0xC3878C) // 710
adr24(0xC3886C) // 711
adr24(0xC387B6) // 712
adr24(0xC388C3) // 713
adr24(0xC38939) // 714
adr24(0xC389BD) // 715
adr24(0xC389DD) // 716
adr24(0xC389FB) // 717
adr24(0xC38AB1) // 718
adr24(0xC38ADC) // 719
adr24(0xC38B3A) // 720
adr24(0xC38B5D) // 721
adr24(0xC38B7F) // 722
adr24(0xC38BC5) // 723
adr24(0xC38BFC) // 724
adr24(0xC38C7C) // 725
adr24(0xC38CB0) // 726
adr24(0xC38CE4) // 727
adr24(0xC38D18) // 728
adr24(0xC38D50) // 729
adr24(0xC38DB3) // 730
adr24(0xC38DD8) // 731
adr24(0xC38DFC) // 732
adr24(0xC38E32) // 733
adr24(0xC38E61) // 734
adr24(0xC38E89) // 735
adr24(0xC38EB9) // 736
adr24(0xC38EEA) // 737
adr24(0xC38EF1) // 738
adr24(0xC38EF8) // 739
adr24(0xC38EFF) // 740
adr24(0xC38F1B) // 741
adr24(0xC38F39) // 742
adr24(0xC38F91) // 743
adr24(0xC38FDF) // 744
adr24(0xC39022) // 745
adr24(0xC39025) // 746
adr24(0xC39030) // 747
adr24(0xC390B3) // 748
adr24(0xC39053) // 749
adr24(0xC390E6) // 750
adr24(0xC39155) // 751
adr24(0xC3918A) // 752
adr24(0xC391AE) // 753
adr24(0xC391E3) // 754
adr24(0xC39072) // 755
adr24(0xC39080) // 756
adr24(0xC39213) // 757
adr24(0xC3924D) // 758
adr24(0xC3928F) // 759
adr24(0xC392AB) // 760
adr24(0xC393C7) // 761
adr24(0xC393DD) // 762
adr24(0xC393FC) // 763
adr24(0xC39440) // 764
adr24(0xC39AD9) // 765
adr24(0xC39AFA) // 766
adr24(0xC39B25) // 767
adr24(0xC39B86) // 768
adr24(0xC39CD7) // 769
adr24(0xC39D3D) // 770
adr24(0xC39D85) // 771
adr24(0xC39DCF) // 772
adr24(0xC39E13) // 773
adr24(0xC39E22) // 774
adr24(0xC39E50) // 775
adr24(0xC39E8B) // 776
adr24(0xC39E7B) // 777
adr24(0xC39E83) // 778
adr24(0xC39EB6) // 779
adr24(0xC39ECA) // 780
adr24(0xC39EDE) // 781
adr24(0xC39EF2) // 782
adr24(0xC39FA0) // 783
adr24(0xC39FBA) // 784
adr24(0xC40E24) // 785
adr24(0xC0AD8A) // 786
adr24(0xC42172) // 787
adr24(0xC42290) // 788
adr24(0xC4222A) // 789
adr24(0xC422E9) // 790
adr24(0xC42304) // 791
adr24(0xC4231F) // 792
adr24(0xC4233A) // 793
adr24(0xC42355) // 794
adr24(0xC42370) // 795
adr24(0xC4238B) // 796
adr24(0xC423A6) // 797
adr24(0xC423C1) // 798
adr24(0xC351FD) // 799
adr24(0xC35214) // 800
adr24(0xC3523F) // 801
adr24(0xC35FE2) // 802
adr24(0xC35FF1) // 803
adr24(0xC36356) // 804
adr24(0xC3639E) // 805
adr24(0xC363C6) // 806
adr24(0xC36447) // 807
adr24(0xC36405) // 808
adr24(0xC36000) // 809
adr24(0xC36073) // 810
adr24(0xC36093) // 811
adr24(0xC360B3) // 812
adr24(0xC360EC) // 813
adr24(0xC3610A) // 814
adr24(0xC36144) // 815
adr24(0xC36169) // 816
adr24(0xC361AA) // 817
adr24(0xC361BB) // 818
adr24(0xC361CC) // 819
adr24(0xC361DB) // 820
adr24(0xC361FB) // 821
adr24(0xC36219) // 822
adr24(0xC36239) // 823
adr24(0xC3626E) // 824
adr24(0xC3629F) // 825
adr24(0xC362E1) // 826
adr24(0xC36311) // 827
adr24(0xC36320) // 828
adr24(0xC36338) // 829
adr24(0xC366BF) // 830
adr24(0xC3661C) // 831
adr24(0xC36647) // 832
adr24(0xC36692) // 833
adr24(0xC366DC) // 834
adr24(0xC36726) // 835
adr24(0xC36474) // 836
adr24(0xC364B1) // 837
adr24(0xC3652A) // 838
adr24(0xC365A3) // 839
adr24(0xC3675D) // 840
adr24(0xC3678E) // 841
adr24(0xC367A4) // 842
adr24(0xC367E6) // 843
adr24(0xC36814) // 844
adr24(0xC3683F) // 845
adr24(0xC36852) // 846
adr24(0xC36867) // 847
adr24(0xC3687C) // 848
adr24(0xC36891) // 849
adr24(0xC368A6) // 850
adr24(0xC368CF) // 851
adr24(0xC368F8) // 852
adr24(0xC3699B) // 853
adr24(0xC369BA) // 854
adr24(0xC369C9) // 855
adr24(0xC369E2) // 856
adr24(0xC369FB) // 857
adr24(0xC36A2A) // 858
adr24(0xC4279F) // 859
adr24(0xC4217B) // 860
adr24(0xC3A2C6) // 861
adr24(0xC3949B) // 862
adr24(0xC394CC) // 863
adr24(0xC394FD) // 864
adr24(0xC3952E) // 865
adr24(0xC3955F) // 866
adr24(0xC39590) // 867
adr24(0xC395C1) // 868
adr24(0xC395F2) // 869
adr24(0xC39623) // 870
adr24(0xC39654) // 871
adr24(0xC39685) // 872
adr24(0xC396B6) // 873
adr24(0xC396E7) // 874
adr24(0xC39718) // 875
adr24(0xC39749) // 876
adr24(0xC3977A) // 877
adr24(0xC397AB) // 878
adr24(0xC397DC) // 879
adr24(0xC3980D) // 880
adr24(0xC3983E) // 881
adr24(0xC3986F) // 882
adr24(0xC398A0) // 883
adr24(0xC398D1) // 884
adr24(0xC39902) // 885
adr24(0xC39933) // 886
adr24(0xC39964) // 887
adr24(0xC39995) // 888
adr24(0xC399C6) // 889
adr24(0xC399F7) // 890
adr24(0xC39A28) // 891
adr24(0xC39A59) // 892
adr24(0xC39A8A) // 893
adr24(0xC3A099) // 894
}
import asm65816
// Use a space that is completely unused in RAM
// cooprocks123e's "Battle OverWorld Sprites" patch already uses 0x31C2 through 0x31C9
define OBJ_destructor_lo = 0x31CA // 60 bytes (30*2)
define OBJ_destructor_hi = 0x3206 // 60 bytes (30*2)
// Next free addresses are 0x3242 through 0x3329
/*
* TABLE OF CONTENTS (CTRL+F to search)
* 1. INSTRUCTIONS
* 1.1. SCRIPT CONTROL INSTRUCTIONS
* 1.2. DATA MOVEMENT INSTRUCTIONS
* 1.3. ARITHMETIC AND LOGICAL INSTRUCTIONS
* 1.4. CONTROL FLOW INSTRUCTIONS
* 1.5. LOOP INSTRUCTIONS
* 1.6. TASK INSTRUCTIONS
* 1.7. CALLBACK INSTRUCTIONS
* 1.8. ENTITY INSTRUCTIONS
* 1.9. OTHER INSTRUCTIONS
* 2. CONVENIENCE MACROS
* 3. CALLBACKS
* 3.1. TICK CALLBACK
* 3.2. ONDRAW CALLBACK
* 3.3. ONPOSITION CALLBACK
* 3.4. ONMOVE CALLBACK
* 3.5. ONDESTROY CALLBACK
* 4. TASKS
* 5. VARIABLES
* 6. THE RESULT REGISTER
*
*
* 1. INSTRUCTIONS
* 1.1. SCRIPT CONTROL INSTRUCTIONS
* m_halt
* Halts script execution (infinite loop)
* m_pause(delay)
* Pauses the script execution for `delay` frames. `delay` must be in range 0..255
* m_pause_result
* Pauses the script execution for [RESULT] frames
* m_pause_var0
* Pauses the script execution for [VAR0] frames
* m_pause_var1 through m_pause_var7
* Same as `m_pause_var0`, but for the other variables
*
* 1.2. DATA MOVEMENT INSTRUCTIONS
* m_set_var0(value)
* VAR0 = value
* m_set_var1(value) through m_set_var7(value)
* Same as `m_set_var0`, but for the other variables
* m_set_result(value)
* RESULT = value
* m_rtovar0
* VAR0 = RESULT
* m_rtovar1 through m_rtovar7
* Same as `m_rtovar0`, but for the other variables
* m_get_var0
* RESULT = VAR0
* m_get_var1 through m_get_var7
* Same as `m_get_var0`, but for the other variables
* m_get_mem16(address)
* RESULT = memory[address]
* The address must be in bank 7E
* The value at the address is interpreted as 16-bit
* m_set_mem8(address, value)
* memory[address] = value
* The address must be in bank 7E
* The value at the address is interpreted as 8-bit
* m_set_mem16(address, value)
* Same as `m_set_mem8`, but the value at the address is interpreted as 16-bit
*
* 1.3. ARITHMETIC AND LOGICAL INSTRUCTIONS
* In the following instructions, `op` can be any of
* add - addition (supports negative)
* and - bitwise AND
* or - bitwise OR
* xor - bitwise XOR
*
* m_op_result(value)
* RESULT = RESULT op value
* m_op_var0(value)
* VAR0 = VAR0 op value
* m_op_var1(value) through m_op_var7(value)
* Same as `m_op_var0`, but for the other variables
* m_op_mem8(address, value)
* memory[address] = memory[address] op value
* The address must be in bank 7E
* The value at `address` is interpreted as 8-bit
* m_op_mem16(address, value)
* Same as `m_op_mem8`, but the value at `address` is interpreted as 16-bit
*
* 1.4. CONTROL FLOW INSTRUCTIONS
* m_jmp(address)
* Jump to `address`. Works exactly like assembly JMP
* m_jml(address)
* Long jump to `address`. Works exactly like assembly JML
* m_multijmp(amount)
* Table jump based on the RESULT register. `amount` tells the amount of addresses in the jump table
* Works exactly like CCScript control code "[09 XX (YYYYYY)*XX]"
* Example usage:
* ```
* m_multijmp(3)
* short Label1 // Jump to `Label1` if RESULT register is 0
* short Label2 // Jump to `Label2` if RESULT register is 1
* Short label3 // Jump to `Label3` if RESULT register is 2
* // Execution will resume here if RESULT is not any of the above values
* ```
* m_jeq(address)
* Absolute jump to `address` if RESULT register is zero. Works exactly like `BEQ_a(address)`
* m_jne(address)
* Absolute jump to `address` if RESULT register is not zero. Works exactly like `BNE_a(address)`
* m_jsr(address)
* Jump to subroutine. Works exactly like assembly JSR
* m_jsl(address)
* Long jump to subroutine. Works exactly like assembly JSL
* m_multijsr(amount)
* Table subroutine jump based on the RESULT register. `amount` tells the amount of addresses in the jump table
* Works exactly like CCScript control code "[1F C0 XX (YYYYYY)*XX]"
* m_rts
* Returns from subroutine. Works exactly like assembly RTS
* m_rtl
* Returns from subroutine. Works exactly like assembly RTL
*
* 1.5. LOOP INSTRUCTIONS
* m_loop(count)
* Starts a loop with `count` iterations
* m_loop_result
* Starts a loop with [RESULT] iterations
* m_endloop
* Ends a loop iteration
* m_breakeq(address)
* Break out of a loop and jump to `address` if RESULT register is zero
* m_breakne(address)
* Break out of a loop and jump to `address` if RESULT register is not zero
*
* 1.6. TASK INSTRUCTIONS
* m_task(address)
* Register and begin task at `address`. The address must be at the same bank as the current bank
* m_task_long(address)
* Register and begin task at `address`. The address can be at any bank
* NOTE: This is not a vanilla instruction!
* m_endtask
* Ends a task. Unknown (not undefined!) behavior if the script isn't a task
* m_endlasttask
* Ends the last registered task. Unknown (not undefined!) behavior if the script has no registered tasks
*
* 1.7. CALLBACK INSTRUCTIONS
* m_ontick(address)
* Sets the entity's ONTICK callback. The address can be at any bank
* m_ontick_nop
* Sets the entity's ONTICK callback to NOP (no-operation, do nothing)
* m_ondraw(address)
* Sets the entity's ONDRAW callback. The address must be in bank C0
* m_onposition(address)
* Sets the entity's ONPOSITION callback. The address must be in bank C0
* m_onmove(address)
* Sets the entity's ONMOVE callback. The address must be in bank C0
* m_ondestroy(address)
* Sets the entity's ONDESTROY callback. The address can be at any bank
* NOTE: This is not a vanilla instruction!
*
* 1.8. ENTITY INSTRUCTIONS
* m_set_spritemap(address)
* Sets the entity's spritemap. The address can be at any bank
* This is mostly undocumented, and not really useful unless you're uploading graphics to VRAM on your own
* m_set_anim(value)
* Sets the entity's animation frame to `value`
* If `value` is -1, the entity is made invisible
* m_set_anim_var0
* Sets the entity's animation frame to [VAR0]
* If VAR0 is a negative number, the entity is made invisible
* m_set_anim_var1 through m_set_anim_var7
* Same as `m_set_anim_var0`, but for the other variables
* m_add_anim(value)
* Adds `value` to the entity's animation frame (supports negative)
* If the resulting frame is negative, the entity is made invisible
* m_inc_anim
* Same as `m_add_anim(1)`
* m_dec_anim
* Same as `m_add_anim(-1)`
* m_priority(value)
* Sets the entity's drawing priority to `value`
* The value must be in range 0..3. Undefined behavior if the value is outside this range
* m_set_xpos(value)
* Sets the entity's X position to `value`
* m_set_ypos(value)
* Sets the entity's Y position to `value`
* m_set_zpos(value)
* Sets the entity's Z position to `value`
* m_add_xpos(value)
* Adds `value` to the entity's X position (supports negative)
* m_add_ypos(value)
* Adds `value` to the entity's Y position (supports negative)
* m_add_zpos(value)
* Adds `value` to the entity's Z position (supports negative)
* m_set_xvel(value)
* Sets the entity's X velocity to `value`
* To get the value in pixels per frame, divide `value` by 256
* m_set_yvel(value)
* Same as `m_set_xvel`, but for the Y velocity
* m_set_zvel(value)
* Same as `m_set_xvel`, but for the Z velocity
* m_add_xpos(value)
* Adds `value` to the entity's X velocity (supports negative)
* To get the value in pixels per frame, divide `value` by 256
* m_add_yvel(value)
* Same as `m_add_xvel`, but for the Y velocity
* m_add_zvel(value)
* Same as `m_add_xvel`, but for the Z velocity
* m_zerovel
* Sets the entity's X/Y/Z velocities to zero
*
* 1.9. OTHER INSTRUCTIONS
* m_end
* Ends the script. Shouldn't be called before the entity is deleted via `asmcall(0xC020F1)`
* You don't really have to worry about calling this, as it's already done via the convenience macro `m_destroy_self`
* m_asmcall(address)
* Call ASM routine at `address`
* The RESULT register is passed as a parameter to the ASM routine in the accumulator
* The RESULT register will also be set to whatever the ASM routine returns in the accumulator
* m_store_result
* Write RESULT register to storage
* Works similar to CCScript control code `store_registers`
* m_load_result
* Load RESULT register from storage
* Works similar to CCScript control code `load_registers`
*
*
* 2. CONVENIENCE MACROS
* NOTE: Most of these will clobber the value in the RESULT register. Please keep this in mind
*
* m_destroy_self
* Deletes the entity and ends the script
* m_choose_random_2(n1, n2)
* Select a random number and store it in the RESULT register
* m_choose_random_3(n1, n2, n3) through m_choose_random_8(n1, n2, n3, n4, n5, n6, n7, n8)
* Same as `m_choose_random_2`, but select a random number from a greater sample
* m_result_greater(value)
* Returns 1 if (RESULT > value), otherwise returns 0
* m_x_less_than(value)
* Returns 1 if (entity_x < value), otherwise returns 0
* m_y_less_than(value)
* Returns 1 if (entity_y < value), otherwise returns 0
* m_rand
* Returns a random number in range 0..255
* m_rand_mod4
* Returns a random number in range 0..3
* m_rand_mod8
* Returns a random number in range 0..7
* m_get_random_angle
* Returns `rand() << 8`, useful for getting a random angle value
* m_get_distance_from_player
* Sets the RESULT register to the distance between the entity and the party leader
* m_wait_until_touch
* Halts script execution until the player touches the entity
* m_wait_until_near_self(rx, ry)
* Halts script execution until the player is near the entity in radius (rx, ry)
* m_wait_until_near_pos(px, py, rx, ry)
* Halts script execution until the player is near position (px, py) in radius (rx, ry)
* m_unlock_text
* Unlocks the text (CCScript) script after a "[1F 61]" control code
* m_make_invisible
* Makes the entity invisible. Equivalent to `m_set_anim(-1)`
* m_disable_collision
* Disables collisions with the entity
* m_invisible_no_collision
* Makes the entity invisible and disables collision
* m_enable_collision
* Enables collisions with the entity
* m_call_npc_text
* Calls the NPC's primary text script
* Undefined behavior if the entity is not an NPC
* m_textcall(address)
* Calls the text script at `address`
* Respects door transitions and such
* m_textcall2(address)
* Calls the text script at `address`
* Immediately call, don't respect door transitions
* m_fadein(delta, delay)
* Perform a screen fade-in
* `delta` is how much the brightness will change every `delay` frames
* NOTE: The game will keep running normally while the fade is in progress
* m_fadeout(delta, delay)
* Same as `m_fadein`, but performs a screen fade-out
* m_wait_fade
* Waits until a screen fade (`m_fadein` or `m_fadeout`) is done
* m_mosaic_in(delta, delay, bgs)
* Perform a screen fade-in with mosaic
* `delta` is how much the brightness will change every `delay` frames
* `bgs` tell which BG layers are affected by the mosaic (bitwise: 0x01, 0x02, 0x04, 0x08)
* NOTE: The game is paused until the screen fade is done
* m_mosaic_out(delta, delay, bgs)
* Same as `m_mosaic_in`, but performs a screen fade-out
* m_create_entity(spr, scr)
* Creates a new entity with spritegroup `spr` and script `scr`
* m_set_surface_flags(flags)
* Sets the entity's surface flags
* m_set_speed(value)
* Sets the entity's movement speed
* To get the value in pixels per frame, divide `value` by 256
* m_set_speed_to_result
* Sets the entity's movement speed to the RESULT register
* m_get_speed
* Stores the entity's movement speed into the RESULT register
* To get the value in pixels per frame, divide `value` by 256
* m_set_facing_anim(dir, anim)
* Sets the entity's facing direction to `dir` and animation frame to `anim`
* If VAR0 is not zero, then something slightly different happens
* m_set_facing(dir)
* Sets the entity's facing direction to `dir`
* m_set_facing_to_result
* Sets the entity's facing direction to the RESULT register
* m_reverse_facing
* Reverses the entity's facing direction
* m_get_facing
* Stores the entity's facing direction into the RESULT register
* m_start_walk(dir)
* Makes the entity start walking towards a certain direction
* m_walk_pixels(pixels)
* Makes the entity walk a certain number of pixels with its current facing direction
* m_set_flag(flagid)
* Sets the event flag `flagid`
* m_unset_flag(flagid)
* Unsets the event flag `flagid`
* m_get_flag(flagid)
* Stores the state of event flag `flagid` into the RESULT register
* m_sound(snd)
* Plays a sound effect
* m_warp_to_pc(pc)
* Instantly teleports the entity to the location of the party character `pc`
* m_warp_to_leader
* Instantly teleports the entity to the location of the party leader
* m_warp_to_sprite(spr)
* Instantly teleports the entity to the location of another entity with spritegroup `spr`
* m_warp_to_dest
* Instantly teleports the entity to its destination
* m_set_new_entity_spawn_pos(x, y, dir)
* Sets the "anchor point" for new entities/npcs created with CCScript control codes "[1F 15]" and "[1F 17]"
* m_set_new_entity_spawn_pos_from_leader
* Same as `m_set_new_sprite_spawn_pos`, but the coordinates are taken from the party leader
* m_set_new_entity_spawn_pos_from_self
* Same as `m_set_new_sprite_spawn_pos`, but the coordinates are taken from the current entity
* m_set_new_entity_spawn_pos_from_warp(warpid)
* Same as `m_set_new_sprite_spawn_pos`, but the coordinates are taken from a warp (as used with CCScript control code `warp`)
* m_face_dest
* Makes the entity face its destination
* m_refresh_graphics
* Forces a refresh on the entity's graphics, using the current animation frame
* m_refresh_graphics_frame0
* Forces a refresh on the entity's graphics, using animation frame 0
* NOTE: This doesn't actually set the entity's animation frame, it just displays frame 0
* m_refresh_graphics_frame1
* Forces a refresh on the entity's graphics, using animation frame 1
* NOTE: This doesn't actually set the entity's animation frame, it just displays frame 1
* m_copy_xy_to_var01
* Copies the entity's (X, Y) position to (VAR0, VAR1)
* m_move_until_at(px, py)
* Makes the entity move towards (px, py), halting script execution until reaching the destination
* m_move_until_at_radius(px, py, radius)
* Same as `m_move_until_at`, but with a configurable detection radius
* m_set_dest_pos(px, py)
* Sets the entity's destination to (px, py)
* m_set_dest_npc(npc)
* Sets the entity's destination to NPC `npc`
* m_set_dest_sprite(spr)
* Sets the entity's destination to sprite with spritegroup `spr`
* m_set_dest_pc(pc)
* Sets the entity's destination to party character `pc`
* Undefined behavior if `pc` is not in the party
* m_set_dest_leader
* Sets the entity's destination to the party leader
* m_set_dest_party_tail
* Sets the entity's destination to the party "tail"
* m_default_dest_radius
* Sets the destination radius check to the lowest possible value, taking into account the entity's movement speed
* m_set_dest_radius(radius)
* Sets the destination radius check to `radius`
* m_walk_to_dest
* Makes the entity move towards its current destination, halting script execution until reaching the destination
* m_walk_to_leader
* Makes the entity walk next to the party leader, halting script execution until reaching the destination
* m_init_basic_moving(speed)
* Initializes a basic moving entity with movement speed `speed`
* To get the speed in pixels per frame, divide `speed` by 256
* The entity will animate with 16-frame delay and respect collision with map tiles
* The entity will also be destroyed automatically if it goes off-screen far enough
*
*
* 3. CALLBACKS
* 3.1. TICK CALLBACK
* An optional function that is executed every frame
* DP local variables $00 through $7F can be freely used by the function
* 3.2. ONDRAW CALLBACK
* A function that is executed every frame, responsible for pushing values into OAM
* DP local variables $00 through $7F can be freely used by the function
* 3.3. ONPOSITION CALLBACK
* A function that is executed every frame, responsible for setting the entity's on-screen position
* DP local variables $00 through $7F can be freely used by the function
* 3.4. ONMOVE CALLBACK
* A function that is executed every frame, responsible for moving the entity based on its velocity values
* DP local variables $00 through $7F can be freely used by the function
* 3.5. ONDESTROY CALLBACK
* An optional function that is executed whenever the entity is destroyed
* DP local variables $00 through $1F can be freely used by the function
* NOTE: This is not a vanilla callback!
*
*
* 4. TASKS:
* "Tasks" are auxiliary scripts that run alongside a main script.
* The tasks will always run after the main script
*
*
* 5. VARIABLES
* "Variables" are a special form of general-purpose storage that are bound to the ENTITY.
* They differ from the RESULT register, which is, in turn, bound to the SCRIPT.
* This means that variables can be used to communicate between the main script and its tasks, since they share the same ENTITY.
* Common examples of this include the "MovTask_Anim" tasks, which generally use VAR4 as a toggle.
* Each entity has eight 16-bit variables, named VAR0 through VAR7.
* Most of the times, the pair (VAR6, VAR7) is the entity's current (X, Y) destination point
*
*
* 6. THE RESULT REGISTER
* The RESULT register is a 16-bit number, and a special form of storage that is bound to the SCRIPT
* This means that the main script and its tasks all have their own distinct RESULT register.
* When an ASMCALL instruction is executed, this register is passed as a parameter to the called ASM routine in the accumulator.
* The RESULT register will also get its value from the return value of the ASM routine
*/
// DO NOT TOUCH ANYTHING BELOW HERE!!
//*****************************************************************************
// MOVEMENT SCRIPT INSTRUCTION DEFINES
//*****************************************************************************
// Unused instructions that can still be replaced: 34, 35, 36, 37, 38, 3A
// SCRIPT CONTROL INSTRUCTIONS
command m_pause(frames) "[06 {byte frames}]"
command m_halt "[09]"
command m_pause_var0 "[21 00]"
command m_pause_var1 "[21 01]"
command m_pause_var2 "[21 02]"
command m_pause_var3 "[21 03]"
command m_pause_var4 "[21 04]"
command m_pause_var5 "[21 05]"
command m_pause_var6 "[21 06]"
command m_pause_var7 "[21 07]"
command m_pause_result "[44]"
// DATA MOVEMENT INSTRUCTIONS
command m_set_var0(val) "[0E 00 {short val}]"
command m_set_var1(val) "[0E 01 {short val}]"
command m_set_var2(val) "[0E 02 {short val}]"
command m_set_var3(val) "[0E 03 {short val}]"
command m_set_var4(val) "[0E 04 {short val}]"
command m_set_var5(val) "[0E 05 {short val}]"
command m_set_var6(val) "[0E 06 {short val}]"
command m_set_var7(val) "[0E 07 {short val}]"
command m_set_mem8(addr, val) "[12 {short addr} {byte val}]"
command m_set_mem16(addr, val) "[15 {short addr} {short val}]"
command m_set_result(val) "[1D {short val}]"
command m_get_mem16(addr) "[1E {short addr}]"
command m_rtovar0 "[1F 00]"
command m_rtovar1 "[1F 01]"
command m_rtovar2 "[1F 02]"
command m_rtovar3 "[1F 03]"
command m_rtovar4 "[1F 04]"
command m_rtovar5 "[1F 05]"
command m_rtovar6 "[1F 06]"
command m_rtovar7 "[1F 07]"
command m_get_var0 "[20 00]"
command m_get_var1 "[20 01]"
command m_get_var2 "[20 02]"
command m_get_var3 "[20 03]"
command m_get_var4 "[20 04]"
command m_get_var5 "[20 05]"
command m_get_var6 "[20 06]"
command m_get_var7 "[20 07]"
// ARITHMETIC AND LOGICAL INSTRUCTIONS
command m_and_mem16(addr, val) "[0D {short addr} 00 {short val}]"
command m_or_mem16(addr, val) "[0D {short addr} 01 {short val}]"
command m_add_mem16(addr, val) "[0D {short addr} 02 {short val}]"
command m_xor_mem16(addr, val) "[0D {short addr} 03 {short val}]"
command m_and_var0(val) "[14 00 00 {short val}]"
command m_and_var1(val) "[14 01 00 {short val}]"
command m_and_var2(val) "[14 02 00 {short val}]"
command m_and_var3(val) "[14 03 00 {short val}]"
command m_and_var4(val) "[14 04 00 {short val}]"
command m_and_var5(val) "[14 05 00 {short val}]"
command m_and_var6(val) "[14 06 00 {short val}]"
command m_and_var7(val) "[14 07 00 {short val}]"
command m_or_var0(val) "[14 00 01 {short val}]"
command m_or_var1(val) "[14 01 01 {short val}]"
command m_or_var2(val) "[14 02 01 {short val}]"
command m_or_var3(val) "[14 03 01 {short val}]"
command m_or_var4(val) "[14 04 01 {short val}]"
command m_or_var5(val) "[14 05 01 {short val}]"
command m_or_var6(val) "[14 06 01 {short val}]"
command m_or_var7(val) "[14 07 01 {short val}]"
command m_add_var0(val) "[14 00 02 {short val}]"
command m_add_var1(val) "[14 01 02 {short val}]"
command m_add_var2(val) "[14 02 02 {short val}]"
command m_add_var3(val) "[14 03 02 {short val}]"
command m_add_var4(val) "[14 04 02 {short val}]"
command m_add_var5(val) "[14 05 02 {short val}]"
command m_add_var6(val) "[14 06 02 {short val}]"
command m_add_var7(val) "[14 07 02 {short val}]"
command m_xor_var0(val) "[14 00 03 {short val}]"
command m_xor_var1(val) "[14 01 03 {short val}]"
command m_xor_var2(val) "[14 02 03 {short val}]"
command m_xor_var3(val) "[14 03 03 {short val}]"
command m_xor_var4(val) "[14 04 03 {short val}]"
command m_xor_var5(val) "[14 05 03 {short val}]"
command m_xor_var6(val) "[14 06 03 {short val}]"
command m_xor_var7(val) "[14 07 03 {short val}]"
command m_and_mem8(addr, val) "[18 {short addr} 00 {byte val}]"
command m_or_mem8(addr, val) "[18 {short addr} 01 {byte val}]"
command m_add_mem8(addr, val) "[18 {short addr} 02 {byte val}]"
command m_xor_mem8(addr, val) "[18 {short addr} 03 {byte val}]"
command m_and_result(val) "[27 00 {short val}]"
command m_or_result(val) "[27 01 {short val}]"
command m_add_result(val) "[27 02 {short val}]"
command m_xor_result(val) "[27 03 {short val}]"
// CONTROL FLOW INSTRUCTIONS
command m_jml(addr) "[03 {adr24(addr)}]"
command m_jsl(addr) "[04 {adr24(addr)}]"
command m_rtl "[05]"
command m_jeq(addr) "[0A {short addr}]"
command m_jne(addr) "[0B {short addr}]"
command m_multijmp(amount) "[10 {byte amount}]"
command m_multijsr(amount) "[11 {byte amount}]"
command m_jmp(addr) "[19 {short addr}]"
command m_jsr(addr) "[1A {short addr}]"
command m_rts "[1B]"
// LOOP INSTRUCTIONS
command m_loop(c) "[01 {byte c}]"
command m_loop_result "[24]"
command m_endloop "[02]"
command m_breakeq(addr) "[16 {short addr}]"
command m_breakne(addr) "[17 {short addr}]"
// TASK INSTRUCTIONS
command m_task(addr) "[07 {short addr}]"
command m_endtask "[0C]"
command m_endlasttask "[13]"
command m_task_long(addr) "[31 {adr24(addr)}]" // NON-VANILLA!
// CALLBACK INSTRUCTIONS
command m_ontick(addr) "[08 {adr24(addr)}]"
command m_ontick_nop "[0F]"
command m_ondraw(addr) "[22 {short addr}]"
command m_onposition(addr) "[23 {short addr}]"
command m_onmove(addr) "[25 {short addr}]"
command m_ondestroy(addr) "[34 {adr24(addr)}]" // NON-VANILLA!
// ENTITY INSTRUCTIONS
command m_set_spritemap(addr) "[1C {adr24(addr)}]"
command m_set_anim_var0 "[26 00]"
command m_set_anim_var1 "[26 01]"
command m_set_anim_var2 "[26 02]"
command m_set_anim_var3 "[26 03]"
command m_set_anim_var4 "[26 04]"
command m_set_anim_var5 "[26 05]"
command m_set_anim_var6 "[26 06]"
command m_set_anim_var7 "[26 07]"
command m_set_xpos(val) "[28 {short val}]"
command m_set_ypos(val) "[29 {short val}]"
command m_set_zpos(val) "[2A {short val}]"
command m_add_xpos(val) "[2B {short val}]"
command m_add_ypos(val) "[2C {short val}]"
command m_add_zpos(val) "[2D {short val}]"
command m_add_xvel(val) "[2E {short val}]"
command m_add_yvel(val) "[2F {short val}]"
command m_add_zvel(val) "[30 {short val}]"
command m_zerovel "[39]"
command m_set_anim(val) "[3B {byte val}]"
command m_inc_anim "[3C]"
command m_dec_anim "[3D]"
command m_add_anim(val) "[3E {byte val}]"
command m_set_xvel(val) "[3F {short val}]"
command m_set_yvel(val) "[40 {short val}]"
command m_set_zvel(val) "[41 {short val}]"
command m_priority(val) "[43 {byte val}]"
// OTHER INSTRUCTIONS
command m_end "[00]"
command m_store_result "[32]" // NON-VANILLA!
command m_load_result "[33]" // NON-VANILLA!
command m_asmcall(addr) "[42 {adr24(addr)}]"
//*****************************************************************************
// VANILLA TASK DEFINES
//*****************************************************************************
define MovTask_Anim8 = 0xC3A09F // Animate with 8 frame delay
define MovTask_Anim24 = 0xC3A0B2 // Animate with 24 frame delay
define MovTask_Anim12 = 0xC3A0C5 // Animate with 12 frame delay
define MovTask_Anim_Var4 = 0xC3A12E // Animate with [VAR4] frame delay
define MovTask_Anim8_Toggle_DestroyIfFar = 0xC3A15E // Animate with 8 frame delay (togglable via VAR4) and destroy if far
define MovTask_Anim12_24_DestroyIfFar = 0xC3A17B // Animate with 12 then 24 frame delay and destroy if far
define MovTask_Anim24_DestroyIfFar = 0xC3A18F // Animate with 24 frame delay and destroy if far
define MovTask_Anim9_DestroyIfFar = 0xC3A1B7 // Animate with 9 frame delay and destroy if far
define MovTask_Anim6_DestroyIfFar = 0xC3A1CB // Animate with 6 frame delay and destroy if far
define MovTask_Anim16_DestroyIfFar = 0xC3A1F3 // Animate with 16 frame delay and destroy if far
define MovTask_HandleCollision = 0xC3A262 // Handle collisions. Doesn't need to be used by stationary entities
define MovTask_DestroyIfFar = 0xC3A2B8 // Destroy if far
define MovTask_EnemyTouch1 = 0xC3A434 // Start battle with enemy on touch
define MovTask_EnemyTouch2 = 0xC3A448 // Very similar to above, I don't actually know what's different
define MovTask_PartyLook = 0xC3AFA3 // Party members look at ENTITY
define MovTask_DestroyIfFar_UnsetFlag_10 = 0xC3B431 // Destroy if far and unset event flag 10
define MovTask_ButterflyTouch = 0xC3DEED // Butterfly effect on touch
define MovTask_CallNpcScriptOnTouch = 0xC36D18 // Call NPC primary text script on touch
//*****************************************************************************
// CONVENIENCE MACROS DEFINITION
//*****************************************************************************
// Select a random number and store it in the RESULT register
command m_choose_random_2(a, b) {
m_asmcall (0xC09F82)
"[02 {short a} {short b}]"
}
command m_choose_random_3(a, b, c) {
m_asmcall (0xC09F82)
"[03 {short a} {short b} {short c}]"
}
command m_choose_random_4(a, b, c, d) {
m_asmcall (0xC09F82)
"[04 {short a} {short b} {short c} {short d}]"
}
command m_choose_random_5(a, b, c, d, e) {
m_asmcall (0xC09F82)
"[05 {short a} {short b} {short c} {short d} {short e}]"
}
command m_choose_random_6(a, b, c, d, e, f) {
m_asmcall (0xC09F82)
"[06 {short a} {short b} {short c} {short d} {short e} {short f}]"
}
command m_choose_random_7(a, b, c, d, e, f, g) {
m_asmcall (0xC09F82)
"[07 {short a} {short b} {short c} {short d} {short e} {short f} {short g}]"
}
command m_choose_random_8(a, b, c, d, e, f, g, h) {
m_asmcall (0xC09F82)
"[08 {short a} {short b} {short c} {short d} {short e} {short f} {short g} {short h}]"
}
// RESULT = 1 if (RESULT > value) else 0
command m_result_greater(val) {
m_asmcall (_ASM_resultgreater)
short val
}
// RESULT = 1 if (ENTITY_x < value) else 0
command m_x_less_than(val) {
m_set_result (val)
m_asmcall (0xC468B5)
}
// RESULT = 1 if (entity_y < value) else 0
command m_y_less_than(val) {
m_set_result (val)
m_asmcall (0xC468DC)
}
// Halts script execution until the player is near the entity in radius (rx, ry)
command m_wait_until_near_self(rx, ry) {
m_set_var2 (rx)
m_set_var3 (ry)
m_jsl (_SCR_waituntilnearself)
}
// Halts script execution until the player is near position (px, py) in radius (rx, ry)
command m_wait_until_near_pos(px, py, rx, ry) {
m_set_var0 (px)
m_set_var1 (py)
m_set_var2 (rx)
m_set_var3 (ry)
m_jsl (_SCR_waituntilnear)
}
// Makes the entity invisible and disables collision
command m_invisible_no_collision {
m_make_invisible
m_disable_collision
}
// Calls a text script (respects door transitions and such)
command m_textcall(textptr) {
m_asmcall (0xC0A88D)
short[1] textptr
short[0] textptr
}
// Calls a text script (Immediately, don't respect door transitions)
command m_textcall2(textptr) {
m_asmcall (0xC0A8A0)
short[1] textptr
short[0] textptr
}
// Deletes the entity and ends the script
command m_destroy_self {
m_asmcall (0xC020F1)
m_end
}
// Perform a screen fade-in
command m_fadein(amount, speed) {
m_asmcall (0xC09FAE)
byte amount
byte speed
}
// Perform a screen fade-out
command m_fadeout(amount, speed) {
m_asmcall (0xC09FBB)
byte amount
byte speed
}
// Perform a screen fade-in with mosaic
command m_mosaic_in(amount, speed, bgs) {
m_asmcall (_ASM_mosaicin)
short amount
short speed
short bgs
}
// Perform a screen fade-out with mosaic
command m_mosaic_out(amount, speed, bgs) {
m_asmcall (0xC0AA07)
short amount
short speed
short bgs
}
// Creates a new entity with spritegroup `spr` and script `scr`
command m_create_entity(spr, scr) {
m_asmcall (0xC0A98B)
short spr
short scr
}
// Sets the entity's surface flags
command m_set_surface_flags(flags) {
m_asmcall (0xC0A679)
byte flags
}
// Sets the entity's movement speed
command m_set_speed(speed) {
m_asmcall (0xC0A685)
short speed
}
// Sets the entity's facing direction to `dir` and animation frame to `anim`
command m_set_facing_anim(facing, anim) {
m_asmcall (0xC0AA6E)
byte facing
byte anim
}
// Sets the entity's facing direction to `dir`
command m_set_facing(dir) {
m_set_result (dir)
m_set_facing_to_result
}
// Reverses the entity's facing direction
command m_reverse_facing {
m_get_facing
m_asmcall (0xC46B37)
m_set_facing_to_result
}
// Makes the entity start walking towards a certain direction
command m_start_walk(dir) {
m_set_result (dir)
m_asmcall (0xC0C83B)
}
// Makes the entity walk a certain number of pixels with its current facing direction
command m_walk_pixels(pixels) {
m_asmcall (0xC0A6A2)
short pixels
}
// Sets the event flag `flagid`
command m_set_flag(flagid) {
m_set_result (1)
m_asmcall (0xC0A857)
short flagid
}
// Unsets the event flag `flagid`
command m_unset_flag(flagid) {
m_set_result (0)
m_asmcall (0xC0A857)
short flagid
}
// Stores the state of event flag `flagid` into the RESULT REGISTER
command m_get_flag(flagid) {
m_asmcall (0xC0A84C)
short flagid
}
// Plays a sound effect
command m_sound(snd) {
m_asmcall (0xC0A841)
short snd
}
// Instantly teleports the entity to the location of the party character `pc`
command m_warp_to_pc(pc) {
m_asmcall (0xC0A864)
byte pc
}
// Instantly teleports the entity to the location of the party character `pc`
command m_warp_to_leader {
m_warp_to_pc (-1)
}
// Instantly teleports the entity to the location of another entity with spritegroup `spr`
command m_warp_to_sprite(spr) {
m_asmcall (0xC0A86F)
short spr
}
// Sets the "anchor point" for new entities/npcs created with CCScript control codes "[1F 15]" and "[1F 17]"
command m_set_new_entity_spawn_pos(x, y, dir) {
m_asmcall (0xC0A912)
short x
short y
byte dir
}
// Same as `m_set_new_sprite_spawn_pos`, but the coordinates are taken from the party leader
command m_set_new_entity_spawn_pos_from_leader {
m_set_result (1)
m_asmcall (0xC46DAD)
}
// Same as `m_set_new_sprite_spawn_pos`, but the coordinates are taken from the current entity
command m_set_new_entity_spawn_pos_from_self {
m_set_result (0)
m_asmcall (0xC46DAD)
}
// Same as `m_set_new_sprite_spawn_pos`, but the coordinates are taken from a "preset warp" (as used with CCScript control code `warp`)
command m_set_new_entity_spawn_pos_from_warp(warpid) {
m_set_result (warpid)
m_asmcall (0xC46DE5)
}
// Makes the entity face its destination
command m_face_dest {
m_asmcall (0xC46ADB) // Get angle towards destination
m_asmcall (0xC46B0A) // Set movement direction based on angle. Returns the direction
m_asmcall (0xC0A65F) // Set facing direction
}
// Makes the entity move towards (px, py), halting script execution until reaching the destination
command m_move_until_at(px, py) {
m_default_dest_radius
m_set_dest_pos (px, py)
m_walk_to_dest
}
// Same as `m_move_until_at`, but with a configurable detection radius
command m_move_until_at_radius(px, py, radius) {
m_set_dest_radius (radius)
m_set_dest_pos (px, py)
m_walk_to_dest
}
// Sets the entity's destination to (px, py)
command m_set_dest_pos(px, py) {
m_set_var6 (px)
m_set_var7 (py)
}
// Sets the entity's destination to NPC `npc`
command m_set_dest_npc(npc) {
m_asmcall (0xC0A92D)
short npc
}
// Sets the entity's destination to sprite with spritegroup `spr`
command m_set_dest_sprite(spr) {
m_asmcall (0xC0A938)
short spr
}
// Sets the entity's destination to party character `pc`
command m_set_dest_pc(pc) {
m_asmcall (0xC0A943)
byte pc
}
// Sets the entity's destination to the party leader
command m_set_dest_leader {
m_asmcall (0xC46B65)
}
// Sets the entity's destination to the party "tail"
command m_set_dest_party_tail {
m_set_dest_pc (-2)
}
// Sets the destination radius check to the lowest possible value, taking into account the entity's movement speed
command m_default_dest_radius {
m_asmcall (_ASM_ceilspeed)
m_rtovar5
}
// Sets the destination radius check to `radius`
command m_set_dest_radius(radius) {
m_asmcall (_ASM_ceilspeed)
m_asmcall (_ASM_mathmax)
short radius
m_rtovar5
}
// Makes the entity move towards its current destination, halting script execution until reaching the destination
command m_walk_to_dest {
m_jsl (_SCR_moveuntilatdest)
}
// Makes the entity walk next to the party leader, halting script execution until reaching the destination
command m_walk_to_leader {
m_set_dest_radius (17)
m_set_dest_leader
m_walk_to_dest
}
// Initializes a basic moving entity with movement speed `speed`
command m_init_basic_moving(speed) {
m_set_result (speed)
m_jsl (_SCR_initbasicmoving)
}
// Waits until a screen fade is done
command m_wait_fade m_jsl (_SCR_waitfade)
// Halts script execution until the player touches the entity
command m_wait_until_touch m_jsl (_SCR_waituntiltouch)
// Makes the entity invisible
command m_make_invisible m_set_anim (-1)
// Unlocks the text (CCScript) script after a "[1F 61]" control code
command m_unlock_text m_set_mem16 (0x9641, 1)
// Copies the entity's (X, Y) position to (VAR0, VAR1)
command m_copy_xy_to_var01 m_asmcall (0xC46C45)
// Instantly teleports the entity to its destination
command m_warp_to_dest m_asmcall (0xC46C87)
// Sets the entity's movement speed to the RESULT register
command m_set_speed_to_result m_asmcall (0xC0A68B)
// Stores the entity's facing direction into the RESULT register
command m_get_facing m_asmcall (0xC0A673)
// Sets the entity's facing direction from the RESULT register
command m_set_facing_to_result m_asmcall (0xC0A66D)
// Returns a random number in range 0..255
command m_rand m_asmcall (0xC08E9A)
// Returns a random number in range 0..3
command m_rand_mod4 m_asmcall (0xC0A633)
// Returns a random number in range 0..7
command m_rand_mod8 m_asmcall (0xC0A63B)
// Returns `rand() << 8`, useful for getting a random angle value
command m_get_random_angle m_asmcall (0xC09FA8)
// Sets the RESULT register to the distance between the entity and the party leader
command m_get_distance_from_player m_asmcall (_ASM_distancefromplayer)
// Disables collisions with the entity
command m_disable_collision m_asmcall (0xC0A82F)
// Enables collisions with the entity
command m_enable_collision m_asmcall (0xC0A838)
// Calls the NPC's primary text script (undefined behavior if entity is not an NPC)
command m_call_npc_text m_asmcall (0xC4681A)
// Forces a refresh on the entity's graphics (use current frame)
// Well, I could just use 0xC0A480, but that does unnecessary setup wasting 33 cycles
command m_refresh_graphics m_asmcall (_ASM_refreshgraphics)
// Forces a refresh on the entity's graphics (use frame 0)
command m_refresh_graphics_frame0 m_asmcall (0xC0A4BF)
// Forces a refresh on the entity's graphics (use frame 1)
command m_refresh_graphics_frame1 m_asmcall (0xC0A4B2)
// Stores the entity's movement speed into the RESULT register
command m_get_speed m_asmcall (0xC0A691)
//*****************************************************************************
// PRIVATE ASM ROUTINES
//*****************************************************************************
//----- Local variables ------//
define _scr_ptr = 0x80
define _scr_stack = 0x84
// 0x86 is never used. It's probably safe to use, but I'm not risking it.
define _obj_offset = 0x88
define _scr_offset = 0x8A
define _temp1 = 0x8C
define _temp2 = 0x8E
define _temp3 = 0x90
define _temp4 = 0x92
define _pc = 0x94 // $94 should not be used as a temp, it's already used to hold the "program counter" of the movement script
define _temp5 = 0x96
define _temp6 = 0x98
define _temp7 = 0x9A
define _temp8 = 0x9C
define _temp9 = 0x9E
define self_offset = 0x1A44
define camera_x = 0x9877
define camera_y = 0x987B
//----- Script variables ------//
define SCR_stack_offset = 0x12E6
define SCR_pc = 0x13FE // Script program counter
define SCR_pb = 0x148A // Script program bank
define SCR_result = 0x1516
//----- Entity variables ------//
define OBJ_map_x = 0x0B8E
define OBJ_map_y = 0x0BCA
define OBJ_vel_x = 0x0CF6
define OBJ_vel_y = 0x0D32
define OBJ_vel_xf = 0x0DAA
define OBJ_vel_yf = 0x0DE6
define OBJ_anim_frame = 0x10F2
define OBJ_speed = 0x2B32
//----- MACROS ------//
// Ceils an 8.8 fixed-point number that's loaded in the accumulator
// Bit magic suggested by Alcaro on SnesLab ( https://canary.discord.com/channels/485971752992636929/486247695795879946/865718830603829248 )
command _CEIL {
DEC
ORA_i (0x00FF)
INC
}
// Fetch byte and increment PC (8-bit accumulator)
command _READBYTE8 {
LDA_dly (_scr_ptr)
INY
}
// Fetch byte and increment PC (16-bit accumulator)
command _READBYTE {
_READBYTE8
AND_i (0x00FF)
}
// Fetch word and increment PC
command _READWORD {
LDA_dly (_scr_ptr)
INY
INY
}
//----- Routines ------//
// Return distance from player
_ASM_distancefromplayer: {
LDX_d (_obj_offset)
LDA_a (camera_x)
SEC
SBC_x (OBJ_map_x)
BPL (4)
EOR_i (0xFFFF)
INC
STA_d (_temp1)
LDA_a (camera_y)
SEC
SBC_x (OBJ_map_y)
BPL (4)
EOR_i (0xFFFF)
INC
CLC
ADC_d (_temp1)
// return abs(OBJ_map_x - camera_x) + abs(OBJ_map_y - camera_y)
RTL
}
// return (result > argument)
_ASM_resultgreater: {
_READWORD
STY_d (_pc)
CMP_x (SCR_result)
LDA_i (0) // FALSE
BCS (1)
INC // TRUE
RTL
}
// ceil(movespeed)
_ASM_ceilspeed: {
LDX_d (_obj_offset)
LDA_x (OBJ_speed)
_CEIL
XBA
AND_i (0x00FF)
RTL
}
// max(result, value)
_ASM_mathmax: {
_READWORD
STY_d (_pc)
CMP_x (SCR_result)
BCS (3)
LDA_x (SCR_result) // 3 bytes
RTL
}
_ASM_mosaicin: {
_READWORD
PHA
_READWORD
TAX
_READWORD
STY_d (_pc)
TAY
PLA
JML (0xC087CE)
}
// Refresh graphics using current animation frame
_ASM_refreshgraphics: {
LDY_d (_obj_offset)
LDA_y (OBJ_anim_frame)
STA_a (0x2892)
JML (0xC0A4C4)
}
//*****************************************************************************
// PRIVATE MOVEMENT SCRIPT ROUTINES
//*****************************************************************************
_SCR_moveuntilatdest: {
m_asmcall (0xC46ADB) // Get angle towards destination
m_asmcall (0xC47044) // Set velocity based on angle (does not modify RESULT)
m_asmcall (0xC46B0A) // Set movement direction based on angle
m_asmcall (0xC0A65F) // Set facing direction
m_refresh_graphics
_loop: m_pause (1) // Wait 1 frame
m_asmcall (0xC0A8DC) // Move towards destination and check for proximity
m_jeq (_loop) // Go back if still not near destination
m_zerovel // Zero velocities when done
m_rtl
}
_SCR_initbasicmoving: {
m_set_speed_to_result
m_onmove (0xA360) // Move if not blocked and update surface flags
m_set_anim (0)
m_refresh_graphics_frame0
m_task_long (MovTask_Anim16_DestroyIfFar)
m_task_long (MovTask_HandleCollision)
m_zerovel
m_rtl
}
_SCR_waituntiltouch: {
m_pause (1)
m_asmcall (0xC0D15C) // Return TRUE if this entity is colliding with the player
m_jeq (_SCR_waituntiltouch)
m_rtl
}
_SCR_waituntilnearself: {
m_pause (1)
m_asmcall (0xC46EF8) // Return TRUE if player is near self in radius (var2, var3) -- always FALSE when teleporting
m_jeq (_SCR_waituntilnearself)
m_rtl
}
_SCR_waituntilnear: {
m_pause (1)
m_asmcall (0xC46E74) // Return TRUE if player is near (var0, var1) in radius (var2, var3) -- always FALSE when teleporting
m_jeq (_SCR_waituntilnear)
m_rtl
}
_SCR_waitfade: {
m_pause (1)
m_get_mem16 (0x0028) // Screen fade amount
m_and_result (0x00FF) // 8-bit variable, so AND it with 0xFF
m_jne (_SCR_waitfade) // Go back if still fading in or out...
m_rtl
}
//*****************************************************************************
// CUSTOM MOVEMENT SCRIPT INSTRUCTION IMPLEMENTATION
//*****************************************************************************
// IMPLEMENTATION: m_task_long [31]
// $C097DC is the entry point for movement instruction 0x31
// It's safe to overwrite until $C097EE (18 bytes)
ROM[0xC097DC] = {
JSR (0x99DD) // Call the original "m_task" movement instruction implementation
// Before we continue, we need to keep this in mind:
// 1. If there are no more script slots available, "m_task" fails silently (it just advances the program counter by 2)
// 2. In case of failure, the carry flag will be set. Otherwise, it will be clear
// 3. The offset of the task script will be in X, but only if "m_task" didn't fail
BCS (5) // Just increment PC by one if "m_task" failed "BCS_a (_ret)"
// No need for 8-bit accumulator, the high byte of the program bank is completely ignored in all cases
LDA_dly (_scr_ptr) // Fetch bank byte of task address
STA_x (SCR_pb) // Store it to the program bank of the new task script
_ret: INY
RTS
// 12 out of 18 bytes
}
// IMPLEMENTATION: m_store_result [32]
// $C097EF is the entry point for movement instruction 0x32
// It's safe to overwrite until $C09801 (18 bytes)
ROM[0xC097EF] = {
TYX
LDY_d (_scr_offset)
LDA_y (SCR_result)
// Scripts have a 16 byte stack
// Our local storage will be at the absolute top of the stack, lowering the script stack capacity to 14 bytes...
LDY_i (14)
STA_diy (_scr_stack)
TXY
RTS
// 13 out of 18 bytes
}
// IMPLEMENTATION: m_load_result [33]
// $C09802 is the entry point for movement instruction 0x33
// It's safe to overwrite until $C09825 (35 bytes)
ROM[0xC09802] = {
TYX
// Scripts have a 16 byte stack
// Our local storage will be at the absolute top of the stack, lowering the script stack capacity to 14 bytes...
LDY_i (14)
LDA_diy (_scr_stack)
LDY_d (_scr_offset)
STA_y (SCR_result)
TXY
RTS
// 13 out of 35 bytes
}
// IMPLEMENTATION: m_ondestroy [34]
// $C09826 is the entry point for movement instruction 0x34
// It's safe to overwrite until $C09849 (35 bytes)
ROM[0xC09826] = {
LDX_d (_obj_offset)
_READWORD // 4 bytes
STA_x (OBJ_destructor_lo)
_READBYTE // 6 bytes
STA_x (OBJ_destructor_hi)
RTS
// 19 out of 35 bytes
}
// Patch so that the game calls the destructor when an entity is destroyed
// Initialize destructor to NULL pointer on entity creation
ROM[0xC020E7] = {
JSL (_nulldestructor)
NOP
NOP
}
_nulldestructor: {
STZ_x (OBJ_destructor_lo)
STZ_x (OBJ_destructor_hi)
// ORIGINAL CODE
STZ_x (0x2AF6)
STZ_x (0x28DA)
// ORIGINAL CODE
RTL
}
// "destroy current entity" (used by movement scripts)
/*
* C0/2109: A2 00 00 LDX #$0000
* C0/210C: A5 02 LDA $02
*/
ROM[0xC02109] = {
JSL (_calldestructor)
NOP
}
// "destroy arbitrary entity" (used by ASM)
/*
* C0/2157: A2 00 00 LDX #$0000
* C0/215A: A5 02 LDA $02
*/
ROM[0xC02157] = {
JSL (_calldestructor)
NOP
}
_calldestructor: {
LDY_d (0x0E)
LDA_y (OBJ_destructor_lo)
ORA_y (OBJ_destructor_hi)
BEQ_a (_ret) // Return if destructor is a NULL pointer
// Allocate a few local variables for the destructor
PHD
TDC
SEC
SBC_i (32) // Give DP locals $00-$1F for free to the destructor
TCD
LDA_y (OBJ_destructor_lo)
STA_a (0x00BC)
LDA_y (OBJ_destructor_hi)
STA_a (0x00BE)
TYA // Pass the entity offset to the destructor
JSL (0xC09279) // Call ASM pointer from $00BC
PLD
_ret: // ORIGINAL CODE
LDX_i (0)
LDA_d (0x02)
// ORIGINAL CODE
RTL
}
import asm65816
import movscr_codes
/*
* Movement script examples
*
* This file includes 3 simple sample scripts that you can use
* as a reference for building your very own scripts.
*
* NOTE: This file will also overwrite the "Who are you talking to?"
* text, causing it to spawn 3 sprites using these 3 sample scripts
*/
// Repoint "Who are you talking to?" text
ROM[0xC7C588] = goto(StartTests)
StartTests: {
"[1F 15 {short 206} {short DVD_LOGO} {byte 1}]" // Taxi
"[1F 15 {short 25} {short TOUCH_SOUND} {byte 1}]" // Robot
"[1F 15 {short 3} {short UNIT_TESTS} {byte 1}]" // Jeff
eob
}
//*****************************************************************************
// SAMPLE #1 (TEST)
define DVD_LOGO = 895
define next_obj_x = 0x2848 // Next frame object's X position (see $C09EFF)
define next_obj_y = 0x284A // Next frame object's Y position (see $C09EFF)
// Bounce around walls, DVD Logo style
M_DvdLogo: {
m_warp_to_leader
m_disable_collision
m_set_speed (0x0200)
m_set_anim (0)
m_refresh_graphics_frame0
m_task_long (MovTask_Anim16_DestroyIfFar)
m_get_random_angle
m_asmcall (0xC47044) // Set velocity based on angle
m_asmcall (0xC46B0A) // Set entity movement direction based on angle. Returns the direction
m_asmcall (0xC0A65F) // Set entity facing if not blocked
loop: m_asmcall (_ASM_BounceDvd) // Returns zero if there wasn't a wall hit
m_jeq (nohit)
// There was a wall hit, reverse the facing
m_reverse_facing
m_refresh_graphics
nohit: m_pause (1)
m_jmp (loop)
_ASM_BounceDvd:
JSL (0xC09F04) // Store pos+vel into $2848 and $284A
TAX // Set Z flag on accumulator value
BEQ_a (_Return) // Didn't move, return
STZ_d (0x00) // Clear "hit wall" flag
LDY_d (_obj_offset)
LDX_y (OBJ_map_y)
LDA_a (next_obj_x)
JSL (0xC05F82) // Get surface flags at coordinate
LDY_d (_obj_offset)
AND_i (0x0080) // SOLID?
BEQ_a (_CheckY) // No, check Y axis
// Yes, negate X velocities
INC_d (0x00) // Set "hit wall" flag
SEC
LDA_i (0)
SBC_y (OBJ_vel_xf)
STA_y (OBJ_vel_xf)
LDA_i (0)
SBC_y (OBJ_vel_x)
STA_y (OBJ_vel_x)
_CheckY:
LDX_a (next_obj_y)
LDA_y (OBJ_map_x)
JSL (0xC05F82)
AND_i (0x0080)
BEQ_a (_ReturnValue)
INC_d (0x00) // Set "hit wall" flag
LDY_d (_obj_offset)
SEC
LDA_i (0)
SBC_y (OBJ_vel_yf)
STA_y (OBJ_vel_yf)
LDA_i (0)
SBC_y (OBJ_vel_y)
STA_y (OBJ_vel_y)
_ReturnValue:
LDA_d (0x00)
_Return:
RTL
}
//*****************************************************************************
// SAMPLE #2 (TOUCH SOUND)
define TOUCH_SOUND = 896
M_TouchSound: {
m_warp_to_leader
m_add_xpos (-32)
m_add_ypos (32)
m_set_anim (0)
m_refresh_graphics_frame0
m_task_long (MovTask_Anim16_DestroyIfFar)
loop: m_wait_until_touch
m_sound (73) // "ping" sound (Sky Runner and Devil's Machine shield)
m_pause (32)
m_jmp (loop)
}
//*****************************************************************************
// SAMPLE #3 (UNIT TESTS)
// Very limited tests, only checks the "happy path"
define UNIT_TESTS = 897
M_UnitTests: {
m_warp_to_leader
m_add_xpos (32)
m_add_ypos (32)
m_set_anim (0)
m_refresh_graphics_frame0
m_task_long (MovTask_Anim16_DestroyIfFar)
m_ondestroy (_ASM_TestDestructor)
m_wait_until_touch
m_textcall2 (STR_StartingTests)
m_jsr (SUB_TestResultGreater)
m_jeq (l1) // Skip m_choose_random if m_result_greater failed
m_jsr (SUB_TestChooseRandom)
l1: m_jsr (SUB_TestFade)
m_jsr (SUB_TestStorage)
loop: m_set_dest_leader
m_face_dest
m_refresh_graphics
m_pause (8)
m_jmp (loop)
_ASM_TestDestructor:
MText(_STR_Destructor)
RTL
_STR_Destructor:
window_open(1)
"@Test destructor called!" wait
window_closetop eob
}
TestFailed: {
FAILED
m_rts
}
//*****************************************************************************
// TEST m_result_greater
//*****************************************************************************
SUB_TestResultGreater: {
m_textcall2 (STR_ResultGreater)
m_set_result (1234)
m_result_greater (1233)
m_jeq (TestFailed)
m_set_result (1234)
m_result_greater (1234)
m_jne (TestFailed)
m_set_result (1234)
m_result_greater (1235)
m_jne (TestFailed)
SUCCESS
m_rts
}
//*****************************************************************************
// TEST m_choose_random
//*****************************************************************************
// Because of the random nature, I'm only testing if the chosen number is in range
SUB_TestChooseRandom: {
m_textcall2 (STR_ChooseRandom)
m_loop (200) // 200 iterations. This is because of the random nature of the test
m_choose_random_2 (0, 1)
m_result_greater (1)
m_jne (TestFailed)
m_choose_random_3 (0, 1, 2)
m_result_greater (2)
m_jne (TestFailed)
m_choose_random_4 (0, 1, 2, 3)
m_result_greater (3)
m_jne (TestFailed)
m_choose_random_5 (0, 1, 2, 3, 4)
m_result_greater (4)
m_jne (TestFailed)
m_choose_random_6 (0, 1, 2, 3, 4, 5)
m_result_greater (5)
m_jne (TestFailed)
m_choose_random_7 (0, 1, 2, 3, 4, 5, 6)
m_result_greater (6)
m_jne (TestFailed)
m_choose_random_8 (0, 1, 2, 3, 4, 5, 6, 7)
m_result_greater (7)
m_jne (TestFailed)
m_endloop
SUCCESS
m_rts
}
//*****************************************************************************
// TEST m_fadein, m_wait_fade, m_fadeout and m_mosaic_out
//*****************************************************************************
SUB_TestFade: {
m_textcall2 (STR_Fade)
m_fadeout (1, 2)
m_wait_fade
m_fadein (1, 2)
m_wait_fade
m_pause (30)
m_mosaic_out (1, 2, 0x03)
m_mosaic_in (1, 2, 0x03)
SUCCESS
m_rts
}
//*****************************************************************************
// TEST m_store_result, m_load_result
//*****************************************************************************
SUB_TestStorage: {
m_textcall2 (STR_Storage)
m_set_result (1234)
m_store_result // Store 1234
m_set_result (5678) // Set the result to something else
m_jsr (dummy1) // Mess around with the stack...
m_load_result // Load 1234 back
m_add_result (-1234)
m_jne (TestFailed) // Fail if 1234-1234 != 0
SUCCESS
m_rts
// Mess aorund with the stack
dummy1: m_jsl (dummy2)
m_rts
dummy2: m_loop (10)
m_endloop
m_rtl
}
//*****************************************************************************
// STRINGS
//*****************************************************************************
STR_StartingTests: window_open(1) "@Starting tests..." newline end
STR_ResultGreater: window_open(1) "@m-result-greater..." newline eob
STR_ChooseRandom: window_open(1) "@m-choose-random..." newline eob
STR_Fade: window_open(1) "@m-fade..." newline eob
STR_Storage: window_open(1) "@m-store-result..." newline eob
STR_Success: "@Success." wait window_closetop eob
STR_Failed: "@Failed." wait window_closetop eob
//*****************************************************************************
// VARIOUS HELPER MACROS
//*****************************************************************************
command SUCCESS {
m_textcall2 (STR_Success)
m_set_result (1)
}
command FAILED {
m_textcall2 (STR_Failed)
m_set_result (0)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment