Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-CD-2011-Script-Decompilation
Path: blob/main/Scripts/R7/Hotaru.txt
1319 views
1
//-------------------Sonic CD Hotaru Script-------------------//
2
//--------Scripted by Christian Whitehead 'The Taxman'--------//
3
//-------Unpacked By Rubberduckycooly's Script Unpacker-------//
4
5
// Some notes:
6
// - This single "Hotaru" Object controls two Hotaru badniks in gameplay
7
// - Both of these Hotarus are localised entirely within this single "Hotaru" Object
8
// - However, even with this, "Hotaru" Object in the scene should be followed by two Blank Objects
9
// - This is needed for spawning flowers, since each individual Hotaru drops its own Flower
10
// - This base Hotaru object will never become a Flower, but it will become a Blank Object if its Hotarus are both destroyed
11
12
// Aliases
13
14
#alias Object.Value0 : Object.Timer
15
16
#alias Object.Value1 : Object.StartPosX
17
#alias Object.Value2 : Object.StartPosY
18
19
// Stored as offsets from the base Hotaru object, not complete positions
20
#alias Object.Value3 : Object.Hotaru1X
21
#alias Object.Value4 : Object.Hotaru2X
22
23
// (Y distance remains the same between both, see Object.HotaruRadius)
24
25
// Bit-field that represents the state of the active Hotarus, bit 0 is Hotaru 1 and bit 1 is Hotaru 2
26
#alias Object.Value5 : Object.ActiveHotarus
27
28
// True or false, for if the floating sparks connecting the Hotarus are active
29
#alias Object.Value6 : Object.EletricityActive
30
31
// Y-Offset, half the distance between the two Hotarus
32
#alias Object.Value7 : Object.HotaruRadius
33
34
#alias Object.PropertyValue : Object.Quality
35
36
// Player Aliases
37
#alias Player.Value4 : Player.InvincibleTimer
38
39
// Object Score Alias
40
#alias Object[26].Value0 : ObjectScore.Timer
41
42
// Hotaru States
43
#alias 0 : HOTARU_IDLE1
44
#alias 1 : HOTARU_IDLE2
45
#alias 2 : HOTARU_TRACKPLAYER
46
#alias 3 : HOTARU_PAUSE
47
#alias 4 : HOTARU_LIGHTSON1
48
#alias 5 : HOTARU_LIGHTSOFF1
49
#alias 6 : HOTARU_LIGHTSON2
50
#alias 7 : HOTARU_LIGHTSOFF2
51
#alias 8 : HOTARU_LIGHTSON3
52
#alias 9 : HOTARU_LIGHTSOFF3
53
#alias 10 : HOTARU_FLASHING
54
#alias 11 : HOTARU_FLYAWAY
55
#alias 12 : HOTARU_OFFSCREEN
56
57
// Global SFX
58
#alias 8 : SFX_G_DESTROY
59
60
// Badnik Quality / Property Values
61
#alias 0 : GOOD_QUALITY
62
#alias 1 : BAD_QUALITY
63
64
// Priority
65
#alias 0 : PRIORITY_BOUNDS
66
#alias 1 : PRIORITY_ACTIVE
67
68
69
// Function declarations
70
#function Hotaru_HotaruBreak
71
72
73
function Hotaru_HotaruBreak
74
75
// This function is called to see if the individual Hotaru being checked should break
76
77
// Preconditions:
78
// - Object.XPos & Object.YPos are set to the positions of the Hotaru being checked (not the base Hotaru object!)
79
// - ArrayPos1 is the EntityNo of where to spawn the Hotaru, if it needs to be broken
80
81
// This wasn't updated for Plus, so no amount of hammers or knuckles will ever work on Hotarus :(
82
83
CheckEqual(Player.Animation, ANI_JUMPING)
84
TempValue0 = CheckResult
85
CheckEqual(Player.Animation, ANI_SPINDASH)
86
TempValue0 |= CheckResult
87
88
if Player.Animation == ANI_FLYING
89
CheckGreater(Player.YPos, Object.YPos)
90
TempValue0 |= CheckResult
91
end if
92
93
// Notably, the invincibility granted by time warping isn't granted here...
94
95
if TempValue0 == true
96
ResetObjectEntity(ArrayPos1, Flower_TypeNo, 0, Object.XPos, Object.YPos)
97
Object[ArrayPos1].DrawOrder = 4
98
99
CreateTempObject(TypeName[Smoke Puff], 0, Object.XPos, Object.YPos)
100
Object[TempObjectPos].DrawOrder = 4
101
102
CreateTempObject(TypeName[Object Score], ObjectScore.Timer, Object.XPos, Object.YPos)
103
Object[TempObjectPos].DrawOrder = 4
104
105
PlaySfx(SFX_G_DESTROY, false)
106
107
if Player.YVelocity > 0
108
FlipSign(Player.YVelocity)
109
else
110
Player.YVelocity += 0xC000
111
end if
112
113
// Grant the player some score
114
switch ObjectScore.Timer
115
case 0
116
Player.Score += 100
117
break
118
119
case 1
120
Player.Score += 200
121
break
122
123
case 2
124
Player.Score += 500
125
break
126
127
case 3
128
Player.Score += 1000
129
break
130
131
end switch
132
else
133
ArrayPos0 = Player.EntityNo
134
ArrayPos0 += 2
135
if Object[ArrayPos0].Type == TypeName[Invincibility]
136
ResetObjectEntity(ArrayPos1, Flower_TypeNo, 0, Object.XPos, Object.YPos)
137
Object[ArrayPos1].DrawOrder = 4
138
139
CreateTempObject(TypeName[Smoke Puff], 0, Object.XPos, Object.YPos)
140
Object[TempObjectPos].DrawOrder = 4
141
142
CreateTempObject(TypeName[Object Score], ObjectScore.Timer, Object.XPos, Object.YPos)
143
Object[TempObjectPos].DrawOrder = 4
144
145
PlaySfx(SFX_G_DESTROY, false)
146
147
if Player.YVelocity > 0
148
FlipSign(Player.YVelocity)
149
end if
150
151
// Grant the player's score
152
switch ObjectScore.Timer
153
case 0
154
Player.Score += 100
155
break
156
157
case 1
158
Player.Score += 200
159
break
160
161
case 2
162
Player.Score += 500
163
break
164
165
case 3
166
Player.Score += 1000
167
break
168
169
end switch
170
else
171
if Object.State > HOTARU_LIGHTSOFF3
172
if Player.InvincibleTimer == 0
173
Player.State = Player_State_GotHit
174
if Player.XPos > Object.XPos
175
Player.Speed = 0x20000
176
else
177
Player.Speed = -0x20000
178
end if
179
end if
180
end if
181
end if
182
end if
183
184
end function
185
186
187
sub ObjectMain
188
if Object.OutOfBounds == true
189
Object.State = HOTARU_OFFSCREEN
190
Object.Timer = 0
191
192
// Set the object back to its starting position
193
// Note that the Hotarus' state being set to HOTARU_OFFSCREEN means that there won't be any visible teleporting
194
Object.XPos = Object.StartPosX
195
Object.YPos = Object.StartPosY
196
197
// Reset the Hotarus to be 70 pixels apart
198
// (Regardless of their Property Value, since that's already been reset...)
199
Object.Hotaru1X = -0x230000
200
Object.Hotaru2X = 0x230000
201
202
Object.EletricityActive = false
203
end if
204
205
// Even if there are really only small differences between the two's behaviours, the GOOD and BAD badnik types just have entirely different
206
if Object.Quality == GOOD_QUALITY
207
switch Object.State
208
case HOTARU_IDLE1
209
Object.Priority = PRIORITY_ACTIVE
210
211
// Right range of 43 pixels for Hotaru 1
212
if Object.Hotaru1X < 0x2B0000
213
// Move both Hotarus in their respective directions by a pixel
214
Object.Hotaru1X += 0x10000
215
Object.Hotaru2X -= 0x10000
216
else
217
// Turn around
218
Object.State = HOTARU_IDLE2
219
end if
220
break
221
222
case HOTARU_IDLE2
223
// Left range of 115 pixels for Hotaru 1
224
if Object.Hotaru1X > -0x730000
225
// Move both Hotarus in their respective directions by a pixel
226
Object.Hotaru1X -= 0x10000
227
Object.Hotaru2X += 0x10000
228
else
229
// Turn around
230
Object.State = HOTARU_IDLE1
231
end if
232
break
233
234
case HOTARU_TRACKPLAYER
235
// Make the Hotarus follow the screen
236
237
TempValue0 = Screen.XOffset
238
TempValue0 += Screen.CenterX
239
TempValue0 <<= 16
240
TempValue1 = false
241
if Object.XPos < TempValue0
242
Object.XPos += 0x20000
243
if Object.XPos >= TempValue0
244
Object.XPos = TempValue0
245
TempValue1 = true
246
end if
247
else
248
Object.XPos -= 0x20000
249
if Object.XPos <= TempValue0
250
Object.XPos = TempValue0
251
TempValue1 = true
252
end if
253
end if
254
255
TempValue0 = Screen.YOffset
256
TempValue0 += 104
257
TempValue0 <<= 16
258
if Object.YPos < TempValue0
259
Object.YPos += 0x20000
260
if Object.YPos >= TempValue0
261
Object.YPos = TempValue0
262
if TempValue1 == true
263
// In a good position, advance states
264
Object.State++
265
end if
266
end if
267
else
268
Object.YPos -= 0x20000
269
if Object.YPos <= TempValue0
270
Object.YPos = TempValue0
271
if TempValue1 == true
272
// In a good position, advance states
273
Object.State++
274
end if
275
end if
276
end if
277
break
278
279
case HOTARU_PAUSE
280
if Object.Timer < 100
281
Object.Timer++
282
else
283
Object.Timer = 0
284
Object.State++
285
end if
286
Object.Frame = Object.Timer
287
Object.Frame %= 20
288
Object.Frame /= 10
289
Object.Frame++
290
break
291
292
// A series of very small and similar states - each frame in the flicker animation is its own state
293
294
case HOTARU_LIGHTSON1
295
if Object.Timer < 4
296
Object.Timer++
297
else
298
Object.Timer = 0
299
Object.State++
300
end if
301
break
302
303
case HOTARU_LIGHTSOFF1
304
if Object.Timer < 12
305
Object.Timer++
306
else
307
Object.Timer = 0
308
Object.State++
309
end if
310
break
311
312
case HOTARU_LIGHTSON2
313
if Object.Timer < 8
314
Object.Timer++
315
else
316
Object.Timer = 0
317
Object.State++
318
end if
319
break
320
321
case HOTARU_LIGHTSOFF2
322
if Object.Timer < 8
323
Object.Timer++
324
else
325
Object.Timer = 0
326
Object.State++
327
end if
328
break
329
330
case HOTARU_LIGHTSON3
331
if Object.Timer < 12
332
Object.Timer++
333
else
334
Object.Timer = 0
335
Object.State++
336
end if
337
break
338
339
case HOTARU_LIGHTSOFF3
340
if Object.Timer < 4
341
Object.Timer++
342
else
343
Object.Timer = 0
344
Object.AnimationTimer = 0
345
Object.State++
346
end if
347
break
348
349
case HOTARU_FLASHING
350
// This is the state where the Sparks are active, although none of them are managed here
351
// Head over to ObjectDraw and ObjectPlayerInteraction for that
352
353
if Object.Timer < 100
354
Object.Timer++
355
else
356
Object.Timer = 0
357
Object.State++
358
end if
359
Object.Frame = Object.Timer
360
Object.Frame %= 20
361
Object.Frame /= 10
362
Object.Frame++
363
break
364
365
case HOTARU_FLYAWAY
366
// Ascend at a rate of 2 pixels per second
367
// (This is rather slow, isn't it?)
368
Object.YPos -= 0x20000
369
370
// Animate the Object
371
Object.Timer++
372
Object.Timer %= 20
373
Object.Frame = Object.Timer
374
Object.Frame /= 10
375
Object.Frame++
376
377
if Object.OutOfBounds == true
378
Object.State = HOTARU_OFFSCREEN
379
Object.Timer = 0
380
381
// Place the Hotaru back at its starting position
382
// Since state is being set to HOTARU_OFFSCREEN, there won't be any visible teleporting
383
Object.XPos = Object.StartPosX
384
Object.YPos = Object.StartPosY
385
386
// Reset the Hotarus to be 70 pixels apart
387
// (Regardless of their initial Property Value, since that's already been reset)
388
Object.Hotaru1X = -0x230000
389
Object.Hotaru2X = 0x230000
390
end if
391
break
392
393
case HOTARU_OFFSCREEN
394
// This state is here so that, if the Hotarus' start position is within bounds, they won't just suddently teleport in front of the player
395
if Object.OutOfBounds == true
396
Object.State = HOTARU_IDLE1
397
Object.Priority = PRIORITY_BOUNDS
398
Object.Frame = 0
399
end if
400
break
401
402
end switch
403
else
404
switch Object.State
405
case HOTARU_IDLE1
406
Object.Priority = PRIORITY_ACTIVE
407
408
Object.Frame = 3
409
410
// Right range of 43 pixels for Hotaru 1
411
if Object.Hotaru1X < 0x2B0000
412
// Move both Hotarus in their respective directions by a half pixel
413
Object.Hotaru1X += 0x8000
414
Object.Hotaru2X -= 0x8000
415
else
416
// Turn around
417
Object.State = HOTARU_IDLE2
418
end if
419
break
420
421
case HOTARU_IDLE2
422
// Left range of 115 pixels for Hotaru 1
423
if Object.Hotaru1X > -0x730000
424
// Move both Hotarus in their respective directions by a half pixel
425
Object.Hotaru1X -= 0x8000
426
Object.Hotaru2X += 0x8000
427
else
428
// Turn around again
429
Object.State = HOTARU_IDLE1
430
end if
431
break
432
433
case HOTARU_TRACKPLAYER
434
// Make the Hotarus follow the screen
435
436
TempValue0 = Screen.XOffset
437
TempValue0 += Screen.CenterX
438
TempValue0 <<= 16
439
TempValue1 = false
440
if Object.XPos < TempValue0
441
Object.XPos += 0x10000
442
if Object.XPos >= TempValue0
443
Object.XPos = TempValue0
444
TempValue1 = true
445
end if
446
else
447
Object.XPos -= 0x10000
448
if Object.XPos <= TempValue0
449
Object.XPos = TempValue0
450
TempValue1 = true
451
end if
452
end if
453
454
TempValue0 = Screen.YOffset
455
TempValue0 += 104
456
TempValue0 <<= 16
457
if Object.YPos < TempValue0
458
Object.YPos += 0x10000
459
if Object.YPos >= TempValue0
460
Object.YPos = TempValue0
461
if TempValue1 == true
462
Object.State++
463
end if
464
end if
465
else
466
Object.YPos -= 0x10000
467
if Object.YPos <= TempValue0
468
Object.YPos = TempValue0
469
if TempValue1 == true
470
Object.State++
471
end if
472
end if
473
end if
474
break
475
476
case HOTARU_PAUSE
477
if Object.Timer < 100
478
Object.Timer++
479
else
480
Object.Timer = 0
481
Object.State++
482
end if
483
Object.Frame = Object.Timer
484
Object.Frame %= 40
485
Object.Frame /= 20
486
Object.Frame += 4
487
break
488
489
// A series of very small and similar states - each frame in the flicker animation is its own state
490
491
case HOTARU_LIGHTSON1
492
if Object.Timer < 10
493
Object.Timer++
494
else
495
Object.Timer = 0
496
Object.State++
497
end if
498
break
499
500
case HOTARU_LIGHTSOFF1
501
if Object.Timer < 30
502
Object.Timer++
503
else
504
Object.Timer = 0
505
Object.State++
506
end if
507
break
508
509
case HOTARU_LIGHTSON2
510
if Object.Timer < 20
511
Object.Timer++
512
else
513
Object.Timer = 0
514
Object.State++
515
end if
516
break
517
518
case HOTARU_LIGHTSOFF2
519
if Object.Timer < 20
520
Object.Timer++
521
else
522
Object.Timer = 0
523
Object.State++
524
end if
525
break
526
527
case HOTARU_LIGHTSON3
528
if Object.Timer < 30
529
Object.Timer++
530
else
531
Object.Timer = 0
532
Object.State++
533
end if
534
break
535
536
case HOTARU_LIGHTSOFF3
537
if Object.Timer < 10
538
Object.Timer++
539
else
540
Object.Timer = 0
541
Object.AnimationTimer = 0
542
Object.State++
543
end if
544
break
545
546
case HOTARU_FLASHING
547
// This is the state where the Sparks should be active, but Bad Hotarus have broken bulbs so they don't get to light up...
548
549
if Object.Timer < 200
550
Object.Timer++
551
else
552
Object.Timer = 0
553
Object.State++
554
end if
555
Object.Frame = Object.Timer
556
Object.Frame %= 40
557
Object.Frame /= 20
558
Object.Frame += 4
559
break
560
561
case HOTARU_FLYAWAY
562
// Ascend at a rate of 2 pixels per second
563
// (This is rather slow, isn't it?)
564
Object.YPos -= 0x20000
565
566
// Animate the Object
567
Object.Timer++
568
Object.Timer %= 40
569
Object.Frame = Object.Timer
570
Object.Frame /= 20
571
Object.Frame += 4
572
573
if Object.OutOfBounds == true
574
Object.State = HOTARU_OFFSCREEN
575
Object.Timer = 0
576
577
// Place the Hotarus back at their starting positions
578
// Because the State's set to HOTARU_OFFSCREEN, that there won't be any visible teleporting
579
Object.XPos = Object.StartPosX
580
Object.YPos = Object.StartPosY
581
582
// Reset the Hotarus to be 70 pixels apart
583
// (Ignoring their Property Value, since it got replaced by Hotaru Condition during startup)
584
Object.Hotaru1X = -0x230000
585
Object.Hotaru2X = 0x230000
586
end if
587
break
588
589
case HOTARU_OFFSCREEN
590
// This state is here so that, if the Hotarus' start position is within bounds, they won't just visibly teleport in front of the player
591
if Object.OutOfBounds == true
592
Object.State = HOTARU_IDLE1
593
Object.Priority = PRIORITY_BOUNDS
594
Object.Frame = 0
595
end if
596
break
597
598
end switch
599
end if
600
601
CallFunction(StageSetup_CheckGoodFuture)
602
603
if Object.ActiveHotarus == 0
604
// If this object doesn't have any more alive Hotarus, than just unload now as it serves no purpose
605
ResetObjectEntity(Object.EntityNo, TypeName[Blank Object], 0, 0, 0)
606
end if
607
608
end sub
609
610
611
sub ObjectPlayerInteraction
612
switch Object.State
613
case HOTARU_IDLE1
614
case HOTARU_IDLE2
615
// Wandering around aimlessly, just check to see if the player's entered the Hotarus' general range yet
616
PlayerObjectCollision(C_TOUCH, -64, -64, 64, 64)
617
if CheckResult == true
618
// Player stepped within range, start following them...
619
Object.State = HOTARU_TRACKPLAYER
620
end if
621
break
622
623
case HOTARU_LIGHTSON2
624
case HOTARU_LIGHTSOFF2
625
case HOTARU_LIGHTSON3
626
case HOTARU_LIGHTSOFF3
627
if Object.Quality == GOOD_QUALITY
628
// Broken Hotarus don't have functional lights...
629
630
if Object.Hotaru1X < 0
631
TempValue0 = Object.Hotaru1X
632
TempValue0 >>= 16
633
634
TempValue1 = Object.Hotaru2X
635
TempValue1 >>= 16
636
else
637
TempValue0 = Object.Hotaru2X
638
TempValue0 >>= 16
639
640
TempValue1 = Object.Hotaru1X
641
TempValue1 >>= 16
642
end if
643
644
TempValue0 += 8
645
TempValue1 -= 8
646
647
PlayerObjectCollision(C_TOUCH, TempValue0, -60, TempValue1, 60)
648
if CheckResult == true
649
// Player's within the sensor range, turn on the lights
650
Object.EletricityActive = true
651
end if
652
653
end if
654
655
// No break?
656
// Well, I suppose it's not too important...
657
658
end switch
659
660
// Don't do any of these further checks if the Hotaru's not even existent
661
if Object.State < HOTARU_OFFSCREEN
662
663
// Backup the base object's position
664
TempValue6 = Object.XPos
665
TempValue7 = Object.YPos
666
667
// Check collision for Hotaru 1
668
GetBit(TempValue1, Object.ActiveHotarus, 0)
669
if TempValue1 == true
670
671
// Adjust the object's position to match that of Hotaru 1
672
Object.XPos += Object.Hotaru1X
673
Object.YPos -= Object.HotaruRadius
674
675
// Check if there was collision
676
PlayerObjectCollision(C_TOUCH, -8, -8, 8, 8)
677
if CheckResult == true
678
// Check if the Hotaru should be broken
679
680
ArrayPos1 = Object.EntityNo
681
ArrayPos1++
682
CallFunction(Hotaru_HotaruBreak)
683
684
if Object[+1].Type == Flower_TypeNo
685
// The Hotaru was destroyed, blank its according bit
686
SetBit(Object.ActiveHotarus, 0, false)
687
end if
688
end if
689
690
// Restore the Object's position to what it was before
691
Object.XPos = TempValue6
692
Object.YPos = TempValue7
693
end if
694
695
// Check collision for Hotaru 2
696
GetBit(TempValue1, Object.ActiveHotarus, 1)
697
if TempValue1 == true
698
699
// Adjust the Object's Position to be where Hotaru 2 is
700
Object.XPos += Object.Hotaru2X
701
Object.YPos += Object.HotaruRadius
702
703
// Check for collision against this Hotaru
704
PlayerObjectCollision(C_TOUCH, -8, -8, 8, 8)
705
if CheckResult == true
706
// Check if the Hotaru should be destroyed or hurt the player
707
708
ArrayPos1 = Object.EntityNo
709
ArrayPos1 += 2
710
CallFunction(Hotaru_HotaruBreak)
711
712
if Object[+2].Type == Flower_TypeNo
713
// The Hotaru has been destroyed, so set its bit to zero
714
SetBit(Object.ActiveHotarus, 1, false)
715
end if
716
end if
717
end if
718
719
// If needed, check for the Sparks
720
if Object.State == HOTARU_FLASHING
721
if Object.EletricityActive == true
722
723
// That is to say, are both Hotarus active? (3 is 0b11 in bits)
724
if Object.ActiveHotarus == 3
725
726
// Check collision for the Sparks
727
728
// First, backup the object's position
729
Object.XPos = TempValue6
730
Object.YPos = TempValue7
731
732
// Check collision for the middle Spark
733
PlayerObjectCollision(C_TOUCH, -12, -12, 12, 12)
734
if CheckResult == true
735
CallFunction(Player_Hit)
736
end if
737
738
// Check collision for the left Spark
739
740
Object.XPos = TempValue6
741
Object.XPos >>= 1
742
743
TempValue1 = TempValue6
744
TempValue1 += Object.Hotaru1X
745
TempValue1 >>= 1
746
747
Object.XPos += TempValue1
748
Object.YPos = TempValue7
749
Object.YPos >>= 1
750
751
TempValue1 = TempValue7
752
TempValue1 -= Object.HotaruRadius
753
TempValue1 >>= 1
754
755
Object.YPos += TempValue1
756
757
PlayerObjectCollision(C_TOUCH, -12, -12, 12, 12)
758
if CheckResult == true
759
CallFunction(Player_Hit)
760
end if
761
762
// Check collision for the right Spark
763
764
Object.XPos = TempValue6
765
Object.XPos >>= 1
766
767
TempValue1 = TempValue6
768
TempValue1 += Object.Hotaru2X
769
TempValue1 >>= 1
770
771
Object.XPos += TempValue1
772
Object.YPos = TempValue7
773
Object.YPos >>= 1
774
775
TempValue1 = TempValue7
776
TempValue1 += Object.HotaruRadius
777
TempValue1 >>= 1
778
779
Object.YPos += TempValue1
780
781
PlayerObjectCollision(C_TOUCH, -12, -12, 12, 12)
782
if CheckResult == true
783
CallFunction(Player_Hit)
784
end if
785
786
end if
787
end if
788
end if
789
790
// Restore the base object's position to what it should be
791
Object.XPos = TempValue6
792
Object.YPos = TempValue7
793
794
end if
795
796
end sub
797
798
799
sub ObjectDraw
800
switch Object.State
801
case HOTARU_IDLE1
802
GetBit(CheckResult, Object.ActiveHotarus, 0)
803
if CheckResult == true
804
TempValue0 = Object.Hotaru1X
805
TempValue0 += Object.XPos
806
TempValue1 = Object.YPos
807
TempValue1 -= Object.HotaruRadius
808
Object.Direction = FACING_LEFT
809
DrawSpriteFX(Object.Frame, FX_FLIP, TempValue0, TempValue1)
810
end if
811
812
GetBit(CheckResult, Object.ActiveHotarus, 1)
813
if CheckResult == true
814
TempValue0 = Object.Hotaru2X
815
TempValue0 += Object.XPos
816
TempValue1 = Object.YPos
817
TempValue1 += Object.HotaruRadius
818
Object.Direction = FACING_RIGHT
819
DrawSpriteFX(Object.Frame, FX_FLIP, TempValue0, TempValue1)
820
end if
821
break
822
823
case HOTARU_IDLE2
824
GetBit(CheckResult, Object.ActiveHotarus, 0)
825
if CheckResult == true
826
TempValue0 = Object.Hotaru1X
827
TempValue0 += Object.XPos
828
TempValue1 = Object.YPos
829
TempValue1 -= Object.HotaruRadius
830
Object.Direction = FACING_RIGHT
831
DrawSpriteFX(Object.Frame, FX_FLIP, TempValue0, TempValue1)
832
end if
833
834
GetBit(CheckResult, Object.ActiveHotarus, 1)
835
if CheckResult == true
836
TempValue0 = Object.Hotaru2X
837
TempValue0 += Object.XPos
838
TempValue1 = Object.YPos
839
TempValue1 += Object.HotaruRadius
840
Object.Direction = FACING_LEFT
841
DrawSpriteFX(Object.Frame, FX_FLIP, TempValue0, TempValue1)
842
end if
843
break
844
845
case HOTARU_TRACKPLAYER
846
// Always face the Player
847
if Player.XPos < Object.XPos
848
Object.Direction = FACING_RIGHT
849
else
850
Object.Direction = FACING_LEFT
851
end if
852
853
GetBit(CheckResult, Object.ActiveHotarus, 0)
854
if CheckResult == true
855
TempValue0 = Object.Hotaru1X
856
TempValue0 += Object.XPos
857
TempValue1 = Object.YPos
858
TempValue1 -= Object.HotaruRadius
859
DrawSpriteFX(Object.Frame, FX_FLIP, TempValue0, TempValue1)
860
end if
861
862
GetBit(CheckResult, Object.ActiveHotarus, 1)
863
if CheckResult == true
864
TempValue0 = Object.Hotaru2X
865
TempValue0 += Object.XPos
866
TempValue1 = Object.YPos
867
TempValue1 += Object.HotaruRadius
868
DrawSpriteFX(Object.Frame, FX_FLIP, TempValue0, TempValue1)
869
end if
870
break
871
872
case HOTARU_PAUSE
873
Object.XPos = Screen.XOffset
874
Object.XPos += Screen.CenterX
875
Object.XPos <<= 16
876
Object.YPos = Screen.YOffset
877
Object.YPos += 104
878
Object.YPos <<= 16
879
880
GetBit(CheckResult, Object.ActiveHotarus, 0)
881
if CheckResult == true
882
TempValue0 = Object.Hotaru1X
883
TempValue0 += Object.XPos
884
TempValue1 = Object.YPos
885
TempValue1 -= Object.HotaruRadius
886
DrawSpriteFX(Object.Frame, FX_FLIP, TempValue0, TempValue1)
887
end if
888
889
GetBit(CheckResult, Object.ActiveHotarus, 1)
890
if CheckResult == true
891
TempValue0 = Object.Hotaru2X
892
TempValue0 += Object.XPos
893
TempValue1 = Object.YPos
894
TempValue1 += Object.HotaruRadius
895
DrawSpriteFX(Object.Frame, FX_FLIP, TempValue0, TempValue1)
896
end if
897
break
898
899
case HOTARU_LIGHTSON1
900
case HOTARU_LIGHTSON2
901
case HOTARU_LIGHTSON3
902
case HOTARU_FLYAWAY
903
GetBit(CheckResult, Object.ActiveHotarus, 0)
904
if CheckResult == true
905
TempValue0 = Object.Hotaru1X
906
TempValue0 += Object.XPos
907
TempValue1 = Object.YPos
908
TempValue1 -= Object.HotaruRadius
909
DrawSpriteFX(6, FX_FLIP, TempValue0, TempValue1)
910
DrawSpriteFX(Object.Frame, FX_FLIP, TempValue0, TempValue1)
911
end if
912
913
GetBit(CheckResult, Object.ActiveHotarus, 1)
914
if CheckResult == true
915
TempValue0 = Object.Hotaru2X
916
TempValue0 += Object.XPos
917
TempValue1 = Object.YPos
918
TempValue1 += Object.HotaruRadius
919
DrawSpriteFX(6, FX_FLIP, TempValue0, TempValue1)
920
DrawSpriteFX(Object.Frame, FX_FLIP, TempValue0, TempValue1)
921
end if
922
break
923
924
case HOTARU_LIGHTSOFF1
925
case HOTARU_LIGHTSOFF2
926
case HOTARU_LIGHTSOFF3
927
GetBit(CheckResult, Object.ActiveHotarus, 0)
928
if CheckResult == true
929
TempValue0 = Object.Hotaru1X
930
TempValue0 += Object.XPos
931
TempValue1 = Object.YPos
932
TempValue1 -= Object.HotaruRadius
933
DrawSpriteFX(Object.Frame, FX_FLIP, TempValue0, TempValue1)
934
end if
935
936
GetBit(CheckResult, Object.ActiveHotarus, 1)
937
if CheckResult == true
938
TempValue0 = Object.Hotaru2X
939
TempValue0 += Object.XPos
940
TempValue1 = Object.YPos
941
TempValue1 += Object.HotaruRadius
942
DrawSpriteFX(Object.Frame, FX_FLIP, TempValue0, TempValue1)
943
end if
944
break
945
946
case HOTARU_FLASHING
947
GetBit(CheckResult, Object.ActiveHotarus, 0)
948
if CheckResult == true
949
TempValue0 = Object.Hotaru1X
950
TempValue0 += Object.XPos
951
TempValue1 = Object.YPos
952
TempValue1 -= Object.HotaruRadius
953
DrawSpriteFX(6, FX_FLIP, TempValue0, TempValue1)
954
DrawSpriteFX(Object.Frame, FX_FLIP, TempValue0, TempValue1)
955
end if
956
957
GetBit(CheckResult, Object.ActiveHotarus, 1)
958
if CheckResult == true
959
TempValue0 = Object.Hotaru2X
960
TempValue0 += Object.XPos
961
TempValue1 = Object.YPos
962
TempValue1 += Object.HotaruRadius
963
DrawSpriteFX(6, FX_FLIP, TempValue0, TempValue1)
964
DrawSpriteFX(Object.Frame, FX_FLIP, TempValue0, TempValue1)
965
end if
966
967
if Object.EletricityActive == true
968
if Object.ActiveHotarus == 3 // Both bits in the number active, meaning that both Hotarus are alive and well
969
970
// Draw the Middle Spark
971
TempValue0 = Object.AnimationTimer
972
TempValue0 >>= 1
973
TempValue0 += 7
974
DrawSprite(TempValue0)
975
976
// Draw the Left Spark
977
978
TempValue1 = Object.XPos
979
TempValue1 >>= 1
980
981
TempValue3 = Object.XPos
982
TempValue3 += Object.Hotaru1X
983
TempValue3 >>= 1
984
985
TempValue1 += TempValue3
986
987
TempValue2 = Object.YPos
988
TempValue2 >>= 1
989
990
TempValue3 = Object.YPos
991
TempValue3 -= Object.HotaruRadius
992
TempValue3 >>= 1
993
994
TempValue2 += TempValue3
995
996
DrawSpriteXY(TempValue0, TempValue1, TempValue2)
997
998
// Draw the Right Spark
999
1000
TempValue1 = Object.XPos
1001
TempValue1 >>= 1
1002
1003
TempValue3 = Object.XPos
1004
TempValue3 += Object.Hotaru2X
1005
TempValue3 >>= 1
1006
1007
TempValue1 += TempValue3
1008
1009
TempValue2 = Object.YPos
1010
TempValue2 >>= 1
1011
1012
TempValue3 = Object.YPos
1013
TempValue3 += Object.HotaruRadius
1014
TempValue3 >>= 1
1015
1016
TempValue2 += TempValue3
1017
1018
DrawSpriteXY(TempValue0, TempValue1, TempValue2)
1019
1020
// Animate the Sparks
1021
Object.AnimationTimer++
1022
Object.AnimationTimer %= 6
1023
1024
end if
1025
end if
1026
break
1027
1028
end switch
1029
1030
end sub
1031
1032
1033
sub ObjectStartup
1034
1035
LoadSpriteSheet("R7/Objects.gif")
1036
1037
// Hotaru Frames
1038
1039
// "Good" type frames (normal lightbulb)
1040
SpriteFrame(-20, -12, 40, 24, 182, 121)
1041
SpriteFrame(-12, -20, 24, 40, 231, 119)
1042
SpriteFrame(-12, -20, 24, 40, 231, 160)
1043
1044
// "Bad" type frames (broken lightbulb)
1045
SpriteFrame(-20, -12, 40, 24, 182, 146)
1046
SpriteFrame(-12, -20, 24, 40, 231, 201)
1047
SpriteFrame(-12, -20, 24, 40, 206, 215)
1048
1049
// blulb
1050
SpriteFrame(-11, -7, 24, 24, 175, 196)
1051
1052
// Eletricity Frames
1053
SpriteFrame(-12, -12, 24, 24, 150, 171)
1054
SpriteFrame(-12, -12, 24, 24, 175, 171)
1055
SpriteFrame(-12, -12, 24, 24, 150, 196)
1056
1057
// Cycle through all Hotaru objects in the level
1058
ArrayPos0 = 32
1059
while ArrayPos0 < 1056
1060
if Object[ArrayPos0].Type == TypeName[Hotaru]
1061
1062
// First, set their starting positions
1063
Object[ArrayPos0].StartPosX = Object[ArrayPos0].XPos
1064
Object[ArrayPos0].StartPosY = Object[ArrayPos0].YPos
1065
1066
// Hotarus draw above other objects, including the Player
1067
Object[ArrayPos0].DrawOrder = 5
1068
1069
// Setup the Hotarus' type and initial positions
1070
switch Object[ArrayPos0].Quality
1071
case 0
1072
// Properties of:
1073
// Radius of 60 pixels
1074
// Good variant, with a functioning Light
1075
// Start the Hotarus 70 pixels apart (35 pixels from the base Pos)
1076
Object[ArrayPos0].HotaruRadius = 0x3C0000
1077
Object[ArrayPos0].Quality = GOOD_QUALITY
1078
Object[ArrayPos0].Hotaru1X = -0x230000
1079
Object[ArrayPos0].Hotaru2X = 0x230000
1080
break
1081
1082
case 1
1083
// Properties of:
1084
// Radius of 60 pixels
1085
// Bad variant, with a broken Light
1086
// Start the Hotarus 70 pixels apart (35 pixels from the base Pos)
1087
Object[ArrayPos0].HotaruRadius = 0x3C0000
1088
Object[ArrayPos0].Quality = BAD_QUALITY
1089
Object[ArrayPos0].Hotaru1X = -0x230000
1090
Object[ArrayPos0].Hotaru2X = 0x230000
1091
break
1092
1093
case 4
1094
// Properties of:
1095
// Radius of 68 pixels
1096
// Good variant, with a functioning Light
1097
// Start the Hotarus 100 pixels apart (50 pixels from the base Pos)
1098
Object[ArrayPos0].HotaruRadius = 0x440000
1099
Object[ArrayPos0].Quality = GOOD_QUALITY
1100
Object[ArrayPos0].Hotaru1X = -0x320000
1101
Object[ArrayPos0].Hotaru2X = 0x320000
1102
break
1103
1104
case 5
1105
// Properties of:
1106
// Radius of 68 pixels
1107
// Bad variant, with a broken Light
1108
// Start the Hotarus 100 pixels apart (50 pixels from the base Pos)
1109
Object[ArrayPos0].HotaruRadius = 0x440000
1110
Object[ArrayPos0].Quality = BAD_QUALITY
1111
Object[ArrayPos0].Hotaru1X = -0x320000
1112
Object[ArrayPos0].Hotaru2X = 0x320000
1113
break
1114
1115
case 8
1116
// Properties of:
1117
// Radius of 56 pixels
1118
// Good variant, with a functioning Light
1119
// Start the Hotarus 140 pixels apart (70 pixels from the base Pos)
1120
Object[ArrayPos0].HotaruRadius = 0x380000
1121
Object[ArrayPos0].Quality = GOOD_QUALITY
1122
Object[ArrayPos0].Hotaru1X = -0x460000
1123
Object[ArrayPos0].Hotaru2X = 0x460000
1124
break
1125
1126
case 9
1127
// Properties of:
1128
// Radius of 56 pixels
1129
// Bad variant, with a broken Light
1130
// Start the Hotarus 140 pixels apart (70 pixels from the base Pos)
1131
Object[ArrayPos0].HotaruRadius = 0x380000
1132
Object[ArrayPos0].Quality = BAD_QUALITY
1133
Object[ArrayPos0].Hotaru1X = -0x460000
1134
Object[ArrayPos0].Hotaru2X = 0x460000
1135
break
1136
1137
case 12
1138
// Properties of:
1139
// Radius of 18 pixels
1140
// Good variant, with a functioning Light
1141
// Start the Hotarus 192 pixels apart (96 pixels from the base Pos)
1142
Object[ArrayPos0].HotaruRadius = 0x120000
1143
Object[ArrayPos0].Quality = GOOD_QUALITY
1144
Object[ArrayPos0].Hotaru1X = -0x600000
1145
Object[ArrayPos0].Hotaru2X = 0x600000
1146
break
1147
1148
case 13
1149
// Properties of:
1150
// Radius of 18 pixels
1151
// Bad variant, with a broken Light
1152
// Start the Hotarus 192 pixels apart (96 pixels from the base Pos)
1153
Object[ArrayPos0].HotaruRadius = 0x120000
1154
Object[ArrayPos0].Quality = BAD_QUALITY
1155
Object[ArrayPos0].Hotaru1X = -0x600000
1156
Object[ArrayPos0].Hotaru2X = 0x600000
1157
break
1158
1159
end switch
1160
1161
// Set both of the Object's Hotarus to active
1162
SetBit(Object[ArrayPos0].ActiveHotarus, 0, true)
1163
SetBit(Object[ArrayPos0].ActiveHotarus, 1, true)
1164
end if
1165
1166
ArrayPos0++
1167
loop
1168
1169
end sub
1170
1171
1172
// ========================
1173
// Editor Subs
1174
// ========================
1175
1176
sub RSDKEdit
1177
if Editor.ReturnVariable == true
1178
switch Editor.VariableID
1179
case EDIT_VAR_PROPVAL // Property Value
1180
CheckResult = Object.PropertyValue
1181
break
1182
case 0 // Condition
1183
CheckResult = Object.PropertyValue
1184
CheckResult &= 1
1185
break
1186
case 1 // Formation
1187
CheckResult = Object.PropertyValue
1188
CheckResult &= 254
1189
break
1190
end switch
1191
else
1192
switch Editor.VariableID
1193
case EDIT_VAR_PROPVAL // Property Value
1194
Object.PropertyValue = Editor.VariableValue
1195
break
1196
case 0 // Condition
1197
CheckNotEqual(Editor.VariableValue, false)
1198
SetBit(Object.PropertyValue, 0, CheckResult)
1199
break
1200
case 1 // Formation
1201
Object.PropertyValue &= 1
1202
Editor.VariableValue &= 254
1203
Object.PropertyValue |= Editor.VariableValue
1204
break
1205
end switch
1206
end if
1207
end sub
1208
1209
1210
sub RSDKDraw
1211
// We got a whole lot to do, we're essentially setting up a new Hotaru every time since we can't depend on values from the last frame
1212
1213
// First, get the sprite to be used
1214
GetBit(TempValue5, Object.Quality, 0)
1215
1216
// Then, get the movement values based on the Hotaru's designated Formation
1217
TempValue0 = Object.Quality
1218
TempValue0 &= 254
1219
1220
switch TempValue0
1221
case 0
1222
default // inaccurate to how it'll be in-game if given an invalid formation, but it doesn't matter too much
1223
TempValue2 = 0x3C0000
1224
TempValue3 = -0x230000
1225
break
1226
case 4
1227
TempValue2 = 0x440000
1228
TempValue3 = -0x320000
1229
break
1230
case 8
1231
TempValue2 = 0x380000
1232
TempValue3 = -0x460000
1233
break
1234
case 12
1235
TempValue2 = 0x120000
1236
TempValue3 = -0x600000
1237
break
1238
end switch
1239
1240
TempValue4 = TempValue3
1241
FlipSign(TempValue4)
1242
1243
TempValue0 = TempValue3
1244
TempValue0 += Object.XPos
1245
TempValue1 = Object.YPos
1246
TempValue1 -= TempValue2
1247
Object.Direction = FACING_LEFT
1248
DrawSpriteFX(TempValue5, FX_FLIP, TempValue0, TempValue1)
1249
1250
TempValue6 = TempValue4
1251
TempValue6 += Object.XPos
1252
TempValue7 = Object.YPos
1253
TempValue7 += TempValue2
1254
Object.Direction = FACING_RIGHT
1255
DrawSpriteFX(TempValue5, FX_FLIP, TempValue6, TempValue7)
1256
1257
if Editor.ShowGizmos == true
1258
Editor.DrawingOverlay = true
1259
1260
// First, draw the Hotaru's movement ranges
1261
DrawLine(TempValue0, TempValue1, TempValue6, TempValue1, 255, 255, 255)
1262
DrawLine(TempValue6, TempValue7, TempValue0, TempValue7, 255, 255, 255)
1263
1264
Object.InkEffect = 1
1265
1266
// Then, draw some lines to indicate this Hotaru's following Flower entities
1267
DrawArrow(Object.XPos, Object.YPos, Object[+1].XPos, Object[+1].YPos, 255, 255, 0)
1268
DrawArrow(Object.XPos, Object.YPos, Object[+2].XPos, Object[+2].YPos, 255, 255, 0)
1269
1270
Object.InkEffect = 0
1271
1272
Editor.DrawingOverlay = false
1273
end if
1274
end sub
1275
1276
1277
sub RSDKLoad
1278
LoadSpriteSheet("R7/Objects.gif")
1279
SpriteFrame(-20, -12, 40, 24, 182, 121)
1280
SpriteFrame(-20, -12, 40, 24, 182, 146)
1281
1282
AddEditorVariable("Condition")
1283
SetActiveVariable("Condition")
1284
AddEnumVariable("Good", GOOD_QUALITY)
1285
AddEnumVariable("Bad", BAD_QUALITY)
1286
1287
AddEditorVariable("Formation")
1288
SetActiveVariable("Formation")
1289
AddEnumVariable("Formation A", 0)
1290
AddEnumVariable("Formation B", 4)
1291
AddEnumVariable("Formation C", 8)
1292
AddEnumVariable("Formation D", 12)
1293
end sub
1294
1295