# Utility DynaWater - Math Calculated Water Palettes

Discussion in 'Engineering & Reverse Engineering' started by Kilo, May 26, 2024.

1. ### Kilo

That inbetween sprite from S&K's title screen Tech Member
558
556
93
S1 - Metal Sonic's Challenge, Sonic 1 Rev01 ASMX Disasm
So DeltaW's new hack just reminded me that I never actually made a proper release for this thing, so I decided to take the time to clean it up and make this thread.
For context, back in 2021 while I was part of the Isle of the Magnetic Artifacts team while planning out gimmicks and features, one of the features I believe we wanted to do was a glowing effect for players. The problem was SHIMA was going to have a lot of characters, and dedicating ROM space to 5 or 6 extra palettes was kind of dumb. So I came up with the solution of doing it by math, and we realized it could also be used for water palettes, and that's how DynaWater was born.

First of all you need 2 free words of RAM to hold the colour modifier values.
Then just plop this down somewhere. Or keep it in it's own ASM file.
Code (Text):
1. ; ====================================================================================================
2. ; DynaWater
3. ; Created August 10, 2021 by Kilo, ProjectFM
4. ; ====================================================================================================
5.
7. pal_watersub = \$FFFFYYYY
8.
9.
10. DynaWater_Init:
11.         moveq   #0,d0
12.         move.b  (zoneID).w,d0
13.         asl.w   #2,d0                           ; Multiply zone ID by 4, since there's 4 acts.
15.         add.w   d0,d0                           ; Multiply by 2, since palette entries are word sized.
18.         move.w  DynaWater_SubTable(pc,d0.w),d1
19.         move.w  d1,(pal_watersub).w             ; Load subtraction value.
20.         bra.w   DynaWater_Update
21.
23.         ; Act   1,    2,    3,    4
24.         dc.w    \$000, \$000, \$000, \$000  ; GHZ
25.         dc.w    \$040, \$040, \$040, \$600  ; LZ
26.         dc.w    \$000, \$000, \$000, \$000  ; MZ
27.         dc.w    \$000, \$000, \$000, \$000  ; SLZ
28.         dc.w    \$000, \$000, \$000, \$000  ; SYZ
29.         dc.w    \$000, \$000, \$000, \$000  ; SBZ
30.         dc.w    \$000, \$000, \$000, \$000  ; Ending
31.         even
32.
33. DynaWater_SubTable:
34.         ; Act   1,    2,    3,    4
35.         dc.w    \$000, \$000, \$000, \$000  ; GHZ
36.         dc.w    \$006, \$006, \$006, \$002  ; LZ
37.         dc.w    \$000, \$000, \$000, \$000  ; MZ
38.         dc.w    \$000, \$000, \$000, \$000  ; SLZ
39.         dc.w    \$000, \$000, \$000, \$000  ; SYZ
40.         dc.w    \$000, \$000, \$000, \$000  ; SBZ
41.         dc.w    \$000, \$000, \$000, \$000  ; Ending
42.         even
43.
44. ; Here's a list of registers to help you keep track of what is what
45. ; a1 - Normal palette buffer.
46. ; a2 - Water palette buffer.
47. ; d0 - Red additional value.
48. ; d1 - Green additional value.
49. ; d2 - Blue additional value.
50. ; d3 - Red subtraction value.
51. ; d4 - Green subtraction value.
52. ; d5 - Blue subtraction value.
53. ; d6 - Loop value.
54. ; d7 - General register for storing color values temporarily.
55.
56. DynaWater_Update:
57.         tst.w   (pal_wateradd).w    ; Is there any addition values for this level?
58.         bne.s   @Cont               ; If so, pass.
59.         tst.w   (pal_watersub).w    ; If not, is there any subtraction values?
60.         beq.w   DynaWater_Return             ; If not, exit, there's no work to be done.
61.
62. @Cont:
63.         lea     (pal_buffer).w,a1   ; Get our palette for this frame.
64.         lea     (pal_water).w,a2
65.         moveq   #64-1,d6            ; Set the loop count.
66.
68.         move.w  d7,d0               ; Red.
69.         move.w  d7,d1               ; Green.
70.         move.w  d7,d2               ; Blue.
71.         and.w  #\$00E,d0             ; Mask out any other channels.
72.         and.w  #\$0E0,d1
73.         and.w  #\$E00,d2
74.         move.w  (pal_watersub).w,d7 ; Repeat for subtraction value.
75.         move.w  d7,d3
76.         move.w  d7,d4
77.         move.w  d7,d5
78.         and.w  #\$00E,d3
79.         and.w  #\$0E0,d4
80.         and.w  #\$E00,d5
81.
82. @Loop:
84.         move.w  (a1),d7             ; Get palette entry for this iteration.
85.         andi.w  #\$E,d7              ; Mask in red channel.
87.         cmpi.w  #\$E,d7              ; Is it the max color value?
88.         bls.s   @RedSub             ; If not, continue.
89.         moveq   #\$E,d7              ; Cap the color value.
90.
91. @RedSub:
92.         sub.w   d3,d7               ; Apply subtraction value.
94.         moveq   #0,d7
95.
97.         move.w  d7,-(sp)            ; We'll store our modified color in the stack until we're done.
98.         move.w  (a1),d7
99.         andi.w  #\$E0,d7
101.         cmpi.w  #\$E0,d7
102.         bls.s   @GreenSub
103.         move.w  #\$E0,d7
104.
105. @GreenSub:
106.         sub.w   d4,d7
108.         moveq   #0,d7
109.
112.         move.w  (a1)+,d7
113.         andi.w  #\$E00,d7
115.         cmpi.w  #\$E00,d7
116.         bls.s   @BlueSub
117.         move.w  #\$E00,d7
118.
119. @BlueSub:
120.         sub.w   d5,d7
121.         bcc.s   @UpdatePalette
122.         moveq   #0,d7
123.
124. @UpdatePalette:
126.         move.w  d7,(a2)+    ; Send to water palette buffer.
127.         dbf     d6,@Loop    ; Loop.
128.
129. DynaWater_Return:
130.         rts
And any time you update the palette, say with palette cycles or scripting, call DynaWater_Update.

And like that you've got math based water palettes! No more time spent making water palettes for extra characters or super forms!

If you want to change how the water looks, modify the addition table to chose which colors to make brighter and by how much, and the subtraction table to make colors darker. And as you can tell it's already set up for non-LZ levels and per-act palettes!

But not only that, with some basic scripting you can do fun things like this:

Worth keeping in mind is that it's not particularly fast, so calling it a lot can cause lag, not helped by how laggy the water transfer stuff already is. Although if someone smarter than me with more time on their hands wants to take a crack at optimizing it I'd be more than appreciative.

Enjoy!

Edit: Check Markey's reply for an optimized version!

Last edited: May 26, 2024
• Useful x 9
• Like x 4
• List
2. ### Hivebrain

3,054
176
43
53.4N, 1.5W
Github
Very interesting. I did a similar thing a while ago, with a couple of differences:
1. Instead of adding/subtracting a value, each colour is anded (so the output colour is never brighter than the original). Your method might be preferable, if slower.
2. A "keep list" prevents some colours from being modified, so the HUD doesn't get obscured.

• Like x 3
• Useful x 1
• List
3. ### penBorefield

Living in my best life Member
168
25
28
Basement
Patching things up
IIRC Sonic 2/3 & Knux used this method.

4. ### Kilo

That inbetween sprite from S&K's title screen Tech Member
558
556
93
S1 - Metal Sonic's Challenge, Sonic 1 Rev01 ASMX Disasm
They do not. Sonic 2 uses the same method as Sonic 1 of loading separate palettes from the palette index. I believe Sonic 3 does something different, but it's still not math based and still stored in the ROM.

5. ### penBorefield

Living in my best life Member
168
25
28
Basement
Patching things up
In the past, just checked in a certain level editor and I realized there's two 64 color palettes (the second is for the water). My bad.

6. ### DigitalDuck

Arriving four years late. Member
5,377
466
63
Lincs, UK
TurBoa, S1RL
I did something similar for S1RL because of the day/night cycle and changes in level palette (all of which are also calculated). I don't remember if I ever actually applied it to water palettes though.

7. ### MarkeyJester

Original, No substitute Resident Jester
2,220
472
63
Japan
It's a reasonable idea I think; making a water version can sometimes just be annoying, and having a quicker limited option would actually be quite the pleasure

Sure, I'll bite.

Let's challenge this by avoiding look up tables:

Code (Text):
1. DynaWater_Init:
2.         move.w  (zoneID).w,d0
3.         ror.b   #\$02,d0
4.         lsr.w   #\$05,d0
6.         move.w  DynaWater_SubTable(pc,d0.w),(pal_watersub).w
7.         bra.w   DynaWater_Update
8.
10.         ; Act   1,    2,    3,    4
11.         dc.w    \$000, \$000, \$000, \$000  ; GHZ
12.         dc.w    \$040, \$040, \$040, \$600  ; LZ
13.         dc.w    \$000, \$000, \$000, \$000  ; MZ
14.         dc.w    \$000, \$000, \$000, \$000  ; SLZ
15.         dc.w    \$000, \$000, \$000, \$000  ; SYZ
16.         dc.w    \$000, \$000, \$000, \$000  ; SBZ
17.         dc.w    \$000, \$000, \$000, \$000  ; Ending
18.         even
19.
20. DynaWater_SubTable:
21.         ; Act   1,    2,    3,    4
22.         dc.w    \$000, \$000, \$000, \$000  ; GHZ
23.         dc.w    \$006, \$006, \$006, \$002  ; LZ
24.         dc.w    \$000, \$000, \$000, \$000  ; MZ
25.         dc.w    \$000, \$000, \$000, \$000  ; SLZ
26.         dc.w    \$000, \$000, \$000, \$000  ; SYZ
27.         dc.w    \$000, \$000, \$000, \$000  ; SBZ
28.         dc.w    \$000, \$000, \$000, \$000  ; Ending
29.         even
30.
31. DynaWater_Update:
32.
34.         or.w    (pal_watersub).w,d0
35.         beq.w   .Return
36.
37.         lea (\$FFFFFB00).w,a1
38.         lea (\$FFFFFA80).w,a2
39.
40.         move.w  #\$0EEE,d3
42.         and.w   d3,d2
43.         and.w   (pal_watersub).w,d3
44.         moveq   #64-1,d7
45.         move.w  #\$1110,d4
46.         move.w  d4,d5
47.         not.w   d5
48.
49.     .Loop:
50.         move.w  (a1)+,d0
52.         move.w  d0,d1
53.         and.w   d4,d1
54.         beq.s   .NoCapUp
55.         move.w  d1,d6
56.         lsr.w   #\$03,d6
57.         sub.w   d6,d1
58.         or.w    d1,d0
59.
60.     .NoCapUp:
61.         or.w    d4,d0
62.         sub.w   d3,d0
63.         move.w  d0,d1
64.         or.w    d5,d1
66.         beq.s   .NoCapDown
67.         subq.w  #1,d1
68.         move.w  d1,d6
69.         asr.w   #\$03,d6
70.         not.w   d6
72.         and.w   d1,d0
73.
74.     .NoCapDown:
75.         move.w  d0,(a2)+
76.         dbf d7,.Loop
77.
78.     .Return:
79.         rts

8. ### Kilo

That inbetween sprite from S&K's title screen Tech Member
558
556
93
S1 - Metal Sonic's Challenge, Sonic 1 Rev01 ASMX Disasm
Wow. This optimization blows my mind-
Great stuff as always Markey!

9. ### MarkeyJester

Original, No substitute Resident Jester
2,220
472
63
Japan
You're welcome, though I did think of a slightly quicker way by fusing the add and sub together as a single value.

This one is faster if there's no over/underflow, though slightly slower if there is. Probability there's less chance of overflow, so it should be overall faster:

EDIT: Please ignore... it's not working quite right, the carry is preventing it from functioning. Oh well, at least I tried...

Code (Text):
1. DynaWater_Init:
2.         move.w    (zoneID).w,d0
3.         ror.b    #\$02,d0
4.         lsr.w    #\$05,d0
6.         move.w    DynaWater_SubTable(pc,d0.w),d0
7.         eori.w    #\$EEE,d0
9.         eori.w    #\$1110,d0
10.         or.w    d0,d1
12.         bra.w    DynaWater_Update
13.
15.         ; Act   1,    2,    3,    4
16.         dc.w    \$000, \$000, \$000, \$000  ; GHZ
17.         dc.w    \$040, \$040, \$040, \$600  ; LZ
18.         dc.w    \$000, \$000, \$000, \$000  ; MZ
19.         dc.w    \$000, \$000, \$000, \$000  ; SLZ
20.         dc.w    \$000, \$000, \$000, \$000  ; SYZ
21.         dc.w    \$000, \$000, \$000, \$000  ; SBZ
22.         dc.w    \$000, \$000, \$000, \$000  ; Ending
23.         even
24.
25. DynaWater_SubTable:
26.         ; Act   1,    2,    3,    4
27.         dc.w    \$000, \$000, \$000, \$000  ; GHZ
28.         dc.w    \$006, \$006, \$006, \$002  ; LZ
29.         dc.w    \$000, \$000, \$000, \$000  ; MZ
30.         dc.w    \$000, \$000, \$000, \$000  ; SLZ
31.         dc.w    \$000, \$000, \$000, \$000  ; SYZ
32.         dc.w    \$000, \$000, \$000, \$000  ; SBZ
33.         dc.w    \$000, \$000, \$000, \$000  ; Ending
34.         even
35.
36. DynaWater_Update:
38.         beq.w    .Return
39.         move.w    #\$1110,d4
40.         move.w    d2,d3
41.         and.w    d4,d3
42.         lea    (\$FFFFFB00).w,a1
43.         lea    (\$FFFFFA80).w,a2
44.         moveq    #64-1,d7
45.
46.     .Loop:
47.         move.w    (a1)+,d0
49.         move.w    d0,d1
50.         and.w    d4,d1
51.         beq.s    .NoCap
52.         move.w    d1,d5
53.         and.w    d3,d5
54.         beq.s    .No0
55.         eor.w    d5,d1
56.         not.w    d5
57.         move.w    d5,d6
58.         asr.w    #\$03,d6
59.         not.w    d6
61.         and.w    d5,d0
62.     .No0:    move.w    d1,d6
63.         beq.s    .NoE
64.         lsr.w    #\$03,d6
65.         sub.w    d6,d1
66.         or.w    d1,d0
67.     .NoE:    or.w    d3,d0
68.
69.     .NoCap:
70.         sub.w    d3,d0
71.         move.w    d0,(a2)+
72.         dbf    d7,.Loop
73.
74.     .Return:
75.         rts

Last edited: May 26, 2024
10. ### BenoitRen

Tech Member
592
282
63
Why avoid look-up tables? I've seen that saving cycles is often done using them.

11. ### MarkeyJester

Original, No substitute Resident Jester
2,220
472
63
Japan
It was just a mild challenge, I'm usually one of the worst for abusing ROM space in projects, and my team members tend to hate me for it.

But here's a LUT version for your curiosity:

Code (Text):
1.         dc.b    \$0,\$0,\$0,\$0,\$0,\$0,\$0,\$0,\$0,\$0,\$0,\$0,\$0,\$0,\$0,\$0
2. LUTB:   dc.b    \$0,\$0,\$2,\$2,\$4,\$4,\$6,\$6,\$8,\$8,\$A,\$A,\$C,\$C,\$E,\$E
3.         dc.b    \$E,\$E,\$E,\$E,\$E,\$E,\$E,\$E,\$E,\$E,\$E,\$E,\$E,\$E,\$E,\$E
4.
5.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
6.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
7.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
8.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
9.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
10.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
11.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
12.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
13.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
14.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
15.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
16.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
17.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
18.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
19.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
20.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
21. LUTG:   dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
22.         dc.b    \$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00,\$00
23.         dc.b    \$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20
24.         dc.b    \$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20,\$20
25.         dc.b    \$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40
26.         dc.b    \$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40,\$40
27.         dc.b    \$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60
28.         dc.b    \$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60,\$60
29.         dc.b    \$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80
30.         dc.b    \$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80,\$80
31.         dc.b    \$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0
32.         dc.b    \$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0,\$A0
33.         dc.b    \$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0
34.         dc.b    \$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0,\$C0
35.         dc.b    \$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0
36.         dc.b    \$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0
37.         dc.b    \$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0
38.         dc.b    \$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0
39.         dc.b    \$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0
40.         dc.b    \$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0
41.         dc.b    \$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0
42.         dc.b    \$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0
43.         dc.b    \$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0
44.         dc.b    \$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0
45.         dc.b    \$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0
46.         dc.b    \$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0
47.         dc.b    \$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0
48.         dc.b    \$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0
49.         dc.b    \$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0
50.         dc.b    \$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0,\$E0
51.
52.
53. DynaWater_Init:
54.         move.w  (zoneID).w,d0
55.         ror.b   #\$02,d0
56.         lsr.w   #\$02,d0
57.         lea DynaWaterTable(pc,d0.w),a0
59.         move.l  (a0)+,(a1)+
60.         move.l  (a0)+,(a1)+
61.         move.l  (a0)+,(a1)+
62.         bra.w   DynaWater_Update
63.
64. DynaWaterTable: dc.l    LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0
65.         dc.l    LUTB+\$0,LUTG+\$4,LUTB-\$6,0, LUTB+\$0,LUTG+\$4,LUTB-\$6,0, LUTB+\$0,LUTG+\$4,LUTB-\$6,0, LUTB+\$6,LUTG+\$0,LUTB-\$2,0
66.         dc.l    LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0
67.         dc.l    LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0
68.         dc.l    LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0
69.         dc.l    LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0
70.         dc.l    LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0, LUTB+\$0,LUTG+\$0,LUTB+\$0,0
71.
72. DynaWater_Update:
74.         move.l  (a4)+,a0
75.         move.l  (a4)+,a3
76.         move.l  (a4),a4
77.         lea (\$FFFFFB00).w,a1
78.         lea (\$FFFFFA80).w,a2
79.         moveq   #64-1,d7
80.         moveq   #\$00,d0
81.
82.     .Loop:
83.         move.b  (a1)+,d0
84.         move.b  (a0,d0.w),(a2)+
85.         move.b  (a1)+,d0
86.         moveq   #\$0E,d1
87.         and.b   d0,d1
88.         sub.b   d1,d0
89.         move.b  (a3,d0.w),d0
90.         or.b    (a4,d1.w),d0
91.         move.b  d0,(a2)+
92.         dbf d7,.Loop
93.         rts

