-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdesign.txt
1111 lines (842 loc) · 28.7 KB
/
design.txt
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
===============================================================================
> List of todos, ordered by importance
===============================================================================
Bosses:
Ganon - needs custom kernel, have some sprites
Patra
Ghini
Glock - Need Design, have sprites
Dig
Gohma Blue (Yellow implemented)
Enemies:
Wizrobes - have sprites, need custom room collision routine
Goryia - Boomerang
Gel - Fix overtime budget (See EnMove_Card_WallCheck)
Armos - Make stairs/item appear
Blue Leevers - need behavior
Base Enemy - Environment Missile attack?
Stalfos - Key drop on kill mechanic?
Zola - Cut?
Bubbles - Cut?
Systems:
Second Quest
* Quest Select at game start
Dungeon Underground
* Underground Passage
* Underground Item
Random Drops
* TypeB and TypeC drop tables?
* 8th enemy has the bomb mechanics?
EnMove_Card_WallCheck
* Find optimizations
Enemy Spawns - Second Implementation
Status Hud - Complete
Door Unlock System - Complete
Raft System - Complete
HIDDEN_CAVE TODO:
1C - Armos entry
21 - Grave magic sword
34 - Armos entry
3D - Armos entry
4B - Bush
4E - Armos Entry
51 - Bush (more variance)
63 - Bush
78 - Bush
===============================================================================
> The Game
===============================================================================
Main Game Loop:
Random
VerticalSync - HUD Element Positioning Kernel
VERTICAL_BLANK
RoomUpdate - if RF_EV_LOAD, goto Halt
Missile System
PlayerPause - if pause game, goto Pause
PlayerInput
PlayerItem
Check Keydoor unlock
Update Doors
Process Audio
Preconfig Sprites
Draw HUD
if TextKernel
Draw Text
Draw World
OVERSCAN
Test Board Exit
Test Player Collision
Process ClearDrop
Process Enemy System
Process Room Script
Process Entities
Process Ball Script
Update Room Clear
Update Shutter Door
Test Game Over
Overscan Wait
Update RoomPush
Halt Game Loop:
VerticalSync
VERTICAL_BLANK
HtTask_Run
UpdateAudio
EnDraw_Del
Draw HUD
Draw World
OVERSCAN
HtTask_Run
Overscan Wait
When in the main loop, halt can be GOTO'd from these different sections:
* VERTICAL_BLANK
* OVERSCAN
Render Modes:
World + Hud (slow, temps can't be used longterm)
World + No Hud a.k.a "Pause" (faster, less memory)
RSWorld + Hud (RSWorld draws no sprites)
RSWorld2 + Hud (Same but vertical color scrolling)
===============================================================================
> The World
===============================================================================
The game space is broken down into worlds and then further down into rooms.
There are two different meanings for the term world.
In game, the worldId variable more accurately tracks the current level the
player is in.
* Bit 0 of the value stores quest type:
* 0 - First quest, Q1, or A
* 1 - Second quest, Q2, or B
* LV_A0, LV_B0 represents the overworld. These are negative in value
* LV_A1-LV_A9, LV_B1-LV_B9 are dungeons 1-9. These are positive in value
Each world has 128 main rooms, and 128 sub-rooms. Currently, sub-rooms are only
used for caves.
In the level data, there are only three worlds per quest.
* World 0 stores Q1 Overworld
* World 1 stores Q1 Levels 1 thru 6
* World 2 stores Q1 Levels 7 thru 9
* World 3 stores Q2 Overworld
* World 4 stores Q2 Levels 1 thru 6
* World 5 stores Q2 Levels 7 thru 9
Each world can only have a maximum of 128 rooms.
===============================================================================
> The World Format
===============================================================================
The data for each room is optimized to fit in 8 bytes per room, or $400 bytes
total per room (exactly the size of a 1 KB bank). The following information
is stored for each room:
- Room Sprite "pointers" for PF1L, PF1R, and PF2 sprites
- roomDoor, or Door flags, which in dungeons track whether a dungeon door should be
either open, locked, a shudder door, or a wall.
- roomWA, the room wall flags. In the overworld, these flags combined with
the room door flags configure the pattern drawn at the wall edges of a
room. In dungeons, they modify walls to either be bombable or false walls.
- The background and foreground colors of the room.
- Whether a dungeon room's "walls" can be crossed with the hover boots
- Whether a dungeon room should be dark.
- roomRS, RoomScript that should execute while in the room
- roomEX, the RoomScript extra parameter
- roomEN, the room encounter
The Python script world.py is responsible for packing the level editor data.
===============================================================================
> The World Format - Room Sprite and Door Flag packing
===============================================================================
The overworld seems to need more PF2 strips than PF1
Due to the 2 char overlap, PF1 for full 4x2 room sprites tend to be all 0s
Thus to better utilize the room sprite sheet, double the room tile budget to
16 bits, over 3 bytes
Playfield sprites are 16 pixels tall (16 bytes), thus a pointer could be
calculated as
SpritePtr + (spriteId * 16)
We can pre-compute the mul 16 by storing the 4 least significant bits of the
sprite index in the upper 4, and the most significant 1-2 bits in the lower
1-2 bits. Thus, AND #$F0 gives the pointer's least significant byte, then
AND #$01/#$03 + #>SpritePtr gives the most significant byte.
LSB! MSB
PF1 Left: 1111 xxx1
PF1 Right: 1111 xxx1
PF2 1111 xx11
unpack algo
Door Flags: xxxx 111x >> 1
xxxx 111x << 4
xxxx 11xx << 1
===============================================================================
> The World Format - Door and Wall Flags
===============================================================================
These configure the openings and behaviors for the room's NSEW exits.
The level editor and game implementations differ slightly.
In the level editor, the door flags for a world bank are split into NSEW arrays,
with each byte representing one room's door flags for that particular wall.
This value is a 3 bit maximum enumeration.
When building the game, the enumeration is split into two bytes:
roomDoors (door flags), which is packed with PF data
roomWA
both bytes have the same general allocation shown below:
N xxxx xx11
S xxxx 11xx
E xx11 xxxx
W 11xx xxxx
Zero Capture Shift Masks
N 0xFC 0x03 0
S 0xF3 0x0C 2
E 0xCF 0x30 4
W 0x3F 0xC0 6
The behavior of door and wall flags differ depending on if the world
is an overworld or a dungeon.
Overworld flags:
E D W Wall Type
0 00 0 = None
1 00 1 = Middle
2 01 0 = Wall with 4 pixel centered opening
3 01 1 = Full Wall
4 10 0 = Wall with top or left open
5 10 1 = Wall with bottom or right open
6 11 0 = Wall with 16 pixel centered open (NS only)
7 11 1 = Unused
Dungeon flags:
The door flags have been optimized for dungeons, and directly correspond to how
the wall openings will be drawn.
E W D
0 00 00 = open
1 00 01 = keydoor
2 00 10 = shutter door
3 00 11 = wall
4 10 11 = bombable wall
5 11 11 = false wall
===============================================================================
> Rooms
===============================================================================
Room Events:
RF_EV_LOAD ; Triggers Room Load event
RF_EV_LOADED ; If set, a new room has been loaded
RF_EV_ENCLEAR ; Enemies Cleared event
RF_NO_ENCLEAR ; Blocks Enemy Cleared from setting Room Cleared
RF_EV_CLEAR ; Room Cleared
Room Special:
RF_PF_IGNORE ; disables playfield collision within room center
; ; Enabled in all rooms with PF2 Triforce design
RF_PF_AXIS ; allows the player to walk over playfield with boots
; ; Enabled in dungeon rooms with RED or WATER colors
RsInit configures room for first frame. Remember to always position ball
===============================================================================
> Systems
===============================================================================
===============================================================================
> Systems - EnSystem (Enemy System)
===============================================================================
EnSystem (SUBROUTINE) manages the generation of enemies within a room.
These are known as encounters. EnSystem proactively spawns one enemy a
frame until either all entity slots are filled, or the roomENCount limit
is reached.
roomEN is an index into a mixed data table of encounters, defined in
EnSysEncounterTable.
Byte 0 (rRoomENFlags)
1000_0000 ; EnType array is length 1
0001_1111 ; Number of spawns in encounter
Byte 1+ is the EnType array of enemies.
In room data/on RF_EV_LOADED, roomEn will point to Byte 0
EnSystem will transfer that byte to wRoomENFlags and increment roomEN to
Byte 1. Then with each spawn, roomEn will increment (if applicable).
Enemies at the start of the array always spawn, while enemies at the end
may not depending on roomENCount
roomENCount tracks the number of living enemies. When it reaches 0,
RF_EV_ENCLEAR will be set. This enables the ClearDrop system
===============================================================================
> Systems - EnSystem - LastDraw
===============================================================================
EN_LAST_DRAWN ; Stores 1 if current entity is was given draw priority
; last frame
In EnDraw_Del, EN_LAST_DRAWN is clear if entity 0 is to be drawn, and set
if entity 1 is to be drawn.
Once the frame is rendered, an entity can test against EN_LAST_DRAWN to
determine if it was drawn that previous frame, and in turn use the prior
built-in collision result.
Entity 1 is executed first, which is why EnDraw_Del sets the flag to on
for Entity 1 being drawn. When swapping to Entity 0, the flag is toggled.
Entity deletions do not have to be factored into flag state:
If Entity 1 is deleted that frame, execution simply moves to Entity 0
If Entity 0 is deleted, the flag becomes desynced, there are no more
entities to process anyway.
The following is a logic table to show how EN_LAST_DRAWN (Pri) should
ideally be set when an entity removes itself.
Pri = 0, Slot0 = Thing, Slot1 = NONE, DELSLOT0 -> Pri = 1 (Phony State)
Pri = 0, Slot0 = Thing, Slot1 = Glob, DELSLOT0 -> Pri = 1 (Phony State)
Pri = 0, Slot0 = Thing, Slot1 = Glob, DELSLOT1 -> Pri = 0
Pri = 1, Slot0 = Thing, Slot1 = Glob, DELSLOT0 -> Pri = 0
Pri = 1, Slot0 = Thing, Slot1 = Glob, DELSLOT1 -> Pri = 1
Pri = 0, Slot0 = Thing, Slot1 = Glob -> Pri = 0
Pri = 1, Slot0 = Thing, Slot1 = Glob -> Pri = 1
===============================================================================
> Systems - ItemGet (SUBROUTINE)
===============================================================================
Sets up Get Item pose
Sets up Get Item or Get Triforce music sequence
Gives player an item immediately
Transforms Entity 0 into EN_ITEM_GET
EN_ITEM, EN_NPC_GIVE_ONE, EN_NPC_SHOP are the only dependenants
Additionally, the ItemGet system allows for Link to hold items above his head.
Affects EnClearDrop and EnShopkeeper var allocation
enState stores ItemGet trigger, for restoring EnClearDrop and playing MS
===============================================================================
> Halt Game
===============================================================================
Halt Tasks
curRoom -> A
curRoom -> B
RoomLoad nextRoom
Animation N
Animation S
Animation W
Animation E
Return Main
===============================================================================
> Halt Game - PFScroll Proto
===============================================================================
roomIdNext
roomId
Move Dir, roomIdNext - roomId,
N = #-$10 ; 0 curRoom -> B
; 1 RoomLoad nextRoom
; 2 nextRoom -> A
; 3 Animation N: roomDY starts #ROOM_PX_HEIGHT*2-1 and decs every frame
S = #+$10 ; 0 curRoom -> A
; 1 RoomLoad nextRoom
; 2 nextRoom -> B;
; 3 Animation S: roomDY starts #ROOM_PX_HEIGHT-1 and incs every frame
W = #-$01 ; 0 curRoom -> A
; 1 RoomLoad nextRoom
; 2 nextRoom -> B
; 3 Animation W:
E = #+$01
I = ** ; 0 curRoom -> A
; 1 RoomLoad nextRoom
; 2 nextRoom -> A
; 3 no animation
===============================================================================
> Enemy Notes
===============================================================================
Darknut Logic:
Directions LRUD, opposes player directions
States:
enStun ds 1 (non-zero = can't take damage)
enRecoil ds 1 (non-zero = recoiling)
enBlockedDir ds 1 UDLR
if collided with pf,
reset x,y
if recoil == 0,
pick new direction
if collided with weapon && stun == 0,
dec health
stun = -16
recoil = -8
x &= 0xFE //stay on grid
y &= 0xFE //stay on grid
//move func handles recoil opposing facing direction
if x & (gridstep-1) != 0 && y & (gridstep-1) != 0,
continue direction
else if Frame & 0x10
pick new direction
Move()
Pick Direction:
a = rand()
50% seek player
50% rand dir
Pre:
Post:
if Health = 0, spawn item
Octorok Logic:
States:
Init
set enTimer to N
Walk
dec timer
if timer = 0, Telegraph attack
else base movement pattern
Telegraph Attack
rotate in place N times (0-7 times?)
at end of state, fire missile
Attack
idle N frames
PlAttack:
enable recoil
dec health 1, 2, 4
Leever Logic:
States:
Init
Rise
Walk
Sink
Hide
Stalfos Logic:
States:
Walk
base movement pattern
Gibdos Logic:
States:
Walk
base movement pattern
Rope Logic:
States:
Walk
base movement pattern
if plX or plY = enX or enY, stalk
Stalk
move until collide with wall
goto walk
LikeLike Logic:
States:
Walk
base movement pattern
Succ
lock player position until dead
Moblin Logic:
Walk
base movement pattern
Telegraph Attack
idle N frames
at end of state, fire missile
Attack
idle N frames
WizrobeA Logic:
States:
Wait
idle N frames, invisible
end of state, spawn on player's x or y lines
Phase In
flicker sprite on/off
Attack
fire missile
idle N frames
Phase Out
flicker sprite on/off
Glock Logic:
Glock is a three headed monster. After the first head takes enough damage, it is severed
and will freely roam the room
Intro
Telegraph
Attack
Glock Head Logic:
Input repeat prevention:
INPT4 == $00 ; fire button pressed
INPT4 == $80 ; fire button not pressed
if last button == not pressed && button == pressed && ItemTimer == 0
ItemButton
1 && 0 && 0
1111 0111 -9
1111 1000 -8
1111 1001 -7
1111 1010 -6
1111 1011 -5
1111 1100 -4
1111 1101 -3
1111 1110 -2
1111 1111 -1
0000 0000 0
set "p_up" "('p_up'+1*('key_pressed'=273o'key72'))"
Position ALL Objects
need x range 0-136 ($88)
10 x 15 gets 0-149
9 x 15 gets 0-134
10 * 5 -1 = 49 cycles, 68 cycles remain
-8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7
7, 6, 5, 4, 3, 2, 1, 0
111, 110, 101, 100, 011, 010, 001, 000
000, 001, 010, 011, 100, 101, 110, 111
Health Display
1-8 = 1 heart
9-16 = 2 hearts
17-24 = 3 hearts
-64 = 8 hearts
65-72 = 9 hearts
73-80 = 10 hearts
LoadRoom is running out of scanlines
Overworld timing:
start 3.0
end 25.44
Dungeon timing:
start 3.15
end 26.27
KeydoorCheck -> Kernel Main loop
1.63
14.06
Enemy Spawn Routine:
PF1L is normal, so bit 0x80 is left, 0x01 is toward center
PF2 is mirrored, so bit 0x80 is center, 0x01 is left/right
PF1R is "mirrored" so bit 0x80 is right, 0x01 is toward center
PF is 4 units per bit, 0x40 is center x, 0x48 is top, 0x10 is bottom ("2x2" sprites)
With standard "2x2" sprites We can force PF2 spawns to only be on the "grid",
using these bit patterns:
0x80 -> x = +-0x00 + 0x40
0x60 -> x = +-0x08 + 0x40
0x18 -> x = +-0x10 + 0x40
0x06 -> x = +-0x18 + 0x40
4 PF2 checks
for y component:
0 = 0x14
1 = 0x1C
2 = 0x24
3 = 0x2C
4 = 0x34
5 = 0x3C
6 = 0x44
Vertically, I think I want to avoid spawning enemies on the first/last pf line;
this gives us 13 possible vertical spawn positions, 7 if we do "on grid" spawns
In total, that's 28 PF2 spawn points
For PF1 spawns, these bit patterns are "on grid"
0x06 -> x = +-0x28 + 0x40
0x18 -> x = +-0x30 + 0x40
That's 14 PF1 spawn points, or 28 total
Together, it makes 56 spawn checks
Algo time!
8 x pos, 7 y pos: 3 bits x, 3 bits y
x bits:
0?? PF2 check
0xx PF2 check type
1?? PF1 check
10? PF1L
11? PF1R
1?x PF1 check type
y bits:
if 7 -> 3, then
0-6 = line * 2 + 1
===============================================================================
> Systems - Generalized Entity Room Collision Check
===============================================================================
Spawning:
enNX - Next Target, 4x4 grid x
enNY - Next Target, 4x4 grid y
enNX, enNY set to initial spawn pos
Movement:
If thisX,thisY = enNX, enNY; NextDir
8x8 Entities can only exist on points
x = $04 to $7C
y = $08 to $50
Real world pixel is 4x4, giving us
x = 31 steps player, 27 other
y = 19 steps player, 15 other
x = $20 stradle PF1L, PF2 (test 0th bit for both)
x = $60 stradle PF1R, PF2
x <= $1C is PF1L (bit 7 -> 0)
x >= $24 and <= $5C is PF2 (bit 0 -> 7 -> 0)
x >= $64 is PF1R (bit 0 -> 7)
Start by handling $20, $60 special case
if $20 bit set
$20 case if v flag not set
$60 if v flag set
else
Text:
load text into ram via loop
36.48
40.55
unroll once
36.48
39.71
Items:
Rupees 1x, 5x
R. Heart
Fairy
Bombs
-- End of Random Drops --
Key
Triforce
Sword, White Sword
Master Sword
Candle (Red Blue?)
Rock Sirloin
Pegasus Boots (ladder)
Red Ring, Blue Ring
Potion
Raft
Magic Key
Bow
Arrow, Silver Arrow
Bracelet, Bracelet Lv2
Recorder?
; ASM TRICKS
sbc = adc(~arg)
; dec to target amount
lda #$00 ; <- Target Value
cmp v
lda v
sbc #$0
sta v
; inc to target amount+1
lda #$00 ; FF loops indefinitely
cmp itemRupees
lda itemRupees
sbc #$FF
sta itemRupees
===============================================================================
> Other - Text24
===============================================================================
Text24 draws 24 characters on a line every 2 frames, but it consumed most of
the rom I budgeted for text, without even storing text in the rom yet. It also
used 24 bytes of ram, which is a ton. when i looked at the code, i discovered
two subroutines, one for frame 1, one for frame 2, which were nearly identical.
The main difference was that one needed a 4 cycle delay while the other needed
a 6 cycle delay to position the sprite data correctly. I was able to
deduplicate the code by using the overflow flag to store whether the delay
should be 4 or 6 cycles long. A branch takes 2 cycles to execute, 3 if the
branch is taken, and so a pair of bvs is enough to implement a variable cycle
delay of 4/6 cycles.
===============================================================================
> Systems - Caves
===============================================================================
Entering an overworld room with a negative id sends you to a cave.
The cave properties are determined by roomEX, which is not clobbered
on entering the cave.
money making game
1/3, 1/3, 1/3
loserA is always -10
loserB is 50/50 -40 or -10
winner is 50/50 +20 or +50
win, -10, lose
-10, win, lose
win, lose, -10
lose, win, -10
-10, lose, win
lose, -10, win
1 1 1 1 1 1
+50, +20, -10, -10, -10, -40
20 8 -4 -4 -4 -16
25 10 -5 -5 -5 -20
Secret
Warp
Hint
Money
Shops
B Pot, Fairy, R Pot
Key, Ring, Meat
Shield, Bomb, Arrow
Shield, Meat, Heart
Shield, Key, Candle
E7 -> 3E conversion
Ram
WorldKernel $ 90
PF $ 3C
RoomClear $ 20 for 256 rooms
RoomFlag $100
Rom
World Data could be packed into $400 bytes per 128 rooms,
instead of $800 per 256. This could require less bank switching
Cart Ram Map Info:
General Layout
A = F000 - Free
B = F400 - Free
C = F800 - World # Ram
D = FC00 - "Always" bank
Drawing
A = F000 - Text Kernel A / Shop Kernel / Sprites (Draw)
B = F400 - Text Kernel B / Sprites (Shop) / Draw Kernel
C = F800 - World # Ram
D = FC00 - "Always" bank
===============================================================================
> World Editor
===============================================================================
1, 2, 3, 4 - Change N/S/W/E room door
Q, W, E - inc Room Tile Left, Mid, Right
A, S, D - dec Room Tile Left, Mid, Right
R, T - inc, dec Room Script
F, G - inc, dec, Room Ex
6, 7 - inc, dec, Room EN
I - Enable Dark Room Editor (Space to toggle room)
O - Toggle Palette
P - Save
M - switch active world
L - Set Doorflags (4 char string of chars 0-3)
Z - copy tile
Space - place copied room
C - Set room colors (hex code)
Delete - Delete room
===============================================================================
> Systems - Atan2
===============================================================================
table lookup:
dx = plX - mX
dy = plY - mY
if dx is negative
xsign |= $80
negate dx
if dy is negative
ysign |= $40
negate dy
while (dx > 7 or dy > 7)
dx >>= 1
dy >>= 1
index = y * 8 + x
index |= xsign
index |= ysign
if x == 0, inc y by $100 ; Solves Atan2 being undefined for 0/0
else if y == 0, inc x by $100
Rs_Maze
roomEX 0 is N,N,N,N to Level 5
roomEX 1 is N,E,S,E to graveyard
===============================================================================
> Systems - Battle System
===============================================================================
Battle algo:
Check if weapon hitbox overlaps enemy hitbox
0, 1, 2, 3, 5
Sword - Direction and Timer
frames -7 to -2 // same as Arrow
EW hitbox is 8x2
NS hitbox is 2x8
frame -1 // possibly not needed?
EW hitbox is 4x2
NS hitbox is 2x4
Bomb - Timer, swap between 4x4 and 8x8 hitbox
Arrow - Direction
EW hitbox is 8x2
NS hitbox is 2x8
Fire - Simple 4x4 hitbox
Flute - No Hit
Wand -> Sword then Fire?
Meat - No Hit
Potion - No Hit
Hitbox system:
To test if two boxes AA and BB overlap:
if AA.x > BB.x - AA.w:
if AA.x < BB.x + BB.w:
# x axis pass
Another way to compute it:
(u8)(AA.x + AA.w-1 - BB.x) < (AA.w + BB.w-1)
HbGetPlAtt: Fetches current player attack and it's hitbox
Battle Weapons:
sword (1, 2, 3)
shield (1, 2)
arrow (1, 2)
fire
wave
bomb
wand
Boomerang
===============================================================================
> Systems - Enemy Missile System
===============================================================================
miType stores missile type and damage
& 0F is missile type
types 0-7 are blocked by small shield
types 8-F are only blocked by magic shield
& F0 is 4-bit signed damage in quarter hp increments (only negative values are valid)
mi0X,mi0Y is the lower left position of an 8x8 missile sprite. This is done so
that displacement calculations can be simplified regardless of the true size
of the missile.
m0x,m0y is the true lower left position of the sprite when drawn by the kernel.
m0x needs to be +1 over mi0X to properly overlap bottom left player pixel.
m0y does not need to be adjusted, but the missle will appear 1 scanline above.
Kernel limitations make it impossible for sprites to align perfectly.
===============================================================================
> Systems - Player Recoil
===============================================================================
Opposite plDir when colliding with enemies
Direction when colliding with projectiles is based on this algo:
compute deltaX
compute deltaY
If plOffGrid x,y = 0
compare absDeltaX with absDeltaY
greater distance sets direction
equal distance use opposite plDir
Else if plOffGrid x = 0
sign of delta y sets direction
Else if plOffGrid y = 0
sign of delta x sets direction
===============================================================================
> Pause Screen
===============================================================================
===============================================================================
> Pause Screen - Tri Sprite
===============================================================================
/|\ 1, 2 (top)
/| |\ 3, 4 (second row)
square left
__
\| 5
|\ 6
square right
__
|/ 7
/| 8
===============================================================================
> Pause Screen - Dungeon Map
===============================================================================
The basic sprite pattern for a dungeon room: