-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathSMW-Utils-Script-Snes9x.lua
1097 lines (970 loc) · 38.4 KB
/
SMW-Utils-Script-Snes9x.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
--[[
Super Mario World (U) Utility Script for Snes9x
https://code.google.com/p/snes9x-rr/
Edited by BrunoValads, checked in snes9x-rr 1.51 v7
http://github.com/brunovalads/smw-stuff
Original script by gocha (with Mr.'s contribution),
checked using snes9x-rr 1.43 v17 svn
=== Cheat Keys ===
Level:
up+select Increment powerup (0 - 255) \
down+select Decrement powerup | hold to
up+L Increment item box (0 - 255) | change quickly
down+L Decrement item box /
L+A Change move method (normal.P-meter,free)
pause+select Exit the current level, with activating the next level
pause+(A+select) Exit the current level, with activating the next level (secret goal)
pause+(B+select) Exit the current level, without activating the next level
Note: color blocks can't be activated if you beat a switch palace with the pause-exit cheat.
=== Other Features ===
- Cut powerup / powerdown animation
- Display P-meter, sprite info, and some other useful info
All of these features can be enabled / disabled by modifying the following option settings.
]]--
-- option start here >>
local smwRegularDebugFuncOn = true
local smwCutPowerupAnimationOn = false
local smwCutPowerdownAnimationOn = false
local smwDragAndDropOn = false
local smwPowerChangeOn = true
local smwItemBoxChangeOn = true
local smwMoveMethodChangeOn = false
-- Move speed definitions for free move mode.
-- I guess you usually won't need to modify these.
local smwFreeMoveSpeed = 2.0 -- px/f
local smwFreeMoveSpeedupMax = 4.0 -- px/f
local smwFreeMovePMeterLength = 1 -- frame(s)
local guiOpacity = 0.8
local showPMeter = false
local showSpriteInfo = true
local showMainInfo = true
local showYoshiInfo = true
local showBounceSpriteInfo = false
local showExtSpriteInfo = false
local showSpeedVector = false
-- << options end here
-- [ generic utility functions ] -----------------------------------------------
if not emu then
error("This script runs under snes9x")
end
if not bit then
require("bit")
end
-- [ gameemu lua utility functions ] -------------------------------------------
function gui.edgelessbox(x1, y1, x2, y2, colour)
gui.line(x1+1, y1, x2-1, y1, colour) -- top
gui.line(x2, y1+1, x2, y2-1, colour) -- right
gui.line(x1+1, y2, x2-1, y2, colour) -- bottom
gui.line(x1, y1+1, x1, y2-1, colour) -- left
end
local pad_max = 2
local pad_press, pad_down, pad_up, pad_prev, pad_send = {}, {}, {}, {}, {}
local pad_presstime = {}
for player = 1, pad_max do
pad_press[player] = {}
pad_presstime[player] = { start=0, select=0, up=0, down=0, left=0, right=0, A=0, B=0, X=0, Y=0, L=0, R=0 }
end
local dev_press, dev_down, dev_up, dev_prev = input.get(), {}, {}, {}
local dev_presstime = {
xmouse=0, ymouse=0, leftclick=0, rightclick=0, middleclick=0,
shift=0, control=0, alt=0, capslock=0, numlock=0, scrolllock=0,
["0"]=0, ["1"]=0, ["2"]=0, ["3"]=0, ["4"]=0, ["5"]=0, ["6"]=0, ["7"]=0, ["8"]=0, ["9"]=0,
A=0, B=0, C=0, D=0, E=0, F=0, G=0, H=0, I=0, J=0, K=0, L=0, M=0, N=0, O=0, P=0, Q=0, R=0, S=0, T=0, U=0, V=0, W=0, X=0, Y=0, Z=0,
F1=0, F2=0, F3=0, F4=0, F5=0, F6=0, F7=0, F8=0, F9=0, F10=0, F11=0, F12=0,
F13=0, F14=0, F15=0, F16=0, F17=0, F18=0, F19=0, F20=0, F21=0, F22=0, F23=0, F24=0,
backspace=0, tab=0, enter=0, pause=0, escape=0, space=0,
pageup=0, pagedown=0, ["end"]=0, home=0, insert=0, delete=0,
left=0, up=0, right=0, down=0,
numpad0=0, numpad1=0, numpad2=0, numpad3=0, numpad4=0, numpad5=0, numpad6=0, numpad7=0, numpad8=0, numpad9=0,
["numpad*"]=0, ["numpad+"]=0, ["numpad-"]=0, ["numpad."]=0, ["numpad/"]=0,
tilde=0, plus=0, minus=0, leftbracket=0, rightbracket=0,
semicolon=0, quote=0, comma=0, period=0, slash=0, backslash=0
}
-- scan button presses
function scanJoypad()
for i = 1, pad_max do
pad_prev[i] = copytable(pad_press[i])
pad_press[i] = joypad.get(i)
pad_send[i] = copytable(pad_press[i])
-- scan keydowns, keyups
pad_down[i] = {}
pad_up[i] = {}
for k in pairs(pad_press[i]) do
pad_down[i][k] = (pad_press[i][k] and not pad_prev[i][k])
pad_up[i][k] = (pad_prev[i][k] and not pad_press[i][k])
end
-- count press length
for k in pairs(pad_press[i]) do
if not pad_press[i][k] then
pad_presstime[i][k] = 0
else
pad_presstime[i][k] = pad_presstime[i][k] + 1
end
end
end
end
-- scan keyboard/mouse input
function scanInputDevs()
dev_prev = copytable(dev_press)
dev_press = input.get()
-- scan keydowns, keyups
dev_down = {}
dev_up = {}
for k in pairs(dev_presstime) do
dev_down[k] = (dev_press[k] and not dev_prev[k])
dev_up[k] = (dev_prev[k] and not dev_press[k])
end
-- count press length
for k in pairs(dev_presstime) do
if not dev_press[k] then
dev_presstime[k] = 0
else
dev_presstime[k] = dev_presstime[k] + 1
end
end
end
-- send button presses
function sendJoypad()
for i = 1, pad_max do
joypad.set(i, pad_send[i])
end
end
-- [ game-specific utility functions ] -----------------------------------------
local moveMethod_normal = 0
local moveMethod_pmeter = 1
local moveMethod_free = 2
local moveMethod_max = 3
local smwMoveMethod = moveMethod_normal
local smwFreeMovePMeter = 0
-- game
local RAM_frameCount = 0x7e0013
local RAM_frameCountAlt = 0x7e0014
local RAM_gameMode = 0x7e0100
local RAM_cameraX = 0x7e001a
local RAM_cameraY = 0x7e001c
local RAM_RNG = 0x7e148d
-- player
local RAM_xPos = 0x7e0094
local RAM_yPos = 0x7e0096
local RAM_xSubPos = 0x7e13da
local RAM_ySubPos = 0x7e13dc
local RAM_xSpeed = 0x7e007b
local RAM_ySpeed = 0x7e007d
local RAM_xSubSpeed = 0x7e007a
local RAM_player = 0x7e0db3
local RAM_powerup = 0x7e0019
local RAM_pMeter = 0x7e13e4
local RAM_takeOffMeter = 0x7e149f
local RAM_starInvCount = 0x7e1490
local RAM_hurtInvCount = 0x7e1497
local RAM_facingDirection = 0x7e0076
local RAM_flightAnimation = 0x7e1407
local RAM_capeSlowFallCount = 0x7e14a5
local RAM_capeSpinTimer = 0x7e14a6
local RAM_movement = 0x7e0071
local RAM_lockSpritesTimer = 0x7e009d
local RAM_marioFrameCount = 0x7e1496
local RAM_blockedStatus = 0x7e0077
local RAM_itemBox = 0x7e0dc2
local RAM_ropeClimbingFlag = 0x7e18be
-- sprites
local RAM_sprite_stunTimer = 0x7e1540
-- bounce sprites
local RAM_bouncesprite_Number = 0x7e1699
local RAM_bouncesprite_xPosHigh = 0x7e16ad
local RAM_bouncesprite_xPosLow = 0x7e16a5
local RAM_bouncesprite_yPosHigh = 0x7e16a9
local RAM_bouncesprite_yPosLow = 0x7e16a1
local RAM_bouncesprite_timer = 0x7e16c5
local RAM_bouncesprite_lastId = 0x7e18cd
local RAM_turnBlockSpinning = 0x7e18ce
-- extended sprites
local RAM_extsprite_number = 0x7e170b
local RAM_extsprite_xPosHigh = 0x7e1733
local RAM_extsprite_xPosLow = 0x7e171f
local RAM_extsprite_yPosHigh = 0x7e1729
local RAM_extsprite_yPosLow = 0x7e1715
local RAM_extsprite_xSpeed = 0x7e1747
local RAM_extsprite_ySpeed = 0x7e173d
local RAM_extsprite_xSub = 0x7e175b
local RAM_extsprite_ySub = 0x7e1751
local RAM_extsprite_table = 0x7e1765
local RAM_extsprite_table2 = 0x7e176f
-- yoshi
-- timers
local RAM_bluePOW = 0x7e14ad
local RAM_grayPOW = 0x7e14ae
local RAM_multipleCoinBlockTimer = 0x7e186b
local RAM_directionalCoinTimer = 0x7e190c
local RAM_pBalloonTimer = 0x7e1891
local RAM_pBalloonTimerAlt = 0x7e13f3
-- cheats
local RAM_frozen = 0x7e13fb
local RAM_paused = 0x7e13d4
local RAM_levelIndex = 0x7e13bf
local RAM_levelFlagTable = 0x7e1ea2
local RAM_typeOfExit = 0x7e0dd5
local RAM_midwayPoint = 0x7e13ce
local RAM_activateNextLevel = 0x7e13ce
local pMeter_max = 112
local takeOffMeter_max = 80
local gameMode_ow = 14
local gameMode_level = 20
local smwSpriteMaxCount = 12
local smwBounceSpriteMaxCount = 4
local smwExtSpriteMaxCount = 10
local smwPlayerPrev, smwPlayer, smwPlayerChanged
local smwGameModePrev, smwGameMode, smwGameModeChanged
local smwPausePrev, smwPause, smwPauseChanged
local smwMovementPrev, smwMovement, smwMovementChanged
-- scan some parameters that control behavior of the script
function smwScanStatus()
smwPlayerPrev = smwPlayer
smwPlayer = memory.readbyte(RAM_player) + 1
smwPlayerChanged = (smwPlayer ~= smwPlayerPrev)
smwGameModePrev = smwGameMode
smwGameMode = memory.readbyte(RAM_gameMode)
smwGameModeChanged = (smwGameMode ~= smwGameModePrev)
smwPausePrev = smwPause
smwPause = (memory.readbyte(RAM_paused) ~= 0)
smwPauseChanged = (smwPause ~= smwPausePrev)
smwMovementPrev = smwMovement
smwMovement = memory.readbyte(RAM_movement)
smwMovementChanged = (smwMovement ~= smwMovementPrev)
end
-- increment powerup
function smwDoPowerUp()
local powerup = memory.readbyte(RAM_powerup)
memory.writebyte(RAM_powerup, powerup + 1)
end
-- decrement powerup
function smwDoPowerDown()
local powerup = memory.readbyte(RAM_powerup)
memory.writebyte(RAM_powerup, powerup - 1)
end
-- increment item box
function smwIncItemBox()
local itemBox = memory.readbyte(RAM_itemBox)
memory.writebyte(RAM_itemBox, itemBox + 1)
end
-- decrement item box
function smwDecItemBox()
local itemBox = memory.readbyte(RAM_itemBox)
memory.writebyte(RAM_itemBox, itemBox - 1)
end
-- set move method
function smwSetMoveMethod(mode)
-- cleanups
if smwMoveMethod == moveMethod_free then
memory.writebyte(RAM_frozen, 0) -- unfreeze Mario
end
-- apply new method
smwMoveMethod = mode % moveMethod_max
end
-- normal move
function smwMoveNormalProc()
-- do nothing
end
-- P-meter mode
function smwMovePMeterProc()
memory.writebyte(RAM_pMeter, pMeter_max)
memory.writebyte(RAM_takeOffMeter, takeOffMeter_max+1)
end
-- count own pmeter for
function smwFreeMovePMeterCount()
local move = pad_press[smwPlayer].left or pad_press[smwPlayer].right
or pad_press[smwPlayer].up or pad_press[smwPlayer].down
-- count up own P-meter
if pad_press[smwPlayer].Y and move then
if smwFreeMovePMeter < smwFreeMovePMeterLength then
smwFreeMovePMeter = smwFreeMovePMeter + 1
end
else
smwFreeMovePMeter = 0
end
end
-- Free move mode
function smwMoveFreeProc()
local x = memory.readword(RAM_xPos) + (memory.readbyte(RAM_xSubPos)/256.0)
local y = memory.readword(RAM_yPos) + (memory.readbyte(RAM_ySubPos)/256.0)
local speed, xv, yv = 0.0, 0.0, 0.0
-- calc Mario's new position
smwFreeMovePMeterCount()
speed = smwFreeMoveSpeed + (smwFreeMoveSpeedupMax * smwFreeMovePMeter / smwFreeMovePMeterLength)
if pad_press[smwPlayer].left then xv = xv - speed end
if pad_press[smwPlayer].right then xv = xv + speed end
if pad_press[smwPlayer].up then yv = yv - speed end
if pad_press[smwPlayer].down then yv = yv + speed end
-- freeze Mario
if smwMovement == 0 then
memory.writebyte(RAM_frozen, 1)
memory.writebyte(RAM_xSpeed, 0)
memory.writebyte(RAM_ySpeed, 0)
x, y = x + xv, y + yv
-- but animate sprites
memory.writebyte(RAM_frameCountAlt, (memory.readbyte(RAM_frameCountAlt) + 1) % 256)
else
memory.writebyte(RAM_frozen, 0)
end
-- make him invulnerable
memory.writebyte(RAM_hurtInvCount, 127)
-- manipulate Mario's position
memory.writeword(RAM_xPos, math.floor(x))
memory.writeword(RAM_yPos, math.floor(y))
memory.writeword(RAM_xSubPos, math.floor(x*16)%16*16)
memory.writeword(RAM_ySubPos, math.floor(y*16)%16*16)
end
local smwMoveMethodProc = { smwMoveNormalProc, smwMovePMeterProc, smwMoveFreeProc }
-- force allow escape
local smwForceSecretExit = false
local smwModExitStatus = false
function smwAllowEscape()
if smwPause and pad_down[smwPlayer].select then
local levelIndex = memory.readbyte(RAM_levelIndex)
local levelFlag = memory.readbyte(RAM_levelFlagTable + levelIndex)
-- save midway point flag
if memory.readbyte(RAM_midwayPoint) == 1 then
memory.writebyte(RAM_levelFlagTable + levelIndex, bit.bor(levelFlag, 0x40))
end
-- exit the level force
memory.writebyte(RAM_levelFlagTable + levelIndex, bit.bor(levelFlag, 0x80))
smwForceSecretExit = pad_press[smwPlayer].A
-- activate next stage (destination must be written by smwRewriteOnPauseExit())
-- pressing B will cancel this effect (exit without activate the next level)
if not pad_press[smwPlayer].B then
memory.writebyte(RAM_activateNextLevel, 1)
else
memory.writebyte(RAM_activateNextLevel, 0)
end
smwModExitStatus = true
end
end
-- allow start+select to beat the level
function smwRewriteOnPauseExit()
if not smwModExitStatus then return end
if memory.readbyte(RAM_typeOfExit) == 0x80 and memory.readbyte(RAM_activateNextLevel) == 1 then
if smwForceSecretExit then
memory.writebyte(RAM_typeOfExit, 2)
else
memory.writebyte(RAM_typeOfExit, 1)
end
smwModExitStatus = false
end
end
-- cut powerup animation
function smwCutPowerupAnimation()
if smwMovement == 2 then -- super
memory.writebyte(RAM_lockSpritesTimer, 0)
memory.writebyte(RAM_movement, 0)
memory.writebyte(RAM_marioFrameCount, 0)
smwDoPowerUp() -- don't know why script needs to process it, anyway
elseif smwMovement == 3 then -- cape
memory.writebyte(RAM_lockSpritesTimer, 0)
memory.writebyte(RAM_movement, 0)
memory.writebyte(RAM_marioFrameCount, 0)
elseif smwMovement == 4 then -- fire
memory.writebyte(RAM_lockSpritesTimer, 0)
memory.writebyte(RAM_movement, 0)
memory.writebyte(RAM_marioFrameCount, 0)
memory.writebyte(0x7e149b, 0)
end
end
-- cut powerdown animation
function smwCutPowerdownAnimation()
if smwMovement == 1 then -- powerdown
memory.writebyte(RAM_lockSpritesTimer, 0)
memory.writebyte(RAM_marioFrameCount, 0) -- stops 1 frame, but who cares
-- memory.writebyte(RAM_movement, 0)
-- memory.writebyte(RAM_hurtInvCount, 127)
end
end
local smwDragTargets = {}
function smwDragAndDrop()
if dev_press["leftclick"] then
local x, y = dev_press["xmouse"], dev_press["ymouse"]
local cameraX = memory.readwordsigned(RAM_cameraX)
local cameraY = memory.readwordsigned(RAM_cameraY)
for id = 0, smwSpriteMaxCount - 1 do
local stat = memory.readbyte(0x7e14c8+id)
local spriteX = memory.readbytesigned(0x7e14e0+id) * 0x100 + memory.readbyte(0x7e00e4+id)
local spriteY = memory.readbytesigned(0x7e14d4+id) * 0x100 + memory.readbyte(0x7e00d8+id)
if stat ~= 0 then
local width, height = 16, 16
local spriteRect = {
left = spriteX - cameraX,
top = spriteY - cameraY,
right = spriteX - cameraX + width,
bottom = spriteY - cameraY + height
}
if smwDragTargets[id] or (x >= spriteRect.left and x <= spriteRect.right and
y >= spriteRect.top and y <= spriteRect.bottom) then
local spriteX = cameraX + x - 8
local spriteY = cameraY + y - 8
memory.writebyte(0x7e14e0+id, math.floor(spriteX / 0x100))
memory.writebyte(0x7e00e4+id, spriteX % 0x100)
memory.writebyte(0x7e14d4+id, math.floor(spriteY / 0x100))
memory.writebyte(0x7e00d8+id, spriteY % 0x100)
smwDragTargets[id] = true
end
end
end
end
if dev_up["leftclick"] then
-- for id = 0, smwSpriteMaxCount - 1 do
-- if smwDragTargets[id] then
-- memory.writebyte(0x7e00b6+id, 0)
-- memory.writebyte(0x7e00aa+id, 0)
-- end
-- end
smwDragTargets = {}
end
end
function getSpriteByMousePos(x, y)
local spriteId = nil
local cameraX = memory.readwordsigned(RAM_cameraX)
local cameraY = memory.readwordsigned(RAM_cameraY)
for id = 0, smwSpriteMaxCount - 1 do
local stat = memory.readbyte(0x7e14c8+id)
local spriteX = memory.readbytesigned(0x7e14e0+id) * 0x100 + memory.readbyte(0x7e00e4+id)
local spriteY = memory.readbytesigned(0x7e14d4+id) * 0x100 + memory.readbyte(0x7e00d8+id)
if stat ~= 0 then
local width, height = 16, 16
local spriteRect = {
left = spriteX - cameraX,
top = spriteY - cameraY,
right = spriteX - cameraX + width,
bottom = spriteY - cameraY + height
}
if x >= spriteRect.left and x <= spriteRect.right and
y >= spriteRect.top and y <= spriteRect.bottom then
spriteId = id
break
end
end
end
return spriteId
end
-- [ game-specific main ] ------------------------------------------------------
local preventItemPopup = false
-- apply various cheats that work in Level
function smwApplyLevelCheats()
if smwRegularDebugFuncOn then
-- power-ups
if smwPowerChangeOn then
if not smwPause and pad_press[smwPlayer].up and pad_down[smwPlayer].select then -- increment
smwDoPowerUp()
preventItemPopup = true
elseif not smwPause and pad_press[smwPlayer].up and pad_press[smwPlayer].select and pad_presstime[smwPlayer].select >= 50 then -- increment quickly
smwDoPowerUp()
end
if not smwPause and pad_press[smwPlayer].down and pad_down[smwPlayer].select then -- decrement
smwDoPowerDown()
preventItemPopup = true
elseif not smwPause and pad_press[smwPlayer].down and pad_press[smwPlayer].select and pad_presstime[smwPlayer].select >= 50 then -- decrement quickly
smwDoPowerDown()
end
end
-- item box
if smwItemBoxChangeOn then
if not smwPause and pad_press[smwPlayer].up and pad_down[smwPlayer].L then -- increment
smwIncItemBox()
elseif not smwPause and pad_press[smwPlayer].up and pad_press[smwPlayer].L and pad_presstime[smwPlayer].L >= 50 then -- increment quickly
smwIncItemBox()
end
if not smwPause and pad_press[smwPlayer].down and pad_down[smwPlayer].L then -- decrement
smwDecItemBox()
elseif not smwPause and pad_press[smwPlayer].down and pad_press[smwPlayer].L and pad_presstime[smwPlayer].L >= 50 then -- decrement quickly
smwDecItemBox()
end
end
-- moving method
if smwMoveMethodChangeOn then
if not smwPause and pad_press[smwPlayer].L and pad_down[smwPlayer].A then
smwSetMoveMethod(smwMoveMethod + 1)
end
if not smwPause then
smwMoveMethodProc[smwMoveMethod+1]()
end
end
end
-- prevent item popup
if preventItemPopup and not pad_press[smwPlayer].select then
preventItemPopup = false
end
if preventItemPopup then
pad_send[smwPlayer].select = not pad_send[smwPlayer].select
end
-- cut powerup/powerdown animation
if smwCutPowerupAnimationOn then
smwCutPowerupAnimation()
end
if smwCutPowerdownAnimationOn then
smwCutPowerdownAnimation()
end
-- allow escape
if smwRegularDebugFuncOn then
smwAllowEscape()
end
-- sprite drag and drop
if smwDragAndDropOn then
smwDragAndDrop()
end
end
-- apply various cheats
function smwApplyCheats()
if smwGameMode == gameMode_level then
smwApplyLevelCheats()
elseif smwGameMode == gameMode_ow then
smwMoveMethod = moveMethod_normal
elseif smwGameMode == 11 then
smwRewriteOnPauseExit()
end
end
-- draw P-meter in the screen
local pMeterMaxCount = 0
function smwDrawPMeter()
if not (smwGameMode == gameMode_level) then return end
local pMeter = memory.readbyte(RAM_pMeter)
local pMeterBarXPos = 8
local pMeterBarYPos = 208
local pMeterBarWidth = 112
local pMeterBarHeight = 6
local pMeterBarLen
local pMeterBorderColor = "#000000ff"
local pMeterMeterColorNormal = "#31bdc5cc"
local pMeterMeterColorMax = { "#ff0000cc", "#ff0000cc", "#ffffffcc" }
local pMeterMeterColor
local pMeterBGColor = "#00000080"
if pMeter >= pMeter_max or smwMoveMethod == moveMethod_pmeter then
pMeterMaxCount = (pMeterMaxCount + 1) % 3
pMeterMeterColor = pMeterMeterColorMax[1+pMeterMaxCount]
pMeterBarLen = pMeterBarWidth
else
pMeterColorPulse = 0
pMeterMeterColor = pMeterMeterColorNormal
pMeterBarLen = math.floor((pMeter/1.0)*pMeterBarWidth/pMeter_max)
end
gui.edgelessbox(pMeterBarXPos, pMeterBarYPos, pMeterBarXPos + pMeterBarWidth, pMeterBarYPos + pMeterBarHeight, "#000000")
gui.box(pMeterBarXPos + 1, pMeterBarYPos + 1, pMeterBarXPos + pMeterBarWidth - 1, pMeterBarYPos + pMeterBarHeight - 1, pMeterBGColor, pMeterBGColor)
if pMeter > 0 then
gui.box(pMeterBarXPos + 1, pMeterBarYPos + 1, pMeterBarXPos + pMeterBarLen - 1, pMeterBarYPos + pMeterBarHeight - 1, pMeterMeterColor, pMeterMeterColor)
end
end
-- draw Mario's speed vector in a grid
function smwDrawSpeedVector()
if not (smwGameMode == gameMode_level) then return end
gui.opacity(guiOpacity)
local spdVectorColor = "red"
local gridColor = "#a9a9a9" -- grey
local gridFillColor = "#b0a9a9a9" -- grey
local xSpeed = memory.readbytesigned(RAM_xSpeed)
local ySpeed = memory.readbytesigned(RAM_ySpeed)
local cameraX = memory.readwordsigned(RAM_cameraX)
local cameraY = memory.readwordsigned(RAM_cameraY)
local xPos = memory.readwordsigned(RAM_xPos) --+ memory.readbyte(RAM_xSubPos)
local yPos = memory.readwordsigned(RAM_yPos) --+ memory.readbyte(RAM_ySubPos)
local deltaMarioCameraX = xPos - cameraX
local deltaMarioCameraY = yPos - cameraY
local marioCenterX = deltaMarioCameraX + 8
local marioCenterY = deltaMarioCameraY + 24
local gridBig = true
local gridSizeFactor = 0
-- clickable menu
local notSelectedColor = "#ffffff55" -- transparent
local selectedColor = "#ff6060aa" -- red
gui.text(205, 196, "Speed vector")
gui.box(204, 205, 222, 217, selectedColor, "black")
local buttonX1 = 222
for size = 1, 3 do
gui.box(buttonX1, 205, buttonX1 + 10, 217, notSelectedColor, "black")
buttonX1 = buttonX1 + 10
end
gui.text(208, 208, "OFF")
gui.text(226, 208, "S")
gui.text(236, 208, "M")
gui.text(246, 208, "B")
if input.get("leftclick") then
local x, y = dev_press["xmouse"], dev_press["ymouse"]
if x > 204 and x < 222 and y > 205 and y < 217 then
gridSizeFactor = 0
elseif x > 222 and x < 232 and y > 205 and y < 217 then
gui.box(204, 205, 222, 217, notSelectedColor, "black")
gui.text(208, 208, "OFF")
gui.box(222, 205, 232, 217, selectedColor, "black")
gui.text(226, 208, "S")
gridSizeFactor = 2
elseif x > 232 and x < 242 and y > 205 and y < 217 then
gui.box(204, 205, 222, 217, notSelectedColor, "black")
gui.text(208, 208, "OFF")
gui.box(232, 205, 242, 217, selectedColor, "black")
gui.text(236, 208, "M")
gridSizeFactor = 1.4285714285714285714285714285714
elseif x > 242 and x < 252 and y > 205 and y < 217 then
gui.box(204, 205, 222, 217, notSelectedColor, "black")
gui.text(208, 208, "OFF")
gui.box(242, 205, 252, 217, selectedColor, "black")
gui.text(246, 208, "B")
gridSizeFactor = 1
end
end
if gridSizeFactor ~= 0 then
-- draw grid
local fillColor = "#ffffff55" -- transparent
local outlineColor = "#090909aa" -- black
local gridX1 = marioCenterX - 50 / gridSizeFactor
local gridY1 = marioCenterY - 100 / gridSizeFactor
local gridX2 = gridX1 + 10 / gridSizeFactor
local gridY2 = gridY1 + 10 / gridSizeFactor
for collumn = 1, 10 do
for row = 1, 17 do
gui.box(gridX1, gridY1, gridX2, gridY2, fillColor, outlineColor)
gridY1 = gridY1 + 10 / gridSizeFactor
gridY2 = gridY2 + 10 / gridSizeFactor
end
gridY1 = marioCenterY - 100 / gridSizeFactor
gridY2 = gridY1 + 10 / gridSizeFactor
gridX1 = gridX1 + 10 / gridSizeFactor
gridX2 = gridX2 + 10 / gridSizeFactor
end
gui.line(marioCenterX - 50 / gridSizeFactor, marioCenterY, marioCenterX + 50 / gridSizeFactor, marioCenterY, "blue")
gui.line(marioCenterX, marioCenterY - 100 / gridSizeFactor, marioCenterX, marioCenterY + 70 / gridSizeFactor, "blue")
-- draw vector
gui.line(marioCenterX, marioCenterY, marioCenterX + xSpeed/gridSizeFactor, marioCenterY + ySpeed/gridSizeFactor, spdVectorColor)
end
end
-- draw sprite info on screen
function smwDrawSpriteInfo()
if not (smwGameMode == gameMode_level) then return end
gui.opacity(guiOpacity)
local cameraX = memory.readwordsigned(RAM_cameraX)
local cameraY = memory.readwordsigned(RAM_cameraY)
local spriteCount = 0
local yoshiTextColor = "#40ff40" -- green
local colorTable = {
"#80ffff", -- cyan
"#a0a0ff", -- blue
"#ff6060", -- red
"#ff80ff", -- magenta
"#ffa100", -- orange
"#ffff80", -- yellow
"#ffffff" -- white
}
for id = 0, smwSpriteMaxCount - 1 do
local stat = memory.readbyte(0x7e14c8+id)
local hOffscreen = (memory.readbyte(0x7e15a0+id) ~= 0)
local vOffscreen = (memory.readbyte(0x7e186c+id) ~= 0)
local number = memory.readbyte(0x7e009e+id)
local x = memory.readbytesigned(0x7e14e0+id) * 0x100 + memory.readbyte(0x7e00e4+id)
local y = memory.readbytesigned(0x7e14d4+id) * 0x100 + memory.readbyte(0x7e00d8+id)
local xsub = memory.readbyte(0x7e14f8+id)
local ysub = memory.readbyte(0x7e14ec+id)
local xspeed = memory.readbyte(0x7e00b6+id)
local yspeed = memory.readbyte(0x7e00aa+id)
local stunTimer = memory.readbyte(RAM_sprite_stunTimer + id)
local hOffscreenAlt = (math.abs(cameraX - x + 112) > 176)
local vOffscreenAlt = (math.abs(cameraY - y + 88) > 208)
if stat ~= 0 then -- not hOffscreen and not vOffscreen then
local dispString = string.format("<%02d> %02x (%d.%02x, %d.%02x) %d", id, number, x, xsub, y, ysub, stunTimer)
local dispString = string.upper(dispString) -- to make sprite hex number easier to read
if number == 53 then -- when Yoshi
local colorString = yoshiTextColor
if not vOffscreenAlt and not hOffscreenAlt then
gui.text(math.min(242, math.max(2, 2 + x - cameraX)), math.min(216, math.max(2, -8 + y - cameraY)), string.format("<%02d>", id), colorString)
end
gui.text(143, 36 + spriteCount * 8, dispString, colorString)
spriteCount = spriteCount + 1
else
local colorString = colorTable[1 + id % #colorTable]
if not vOffscreenAlt and not hOffscreenAlt then
gui.text(math.min(242, math.max(2, 2 + x - cameraX)), math.min(216, math.max(2, -8 + y - cameraY)), string.format("<%02d>", id), colorString)
end
gui.text(143, 36 + spriteCount * 8, dispString, colorString)
spriteCount = spriteCount + 1
end
end
end
gui.text(214, 2, string.format("Sprites:%02d", spriteCount))
end
-- draw bounce sprite info on screen
function smwDrawBounceSpriteInfo()
if not (smwGameMode == gameMode_level) then return end
gui.opacity(guiOpacity)
local cameraX = memory.readwordsigned(RAM_cameraX)
local cameraY = memory.readwordsigned(RAM_cameraY)
local bounceSpriteCount = 0
local colorTable = {
"#ff8400", -- yellow to orange gradient 3 (orange)
"#ffad00", -- yellow to orange gradient 2
"#ffd600", -- yellow to orange gradient 1
"#ffff00", -- yellow to orange gradient 0 (yellow)
}
for id = 0, smwBounceSpriteMaxCount - 1 do
local number = memory.readbyte(RAM_bouncesprite_Number + id)
local x = memory.readbytesigned(RAM_bouncesprite_xPosHigh + id) * 0x100 + memory.readbyte(RAM_bouncesprite_xPosLow + id)
local y = memory.readbytesigned(RAM_bouncesprite_yPosHigh + id) * 0x100 + memory.readbyte(RAM_bouncesprite_yPosLow + id)
local hOffscreenAlt = (math.abs(cameraX - x + 112) > 176)
local vOffscreenAlt = (math.abs(cameraY - y + 88) > 208)
if number ~= 0 then -- not hOffscreen and not vOffscreen then
local dispString = string.format("|%02d| %02d (%d, %d)", id, number, x, y)
local colorString = colorTable[1 + id % #colorTable]
if not vOffscreenAlt and not hOffscreenAlt and number == 7 then -- when turn block
spinningTimer = memory.readbyte(RAM_turnBlockSpinning + id)
gui.text(math.min(242, math.max(2, 2 + x - cameraX)), math.min(216, math.max(2, -8 + y - cameraY)), string.format("|%02d|\n%d", id, spinningTimer), colorString)
elseif not vOffscreenAlt and not hOffscreenAlt then
gui.text(math.min(242, math.max(2, 2 + x - cameraX)), math.min(216, math.max(2, -8 + y - cameraY)), string.format("|%02d|", id), colorString)
end
gui.text(180, 148 + bounceSpriteCount * 8, dispString, colorString)
bounceSpriteCount = bounceSpriteCount + 1
end
end
gui.text(186, 140, string.format("Bounce sprites:%02d", bounceSpriteCount))
end
-- draw extended sprite info on screen
function smwDrawExtSpriteInfo()
if not (smwGameMode == gameMode_level) then return end
gui.opacity(guiOpacity)
local cameraX = memory.readwordsigned(RAM_cameraX)
local cameraY = memory.readwordsigned(RAM_cameraY)
local extSpriteCount = 0
local colorTable = {
"#ff0000", -- orange to red gradient 9 (red)
"#ff1200", -- orange to red gradient 8
"#ff2400", -- orange to red gradient 7
"#ff3700", -- orange to red gradient 6
"#ff4900", -- orange to red gradient 5
"#ff5b00", -- orange to red gradient 4
"#ff6e00", -- orange to red gradient 3
"#ff8000", -- orange to red gradient 2
"#ff9200", -- orange to red gradient 1
"#ffa500", -- orange to red gradient 0 (orange)
}
for id = 0, smwExtSpriteMaxCount - 1 do
local number = memory.readbyte(RAM_extsprite_number + id)
local x = memory.readbytesigned(RAM_extsprite_xPosHigh + id) * 0x100 + memory.readbyte(RAM_extsprite_xPosLow + id)
local y = memory.readbytesigned(RAM_extsprite_yPosHigh + id) * 0x100 + memory.readbyte(RAM_extsprite_yPosLow + id)
if number ~= 0 then -- not hOffscreen and not vOffscreen then
local dispString = string.format("!%02d! %02x (%d, %d)", id, number, x, y)
local dispString = string.upper(dispString) -- to make extended sprite hex number easier to read
local colorString = colorTable[1 + id % #colorTable]
gui.text(math.min(242, math.max(2, 2 + x - cameraX)), math.min(216, math.max(2, -8 + y - cameraY)), string.format("!%02d!", id), colorString)
gui.text(180, 143 + extSpriteCount * 8, dispString, colorString)
extSpriteCount = extSpriteCount + 1
end
end
gui.text(178, 135, string.format("Extended sprites:%02d", extSpriteCount))
end
-- draw main info on screen
function smwDrawMainInfo()
if not (smwGameMode == gameMode_level) then return end
gui.opacity(guiOpacity)
local frameCount = memory.readbyte(RAM_frameCount)
local frameCountAlt = memory.readbyte(RAM_frameCountAlt)
local powerup = memory.readbyte(RAM_powerup)
local pMeter = memory.readbyte(RAM_pMeter)
local takeOffMeter = memory.readbyte(RAM_takeOffMeter)
local starInvCount = memory.readbyte(RAM_starInvCount)
local hurtInvCount = memory.readbyte(RAM_hurtInvCount)
local cameraX = memory.readword(RAM_cameraX)
local cameraY = memory.readword(RAM_cameraY)
local xSpeed = memory.readbytesigned(RAM_xSpeed)
local ySpeed = memory.readbytesigned(RAM_ySpeed)
local xSubSpeed = memory.readbyte(RAM_xSubSpeed)
local xPos = memory.readwordsigned(RAM_xPos)
local yPos = memory.readwordsigned(RAM_yPos)
local xSubPos = memory.readbyte(RAM_xSubPos)
local ySubPos = memory.readbyte(RAM_ySubPos)
local facingDirection = memory.readbyte(RAM_facingDirection)
local flightAnimation = memory.readbyte(RAM_flightAnimation)
local capeSlowFallCount = memory.readbyte(RAM_capeSlowFallCount)
local capeSpinTimer = memory.readbyte(RAM_capeSpinTimer)
local bluePOW = memory.readbyte(RAM_bluePOW)
local grayPOW = memory.readbyte(RAM_grayPOW)
local multipleCoinBlockTimer = memory.readbyte(RAM_multipleCoinBlockTimer)
local directionalCoinTimer = memory.readbyte(RAM_directionalCoinTimer)
local pBalloonTimer = memory.readbyte(RAM_pBalloonTimer)
local pBalloonTimerAlt = memory.readbyte(RAM_pBalloonTimerAlt)
local ropeClimbingFlag = memory.readbyte(RAM_ropeClimbingFlag)
local RNG = memory.readbyte(RAM_RNG)
local blockedStatus = memory.readbyte(RAM_blockedStatus)
local itemBox = memory.readbyte(RAM_itemBox)
local itemBoxSprite = itemBox + 115
if itemBox >= 141 then
itemBoxSprite = itemBox - 141
else
itemBoxSprite = itemBox + 115
end
local timerPosition = 148
local timerCount = 1
function timerCountPlus()
timerCount = timerCount + 1
end
if ropeClimbingFlag == 8 then
gui.text(1, 148, string.format("rope flag: ON"))
end
if multipleCoinBlockTimer ~= 0 then
gui.text(1, timerPosition - timerCount * 8, string.format("multiCoin: %d", multipleCoinBlockTimer))
timerCountPlus()
end
if grayPOW ~= 0 then
gui.text(1, timerPosition - timerCount * 8, string.format("grayPOW: %d", grayPOW * 4 - frameCountAlt % 4))
timerCountPlus()
end
if bluePOW ~= 0 then
gui.text(1, timerPosition - timerCount * 8, string.format("bluePOW: %d", bluePOW * 4 - frameCountAlt % 4))
timerCountPlus()
end
if directionalCoinTimer ~= 0 then
gui.text(1, timerPosition - timerCount * 8, string.format("dirCoin: %d", directionalCoinTimer * 4 - frameCount % 4))
timerCountPlus()
end
if starInvCount ~= 0 then
gui.text(1, timerPosition - timerCount * 8, string.format("star: %d", starInvCount * 4 - (frameCountAlt - 3) % 4))
timerCountPlus()
end
if hurtInvCount ~= 0 then
gui.text(1, timerPosition - timerCount * 8, string.format("invinc: %d", hurtInvCount))
timerCountPlus()
end
if pBalloonTimerAlt ~= 0 then
gui.text(1, timerPosition - timerCount * 8, string.format("P-balloon: %d", pBalloonTimer * 4 - frameCount % 4))
timerCountPlus()
end
if powerup == 2 then
gui.text(1, 84, string.format("%d, %d", capeSpinTimer, capeSlowFallCount))
end
if pMeter ~= 0 or takeOffMeter ~= 0 then
gui.text(1, 76, string.format("Meter (%03d, %02d)", pMeter, takeOffMeter))
end
if facingDirection == 0 then
facingDirection = "<-"
else
facingDirection = "->"
end
if flightAnimation == 0 then
gui.text(1, 52, string.format("%s", facingDirection))
else
gui.text(1, 52, string.format("%s, %d", facingDirection, flightAnimation))
end
-- Blocked Status
local blockedStatusStr = {}
if blockedStatus == 1 then
blockedStatusStr = "R "
elseif blockedStatus == 2 then
blockedStatusStr = " L "
elseif blockedStatus == 4 then
blockedStatusStr = " D "
elseif blockedStatus == 5 then
blockedStatusStr = "R D "
elseif blockedStatus == 6 then
blockedStatusStr = " LD "
elseif blockedStatus == 8 then
blockedStatusStr = " U "
elseif blockedStatus == 9 then
blockedStatusStr = "R U "
elseif blockedStatus == 10 then
blockedStatusStr = " LU "
elseif blockedStatus == 25 then
blockedStatusStr = " M"
elseif blockedStatus == 129 then
blockedStatusStr = "R border"
elseif blockedStatus == 130 then
blockedStatusStr = " L border"
elseif blockedStatus == 133 then
blockedStatusStr = "R D border"
elseif blockedStatus == 134 then
blockedStatusStr = " LD border"
else
blockedStatusStr = " "
end
-- draw blocked status
local weak_color = "#b0a9a9a9" -- gray
local warning_color = "#ff2222" -- red
gui.text(1, 60, "Blocked: ")
gui.text(36, 60, "RLDUM", weak_color)
gui.text(36, 60, string.format("%s", blockedStatusStr), warning_color)
gui.text(1, 36, string.format("Pos (%d.%02x, %d.%02x)", xPos, xSubPos, yPos, ySubPos))
local xSpeedComposite = xSpeed + (xSubSpeed / 0x100)
local xSpeedInt, xSpeedFrac = math.modf(xSpeedComposite)