12. ### Devon

A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
1,303
1,513
93
Just my personal opinion, but I don't think the LUT version is that bad at all, with what looks to be 1232 bytes for the tables in total (if I did my counting right). I think it'd be more concerning if it were like 50 times that lol

13. ### Kilo

That inbetween sprite from S&K's title screen Tech Member
558
556
93
S1 - Metal Sonic's Challenge, Sonic 1 Rev01 ASMX Disasm
Yeah I'm personally all for that LUT version. While it doesn't do much for ROM space (Actually it does more harm because it's 5 times more data than the water palette data in Sonic 1 ) it still saves on dev time actually making the palettes, mostly in cases such as having multiple characters with super forms and the such. And still with my rainbow example, it allows for scripting effects. One usage I saw back when I made it was actually making the water darker as you got deeper which I thought was a really good use.

14. ### Jeffery Mewtamer

Blind Bookworm Member
1,939
122
43
I don't know the first thing about 68k assembly, so I haven't a clue what these code snippets actually do, but I'm also guessing this could be used to automate characters and objects going into different fluids(e.g. clean water versus algae infested water versus muddy water versus toxic ooze).

15. ### Kilo

That inbetween sprite from S&K's title screen Tech Member
558
556
93
S1 - Metal Sonic's Challenge, Sonic 1 Rev01 ASMX Disasm
Not particularly, all this does is make changing the colour of water easier. It doesn't gain properties, stuff like that would require it's own routines.

16. ### Jeffery Mewtamer

Blind Bookworm Member
1,939
122
43
Well, I was thinking along the lines of clean water giving everything a cyan tint, algae tinting things green, muddy water being brown, toxic ooze being purple ala Chemical Plant Zone, etc. Presumably, giving those liquids non-visual distinctions(such as mud slowing you down more than clean water or the toxic ooze draining rings because its poisonous) would be a separate thing.

17. ### RetroFlare

Member
21
1
3
I followed all the steps but unfortunately the water turned out like this

18. ### TDRR

Member
I actually ran into this just today lol, but with Hivebrain's WaterFilter from S1Squared. Either way, the issue is the exact same for both.
In S1Squared, palette fades are handled differently, so this just works out of the box. You either have to transplant those changes, or do a simpler hack, which is putting the call to DynaWater_Init right before "dbf d4, .mainloop" in PaletteFadeIn. You might not need to check for Labyrinth Zone in your case, I just did it to avoid the extra work, and calling the function unconditionally probably doesn't cause any issues.

Here's an example. I marked my additions with ++. You might need to backup some registers to the stack since DynaWater_Init uses a lot more registers, some of which overlap with the ones used here.
Code (Text):
2.         move.w    #\$003F,(v_pfade_start).w ; set start position = 0; size = \$40
3.
5.         moveq    #0,d0
6.         lea    (v_pal_dry).w,a0
9.         moveq    #cBlack,d1
11.
12. .fill:
13.         move.w    d1,(a0)+
14.         dbf    d0,.fill     ; fill palette with black
15.
16.         move.w    #\$15,d4
17.
18. .mainloop:
19.         move.b    #\$12,(v_vbla_routine).w
20.         bsr.w    WaitForVBla
22.         bsr.w    RunPLC
23.         cmpi.b    #id_LZ,(v_zone).w ; is level LZ? ; ++
24.         bne        .NoWater ; ++
25.         bsr        WaterFilter ; generate underwater palette ; ++
26. .NoWater:   ; ++
27.         dbf    d4,.mainloop
28.         rts
29. ; End of function PaletteFadeIn

Last edited: Jun 17, 2024
19. ### RetroFlare

Member
21
1
3
It worked! Thank you so much