/
RPSamael.lua
1955 lines (1717 loc) · 75.2 KB
/
RPSamael.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
local SamaelMod = {}
-- Includes
local RPGlobals = require("src/rpglobals")
local RPSchoolbag = require("src/rpschoolbag")
-- Samael was originally created by Ghostbroster
-- It is heavily modified by Zamiel for Racing+
-- (and to pass the linter)
--References and junk
local scytheID = Isaac.GetEntityTypeByName("Samael Scythe") --Entity ID of the scythe weapon entity
local samaelID = Isaac.GetPlayerTypeByName("Samael") --Character ID of Samael
local projVariant = Isaac.GetEntityVariantByName("Magic Scythe") --Entity variant number of the scythe projectile
local specialAnim = Isaac.GetEntityTypeByName("Samael Special Animations") --Entity for showing special animations
local hitBoxType = 617 --Subtype of the scythe's hitbox entity (It is a subtype of a Sacrificial Dagger)
local hood = Isaac.GetCostumeIdByPath("gfx/characters/samaelhood.anm2") --Hood+horns+bandages costume
local cloak = Isaac.GetCostumeIdByPath("gfx/characters/samaelcloak.anm2") --Cloak costume
local samaelDeadEye = Isaac.GetItemIdByName("Samael Dead Eye") --Custom version of deadeye for Samael
local samaelChocMilk = Isaac.GetItemIdByName("Samael Chocolate Milk") --Replaces chocolate milk for Samael
local samaelDrFetus = Isaac.GetItemIdByName("Samael Dr. Fetus")
--Replaces Dr. Fetus for Samael if brimstone is also acquired
local samaelMarked = Isaac.GetItemIdByName("Samael Marked") --Replaces marked for Samael
local wraithItem = Isaac.GetItemIdByName("Wraith Skull") --Spacebar Wraith Mode Activation
local deadEyeCountdown = 3
--Wraith meter HUD sprite
local wraithMeter = Sprite()
wraithMeter:Load("gfx/samael_wraithmeter.anm2", true)
--local wraithIsCharged = false
--Static variables (can be used as tweaks/settings)
local scytheDamageMultiplier = 1.0 --Scythe damage = damage stat * this
local scytheProjectileDamageMultiplier = 1.0 --Scythe projectile damage = damage stat * this
local chargeTimeMax = 40 --Maximum number of frames to charge a projectile
local chargeTimeMid = 20 --Charge time at default fire delay (10)
local chargeTimeMin = 10 --Minimum number of frames to charge a projectile
local swingDelayCap = 30 --Cap on how high the swingDelay can be (maximum frames between scythe swings)
local knockbackMagnitude = 4 --How much of a knockback effect the scythe has
local luckCap = 15 --How much luck samael needs to get status effects 100% of the time with melee
--local maxSizeRange = 50 --How much range to get a scythe thats twice as big
--Dynamic variables (they change)
local spawned = false --Is the scythe spawned?
local scytheState = 0 --State of the scythe
local scytheScale = 1 --Size of the scythe
local scytheColor --For storing what colour the scythe should be
local chargeTime = 0 --Number of frames required to charge a projectile
local charge = 0 --Current charge (For charging a projectile)
local lastCharge = 0
--Amount charged before being released (used for mom's knife so you need to charge at least 5 frames)
local lastDirection = -1 --Saves the last attack direction in order to keep track of it while an attack is taking place
local hitPos = nil --Where the hitbox for the melee scythe should be placed
local swing = 0 --Alternating value that denotes whether the scythe is doing a left or right swing
local swingDelay = 0 --If above 0, this is the number of frames until the scythe can be swung again
local costumeEquipped = false --Are his costumes equipped?
local dying = false --Flag for dying animation (to activate custom death animation)
local itemChecks = {}
--Array for checking if certain items are held to give damage boosts (multishot items, blood clot etc)
local numItems = -1 --Number of items currently held (used to identify when you get a new item)
local canShoot = false --If false, stops the player from firing tears normally
local isaacDying = false
local hits = 0 --Number of hits the scythe made in the current swing
local properDamage = 3.5 --For keeping track of the player's proper damage stat when applying boosts
local deadEyeBoost = 0 --For keeping track of how much damage was added with deadeye
local epiphoraCounter = 0 --How many times you've swung in the same direction
local lastEpiphoraDirection = -1
local hideScythe = false --Visibly hide the scythe (used with mom's knife synergy)
local laserRingSpawned = false --For spawning tech x rings around samael when he swings
local mawSoundCanceler = 0 --For blocking the maw of the void sound effect with the Godhead synergy's light circle
--local spawnSkull = false
--Three Dollar Bill/Fruit Cake effects and other tear effect related variables
local rainbowEffects = {
"slow",
"fear",
"fire",
"confuse",
"freeze",
"creep",
"poison",
"pee",
"charm",
}
local threeDollarBillEffect = "none"
local cakeEffects = {
"parasite",
"bone",
"greed",
"fire",
"keeper",
"light",
"confuse",
"shock",
"freeze",
"fear",
"charm",
"creep",
"shrink",
"fly",
"poison",
"slow",
}
local threeDollarBillTimer = 0
local fruitCakeEffect = "none"
local parasiteTriggered = false
local jacobTriggered = false
local wraithTime = 0 --Time left for wraith mode
local wraithCharge = 0 --How much the wraith ability has been charged (out of 100)
local wraithActive = false --Is wraith form active?
local wraithCooldown = 0 --Brief cooldown after wraith form where you still can't take damage
local lastFrameWraithCharge = 0 --% of wraith meter charged during last update
local wraithChargeCooldown = 0 --Cooldown before the wraith meter charges normally again
local wraithActivationCooldown = 0 --Minimum cooldown between wraith mode activations
local wraithChargePenalty = 0
local fireDelayPenalty = 0 --Nerfs
local fireDelayReduced = false
local scytheAnimationEndFrame = 0
function SamaelMod:PostUpdate()
local player = Isaac.GetPlayer(0)
if player:GetPlayerType() == samaelID then --If the player is Samael
local level = Game():GetLevel()
local room = level:GetCurrentRoom()
if wraithActivationCooldown > 0 then
wraithActivationCooldown = wraithActivationCooldown - 1
end
if mawSoundCanceler > 0 then
SamaelMod:playSound(426, 0, 3)
mawSoundCanceler = mawSoundCanceler - 1
end
local roomFrames = room:GetFrameCount() --Framecount of current room (to identify a new room)
SamaelMod:wraithModeHandler()
if player:HasCollectible(wraithItem) then
player:SetActiveCharge(math.ceil(wraithCharge))
-- Mute the annoying sound effect
local sfx = SFXManager()
sfx:Stop(SoundEffect.SOUND_BEEP) -- 171
elseif RPGlobals.run.schoolbag.item == wraithItem then
RPGlobals.run.schoolbag.charge = math.ceil(wraithCharge)
end
if wraithChargeCooldown > 0 then
wraithChargeCooldown = wraithChargeCooldown - 1
end
if roomFrames == 1 then
--Respawn scythe every room (It does not persist otherwise. I prefer it this way.
-- It's easy to manage, since this is all you have to do to fix it.)
--Isaac.SaveModData(SamaelMod, tostring(math.floor(wraithCharge))) --Also save the wraithCharge
spawned = false
end
if not canShoot then
player.FireDelay = 10 --Disable tears
end
--Custom death animation
if player:GetSprite():IsPlaying("Death") then --When player dies
if not dying then
--Spawn the special animations entity
local special = Isaac.Spawn(specialAnim, 0, 0, player.Position, Vector(0,0), player):ToNPC()
special:ClearEntityFlags(EntityFlag.FLAG_APPEAR)
special:GetSprite():Play("Death", 1) --Play custom death animation
special.CanShutDoors = false
dying = true --Set dying flag
end
player:GetSprite().Color = Color(0,0,0,0,0,0,0) --Make the player invisible
elseif dying then --If the player is not dying, and the dying flag is on
dying = false --Turn the flag off
player:GetSprite().Color = Color(1,1,1,1,0,0,0)
end
--Three Dollar Bill
if player:HasCollectible(CollectibleType.COLLECTIBLE_3_DOLLAR_BILL) then
if threeDollarBillTimer <= 0 then
threeDollarBillTimer = 100
math.randomseed(Isaac.GetFrameCount() + Game():GetFrameCount())
threeDollarBillEffect = rainbowEffects[ math.random( #rainbowEffects ) ]
SamaelMod:getScytheColor()
else
threeDollarBillTimer = threeDollarBillTimer - 1
end
else
threeDollarBillTimer = 0
threeDollarBillEffect = "none"
end
--Only spawn epic fetus targets if the fire button has been briefly held down
if player:HasCollectible(CollectibleType.COLLECTIBLE_EPIC_FETUS) and charge < 5 then
player.FireDelay = 3
end
if not spawned then --If scythe is not spawned
local scythe = Isaac.Spawn(scytheID, 0, 0, player.Position, Vector(0,0), player) --Spawn the scythe
scythe = scythe:ToNPC()
SamaelMod:getScytheColor()
scythe:GetSprite().Color = scytheColor
scythe.GridCollisionClass = GridCollisionClass.COLLISION_NONE
scythe:ClearEntityFlags(EntityFlag.FLAG_APPEAR) --Skip spawning animations
scythe.CanShutDoors = false --Its not an enemy
scytheState = 0 --Reset scythe state
--Charge time persists, but set it back to the cap to trigger the flashing again if need be
if charge > chargeTime then
charge = chargeTime
end
spawned = true --Scythe is spawned
laserRingSpawned = false
end
if chargeTime == 0 then --Set charge time on init (only really activates upon using the luamod command)
SamaelMod:calcChargeTime()
end
--Replace deadeye with a custom item for samael
if player:HasCollectible(CollectibleType.COLLECTIBLE_DEAD_EYE) then
player:RemoveCollectible(CollectibleType.COLLECTIBLE_DEAD_EYE)
Isaac.DebugString("Removing collectible " .. CollectibleType.COLLECTIBLE_DEAD_EYE)
player:AddCollectible(samaelDeadEye, 0, false)
end
--Replace chocolate milk with a custom item for samael
if player:HasCollectible(CollectibleType.COLLECTIBLE_CHOCOLATE_MILK) then
player:RemoveCollectible(CollectibleType.COLLECTIBLE_CHOCOLATE_MILK)
Isaac.DebugString("Removing collectible " .. CollectibleType.COLLECTIBLE_CHOCOLATE_MILK)
player:AddCollectible(samaelChocMilk, 0, false)
end
--Replace Dr Fetus with a custom item if brimstone is also aquired
if player:HasCollectible(CollectibleType.COLLECTIBLE_BRIMSTONE) and
player:HasCollectible(CollectibleType.COLLECTIBLE_DR_FETUS) then
player:RemoveCollectible(CollectibleType.COLLECTIBLE_DR_FETUS)
Isaac.DebugString("Removing collectible " .. CollectibleType.COLLECTIBLE_DR_FETUS)
player:AddCollectible(samaelDrFetus, 0, false)
elseif not player:HasCollectible(CollectibleType.COLLECTIBLE_BRIMSTONE) and
player:HasCollectible(samaelDrFetus) then
player:RemoveCollectible(samaelDrFetus)
Isaac.DebugString("Removing collectible " .. tostring(samaelDrFetus) .. " (Samael Dr. Fetus)")
player:AddCollectible(CollectibleType.COLLECTIBLE_DR_FETUS, 0, false)
end
--Marked is awful, replace it
if player:HasCollectible(CollectibleType.COLLECTIBLE_MARKED) then
player:RemoveCollectible(CollectibleType.COLLECTIBLE_MARKED)
Isaac.DebugString("Removing collectible " .. CollectibleType.COLLECTIBLE_MARKED)
player:AddCollectible(samaelMarked, 0, false)
end
--Cursed eye + dr fetus doesnt work, so lets just get rid of the shitty item
if player:HasCollectible(CollectibleType.COLLECTIBLE_CURSED_EYE) and
player:HasCollectible(CollectibleType.COLLECTIBLE_DR_FETUS) then
player:RemoveCollectible(CollectibleType.COLLECTIBLE_CURSED_EYE)
Isaac.DebugString("Removing collectible " .. CollectibleType.COLLECTIBLE_CURSED_EYE)
end
--Checking for certain items that are incompatable with Samael's standard scythe swing,
--and granting bonus damage for them
if numItems ~= player:GetCollectibleCount() then
local itemChecksNew = {
player:GetCollectibleNum(CollectibleType.COLLECTIBLE_CHEMICAL_PEEL),
player:GetCollectibleNum(CollectibleType.COLLECTIBLE_BLOOD_CLOT),
player:GetCollectibleNum(CollectibleType.COLLECTIBLE_CUPIDS_ARROW),
player:GetCollectibleNum(CollectibleType.COLLECTIBLE_SAGITTARIUS),
player:GetCollectibleNum(CollectibleType.COLLECTIBLE_LUMP_OF_COAL)*2,
}
if numItems == -1 then --Resetting after player init
itemChecks = {}
for i = 1, #itemChecksNew do
itemChecks[i] = 0
end
end
for i = 1, #itemChecks do --Check if any of the values have changed
if itemChecks[i] ~= itemChecksNew[i] then
player:AddCacheFlags(CacheFlag.CACHE_DAMAGE)
break
end
end
itemChecks = itemChecksNew
numItems = player:GetCollectibleCount()
player:EvaluateItems()
end
else
--Make sure custom items do not persist outside of Samael
if player:HasCollectible(samaelDeadEye) then
player:RemoveCollectible(samaelDeadEye)
Isaac.DebugString("Removing collectible " .. tostring(samaelDeadEye) .. " (Samael Dead Eye)")
player:AddCollectible(CollectibleType.COLLECTIBLE_DEAD_EYE, 0, false)
end
if player:HasCollectible(samaelChocMilk) then
player:RemoveCollectible(samaelChocMilk)
Isaac.DebugString("Removing collectible " .. tostring(samaelChocMilk) .. " (Samael Chocolate Milk)")
player:AddCollectible(CollectibleType.COLLECTIBLE_CHOCOLATE_MILK, 0, false)
end
if player:HasCollectible(samaelDrFetus) then
player:RemoveCollectible(samaelDrFetus)
Isaac.DebugString("Removing collectible " .. tostring(samaelDrFetus) .. " (Samael Dr. Fetus)")
player:AddCollectible(CollectibleType.COLLECTIBLE_DR_FETUS, 0, false)
end
if player:HasCollectible(samaelMarked) then
player:RemoveCollectible(samaelMarked)
Isaac.DebugString("Removing collectible " .. tostring(samaelMarked) .. " (Samael Marked)")
player:AddCollectible(CollectibleType.COLLECTIBLE_MARKED, 0, false)
end
if not player:GetSprite():IsPlaying("Death") and dying then
dying = false --Turn the flag off
player:GetSprite().Color = Color(1,1,1,1,0,0,0)
end
if costumeEquipped then
player:TryRemoveNullCostume(cloak)
player:TryRemoveNullCostume(hood)
costumeEquipped = false
end
--if spawnSkull then spawnSkull = false end
end
end
--------Wraith mode functionalities--------
function SamaelMod:wraithModeHandler()
local player = Isaac.GetPlayer(0)
--local controller = player.ControllerIndex
local roomFrames = Game():GetLevel():GetCurrentRoom():GetFrameCount()
--Framecount of current room (to identify a new room)
--Stop wraith form
if wraithActive and
(player:GetSprite():IsPlaying("Trapdoor") or
roomFrames == 1 or
dying or
isaacDying or
player:HasCollectible(wraithItem) == false) then
SamaelMod:triggerWraithModeEnd()
end
if wraithCooldown > 0 then --On cooldown after wraith form wears off (briefly flashing and still invulnerable)
wraithCooldown = wraithCooldown - 1
if wraithCooldown % 4 == 0 then
player:SetColor(Color(0.6,0.3,0.3,0.4,0,0,0), 2, 990, false, false)
end
if wraithCooldown == 0 then
wraithActive = false
end
elseif wraithActive then --Full wraith form is active
wraithTime = wraithTime - 1
wraithCharge = 0
player:GetSprite().Color = Color(0,0,0,0,0,0,0)
Isaac.Spawn(EntityType.ENTITY_EFFECT, EffectVariant.DARK_BALL_SMOKE_PARTICLE, 0,
player.Position, Vector(0,0), player) --Smoke trail
if wraithTime == 0 then --When wraith time is over
wraithCooldown = 24
player.MoveSpeed = player.MoveSpeed - 0.3
--Isaac.DebugString("Decreased speed from the Wraith ability being over: " .. tostring(player.MoveSpeed))
player.EntityCollisionClass = EntityCollisionClass.ENTCOLL_ALL
SamaelMod:playSound(316, 1.8, 1.25)
--Black poof effect
local poof = Isaac.Spawn(EntityType.ENTITY_EFFECT, EffectVariant.POOF02, 0,
player.Position, Vector(0,0), player):ToEffect()
poof:GetSprite().Color = Color(0, 0, 0, 0.66, 0, 0, 0)
poof:FollowParent(player)
end
end
end
function SamaelMod:triggerWraithModeEnd()
local player = Isaac.GetPlayer(0)
if dying then
player:SetColor(Color(0, 0, 0, 0, 0, 0, 0), 57, 999, false, false)
end
if wraithCooldown == 0 then
player.MoveSpeed = player.MoveSpeed - 0.3
--Isaac.DebugString("Decreased speed from the Wraith ability being over (inside triggerWraithModeEnd function): " ..
--tostring(player.MoveSpeed))
end
wraithActive = false
wraithCooldown = 0
wraithTime = 0
player:GetSprite().Color = Color(1, 1, 1, 1, 0, 0, 0)
player.EntityCollisionClass = EntityCollisionClass.ENTCOLL_ALL
Isaac.DebugString("Ended wraith mode.")
end
-----------NPC update function for the scythe entity-----------
function SamaelMod:scytheUpdate(scythe)
local player = Isaac.GetPlayer(0)
scythe = scythe:ToNPC()
if player:GetPlayerType() ~= samaelID then
scythe:Remove()
return
end
if dying then
return
end
local hitBox --Local variable to store reference to hitbox entity
if scythe.Child == nil then --If the scythe has no child (no spawned hitbox)
hitBox = Isaac.Spawn(EntityType.ENTITY_FAMILIAR, FamiliarVariant.SACRIFICIAL_DAGGER, hitBoxType,
player.Position, Vector(0,0), scythe) --Spawn the hitbox
hitBox:ClearEntityFlags(EntityFlag.FLAG_APPEAR) --Skip appear animations
hitBox = hitBox:ToFamiliar()
scythe.Child = hitBox --Set it as the scythe's child
hitBox.Parent = scythe --Set the scythe as its parent
hitBox.CollisionDamage = 0 --No Collision damage until activated
hitBox.Coins = -1
--Coins is used to store directions, because the actual direction-related attributes
--were crashing my game for some reason
hitBox.Size = 40 --Set its size (how big of a radius)
hitBox.Position = Vector(0,0) --Move it off of the screen
hitBox.EntityCollisionClass = EntityCollisionClass.ENTCOLL_ENEMIES
else
hitBox = scythe.Child:ToFamiliar() --If scythe has a child, then the hitbox exists. Set this as a reference to it
end
local sprite = scythe:GetSprite() --The Scythe's sprite
local headDirection = player:GetHeadDirection()
local fireDirection = player:GetFireDirection()
local direction = -1
local projVel = Vector(0,0) --For storing the proper velocity of a projectile (calculated later)
--local proj = nil --For storing a projectile when fired
--Keep the scythe on the player
scythe.Position = Vector(player.Position.X, player.Position.Y)
scythe.Velocity = player.Velocity
if swingDelay > 0 then --Decrement the swingdelay (if it exists)
swingDelay = swingDelay - 1
end
--Spawn a tech x laser around Samael when he swings his scythe with that item acquired
if player:HasCollectible(CollectibleType.COLLECTIBLE_TECH_X) then
if scythe.Target == nil then
laserRingSpawned = false
end
if not laserRingSpawned and scytheState == 2 then
local laser = player:FireTechXLaser(player.Position, Vector(0,0), 66):ToLaser()
laser.Parent = scythe
if laser.Variant ~= 2 then
laser.Variant = 2
laser.SpriteScale = Vector(0.5, 1)
end
laser.TearFlags = laser.TearFlags | 1<<36
laser.CollisionDamage = laser.CollisionDamage*0.3
scythe.Target = laser
laserRingSpawned = true
end
end
--Hide the scythe whenever it is "thrown" via the mom's knife synergy
if hideScythe or isaacDying then
scythe:GetSprite().Color = Color(0,0,0,0,0,0,0)
swingDelay = 4
else
if player:HasCollectible(samaelDeadEye) and
not player:HasCollectible(CollectibleType.COLLECTIBLE_MOMS_KNIFE) then --Set redness for deadEye boost
scytheColor.RO = (deadEyeBoost/properDamage)/2
end
scythe:GetSprite().Color = scytheColor --Set colour
end
if hideScythe and not player:HasCollectible(CollectibleType.COLLECTIBLE_MOMS_KNIFE) then
hideScythe = false
end
--scytheState 0 = Idle, not attacking
--scytheState 1 = Ready/Charging, holding down an attack direction (holding up the scythe)
--scytheState 2 = Swinging the scythe
--READY ATTACK: When the player is holding down a fire direction, and the scythe is not on cooldown
if fireDirection ~= -1 and swingDelay == 0 then
if not canShoot or player:HasCollectible(CollectibleType.COLLECTIBLE_MOMS_KNIFE) then
if charge < chargeTime then --Make the scythe being held up, unless the projectile attack is charged...
if scytheScale >= 1.5 then
sprite:SetFrame("BigSwing", 0) --Play scythe swing animation
else
sprite:SetFrame("Swing", 0) --Play scythe swing animation
end
elseif charge == chargeTime then --If the player has been charging long enough to fire a projectile, flash red
if not player:HasCollectible(CollectibleType.COLLECTIBLE_MOMS_KNIFE) then
if scytheScale >= 1.5 then
sprite:Play("BigCharge", 1)
else
sprite:Play("Charge", 1)
end
end
end
if not hideScythe then
charge = charge+1 --Add charge
end
else
if player:HasCollectible(CollectibleType.COLLECTIBLE_EPIC_FETUS) then
charge = charge + 1 --Add charge
end
if scytheScale >= 1.5 then
sprite:Play("BigIdle", 1)
else
sprite:Play("Idle", 1)
end
end
if scytheState == 2 then --If previous attack was interrupted (due to fast attack rate)
hitBox.Coins = -1 --Reset hitbox (explained more later)
hitBox.CollisionDamage = 0
SamaelMod:deadEyeFunc(true)
if swing == 0 then --Switch 'swing' value (left or right swing)
swing = 1
else
swing = 0
end
end
scytheState = 1 --Scythe is ready, or charging
lastDirection = fireDirection --Update with current attack direction
direction = lastDirection --Current direction for rendering
end
--INITIATE ATTACK: When the player releases the fire direction, swing the scythe
if fireDirection == -1 and scytheState == 1 then
-- Do not fire a projectile when player picks something up
if player:IsHoldingItem() then
charge = 0
end
--If they were charging long enough to fire a projectile...
if charge >= chargeTime and
not player:HasCollectible(CollectibleType.COLLECTIBLE_MOMS_KNIFE) and
not player:HasCollectible(CollectibleType.COLLECTIBLE_EPIC_FETUS) then
--Choose the velocity of the projectile based on the last fire direction before release
if lastDirection == Direction.UP then
projVel = Vector(0, (-1)*player.ShotSpeed*10)
elseif lastDirection == Direction.DOWN then
projVel = Vector(0, player.ShotSpeed*10)
elseif lastDirection == Direction.LEFT then
projVel = Vector((-1)*player.ShotSpeed*10, 0)
elseif lastDirection == Direction.RIGHT then
projVel = Vector(player.ShotSpeed*10, 0)
end
projVel = projVel:__add(player.Velocity) --Add the players velocity to the projectile's velocity
scythe:PlaySound(133, 1, 0, false, 0.66) --Play the tech firing sound (albeit somewhat pitch shifted)
local numTears = SamaelMod:getNumTears()
if numTears == 0 then --0 actually means 1 in this case
SamaelMod:fireScytheProjectile(projVel, 0, 0, 0) --Normal shot
elseif numTears == 2 and not player:HasCollectible(CollectibleType.COLLECTIBLE_THE_WIZ) then --double shot
if lastDirection == Direction.UP or lastDirection == Direction.DOWN then
SamaelMod:fireScytheProjectile(projVel, 0, 8, 0)
SamaelMod:fireScytheProjectile(projVel, 0, -8, 0)
elseif lastDirection == Direction.LEFT or lastDirection == Direction.RIGHT then
SamaelMod:fireScytheProjectile(projVel, 0, 0, 8)
SamaelMod:fireScytheProjectile(projVel, 0, 0, -8)
end
else --triple shot and above
local arc = 20 --how wide the projectiles disperse (this value is actually half of the total angle)
local angle
for i=1,numTears do --For each projectile
angle = -arc+(arc*2)*((i-1)/(numTears-1)) --Firing angle of this particular projectile
--Apply the wiz effect (widen the angle and split into two groups)
if player:HasCollectible(CollectibleType.COLLECTIBLE_THE_WIZ) then
if i <= numTears/2 then
angle = angle - 25
else
angle = angle + 25
end
end
SamaelMod:fireScytheProjectile(projVel, angle, 0,0) --Fire the projectile
end
end
--Loki's Horns & Mom's Eye
local lokiTriggered = false
if player:HasCollectible(CollectibleType.COLLECTIBLE_LOKIS_HORNS) or
player:HasCollectible(CollectibleType.COLLECTIBLE_MOMS_EYE) then
math.randomseed(Isaac.GetFrameCount() + Game():GetFrameCount())
local luck = math.floor(player.Luck)+2
if luck < 1 then luck = 1 end
if player:HasCollectible(CollectibleType.COLLECTIBLE_LOKIS_HORNS) then
if luck > 9 then luck = 9 end
if math.random(10-luck) == 1 then
if lastDirection ~= Direction.UP then
SamaelMod:fireScytheProjectile(Vector(0, (-1)*player.ShotSpeed*10):__add(player.Velocity), 0, 0,0) end
if lastDirection ~= Direction.DOWN then
SamaelMod:fireScytheProjectile(Vector(0, player.ShotSpeed*10):__add(player.Velocity), 0, 0,0) end
if lastDirection ~= Direction.LEFT then
SamaelMod:fireScytheProjectile(Vector((-1)*player.ShotSpeed*10, 0):__add(player.Velocity), 0, 0,0) end
if lastDirection ~= Direction.RIGHT then
SamaelMod:fireScytheProjectile(Vector(player.ShotSpeed*10, 0):__add(player.Velocity), 0, 0,0) end
lokiTriggered = true
end
end
if player:HasCollectible(CollectibleType.COLLECTIBLE_MOMS_EYE) and not lokiTriggered then
if luck > 4 then luck = 4 end
if math.random(5-luck) == 1 then
if lastDirection == Direction.UP then
SamaelMod:fireScytheProjectile(Vector(0, player.ShotSpeed*10):__add(player.Velocity), 0, 0,0)
elseif lastDirection == Direction.DOWN then
SamaelMod:fireScytheProjectile(Vector(0, (-1)*player.ShotSpeed*10):__add(player.Velocity), 0, 0,0)
elseif lastDirection == Direction.LEFT then
SamaelMod:fireScytheProjectile(Vector(player.ShotSpeed*10, 0):__add(player.Velocity), 0, 0,0)
elseif lastDirection == Direction.RIGHT then
SamaelMod:fireScytheProjectile(Vector((-1)*player.ShotSpeed*10, 0):__add(player.Velocity), 0, 0,0)
end
end
end
end
end
lastCharge = charge
charge = 0 --Reset charge
if scytheScale >= 1.5 then
sprite:Play("BigSwing", 1) --Play scythe swing animation
-- (this is 11 frames long)
else
sprite:Play("Swing", 1) --Play scythe swing animation
-- (this is 11 frames long)
end
--Choose fruit cake effect
if player:HasCollectible(CollectibleType.COLLECTIBLE_FRUIT_CAKE) then
math.randomseed(Isaac.GetFrameCount() + Game():GetFrameCount())
fruitCakeEffect = cakeEffects[ math.random( #cakeEffects ) ]
elseif fruitCakeEffect ~= "none" then
fruitCakeEffect = "none"
end
--Godhead light ring
if player:HasCollectible(CollectibleType.COLLECTIBLE_GODHEAD) then
local god = player:SpawnMawOfVoid(25):ToLaser()
mawSoundCanceler = 3
SamaelMod:playSound(133, 1, 1.3)
god.CollisionDamage = player.Damage*0.3
god:SetBlackHpDropChance(0)
local godSprite = god:GetSprite()
godSprite:Load("gfx/007.008_light ring.anm2", true)
godSprite:Play("LargeRedLaser", true)
end
if player:HasCollectible(CollectibleType.COLLECTIBLE_EPIPHORA) then
SamaelMod:epiphoraFunc() --Epiphora
end
swingDelay = SamaelMod:calcSwingDelay()+1 --Set new swing delay (+1 so as to not count this frame)
scythe:PlaySound(38, 1.75, 0, false, 1.2) --Play swinging sound
scytheState = 2 --Set state to 2 (swinging scythe)
scytheAnimationEndFrame = Game():GetFrameCount() + 11 -- Both animations are 11 frames long
direction = lastDirection --Set render direction to the saved firing direction
end
--CURRENTLY SWINGING THE SCYTHE
if scytheState == 2 then
direction = lastDirection
end
--If scythe is swinging, and the animation is between the frames where the scythe can hit enemies
if scytheState == 2 and sprite:GetFrame() >= 1 and sprite:GetFrame() <= 5 then
--On the first hit frame, send the attack direction to the hitBox entity and set its collision damage
if sprite:GetFrame() == 1 then
--I'm using Coins to store this value because the actual Direction attributes for familiars
--was crashing the game. Oh well!
hitBox.Coins = lastDirection
hitBox.CollisionDamage = player.Damage*scytheDamageMultiplier
if player:HasCollectible(CollectibleType.COLLECTIBLE_LOST_CONTACT) then
hitBox.EntityCollisionClass = EntityCollisionClass.ENTCOLL_ALL
end
if wraithActive and
player:HasCollectible(CollectibleType.COLLECTIBLE_SOY_MILK) and
not player:HasCollectible(CollectibleType.COLLECTIBLE_LIBRA) then
hitBox.CollisionDamage = hitBox.CollisionDamage * 1.75
end
if player:HasCollectible(CollectibleType.COLLECTIBLE_DR_FETUS) or
player:HasCollectible(CollectibleType.COLLECTIBLE_BRIMSTONE) or
player:HasCollectible(CollectibleType.COLLECTIBLE_MOMS_KNIFE) then
hitBox.CollisionDamage = hitBox.CollisionDamage * 1.5
end
if player:HasCollectible(CollectibleType.COLLECTIBLE_GHOST_PEPPER) and math.random(25) == 1 then
local flameSpeed = 10
--Choose the velocity of the projectile based on the last fire direction before release
if lastDirection == Direction.UP then
projVel = Vector(0, -flameSpeed)
elseif lastDirection == Direction.DOWN then
projVel = Vector(0, flameSpeed)
elseif lastDirection == Direction.LEFT then
projVel = Vector(-flameSpeed, 0)
elseif lastDirection == Direction.RIGHT then
projVel = Vector(flameSpeed, 0)
end
projVel = projVel:__add(player.Velocity) --Add the players velocity to the projectile's velocity
Isaac.Spawn(EntityType.ENTITY_EFFECT, EffectVariant.RED_CANDLE_FLAME, 0, player.Position, projVel, player)
end
end
hitPos = hitBox.Position
if sprite:GetFrame() == 5 then --After this duration, get rid of the hitbox
hitPos = nil
hitBox.Coins = -1 --"No direction"
hitBox.CollisionDamage = 0 --Remove the collision damage (just making sure it cant hurt anything)
hitBox.EntityCollisionClass = EntityCollisionClass.ENTCOLL_ENEMIES
SamaelMod:deadEyeFunc(false)
end
elseif scytheState == 2 and
(sprite:IsFinished("Swing") or sprite:IsFinished("BigSwing")) then --If the swinging animation finished
sprite.Rotation = 0
scytheState = 0
if swing == 0 then --Switch the side of the scythe
swing = 1
else
swing = 0
end
end
if scytheState == 0 then --When nothing else is going on, idle state
if scytheScale >= 1.5 then
sprite:Play("BigIdle", 1)
else
sprite:Play("Idle", 1)
end
direction = headDirection
end
if dying then
direction = Direction.DOWN
if scytheScale >= 1.5 then
sprite:Play("BigIdle", 1)
else
sprite:Play("Idle", 1)
end
hitBox.CollisionDamage = 0
end
scythe.RenderZOffset = 0
--Render the scythe at the correct angle and whatnot depending on the direction
if direction == Direction.DOWN then
sprite.Rotation = 0
scythe.RenderZOffset = 10
scythe.Position = Vector(player.Position.X, player.Position.Y+2*scytheScale)
--scythe.SpriteOffset = Vector(0,-4)
elseif direction == Direction.UP then
sprite.Rotation = 180
scythe.Position = Vector(player.Position.X, player.Position.Y-20*scytheScale)
--scythe.SpriteOffset = Vector(0,-10)
elseif direction == Direction.LEFT then
sprite.Rotation = 90
scythe.Position = Vector(player.Position.X-10*scytheScale, player.Position.Y-6*scytheScale)
-- scythe.SpriteOffset = Vector(-10,1)
elseif direction == Direction.RIGHT then
sprite.Rotation = -90
scythe.Position = Vector(player.Position.X+10*scytheScale, player.Position.Y-6*scytheScale)
--scythe.SpriteOffset = Vector(10,5)
end
--Flipping the scythe when needed (alternating swings)
if swing == 0 then
sprite.FlipX = false
else
sprite.FlipX = true
if direction == Direction.RIGHT then
sprite.Rotation = 90
--scythe.SpriteOffset = Vector(10,-20)
elseif direction == Direction.LEFT then
sprite.Rotation = -90
--scythe.SpriteOffset = Vector(-10,-17)
end
end
--Scale the scythe
if scytheScale >= 1.5 then
scythe.Scale = scytheScale-(scytheScale/2) --Using big scythe sprite
else
scythe.Scale = scytheScale --Using small scythe sprite
end
end
-----------Return the number of tears the player would normally fire
function SamaelMod:getNumTears()
local player = Isaac.GetPlayer(0)
local numTears = 0 --Count the number of projectiles needed (from multishot items)
numTears = numTears + 2*player:GetCollectibleNum(CollectibleType.COLLECTIBLE_20_20)
numTears = numTears + 3*player:GetCollectibleNum(CollectibleType.COLLECTIBLE_INNER_EYE)
numTears = numTears + 4*player:GetCollectibleNum(CollectibleType.COLLECTIBLE_MUTANT_SPIDER)
if player:HasCollectible(CollectibleType.COLLECTIBLE_THE_WIZ) then
numTears = numTears+2*player:GetCollectibleNum(CollectibleType.COLLECTIBLE_THE_WIZ)
if numTears % 2 == 1 then --Need an even number with the wiz
numTears = numTears+1
end
end
return numTears
end
-----------Fire one of samael's unique scythe projectiles-----------
function SamaelMod:fireScytheProjectile(projVel, angle, XOffset, YOffset)
local player = Isaac.GetPlayer(0)
projVel = projVel:Rotated(angle) --Apply the angle (for multishot and such)
local pos = Vector(player.Position.X + XOffset, player.Position.Y + YOffset) --Apply offsets
if player:HasCollectible(CollectibleType.COLLECTIBLE_DR_FETUS) then --Fire dr fetus bomb
player:FireBomb(pos, projVel)
else --Fire scythe projectile
local proj = player:FireTear(pos, projVel, true, false, true) --Fire the tear
proj = proj:ToTear()
local var = proj.Variant
if player:HasCollectible(CollectibleType.COLLECTIBLE_IPECAC) then
proj:ChangeVariant(8)
else
--Exclude certain special tears(teeth,boogers,etc)
if var~=2 and var~=26 and var~=27 and var~=28 and var~=30 and var~=31 and
not player:HasCollectible(CollectibleType.COLLECTIBLE_GODHEAD) then
proj:ChangeVariant(projVariant) --Change to custom scythe projectile
elseif player:HasCollectible(CollectibleType.COLLECTIBLE_GODHEAD) then
proj:SetColor(Color(0,0,0,255,0,0,0), 999,999,false,false)
else
proj.Scale = proj.Scale*1.5
end
proj.TearFlags = proj.TearFlags | 1 << 1 --Add Piercing
proj.TearFlags = proj.TearFlags | 1 --Add Spectral
proj.SpriteScale = Vector(proj.Scale,proj.Scale) --Set proper size
end
proj.CollisionDamage = proj.CollisionDamage*scytheProjectileDamageMultiplier --Set new tear damage
if player:HasCollectible(samaelChocMilk) then
local chocBoost = math.min(charge/chargeTime, 3)
proj.CollisionDamage = proj.CollisionDamage*chocBoost
proj.Scale = math.max(proj.Scale, proj.Scale*chocBoost*(2/3))
end
end
end
-- Custom deadeye functionality
-- (increases damage every time a scythe swing hits something; bonus is lost when a swing misses
function SamaelMod:deadEyeFunc(interrupt)
local player = Isaac.GetPlayer(0)
if player:HasCollectible(samaelDeadEye) then
if hits > 0 then
if deadEyeBoost < properDamage * 2 then
deadEyeBoost = deadEyeBoost + properDamage * 0.2
--Add to deadEyeBoost to keep track of how much damage has been added by this effect
player.Damage = player.Damage+properDamage*0.2 --Add damage
end
deadEyeCountdown = 3
elseif interrupt then
deadEyeCountdown = deadEyeCountdown - 1
if deadEyeCountdown <= 0 then
player.Damage = player.Damage - deadEyeBoost --Revert damage to original value
deadEyeBoost = 0
end
else
player.Damage = player.Damage - deadEyeBoost --Revert damage to original value
deadEyeBoost = 0
end
end
hits = 0
end
-------Custom epiphora functionality (increased attack rate for attacking in the same direction repeatedly)-------
function SamaelMod:epiphoraFunc()
if lastEpiphoraDirection == lastDirection then
epiphoraCounter = epiphoraCounter + 1 --Add to counter
else
epiphoraCounter = 0
end
lastEpiphoraDirection = lastDirection
end
-----------Add colours to the scythe, if certain items are collected-----------
function SamaelMod:getScytheColor()
local player = Isaac.GetPlayer(0)
local color = Color(1,1,1,1,0,0,0)
local red = {
player:HasCollectible(CollectibleType.COLLECTIBLE_BLOOD_MARTYR),
player:HasCollectible(CollectibleType.COLLECTIBLE_CHEMICAL_PEEL),
player:HasCollectible(CollectibleType.COLLECTIBLE_BLOOD_CLOT),
player:HasCollectible(CollectibleType.COLLECTIBLE_MOMS_CONTACTS),
player:HasCollectible(CollectibleType.COLLECTIBLE_PACT),
player:HasCollectible(CollectibleType.COLLECTIBLE_ABADDON),
player:HasCollectible(CollectibleType.COLLECTIBLE_TOOTH_PICKS),
player:HasCollectible(CollectibleType.COLLECTIBLE_STIGMATA),
}
local green = {
player:HasCollectible(CollectibleType.COLLECTIBLE_SCORPIO),
player:HasCollectible(CollectibleType.COLLECTIBLE_COMMON_COLD),
player:HasCollectible(CollectibleType.COLLECTIBLE_IPECAC),
player:HasCollectible(CollectibleType.COLLECTIBLE_SERPENTS_KISS),
player:HasCollectible(CollectibleType.COLLECTIBLE_MYSTERIOUS_LIQUID),
}
local yellow = {
player:HasCollectible(CollectibleType.COLLECTIBLE_NUMBER_ONE),
player:HasCollectible(CollectibleType.COLLECTIBLE_SULFURIC_ACID),
player:HasCollectible(CollectibleType.COLLECTIBLE_MOMS_PERFUME),
}
local orange = {
player:HasCollectible(CollectibleType.COLLECTIBLE_FIRE_MIND),
player:HasCollectible(CollectibleType.COLLECTIBLE_DEAD_ONION),
player:HasCollectible(CollectibleType.COLLECTIBLE_PARASITE),
player:HasCollectible(samaelChocMilk),
}
--local purple = {player:HasCollectible(CollectibleType.COLLECTIBLE_SPOON_BENDER)}
--local pink = {player:HasCollectible(CollectibleType.COLLECTIBLE_MOMS_EYESHADOW)}
if threeDollarBillEffect == "fire" then table.insert(orange, true) end
if threeDollarBillEffect == "poison" or threeDollarBillEffect == "creep" then table.insert(green, true) end
if threeDollarBillEffect == "freeze" then table.insert(red, true) end
if threeDollarBillEffect == "pee" then table.insert(yellow, true) end
for i = 1, #red do
if red[i] then
color.R = color.R+0.8
end
end
for i = 1, #green do
if green[i] then
color.G = color.G+1.5
end
end
for i = 1, #yellow do
if yellow[i] then
color.R = color.R+1.5
color.G = color.G+1.5
end
end
for i = 1, #orange do
if orange[i] then
color.R = color.R+2
color.G = color.G+1
end
end
if player:HasCollectible(CollectibleType.COLLECTIBLE_SPOON_BENDER) then
color.R = color.R+2
color.B = color.B+2
end
if player:HasCollectible(CollectibleType.COLLECTIBLE_MOMS_EYESHADOW) or threeDollarBillEffect == "charm" then
color.R = color.R+2
color.B = color.B+1
end
--Normalize colour
local max = math.max(color.R, color.G, color.B)
local min = math.min(color.R-1, color.G-1, color.B-1)
color.R = (color.R-min)/(max-min)
color.G = (color.G-min)/(max-min)
color.B = (color.B-min)/(max-min)
--Lighten
if player:HasCollectible(CollectibleType.COLLECTIBLE_SACRED_HEART) then
color.R = color.R + 0.5
color.B = color.B + 0.5
color.G = color.G + 0.5
--Darken
elseif player:HasCollectible(CollectibleType.COLLECTIBLE_DARK_MATTER) or
player:HasCollectible(CollectibleType.COLLECTIBLE_EVES_MASCARA) or
threeDollarBillEffect == "fear" then
color.R = color.R * 0.2
color.B = color.B * 0.2
color.G = color.G * 0.2
--Darken less
elseif player:HasCollectible(CollectibleType.COLLECTIBLE_DEAD_ONION) or
player:HasCollectible(samaelChocMilk) or
threeDollarBillEffect == "slow" then
color.R = color.R * 0.5
color.B = color.B * 0.5
color.G = color.G * 0.5
end
scytheColor = color
end
-----------Play sound using dummy NPC if needed-----------
function SamaelMod:playSound(ID, volume, pitch)
local sfx = SFXManager()
sfx:Play(ID, volume, 0, false, pitch) -- (SoundEffect ID, float Volume, integer FrameDelay, boolean Loop, float Pitch)
end
-----------On player init (start/continue)-----------
function SamaelMod:PostPlayerInit(player)
if player:GetPlayerType() ~= samaelID then --If the player is Samael
return
end