-
Notifications
You must be signed in to change notification settings - Fork 46
/
Cryptid.lua
3361 lines (3271 loc) · 106 KB
/
Cryptid.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
--- STEAMODDED HEADER
--- MOD_NAME: Cryptid
--- MOD_ID: Cryptid
--- PREFIX: cry
--- MOD_AUTHOR: [MathIsFun_, Cryptid and Balatro Discords]
--- MOD_DESCRIPTION: Adds unbalanced ideas to Balatro.
--- BADGE_COLOUR: 708b91
--- DEPENDENCIES: [Talisman>=2.0.0-beta8, Steamodded>=1.0.0~ALPHA-1225a]
--- VERSION: 0.5.3a
--- PRIORITY: 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
----------------------------------------------
------------MOD CODE -------------------------
-- Currently there's no rhyme or reason to how the contents of this file are organized. It's kind of just an "anything goes" sort of file.
-- If you're learning about Cryptid's codebase, the files in the Items folder are generally much more organized.
-- Enables debug features (I think this is currently useless.)
--Cryptid.debug = true
-- Save the mod path permanently.
local mod_path = "" .. SMODS.current_mod.path
-- Load Options
Cryptid_config = SMODS.current_mod.config
-- This will save the current state even when settings are modified
Cryptid.enabled = copy_table(Cryptid_config)
--backwards compat moment
cry_enable_jokers = Cryptid.enabled["Misc. Jokers"]
cry_enable_epics = Cryptid.enabled["Epic Jokers"]
cry_enable_exotics = Cryptid.enabled["Exotic Jokers"]
cry_minvasion = Cryptid.enabled["M Jokers"]
-- Gradient isn't included since other logic seems to also handle it
SMODS.Rarity{
key = "exotic",
loc_txt = {},
badge_colour = HEX('708b91'),
}
SMODS.Rarity{
key = "epic",
loc_txt = {},
badge_colour = HEX('571d91'),
default_weight = 0.003,
pools = {["Joker"] = true},
get_weight = function(self, weight, object_type)
-- The game shouldn't try generating Epic Jokers when they are disabled
if Cryptid_config["Epic Jokers"] then
return 0.003
else
return 0
end
end,
}
SMODS.Rarity{
key = "candy",
loc_txt = {},
badge_colour = HEX("e91ff0"),
}
SMODS.Rarity{
key = "cursed",
loc_txt = {},
badge_colour = HEX("474931"),
}
--Add Event type - used for events in e.g. Chocolate Dice
SMODS.Events = {}
SMODS.Event = SMODS.GameObject:extend{
obj_table = SMODS.Events,
obj_buffer = {},
required_params = {
"key"
},
inject = function() end,
set = "Event",
class_prefix = "ev",
-- This should be called to start an event.
start = function(self)
G.GAME.events[self.key] = true
end,
-- This should be called to finish an event.
finish = function(self)
G.GAME.events[self.key] = nil
end,
-- Runs once before and after jokers, as well as a few special cases
calculate = function(self, context)
end,
-- used for Chocolate Die tooltips, can maybe be repurposed later
loc_vars = function(self, info_queue, center)
info_queue[#info_queue + 1] = { set = "Other", key = self.key }
end,
}
--Calculate events on cash out
local gfco = G.FUNCS.cash_out
G.FUNCS.cash_out = function(e)
local ret = gfco(e)
for k, v in pairs(SMODS.Events) do
if G.GAME.events[k] then
v:calculate({cash_out = true})
end
end
return ret
end
-- Calculate events on start of shop
local guis = G.UIDEF.shop
G.UIDEF.shop = function(e)
local ret = guis(e)
for k, v in pairs(SMODS.Events) do
if G.GAME.events[k] then
v:calculate({start_shop = true})
end
end
return ret
end
-- Calculations for Please Take One. Incredibly scuffed and should get moved to Spooky file later
local gure = Game.update_round_eval
function Game:update_round_eval(dt)
if G.GAME.events.ev_cry_choco6 and not pack_opened and not G.STATE_COMPLETE then
G.STATE_COMPLETE = true
for k, v in pairs(SMODS.Events) do
if G.GAME.events[k] then
v:calculate({pre_cash = true})
end
end
return end
if G.GAME.events.ev_cry_choco6 and pack_opened and G.STATE_COMPLETE and not G.round_eval then G.STATE_COMPLETE = false; return end
gure(self, dt)
end
--Add Unique consumable set - used for unique consumables that aren't normally obtained (e.g. Potion)
SMODS.ConsumableType{
key = "Unique",
primary_colour = G.C.MONEY,
secondary_colour = G.C.MONEY,
collection_rows = { 4, 4 },
shop_rate = 0.0,
loc_txt = {},
default = "c_cry_potion",
can_stack = false,
can_divide = false,
}
-- Create G.GAME.events when starting a run, so there's no errors
local gigo = Game.init_game_object
function Game:init_game_object()
local g = gigo(self)
g.events = {}
return g
end
--Changes main menu colors and stuff
if Cryptid.enabled["Menu"] then
local oldfunc = Game.main_menu
Game.main_menu = function(change_context)
local ret = oldfunc(change_context)
-- adds a Cryptid spectral to the main menu
local newcard = create_card('Spectral',G.title_top, nil, nil, nil, nil, 'c_cryptid', 'elial1')
-- recenter the title
G.title_top.T.w = G.title_top.T.w*1.7675
G.title_top.T.x = G.title_top.T.x - 0.8
G.title_top:emplace(newcard)
-- make the card look the same way as the title screen Ace of Spades
newcard.T.w = newcard.T.w * 1.1*1.2
newcard.T.h = newcard.T.h *1.1*1.2
newcard.no_ui = true
-- make the title screen use different background colors
G.SPLASH_BACK:define_draw_steps({{
shader = 'splash',
send = {
{name = 'time', ref_table = G.TIMERS, ref_value = 'REAL_SHADER'},
{name = 'vort_speed', val = 0.4},
{name = 'colour_1', ref_table = G.C, ref_value = 'CRY_EXOTIC'},
{name = 'colour_2', ref_table = G.C, ref_value = 'DARK_EDITION'},
}}})
return ret
end
end
--Localization colors
local lc = loc_colour
function loc_colour(_c, _default)
if not G.ARGS.LOC_COLOURS then
lc()
end
G.ARGS.LOC_COLOURS.cry_code = G.C.SET.Code
G.ARGS.LOC_COLOURS.heart = G.C.SUITS.Hearts
G.ARGS.LOC_COLOURS.diamond = G.C.SUITS.Diamonds
G.ARGS.LOC_COLOURS.spade = G.C.SUITS.Spades
G.ARGS.LOC_COLOURS.club = G.C.SUITS.Clubs
for k, v in pairs(G.C) do
if string.len(k) > 4 and string.sub(k, 1, 4) == 'CRY_' then
G.ARGS.LOC_COLOURS[string.lower(k)] = v
end
end
return lc(_c, _default)
end
-- Midground sprites - used for Exotic Jokers and Gateway
-- don't really feel like explaining this deeply, it's based on code for The Soul and Legendary Jokers
local set_spritesref = Card.set_sprites
function Card:set_sprites(_center, _front)
set_spritesref(self, _center, _front)
if _center and _center.name == "cry-Gateway" then
self.children.floating_sprite = Sprite(
self.T.x,
self.T.y,
self.T.w,
self.T.h,
G.ASSET_ATLAS[_center.atlas or _center.set],
{ x = 2, y = 0 }
)
self.children.floating_sprite.role.draw_major = self
self.children.floating_sprite.states.hover.can = false
self.children.floating_sprite.states.click.can = false
self.children.floating_sprite2 = Sprite(
self.T.x,
self.T.y,
self.T.w,
self.T.h,
G.ASSET_ATLAS[_center.atlas or _center.set],
{ x = 1, y = 0 }
)
self.children.floating_sprite2.role.draw_major = self
self.children.floating_sprite2.states.hover.can = false
self.children.floating_sprite2.states.click.can = false
end
if _center and _center.soul_pos and _center.soul_pos.extra then
self.children.floating_sprite2 = Sprite(
self.T.x,
self.T.y,
self.T.w,
self.T.h,
G.ASSET_ATLAS[_center.atlas or _center.set],
_center.soul_pos.extra
)
self.children.floating_sprite2.role.draw_major = self
self.children.floating_sprite2.states.hover.can = false
self.children.floating_sprite2.states.click.can = false
end
end
--this is where the code starts to get really scuffed... I'd recommend closing your eyes
--anyway this function basically hardcodes unredeeming a voucher
function cry_debuff_voucher(center) -- sorry for all the mess here...
local new_center = G.GAME.cry_voucher_centers[center]
local center_table = {
name = new_center and new_center.name,
extra = new_center and new_center.config.extra,
}
if center_table.name == "Overstock" or center_table.name == "Overstock Plus" then
G.E_MANAGER:add_event(Event({
func = function()
change_shop_size(-center_table.extra)
return true
end,
}))
end
if center_table.name == "Tarot Merchant" or center_table.name == "Tarot Tycoon" then
G.E_MANAGER:add_event(Event({
func = function()
G.GAME.tarot_rate = G.GAME.tarot_rate / center_table.extra
return true
end,
}))
end
if center_table.name == "Planet Merchant" or center_table.name == "Planet Tycoon" then
G.E_MANAGER:add_event(Event({
func = function()
G.GAME.planet_rate = G.GAME.planet_rate / center_table.extra
return true
end,
}))
end
if center_table.name == "Hone" or center_table.name == "Glow Up" then
G.E_MANAGER:add_event(Event({
func = function()
G.GAME.edition_rate = G.GAME.edition_rate / center_table.extra
return true
end,
}))
end
if center_table.name == "Magic Trick" then
G.E_MANAGER:add_event(Event({
func = function()
G.GAME.playing_card_rate = 0
return true
end,
}))
end
if center_table.name == "Crystal Ball" then
G.E_MANAGER:add_event(Event({
func = function()
G.consumeables.config.card_limit = G.consumeables.config.card_limit - center_table.extra
return true
end,
}))
end
if center_table.name == "Clearance Sale" then
G.E_MANAGER:add_event(Event({
func = function()
G.GAME.discount_percent = 0
for k, v in pairs(G.I.CARD) do
if v.set_cost then
v:set_cost()
end
end
return true
end,
}))
end
if center_table.name == "Liquidation" then
G.E_MANAGER:add_event(Event({
func = function()
G.GAME.discount_percent = 25
for k, v in pairs(G.I.CARD) do
if v.set_cost then
v:set_cost()
end
end
return true
end,
}))
end
if center_table.name == "Reroll Surplus" or center_table.name == "Reroll Glut" then
G.E_MANAGER:add_event(Event({
func = function()
G.GAME.round_resets.reroll_cost = G.GAME.round_resets.reroll_cost + center_table.extra
G.GAME.current_round.reroll_cost = math.max(0, G.GAME.current_round.reroll_cost + center_table.extra)
return true
end,
}))
end
if center_table.name == "Seed Money" then
G.E_MANAGER:add_event(Event({
func = function()
G.GAME.interest_cap = 25 --note: does not account for potential deck effects
return true
end,
}))
end
if center_table.name == "Money Tree" then
G.E_MANAGER:add_event(Event({
func = function()
G.GAME.interest_cap = G.P_CENTERS.v_seed_money.extra
return true
end,
}))
end
if center_table.name == "Grabber" or center_table.name == "Nacho Tong" then
G.GAME.round_resets.hands = G.GAME.round_resets.hands - center_table.extra
ease_hands_played(-center_table.extra)
end
if center_table.name == "Paint Brush" or center_table.name == "Palette" then
G.hand:change_size(-center_table.extra)
end
if center_table.name == "Wasteful" or center_table.name == "Recyclomancy" then
G.GAME.round_resets.discards = G.GAME.round_resets.discards - center_table.extra
ease_discard(-center_table.extra)
end
if center_table.name == "Antimatter" then
G.E_MANAGER:add_event(Event({
func = function()
if G.jokers then
G.jokers.config.card_limit = G.jokers.config.card_limit - center_table.extra
end
return true
end,
}))
end
if center_table.name == "Hieroglyph" or center_table.name == "Petroglyph" then
ease_ante(center_table.extra)
G.GAME.round_resets.blind_ante = G.GAME.round_resets.blind_ante or G.GAME.round_resets.ante
G.GAME.round_resets.blind_ante = G.GAME.round_resets.blind_ante + center_table.extra
if center_table.name == "Hieroglyph" then
G.GAME.round_resets.hands = G.GAME.round_resets.hands + center_table.extra
ease_hands_played(center_table.extra)
end
if center_table.name == "Petroglyph" then
G.GAME.round_resets.discards = G.GAME.round_resets.discards + center_table.extra
ease_discard(center_table.extra)
end
end
end
function cry_edition_to_table(edition) -- look mom i figured it out (this does NOT need to be a function)
if edition then
return { [edition] = true }
end
end
-- check if Director's Cut or Retcon offers a cheaper reroll price
function cry_cheapest_boss_reroll()
local dcut = G.GAME.cry_voucher_centers["v_directors_cut"].config.extra or 1e308
local retc = G.GAME.cry_voucher_centers["v_retcon"].config.extra or 1e308
if dcut < retc then
return dcut
else
return retc
end
end
-- generate a random edition (e.g. Antimatter Deck)
function cry_poll_random_edition()
local random_edition = pseudorandom_element(G.P_CENTER_POOLS.Edition, pseudoseed("cry_ant_edition"))
while random_edition.key == "e_base" do
random_edition = pseudorandom_element(G.P_CENTER_POOLS.Edition, pseudoseed("cry_ant_edition"))
end
ed_table = { [random_edition.key:sub(3)] = true }
return ed_table
end
function cry_voucher_debuffed(name) -- simple function but idk
if G.GAME.voucher_sticker_index and G.GAME.voucher_sticker_index.perishable[name] then
if G.GAME.voucher_sticker_index.perishable[name] == 0 then
return true
end
end
return false
end
function cry_voucher_pinned(name)
if G.GAME.voucher_sticker_index then
if G.GAME.voucher_sticker_index.pinned[name] then
return true
end
end
return false
end
-- gets a random, valid consumeable (used for Hammerspace, CCD Deck, Blessing, etc.)
function get_random_consumable(seed, excluded_flags, banned_card, pool, no_undiscovered)
-- set up excluded flags - these are the kinds of consumables we DON'T want to have generating
excluded_flags = excluded_flags or { "hidden", "no_doe", "no_grc" }
local selection = "n/a"
local passes = 0
local tries = 500
while true do
tries = tries - 1
passes = 0
-- create a random consumable naively
local key = pseudorandom_element(pool or G.P_CENTER_POOLS.Consumeables, pseudoseed(seed or "grc")).key
selection = G.P_CENTERS[key]
-- check if it is valid
if selection.discovered or not no_undiscovered then
for k, v in pairs(excluded_flags) do
if not center_no(selection, v, key, true) then
--Makes the consumable invalid if it's a specific card unless it's set to
--I use this so cards don't create copies of themselves (eg potential inf Blessing chain, Hammerspace from Hammerspace...)
if not banned_card or (banned_card and banned_card ~= key) then
passes = passes + 1
end
end
end
end
-- use it if it's valid or we've run out of attempts
if passes >= #excluded_flags or tries <= 0 then
if tries <= 0 and no_undiscovered then
return G.P_CENTERS["c_strength"]
else
return selection
end
end
end
end
function cry_get_next_voucher_edition() -- currently only for editions + sticker decks, can be modified if voucher stickering/editioning becomes more important
if G.GAME.modifiers.cry_force_edition then
return cry_edition_to_table(G.GAME.modifiers.cry_force_edition)
elseif G.GAME.modifiers.cry_force_random_edition then
return cry_poll_random_edition()
end
end
-- code to generate Stickers for Vouchers, based on that for Jokers
function cry_get_next_voucher_stickers()
local eternal_perishable_poll = pseudorandom("cry_vet" .. (key_append or "") .. G.GAME.round_resets.ante)
local ret = { eternal = false, perishable = false, rental = false, pinned = false, banana = false }
if
(G.GAME.modifiers.cry_force_sticker == "eternal")
or G.GAME.modifiers.cry_sticker_sheet_plus
or (
G.GAME.modifiers.cry_any_stickers
and (G.GAME.modifiers.enable_eternals_in_shop and eternal_perishable_poll > 0.8)
)
then
ret.eternal = true
end
if G.GAME.modifiers.enable_perishables_in_shop and G.GAME.modifiers.cry_any_stickers then -- bloated as shit
if
not G.GAME.modifiers.cry_eternal_perishable_compat
and ((eternal_perishable_poll > 0.4) and (eternal_perishable_poll <= 0.7))
then
ret.perishable = true
end
if
G.GAME.modifiers.cry_eternal_perishable_compat
and pseudorandom("cry_vper" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7
then
ret.perishable = true
end
end
if (G.GAME.modifiers.cry_force_sticker == "perishable") or G.GAME.modifiers.cry_sticker_sheet_plus then
ret.perishable = true
end
if
G.GAME.modifiers.cry_force_sticker == "rental"
or G.GAME.modifiers.cry_sticker_sheet_plus
or (
G.GAME.modifiers.cry_any_stickers
and (
G.GAME.modifiers.enable_rentals_in_shop
and pseudorandom("cry_vssjr" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7
)
)
then
ret.rental = true
end
if
G.GAME.modifiers.cry_force_sticker == "pinned"
or G.GAME.modifiers.cry_sticker_sheet_plus
or (
G.GAME.modifiers.cry_any_stickers
and (
G.GAME.modifiers.cry_enable_pinned_in_shop
and pseudorandom("cry_vpin" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7
)
)
then
ret.pinned = true
end
if G.GAME.modifiers.cry_force_sticker == "banana" or G.GAME.modifiers.cry_sticker_sheet_plus then
ret.banana = true
end
if
not G.GAME.modifiers.cry_eternal_perishable_compat
and G.GAME.modifiers.enable_banana
and G.GAME.modifiers.cry_any_stickers
and (pseudorandom("cry_bpbanana" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7)
and (eternal_perishable_poll <= 0.7)
then
ret.banana = true
end
if
G.GAME.modifiers.cry_eternal_perishable_compat
and G.GAME.modifiers.enable_banana
and G.GAME.modifiers.cry_any_stickers
and (pseudorandom("cry_bpbanana" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7)
then
ret.banana = true
end
return ret
end
-- Calculates Rental sticker for Consumables
function Card:cry_calculate_consumeable_rental()
if self.ability.rental then
ease_dollars(-G.GAME.cry_consumeable_rental_rate)
card_eval_status_text(self, "dollars", -G.GAME.cry_consumeable_rental_rate)
end
end
-- Calculates Perishable sticker for Consumables
function Card:cry_calculate_consumeable_perishable()
if not self.ability.perish_tally then
self.ability.perish_tally = 1
end
if self.ability.perishable and self.ability.perish_tally > 0 then
self.ability.perish_tally = 0
card_eval_status_text(
self,
"extra",
nil,
nil,
nil,
{ message = localize("k_disabled_ex"), colour = G.C.FILTER, delay = 0.45 }
)
self:set_debuff()
end
end
-- Update the Cryptid member count using HTTPS
function update_cry_member_count()
if Cryptid.enabled["HTTPS Module"] == true then
if not GLOBAL_cry_member_update_thread then
-- start up the HTTPS thread if needed
local file_data = assert(NFS.newFileData(mod_path .. "https/thread.lua"))
GLOBAL_cry_member_update_thread = love.thread.newThread(file_data)
GLOBAL_cry_member_update_thread:start()
end
local old = GLOBAL_cry_member_count or 8830
-- get the HTTPS thread's value for Cryptid members
local ret = love.thread.getChannel("member_count"):pop()
if ret then
GLOBAL_cry_member_count = string.match(ret, '"approximate_member_count"%s*:%s*(%d+)') -- string matching a json is odd but should be fine?
end
if not GLOBAL_cry_member_count then
GLOBAL_cry_member_count = old
-- Something failed, print the error
local error = love.thread.getChannel("member_error"):pop()
if error then
sendDebugMessage(error)
end
end
else
-- Use a fallback value if HTTPS is disabled (you all are awesome)
GLOBAL_cry_member_count = 8830
end
end
-- deal with Rigged and Fragile when scoring a playing card
local ec = eval_card
function eval_card(card, context)
if not card or card.will_shatter then
return
end
-- Store old probability for later reference
local ggpn = G.GAME.probabilities.normal
if card.ability.cry_rigged then
G.GAME.probabilities.normal = 1e9
end
local ret = ec(card, context)
if card.ability.cry_rigged then
G.GAME.probabilities.normal = ggpn
end
return ret
end
-- deal wirh Rigged on Consumables
local uc = Card.use_consumeable
function Card:use_consumeable(area, copier)
local ggpn = G.GAME.probabilities.normal
if self.ability.cry_rigged then
G.GAME.probabilities.normal = 1e9
end
local ret = uc(self, area, copier)
if self.ability.cry_rigged then
G.GAME.probabilities.normal = ggpn
end
return ret
end
--some functions to minimize the load on calculate_joker itself
function Card:cry_copy_ability()
local orig_ability = {}
if self.ability then
for i, j in pairs(self.ability) do
if (type(j) == "table") and is_number(j) then
orig_ability[i] = to_big(j)
elseif type(j) == "table" then
orig_ability[i] = {}
for i2, j2 in pairs(j) do
orig_ability[i][i2] = j2
end
else
orig_ability[i] = j
end
end
end
return orig_ability
end
local cj = Card.calculate_joker
function Card:cry_double_scale_calc(orig_ability, in_context_scaling)
if
self.ability.name ~= "cry-happyhouse"
and self.ability.name ~= "Acrobat"
and self.ability.name ~= "cry-sapling"
and self.ability.name ~= "cry-mstack"
and self.ability.name ~= "cry-notebook"
and self.ability.name ~= "Invisible Joker"
and self.ability.name ~= "cry-Old Invisible Joker"
then
local jkr = self
if jkr.ability and type(jkr.ability) == "table" then
if not G.GAME.cry_double_scale[jkr.sort_id] or not G.GAME.cry_double_scale[jkr.sort_id].ability then
if not G.GAME.cry_double_scale[jkr.sort_id] then
G.GAME.cry_double_scale[jkr.sort_id] = { ability = { double_scale = true } }
end
for k, v in pairs(jkr.ability) do
if type(jkr.ability[k]) ~= "table" then
G.GAME.cry_double_scale[jkr.sort_id].ability[k] = v
else
G.GAME.cry_double_scale[jkr.sort_id].ability[k] = {}
for _k, _v in pairs(jkr.ability[k]) do
G.GAME.cry_double_scale[jkr.sort_id].ability[k][_k] = _v
end
end
end
end
if G.GAME.cry_double_scale[jkr.sort_id] and not G.GAME.cry_double_scale[jkr.sort_id].scaler then
local dbl_info = G.GAME.cry_double_scale[jkr.sort_id]
if jkr.ability.name == "cry-Number Blocks" then
dbl_info.base = { "extra", "money" }
dbl_info.scaler = { "extra", "money_mod" }
dbl_info.scaler_base = jkr.ability.extra.money_mod
dbl_info.offset = 1
end
if jkr.ability.name == "cry-Exponentia" then
dbl_info.base = { "extra", "Emult" }
dbl_info.scaler = { "extra", "Emult_mod" }
dbl_info.scaler_base = jkr.ability.extra.Emult_mod
dbl_info.offset = 1
end
if jkr.ability.name == "cry-Redeo" then
dbl_info.base = { "extra", "money_req" }
dbl_info.scaler = { "extra", "money_mod" }
dbl_info.scaler_base = jkr.ability.extra.money_mod
dbl_info.offset = 1
end
if jkr.ability.name == "cry-Chili Pepper" then
dbl_info.base = { "extra", "Xmult" }
dbl_info.scaler = { "extra", "Xmult_mod" }
dbl_info.scaler_base = jkr.ability.extra.Xmult_mod
dbl_info.offset = 1
end
if jkr.ability.name == "cry-Scalae" then
dbl_info.base = { "extra", "shadow_scale" }
dbl_info.scaler = { "extra", "shadow_scale_mod" }
dbl_info.scaler_base = jkr.ability.extra.scale_mod
dbl_info.offset = 1
end
if jkr.ability.name == "cry-mprime" then
dbl_info.base = { "extra", "mult" }
dbl_info.scaler = { "extra", "bonus" }
dbl_info.scaler_base = jkr.ability.extra.bonus
dbl_info.offset = 1
end
if jkr.ability.name == "Yorick" then
dbl_info.base = { "x_mult" }
dbl_info.scaler = { "extra", "xmult" } --not kidding
dbl_info.scaler_base = 1
dbl_info.offset = 1
end
if jkr.ability.name == "Hologram" then
dbl_info.base = { "x_mult" }
dbl_info.scaler = { "extra" }
dbl_info.scaler_base = jkr.ability.extra
dbl_info.offset = 1
end
if jkr.ability.name == "Gift Card" then
dbl_info.base = { "extra_value" }
dbl_info.scaler = { "extra" }
dbl_info.scaler_base = jkr.ability.extra
dbl_info.offset = 1
end
if jkr.ability.name == "Throwback" then
dbl_info.base = { "x_mult" }
dbl_info.scaler = { "extra" }
dbl_info.scaler_base = jkr.ability.x_mult or 1
dbl_info.offset = 1
end
if jkr.ability.name == "Egg" then
dbl_info.base = { "extra_value" }
dbl_info.scaler = { "extra" }
dbl_info.scaler_base = jkr.ability.extra
dbl_info.offset = 1
end
local default_modifiers = {
mult = 0,
h_mult = 0,
h_x_mult = 0,
h_dollars = 0,
p_dollars = 0,
t_mult = 0,
t_chips = 0,
x_mult = 1,
h_size = 0,
d_size = 0,
}
for k, v in pairs(jkr.ability) do
--extra_value is ignored because it can be scaled by Gift Card
if
k ~= "extra_value"
and dbl_info.ability[k] ~= v
and is_number(v)
and is_number(dbl_info.ability[k])
then
dbl_info.base = { k }
local predicted_mod = math.abs(to_number(to_big(v)) - to_number(to_big(dbl_info.ability[k])))
local best_key = { "" }
local best_coeff = 10 ^ 100
for l, u in pairs(jkr.ability) do
if not (default_modifiers[l] and default_modifiers[l] == u) then
if l ~= k and is_number(u) then
if
to_number(to_big(predicted_mod / u)) >= 0.999
and to_number(to_big(predicted_mod / u)) < to_number(to_big(best_coeff))
then
best_coeff = to_number(to_big(predicted_mod / u))
best_key = { l }
end
end
if type(jkr.ability[l]) == "table" then
for _l, _u in pairs(jkr.ability[l]) do
if
is_number(_u)
and to_number(to_big(predicted_mod / _u)) >= 0.999
and to_number(to_big(predicted_mod / _u))
< to_number(to_big(best_coeff))
then
best_coeff = to_number(to_big(predicted_mod / _u))
best_key = { l, _l }
end
end
end
end
end
dbl_info.scaler = best_key
end
if
type(jkr.ability[k]) == "table"
and type(dbl_info.ability) == "table"
and type(dbl_info.ability[k]) == "table"
then
for _k, _v in pairs(jkr.ability[k]) do
if
dbl_info.ability[k][_k] ~= _v
and is_number(_v)
and is_number(dbl_info.ability[k][_k])
then
dbl_info.base = { k, _k }
local predicted_mod = math.abs(_v - dbl_info.ability[k][_k])
local best_key = { "" }
local best_coeff = 10 ^ 100
for l, u in pairs(jkr.ability) do
if is_number(u) and to_number(to_big(predicted_mod / u)) >= 0.999 then
if to_number(to_big(predicted_mod / u)) < to_number(to_big(best_coeff)) then
best_coeff = to_number(to_big(predicted_mod / u))
best_key = { l }
end
end
if type(jkr.ability[l]) == "table" then
for _l, _u in pairs(jkr.ability[l]) do
if
(l ~= k or _l ~= _k)
and is_number(_u)
and to_number(to_big(predicted_mod / _u)) >= 0.999
then
if
to_number(to_big(predicted_mod / _u))
< to_number(to_big(best_coeff))
then
best_coeff = to_number(to_big(predicted_mod / _u))
best_key = { l, _l }
end
end
end
end
end
dbl_info.scaler = best_key
end
end
end
end
if dbl_info.scaler then
dbl_info.scaler_base = #dbl_info.scaler == 2
and orig_ability[dbl_info.scaler[1]][dbl_info.scaler[2]]
or orig_ability[dbl_info.scaler[1]]
dbl_info.offset = 1
end
end
end
end
local orig_scale_base = nil
local orig_scale_scale = nil
if G.GAME.cry_double_scale[self.sort_id] and G.GAME.cry_double_scale[self.sort_id].scaler then
local jkr = self
local dbl_info = G.GAME.cry_double_scale[self.sort_id]
if #dbl_info.base == 2 then
if
not (
type(jkr.ability) ~= "table"
or not orig_ability[dbl_info.base[1]]
or type(orig_ability[dbl_info.base[1]]) ~= "table"
or not orig_ability[dbl_info.base[1]][dbl_info.base[2]]
)
then
orig_scale_base = orig_ability[dbl_info.base[1]][dbl_info.base[2]]
end
else
if jkr.ability[dbl_info.base[1]] then
orig_scale_base = orig_ability[dbl_info.base[1]]
end
end
if #dbl_info.scaler == 2 then
if
not (not orig_ability[dbl_info.scaler[1]] or not orig_ability[dbl_info.scaler[1]][dbl_info.scaler[2]])
then
orig_scale_scale = orig_ability[dbl_info.scaler[1]][dbl_info.scaler[2]]
end
else
if orig_ability[dbl_info.scaler[1]] then
orig_scale_scale = orig_ability[dbl_info.scaler[1]]
end
end
end
if orig_scale_base and orig_scale_scale then
local new_scale_base = nil
local true_base = nil
local jkr = self
local dbl_info = G.GAME.cry_double_scale[self.sort_id]
if #dbl_info.base == 2 then
if
not (
type(jkr.ability) ~= "table"
or not jkr.ability[dbl_info.base[1]]
or type(jkr.ability[dbl_info.base[1]]) ~= "table"
or not jkr.ability[dbl_info.base[1]][dbl_info.base[2]]
)
then
new_scale_base = jkr.ability[dbl_info.base[1]][dbl_info.base[2]]
end
else
if jkr.ability[dbl_info.base[1]] then
new_scale_base = jkr.ability[dbl_info.base[1]]
end
end
true_base = dbl_info.scaler_base
if
new_scale_base and ((to_big(math.abs(new_scale_base - orig_scale_base)) > to_big(0)) or in_context_scaling)
then
for i = 1, #G.jokers.cards do
local obj = G.jokers.cards[i].config.center
if obj.cry_scale_mod and type(obj.cry_scale_mod) == "function" then
local ggpn = G.GAME.probabilities.normal
if G.jokers.cards[i].ability.cry_rigged then
G.GAME.probabilities.normal = 1e9
end
local o = obj:cry_scale_mod(
G.jokers.cards[i],
jkr,
orig_scale_scale,
true_base,
orig_scale_base,
new_scale_base
)
if G.jokers.cards[i].ability.cry_rigged then
G.GAME.probabilities.normal = ggpn
end
if o then
if #dbl_info.scaler == 2 then
if
not (
not jkr.ability[dbl_info.scaler[1]]
or not jkr.ability[dbl_info.scaler[1]][dbl_info.scaler[2]]
)
then
jkr.ability[dbl_info.scaler[1]][dbl_info.scaler[2]] = o
orig_scale_scale = o
end
else
if jkr.ability[dbl_info.scaler[1]] then
jkr.ability[dbl_info.scaler[1]] = o
orig_scale_scale = o
end
end
card_eval_status_text(
G.jokers.cards[i],
"extra",
nil,
nil,
nil,
{ message = localize("k_upgrade_ex") }
)
end
local reps = {}
for i2 = 1, #G.jokers.cards do
local _card = G.jokers.cards[i2]
local ggpn = G.GAME.probabilities.normal
if _card.ability.cry_rigged then
G.GAME.probabilities.normal = 1e9
end
local check =
cj(G.jokers.cards[i2], { retrigger_joker_check = true, other_card = G.jokers.cards[i] })
if _card.ability.cry_rigged then
G.GAME.probabilities.normal = ggpn
end
if type(check) == "table" then
reps[i2] = check and check.repetitions and check or 0
else
reps[i2] = 0
end
if
G.jokers.cards[i2] == G.jokers.cards[i]
and G.jokers.cards[i].edition
and G.jokers.cards[i].edition.retriggers
then
local old_repetitions = reps[i] ~= 0 and reps[i].repetitions or 0
local check = false --G.jokers.cards[i]:calculate_retriggers()
if check and check.repetitions then
check.repetitions = check.repetitions + old_repetitions
reps[i] = check
end
end
end
for i0, j in ipairs(reps) do
if (type(j) == "table") and j.repetitions and (j.repetitions > 0) then
for r = 1, j.repetitions do
card_eval_status_text(j.card, "jokers", nil, nil, nil, j)
local ggpn = G.GAME.probabilities.normal
if G.jokers.cards[i].ability.cry_rigged then