Compare commits

..

No commits in common. "main" and "rewrite" have entirely different histories.

64 changed files with 1641 additions and 3537 deletions

5
.gitignore vendored
View file

@ -39,8 +39,3 @@ luac.out
*.x86_64 *.x86_64
*.hex *.hex
# Syncthing
.stfolder
# Autogenerated
assets/1x/jokers

12
.vscode/settings.json vendored
View file

@ -1,8 +1,4 @@
{ {
"Lua.diagnostics.disable": [
"duplicate-set-field",
"lowercase-global",
],
"Lua.diagnostics.severity": { "Lua.diagnostics.severity": {
"ambiguity-1": "Error", "ambiguity-1": "Error",
"assign-type-mismatch": "Error", "assign-type-mismatch": "Error",
@ -22,12 +18,14 @@
"duplicate-doc-field": "Error", "duplicate-doc-field": "Error",
"duplicate-doc-param": "Error", "duplicate-doc-param": "Error",
"duplicate-index": "Error", "duplicate-index": "Error",
"duplicate-set-field": "Error",
"empty-block": "Error", "empty-block": "Error",
"global-element": "Error", "global-element": "Error",
"global-in-nil-env": "Error", "global-in-nil-env": "Error",
"incomplete-signature-doc": "Error", "incomplete-signature-doc": "Error",
"inject-field": "Error", "inject-field": "Error",
"invisible": "Error", "invisible": "Error",
"lowercase-global": "Error",
"missing-fields": "Error", "missing-fields": "Error",
"missing-global-doc": "Error", "missing-global-doc": "Error",
"missing-local-export-doc": "Error", "missing-local-export-doc": "Error",
@ -64,9 +62,5 @@
"unused-label": "Error", "unused-label": "Error",
"unused-local": "Error", "unused-local": "Error",
"unused-vararg": "Error", "unused-vararg": "Error",
}, }
"Lua.runtime.version": "LuaJIT",
"Lua.workspace.library": [
"${3rd}/love2d/library"
],
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 897 B

After

Width:  |  Height:  |  Size: 904 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View file

@ -1,10 +0,0 @@
#!/bin/sh
directory=$(dirname $(readlink -f "$0"))
mkdir -p "$directory/1x/jokers"
for i in $(seq 0 $(cd "$directory/1x/jokers/" && find *.png | tail -n +2 | wc -l)); do
files="$files $directory/1x/jokers/$i.png";
done
magick montage -background '#00000000' $files -geometry +0+0 "$directory/1x/joker.png"
"$directory/upscale.sh"

View file

@ -1,199 +0,0 @@
#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH)
#define MY_HIGHP_OR_MEDIUMP highp
#else
#define MY_HIGHP_OR_MEDIUMP mediump
#endif
extern MY_HIGHP_OR_MEDIUMP vec2 frozen;
extern MY_HIGHP_OR_MEDIUMP number dissolve;
extern MY_HIGHP_OR_MEDIUMP number time;
extern MY_HIGHP_OR_MEDIUMP vec4 texture_details;
extern MY_HIGHP_OR_MEDIUMP vec2 image_details;
extern bool shadow;
extern MY_HIGHP_OR_MEDIUMP vec4 burn_colour_1;
extern MY_HIGHP_OR_MEDIUMP vec4 burn_colour_2;
vec4 dissolve_mask(vec4 tex, vec2 texture_coords, vec2 uv)
{
if (dissolve < 0.001) {
return vec4(shadow ? vec3(0.,0.,0.) : tex.xyz, shadow ? tex.a*0.3: tex.a);
}
float adjusted_dissolve = (dissolve*dissolve*(3.-2.*dissolve))*1.02 - 0.01;
float t = time * 10.0 + 2003.;
vec2 floored_uv = (floor((uv*texture_details.ba)))/max(texture_details.b, texture_details.a);
vec2 uv_scaled_centered = (floored_uv - 0.5) * 2.3 * max(texture_details.b, texture_details.a);
vec2 field_part1 = uv_scaled_centered + 50.*vec2(sin(-t / 143.6340), cos(-t / 99.4324));
vec2 field_part2 = uv_scaled_centered + 50.*vec2(cos( t / 53.1532), cos( t / 61.4532));
vec2 field_part3 = uv_scaled_centered + 50.*vec2(sin(-t / 87.53218), sin(-t / 49.0000));
float field = (1.+ (
cos(length(field_part1) / 19.483) + sin(length(field_part2) / 33.155) * cos(field_part2.y / 15.73) +
cos(length(field_part3) / 27.193) * sin(field_part3.x / 21.92) ))/2.;
vec2 borders = vec2(0.2, 0.8);
float res = (.5 + .5* cos( (adjusted_dissolve) / 82.612 + ( field + -.5 ) *3.14))
- (floored_uv.x > borders.y ? (floored_uv.x - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve)
- (floored_uv.y > borders.y ? (floored_uv.y - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve)
- (floored_uv.x < borders.x ? (borders.x - floored_uv.x)*(5. + 5.*dissolve) : 0.)*(dissolve)
- (floored_uv.y < borders.x ? (borders.x - floored_uv.y)*(5. + 5.*dissolve) : 0.)*(dissolve);
if (tex.a > 0.01 && burn_colour_1.a > 0.01 && !shadow && res < adjusted_dissolve + 0.8*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) {
if (!shadow && res < adjusted_dissolve + 0.5*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) {
tex.rgba = burn_colour_1.rgba;
} else if (burn_colour_2.a > 0.01) {
tex.rgba = burn_colour_2.rgba;
}
}
return vec4(shadow ? vec3(0.,6.,0.) : tex.xyz, res > adjusted_dissolve ? (shadow ? tex.a*0.3: tex.a) : .0);
}
#define DEPTH 21
#define PHI ((1. + sqrt(5.)) / 2.)
#define PI 3.141592653589
#define S(M, O) (0.5 + 0.5 * sin(O + M * pos.x)) / 6.
#define C(M, O) (0.5 + 0.5 * cos(O + M * pos.y)) / 6.
vec2 rotate(vec2 v, float a)
{
float s = sin(a);
float c = cos(a);
mat2 m = mat2(c, -s, s, c);
return m * v;
}
float cr(vec2 a, vec2 b)
{
return a.x * b.y - b.x * a.y;
}
float cr(vec2 a, vec2 b, vec2 c)
{
return cr(b - a, c - b);
}
vec4 effect(vec4 colour, Image texture, vec2 texture_coords, vec2 screen_coords)
{
vec4 tex = Texel(texture, texture_coords);
vec2 uv = (((texture_coords)*(image_details)) - texture_details.xy*texture_details.ba)/texture_details.ba;
if (uv.x > uv.x * 2.) {
uv = frozen;
}
// Slightly adapted from 'snowflake lattice' by pali6.
// https://www.shadertoy.com/view/7sG3zd
// https://www.shadertoy.com/user/pali6
float iTime = 0.0;
vec2 pos = (((texture_coords)*(image_details)) - texture_details.xx*texture_details.ba)/texture_details.ba;
float scale = 72. + 18. * sin(iTime / 10.) + 20. * sin(4. + iTime / 15.) + 30. * sin(iTime / 6.);
vec2 camPos = vec2(0.5, 0.5);
float SF = 1. / pow(scale, 0.3);
camPos += vec2(0.02 * sin(iTime / 15.), -0.07 * cos(iTime / 19.)) * SF;
camPos += vec2(-0.1 * sin(3. + iTime / 37.), -0.1 * cos(4. + iTime / 27.)) * SF;
camPos += vec2(0.07 * sin(9. + iTime / 47.), -0.08 * cos(20. + iTime / 31.)) * SF;
camPos += vec2(0.04 * sin(19. + iTime / 7.), 0.06 * cos(25. + iTime / 5.)) * SF;
pos -= vec2(0.5, 0.5);
pos = rotate(pos, iTime / 30. + PI * cos(iTime / 37.) + 2. * PI * sin(iTime / 87. + 10.));
pos /= scale;
pos += camPos;
vec2 ota = vec2(-1., 0.);
vec2 otb = vec2(2., 0.);
vec2 ta = ota;
vec2 tb = otb;
vec2 tc;
bool gn = true;
float fl = -1.;
float f = 0.;
float nDark = 0.;
for (int i = 0; i < DEPTH; i++)
{
vec2 next;
if (gn)
tc = ta + rotate(tb - ta, fl * PI / 5.) / PHI;
else
tc = ta + rotate(tb - ta, fl * 2. * PI / 5.) * PHI;
if(!(gn && min(length(pos - ta), length(pos - tb)) > length(ta - tb) / 3. || !gn && length(pos - tb) <= length(tc - tb) * PHI / 3.5))
{
nDark += 1.;
}
if (gn)
{
next = tb + (ta - tb) / PHI;
if (abs(cr(next, tc, pos)) * float(i + 1) < 0.000001)
f = 1.;
if (cr(next, tc, pos) * cr(next, tc, tb) > 0.)
{
gn = false;
ta = next;
tb = tc;
fl *= -1.;
i--;
}
else
{
gn = true;
tb = ta;
ta = tc;
}
}
else
{
next = tc + (tb - tc) / PHI;
if (abs(cr(ta, next, pos)) * float(i + 1) < 0.000001)
f = 1.;
if (cr(ta, next, pos) * cr(ta, next, tb) > 0.)
{
gn = false;
ta = tb;
tb = next;
}
else
{
gn = true;
tb = ta;
ta = tc;
}
}
}
colour = (nDark / float(DEPTH)) * vec4(
0.1 + S(30., 10.) + C(10., 11.) + S(50., 16.) + S(99., 15.) + S(1., 42.) + C(1.5, 73.),
0.1 + C(20., 10.) + S(70., 12.) + C(23., 18.) + C(67., 119.) + S(1.2, 49.) + C(0.9, 79.),
0.15 + S(70., 10.5) + C(31., 17.) + S(55., 135.) + C(123., 15.) + S(0.7, 13.) + C(0.11, 31.),
1.
) * 1.5;
return dissolve_mask(tex*colour, texture_coords, uv);
}
extern MY_HIGHP_OR_MEDIUMP vec2 mouse_screen_pos;
extern MY_HIGHP_OR_MEDIUMP float hovering;
extern MY_HIGHP_OR_MEDIUMP float screen_scale;
#ifdef VERTEX
vec4 position( mat4 transform_projection, vec4 vertex_position )
{
if (hovering <= 0.){
return transform_projection * vertex_position;
}
float mid_dist = length(vertex_position.xy - 0.5*love_ScreenSize.xy)/length(love_ScreenSize.xy);
vec2 mouse_offset = (vertex_position.xy - mouse_screen_pos.xy)/screen_scale;
float scale = 0.2*(-0.03 - 0.3*max(0., 0.3-mid_dist))
*hovering*(length(mouse_offset)*length(mouse_offset))/(2. -mid_dist);
return transform_projection * vertex_position + vec4(0.,0.,0.,scale);
}
#endif

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,4 +0,0 @@
#!/bin/sh
directory=$(dirname $(readlink -f "$0"))
mkdir -p "$directory/1x/jokers"
magick "$directory/1x/joker.png" -crop 71x95 +repage +adjoin "$directory/1x/jokers/%01d.png"

View file

@ -1,18 +1 @@
@echo off for /F %%x in ('dir /B/D 1x') do magick 1x\%%x -scale 200%% 2x\%%x
for /F %%x in ('dir /B/D 2x') do (
if %%~xx == .png (
if not exist 1x\%%x (
echo Removing deleted file 1x\%%x from 2x\
del 2x\%%x
)
)
)
for /F %%x in ('dir /B/D 1x') do (
if %%~xx == .png (
REM https://stackoverflow.com/questions/1687014/how-do-i-compare-timestamps-of-files-in-a-batch-script/58323817#58323817
xcopy /LDY /-I 1x\%%x 2x\%%x | findstr /B /C:"1 " > nul 2>&1 && (
echo Updating changed file 1x\%%x in 2x\
magick 1x\%%x -filter point -resize 200%% 2x\%%x
)
)
)

View file

@ -1,12 +1 @@
return { return {escapey_debugger = false, equinox_assist = false}
animated_icon = true,
cool_phones = true,
equinox_assist = false,
faster_planets = false,
harsh_ante_scaling = false,
illusion_seal = true,
import_funky = false,
no_wild_debuff = true,
scribable_basket = false,
vitriol = true,
}

View file

@ -6,9 +6,15 @@ return {
text = { text = {
"{C:attention}Small{} and {C:attention}Big Blinds{} are", "{C:attention}Small{} and {C:attention}Big Blinds{} are",
"replaced with {C:attention}Boss Blinds", "replaced with {C:attention}Boss Blinds",
"that are {C:dark_edition}debuffed twice", "that are {C:dark_edition}debuffed #1# times",
"", },
"{C:Bakery_credit_fg_Roland_bakersdozenbagels,s:0.75}Art: BakersDozenBagels", },
b_Roland_go = {
name = "Pass GO Deck",
text = {
"Start with a {C:attention}Credit Card",
"Set money to {C:money}$0",
"when entering the shop",
}, },
}, },
b_Roland_swapper = { b_Roland_swapper = {
@ -16,39 +22,32 @@ return {
text = { text = {
"{C:tarot}Tarot {}and {C:spectral}Spectral", "{C:tarot}Tarot {}and {C:spectral}Spectral",
"cards swap places", "cards swap places",
"",
"{C:Bakery_credit_fg_Roland_bakersdozenbagels,s:0.75}Art: BakersDozenBagels",
}, },
}, },
}, },
BakeryCharm = { BakeryCharm = {
BakeryCharm_Roland_cocacola = { BakeryCharm_Roland_cocacola = {
name = SMODS.Mods.Roland.config.cool_phones and "coca cola phone" or "Pentagram", name = "coca cola phone",
text = { text = {"Values on consumables", "increase by {C:attention}1"},
"{C:attention}Discard 0 cards {}to",
"gain {C:red}#1# {}discards and",
"lose {C:attention}#2# {}hand size",
"this round",
},
}, },
BakeryCharm_Roland_fat = { BakeryCharm_Roland_fat = {
name = SMODS.Mods.Roland.config.cool_phones and "fat i phone" or "Product", name = "fat i phone",
text = { text = {
"{C:attention}+#1# Booster Pack {}slots", -- "{C:attention}+#1# Booster Pack {}slots",
"All {C:attention}Booster Packs {}are {C:attention}Mega", "All {C:attention}Booster Packs {}are {C:attention}Mega",
"{C:inactive}(Whenever applicable)", "{C:inactive}(Whenever applicable)",
}, },
}, },
BakeryCharm_Roland_flexible = { BakeryCharm_Roland_flexible = {
name = SMODS.Mods.Roland.config.cool_phones and "flexi ble phone" or "Ring", name = "flexi ble phone",
text = {unpack(G.localization.descriptions.Joker.j_ring_master.text)}, text = {unpack(G.localization.descriptions.Joker.j_ring_master.text)},
}, },
BakeryCharm_Roland_hand = { BakeryCharm_Roland_hand = {
name = SMODS.Mods.Roland.config.cool_phones and "hand phone" or "Shell", name = "hand phone",
text = {"{C:attention}+#1# {}hand size", "{C:blue}#2# {}hands"}, text = {"{C:attention}+#1# {}hand size", "{C:blue}#2# {}hands"},
}, },
BakeryCharm_Roland_wii = { BakeryCharm_Roland_wii = {
name = SMODS.Mods.Roland.config.cool_phones and "wii phone" or "Tire", name = "wii phone",
text = { text = {
"Enter the shop", "Enter the shop",
"when any {C:attention}Blind", "when any {C:attention}Blind",
@ -57,49 +56,37 @@ return {
}, },
}, },
Blind = { Blind = {
bl_Roland_blizzard = {
name = "The Blizzard",
text = {"All cards", "are {C:dark_edition}Frozen"},
},
bl_Roland_divide = { bl_Roland_divide = {
name = "The Great Divide", name = "The Great Divide",
text = {"Half of the deck", "is {C:red}discarded"}, text = {"Half of the deck", "is discarded"},
}, },
bl_Roland_equinox = { bl_Roland_equinox = {
name = "The Equinox", name = "The Equinox",
text = {"{C:inactive}No UI"}, text = {"No UI"},
}, },
bl_Roland_falseshuffle = { bl_Roland_falseshuffle = {
name = "The False Shuffle", name = "The False Shuffle",
text = {"{C:enhanced}Enhanced {}cards", "are drawn {C:attention}last"}, text = {"Enhanced cards", "are drawn last"},
}, },
bl_Roland_improbable = { bl_Roland_improbable = {
name = "The Improbable", name = "The Improbable",
text = {"All {C:green}probabilities", "cannot happen"}, text = {"All probabilities", "cannot happen"},
}, },
bl_Roland_mitotic = { bl_Roland_mitotic = {
name = "The Mitotic", name = "The Mitotic",
text = {"{C:red}Discarded {}cards", "are {C:attention}copied"}, text = {"Discarded cards", "are copied"},
}, },
bl_Roland_nimble = { bl_Roland_nimble = {
name = "The Nimble", name = "The Nimble",
text = {"The {C:attention}first 5 {}cards", "drawn are {C:blue}played"}, text = {"The first 5 cards", "drawn are played"},
}, },
bl_Roland_tranquilizer = { bl_Roland_tranquilizer = {
name = "The Tranquilizer", name = "The Tranquilizer",
text = { text = {"#1#s are debuffed", "Changes based on", "most common rank"},
"{C:attention}#1#s {}are {C:red}debuffed",
"Changes based on",
"most common rank",
},
}, },
bl_Roland_venerable_visage = { bl_Roland_venerable_visage = {
name = "Venerable Visage", name = "Venerable Visage",
text = { text = {"Infinite discards", "Lose if any card was", "not played or discarded"},
"Infinite {C:red}discards",
"Lose if {C:attention}any {}card was",
"not {C:blue}played {}or {C:red}discarded",
},
fail = { fail = {
"Blasphemy.", "Blasphemy.",
"Blunder.", "Blunder.",
@ -118,12 +105,6 @@ return {
}, },
}, },
}, },
Edition = {
e_Roland_frozen = {
name = "Frozen",
text = {"Values on", "this card do", "not change"},
},
},
Enhanced = { Enhanced = {
m_wild = { m_wild = {
name = "Wild Card", name = "Wild Card",
@ -131,36 +112,6 @@ return {
}, },
}, },
Joker = { Joker = {
j_Roland_amber = {
name = "Amber Acorn",
text = {
"{X:mult,C:white}X#1#{} Mult",
"Moves before",
"hand is played",
},
},
j_Roland_arctic = {
name = "Arctic Circle",
text = {"Retrigger {C:attention}all", "{C:dark_edition}Frozen {}cards"},
},
j_Roland_artemis = {
name = "Artemis X",
text = {
"Earn {C:money}$#1# {}at end of round",
"Payout increases by {C:money}$#2#",
"when these {C:planet}Planet {}cards",
"are used in order:",
"{V:1}Earth{C:inactive} - {V:2}Mars{C:inactive} - {V:3}Earth",
},
},
j_Roland_basket = {
name = "Basket",
text = {
"Sell this Joker to",
"create {C:dark_edition}Negative {}copies",
"of every consumable",
},
},
j_Roland_bulldozer = { j_Roland_bulldozer = {
name = "Bulldozer", name = "Bulldozer",
text = { text = {
@ -169,28 +120,43 @@ return {
"{C:inactive}(Currently {X:red,C:white}X#1#{C:inactive})", "{C:inactive}(Currently {X:red,C:white}X#1#{C:inactive})",
}, },
}, },
j_Roland_cold = { j_Roland_escapey = {
name = "Cold Turkey", name = "Escapey",
text = { text = {
"Third scored card", "Use to {C:attention}destroy tags{} or {C:attention}unselected consumables",
"gives {X:red,C:white}X#1#{} Mult", "in exchange for {C:planet}leveling up {X:planet,C:white}#1#{C:planet} random hands",
"if it is {C:dark_edition}Frozen", "{C:inactive,s:0.75,E:1}#2#{C:red,s:1.5,E:1}#3#{s:0.9}#4#{C:blue,E:1,s:0.9}#5#{s:0.9}#6#",
},
merge = {"Since none apply, fuse with other ", " jokers"},
quotes = {
marble = {"there is no escape..."},
normal = {
"I can't wait to work with you!",
"Did you need something from me?",
"Oh! I'm just so happy to see you!",
"Can I say something irrelevant? I promise it won't be long.",
"Tell me about your buddies! Assuming you have them, anyway.",
},
scared = {
"What am I going to do?!",
"I'm not scared, you are!",
"Tell me when this is over...",
"I can't keep looking at this!",
"Let me go hide in this corner... Okay?",
}, },
}, },
j_Roland_cerulean = { },
name = "Cerulean Bell", j_Roland_phytoestrogens = {
name = "Phytoestrogens",
text = {"{C:mult}+Chips{} Mult", "{X:mult,C:white}X#1#{} Mult"},
},
j_Roland_monomino = {
name = "Monomino",
text = { text = {
"{X:mult,C:white}X#1#{} Mult", "Gains {C:money}$#1#{} of",
"All other {C:attention}Jokers {}cannot", "{C:attention}sell value{} if",
"be selected or dragged", "played hand is a",
}, "{C:attention}#2#",
},
j_Roland_crimson = {
name = "Crimson Heart",
text = {
"{X:mult,C:white}X#1#{} Mult",
"{C:red}Debuffs {C:attention}Joker",
"to the left",
}, },
}, },
j_Roland_domino = { j_Roland_domino = {
@ -202,51 +168,24 @@ return {
"{C:inactive}(Currently {C:red}+#3#{C:inactive} Mult)", "{C:inactive}(Currently {C:red}+#3#{C:inactive} Mult)",
}, },
}, },
j_Roland_excalibur = { j_Roland_trimino = {
name = "Excalibur", name = "Trimino",
text = { text = {
"Score {C:attention}#1# {C:enhanced}Stone Cards", "Copies {C:attention}#1# {C:green}random",
"to {C:attention}pull {}this {C:attention}Joker", "scoring cards and",
"{C:inactive}(Currently {C:attention}#2#{C:inactive})", "{C:red,E:1}self-destructs {}if",
"played hand is a",
"{C:attention}#2#",
}, },
}, },
j_Roland_excalibur_Back = { j_Roland_misfortune = {
name = "Excalibur", name = "Misfortune Cookie",
text = { text = {
"Scored and held in", "{C:tarot}Tarot{} cards created",
"hand {C:enhanced}Stone Cards", "by {C:tarot}Purple Seals",
"each give {X:mult,C:white}X#3#{} Mult", "become {C:dark_edition}Negative",
}, },
}, },
j_Roland_hardboiled = {
name = "Hard-Boiled",
text = {
"Sell this Joker to",
"turn all cards in",
"hand {C:dark_edition}Frozen",
},
},
j_Roland_idle = {
name = "The Idle",
text = {
"Use to add card's",
"rank and suit",
"First {C:attention}scoring {}card",
"of each entry",
"gives {X:mult,C:white}X#19#{} Mult",
"{C:inactive}(Must be distinct)",
"{V:1,s:0.75}#1#{C:inactive,s:0.75}#2#{V:2,s:0.75}#3#{C:inactive,s:0.75}, " ..
"{V:3,s:0.75}#4#{C:inactive,s:0.75}#5#{V:4,s:0.75}#6#{C:inactive,s:0.75}",
"{V:5,s:0.75}#7#{C:inactive,s:0.75}#8#{V:6,s:0.75}#9#, " ..
"{V:7,s:0.75}#10#{C:inactive,s:0.75}#11#{V:8,s:0.75}#12#{C:inactive,s:0.75}",
"{V:9,s:0.75}#13#{C:inactive,s:0.75}#14#{V:10,s:0.75}#15#{C:inactive,s:0.75}, " ..
"{V:11,s:0.75}#16#{C:inactive,s:0.75}#17#{V:12,s:0.75}#18#",
},
},
j_Roland_jokersr = {
name = "Joker Sr.",
text = {"{X:mult,C:white}X#1#{} Mult"},
},
j_Roland_martingale = { j_Roland_martingale = {
name = "Martingale", name = "Martingale",
text = { text = {
@ -260,23 +199,6 @@ return {
"{s:0}Otherwise, {C:green,s:0}#1# in #3#{s:0} chance to give {X:mult,C:white,s:0}X#9#{s:0} Mult", "{s:0}Otherwise, {C:green,s:0}#1# in #3#{s:0} chance to give {X:mult,C:white,s:0}X#9#{s:0} Mult",
}, },
}, },
j_Roland_misfortune = {
name = "Misfortune Cookie",
text = {
"{C:tarot}Tarot{} cards created",
"by {C:tarot}Purple Seals",
"become {C:dark_edition}Negative",
},
},
j_Roland_monomino = {
name = "Monomino",
text = {
"Gains {C:money}$#1#{} of",
"{C:attention}sell value{} if",
"played hand is a",
"{C:attention}#2#",
},
},
j_Roland_mrsbones = { j_Roland_mrsbones = {
name = "Mrs. Bones", name = "Mrs. Bones",
text = { text = {
@ -291,14 +213,6 @@ return {
name = "Ms. Joker", name = "Ms. Joker",
text = {"{C:chips}+#1#{} Chips"}, text = {"{C:chips}+#1#{} Chips"},
}, },
j_Roland_nilly = {
name = "Nilly",
text = {
"Toggleable {X:mult,C:white}X0{} Mult",
"{C:inactive}(Currently {C:attention}#1#{C:inactive})",
"{C:inactive,s:0.75,E:1}Wait... what?",
},
},
j_Roland_oops = { j_Roland_oops = {
name = "Oops! All 7s", name = "Oops! All 7s",
text = { text = {
@ -307,10 +221,6 @@ return {
"{C:inactive}(Currently {X:green,C:white}X#2#{C:inactive})", "{C:inactive}(Currently {X:green,C:white}X#2#{C:inactive})",
}, },
}, },
j_Roland_phytoestrogens = {
name = "Phytoestrogens",
text = {"{C:mult}+Chips{} Mult, {X:mult,C:white}X#1#{} Mult"},
},
j_Roland_sapling = { j_Roland_sapling = {
name = "Sapling", name = "Sapling",
text = { text = {
@ -320,25 +230,16 @@ return {
"least {C:attention}#2# suits", "least {C:attention}#2# suits",
}, },
}, },
j_Roland_snowsquall = { j_Roland_srjoker = {
name = "Snowsquall", name = "Sr. Joker",
text = { text = {"{X:mult,C:white}X#1#{} Mult"},
"This Joker gains",
"{C:mult}+#1# {}Mult for every",
"played {C:dark_edition}Frozen {}card",
"{C:inactive}(Currently {C:red}+#2#{C:inactive} Mult)",
},
},
j_Roland_suitable = {
name = "Suitable",
text = {"{V:1}#1# {}are {C:attention}Wild", "Suit changes", "every round"},
}, },
j_Roland_sunny = { j_Roland_sunny = {
name = "Sunny Side Up", name = "Sunny Side Up",
text = { text = {
"Sell this Joker to", "Sell this Joker to",
"{C:attention}draw{} the bottom", "{C:attention}draw{} the bottom",
"card of the deck", "card of the {C:hand}deck",
"{C:inactive}(Currently {V:1}#1#{C:inactive}#2#{V:2}#3#{C:inactive})", "{C:inactive}(Currently {V:1}#1#{C:inactive}#2#{V:2}#3#{C:inactive})",
}, },
}, },
@ -349,36 +250,6 @@ return {
"give {X:red,C:white}X#1#{} Mult", "give {X:red,C:white}X#1#{} Mult",
}, },
}, },
j_Roland_trimino = {
name = "Trimino",
text = {
"Copies {C:attention}#1# {C:green}random",
"scoring cards and",
"{C:red,E:1}self-destructs {}if",
"played hand is a",
"{C:attention}#2#",
},
},
j_Roland_verdant = {
name = "Verdant Leaf",
text = {
"{X:mult,C:white}X#1#{} Mult",
"{C:red}Debuffs {}the {C:attention}first",
"{C:attention}#2# {}scored cards",
},
},
j_Roland_violet = {
name = "Violet Vessel",
text = {
"{X:mult,C:white}X#1#{} Mult,",
"but {X:mult,C:white}X#2#{} Mult",
"{C:attention}before {}scoring",
},
},
j_Roland_venerable = {
name = "Venerable Visage",
text = {"{X:red,C:white}X#1#{} discards each round"},
},
j_Roland_yard = { j_Roland_yard = {
name = "Yard Sale", name = "Yard Sale",
text = { text = {
@ -404,18 +275,30 @@ return {
text = { text = {
"{C:attention}Small{} and {C:attention}Big Blinds{} are", "{C:attention}Small{} and {C:attention}Big Blinds{} are",
"replaced with {C:attention}Boss Blinds", "replaced with {C:attention}Boss Blinds",
"that are {C:dark_edition}debuffed twice", "that are {C:dark_edition}debuffed #1# times",
"",
"{C:Bakery_credit_fg_Roland_bakersdozenbagels,s:0.75}Art: BakersDozenBagels",
}, },
}, },
sleeve_Roland_blossom_alt = { sleeve_Roland_blossom_alt = {
name = "Efflorescent Sleeve", name = "Efflorescent Sleeve",
text = { text = {
"{C:dark_edition}Debuff {X:dark_edition,C:white}#1#X<ante>{C:dark_edition} times", "{C:attention}Blinds{} are {C:dark_edition}debuffed",
"{C:inactive}(Instead of twice)", "{C:dark_edition}({X:dark_edition,C:white}ante{} {X:dark_edition,C:white}number{C:dark_edition}) times",
"", "{C:inactive}(Instead of #1#)",
"{C:Bakery_credit_fg_Roland_bakersdozenbagels,s:0.75}Art: BakersDozenBagels", },
},
sleeve_Roland_go = {
name = "Pass GO Sleeve",
text = {
"Start with a {C:attention}Credit Card",
"Set money to {C:money}$0",
"when entering the shop",
},
},
sleeve_Roland_go_alt = {
name = "Free Parking Sleeve",
text = {
"Start with an additional",
"{X:attention,C:white}#1#{C:attention} Credit Cards",
}, },
}, },
sleeve_Roland_swapper = { sleeve_Roland_swapper = {
@ -423,42 +306,39 @@ return {
text = { text = {
"{C:tarot}Tarot {}and {C:spectral}Spectral", "{C:tarot}Tarot {}and {C:spectral}Spectral",
"cards swap places", "cards swap places",
"",
"{C:Bakery_credit_fg_Roland_bakersdozenbagels,s:0.75}Art: BakersDozenBagels",
}, },
}, },
sleeve_Roland_swapper_alt = { sleeve_Roland_swapper_alt = {
name = "Sleeve Swapper", name = "Sleeve Swapper",
text = { text = {
"Swap {C:attention}all {}consumables", "{C:attention}All {}consumable",
"", "types swap places",
"{C:Bakery_credit_fg_Roland_bakersdozenbagels,s:0.75}Art: BakersDozenBagels",
}, },
}, },
}, },
Spectral = { Spectral = {
c_Roland_afterimage = { c_Roland_afterimage = {
name = "Afterimage", name = "Afterimage",
text = {
"Add {C:dark_edition}Negative {}to {C:attention}#1#",
"selected card in hand",
"{C:red}#2#{} hand size",
},
},
c_Roland_dual = {
name = "Dual",
text = { text = {
"Add random seals", "Add random seals",
"to {C:attention}#1#{} random", "to {C:attention}#1#{} random",
"cards in hand", "cards in {C:hands}hand",
}, },
}, },
c_Roland_primal = { c_Roland_mirror = {
name = "Primal Force", name = "Mirror",
text = { text = {
"Enhance {C:attention}all {}cards", "Add a {C:dark_edition}Glass Tag",
"held in hand to",
"{C:enhanced}Stone Cards",
},
},
c_Roland_refract = {
name = "Refract",
text = {
"Add a {C:dark_edition}Glass Seal",
"to {C:attention}#1#{} selected", "to {C:attention}#1#{} selected",
"cards in hand", "card in {C:hands}hand",
}, },
}, },
c_Roland_void = { c_Roland_void = {
@ -470,47 +350,6 @@ return {
}, },
}, },
}, },
Tag = {
tag_Roland_freeze = {
name = "Freeze Tag",
text = {"Apply {C:dark_edition}Frozen", "to the next {C:attention}#1#", "scored cards"},
},
tag_Roland_invisible = {
name = "Invisible Tag",
text = {
"{C:attention}Duplicate {}a random",
"Joker after {C:attention}#1#{} rounds",
"{C:inactive}(Must have room)",
"{C:inactive}(Currently {C:attention}#2#{C:inactive}/#1#)",
},
},
},
Tarot = {
c_Roland_coolheaded = {
name = "Coolheaded",
text = {
"Add {C:dark_edition}Frozen {}to {C:attention}#1#",
"selected {C:attention}playing",
"{C:attention}card {}or {C:attention}Joker",
},
},
},
Voucher = {
v_Roland_ceres = {
name = "Ceres",
text = {
"Level up {C:attention}Flush House",
"by {C:attention}#1# {}when it is played",
},
},
v_Roland_neptune = {
name = "Neptune",
text = {
"Level up {C:attention}Straight Flush",
"by {C:attention}#1# {}when it is played",
},
},
},
}, },
misc = { misc = {
challenge_names = { challenge_names = {
@ -519,39 +358,25 @@ return {
c_Roland_Eternally_Crimson = "Eternally Crimson", c_Roland_Eternally_Crimson = "Eternally Crimson",
c_Roland_Eternally_Verdant = "Eternally Verdant", c_Roland_Eternally_Verdant = "Eternally Verdant",
c_Roland_Eternally_Violet = "Eternally Violet", c_Roland_Eternally_Violet = "Eternally Violet",
c_Roland_Glass = "Glass House",
c_Roland_Go = "Pass GO",
c_Roland_Jokerful = "Jokerful", c_Roland_Jokerful = "Jokerful",
c_Roland_Ornate = "Ornate",
c_Roland_Pastries = "Sweet Pastries", c_Roland_Pastries = "Sweet Pastries",
c_Roland_Spin_To_Win = "Spin to Win", c_Roland_Showdown = "Showdown",
}, },
v_dictionary = { v_dictionary = {
b_Roland_add = "ADD",
b_Roland_animated_icon = "Animated Icon (requires restart)",
b_Roland_bye = "Bye!", b_Roland_bye = "Bye!",
b_Roland_comma = ", ", b_Roland_comma = ", ",
b_Roland_cool_phones = "cool phones (requires restart)",
b_Roland_debuffed = "DEBUFFED", b_Roland_debuffed = "DEBUFFED",
b_Roland_disabled = "Disabled",
b_Roland_enabled = "Enabled",
b_Roland_entering_shop = "Entering shop!", b_Roland_entering_shop = "Entering shop!",
b_Roland_equinox_assist = "Assist: Only hide text (Equinox)", b_Roland_escape = "ESCAPE",
b_Roland_faster_planets = "Faster Planets", b_Roland_fuse = "FUSE",
b_Roland_full = "FULL", b_Roland_debug_export = "Debug: Import funky.lua to _G",
b_Roland_scribable_basket = "Scribable Basket (overpowered)", b_Roland_equinox_assist = "Assist: Only hide text during Equinox",
b_Roland_harsh_ante_scaling = "Harsh ante scaling (Ante 40+)", b_Roland_most_common_card = "(Rank)",
b_Roland_illusion_seal = "Allow seals from Illusion voucher",
b_Roland_import_funky = "Debug: Import funky.lua",
b_Roland_most_common_card = "Rank",
b_Roland_no_wild_debuff = "No wild card debuffs",
b_Roland_na = "N/A", b_Roland_na = "N/A",
b_Roland_of = " of ", b_Roland_of = " of ",
b_Roland_toggle = "TOGGLE",
b_Roland_unassigned = "(Unassigned)",
b_Roland_vitriol = "Vitriol (Venerable Visage)",
}, },
labels = { labels = {
Roland_frozen = "Frozen",
roland_glass_seal = "Glass Seal", roland_glass_seal = "Glass Seal",
}, },
v_text = { v_text = {
@ -560,11 +385,10 @@ return {
ch_c_Roland_Eternally_Crimson = {"{C:attention}Crimson Heart{}'s effect is active every blind"}, ch_c_Roland_Eternally_Crimson = {"{C:attention}Crimson Heart{}'s effect is active every blind"},
ch_c_Roland_Eternally_Verdant = {"{C:attention}Verdant Leaf{}'s effect is active every blind"}, ch_c_Roland_Eternally_Verdant = {"{C:attention}Verdant Leaf{}'s effect is active every blind"},
ch_c_Roland_Eternally_Violet = {"{C:attention}Violet Vessel{}'s effect is active every blind"}, ch_c_Roland_Eternally_Violet = {"{C:attention}Violet Vessel{}'s effect is active every blind"},
ch_c_Roland_Glass1 = {"Played cards with no seal have a"},
ch_c_Roland_Glass2 = {"{C:green}1 in 2{} chance to gain {C:dark_edition}Glass Seal"},
ch_c_Roland_Go = {"Set money to {C:money}$0 {}when entering the shop"},
ch_c_Roland_Jokerful = {"The only jokers are {C:mult}Joker{} and {C:chips}Ms. Joker"}, ch_c_Roland_Jokerful = {"The only jokers are {C:mult}Joker{} and {C:chips}Ms. Joker"},
ch_c_Roland_Ornate = {"Anything vanilla is banned"},
ch_c_Roland_Pastries = {"All blinds, cards, and tags are of {C:gold}Bakery{} or {C:blue}Roland"}, ch_c_Roland_Pastries = {"All blinds, cards, and tags are of {C:gold}Bakery{} or {C:blue}Roland"},
ch_c_Roland_Showdown = {"Showdown blinds are {C:attention}Venerable Visage"},
ch_c_Roland_Showdown_Amber = {"Showdown blinds are {C:attention}Amber Acorn {}(stacks with above)"}, ch_c_Roland_Showdown_Amber = {"Showdown blinds are {C:attention}Amber Acorn {}(stacks with above)"},
ch_c_Roland_Showdown_Cerulean = {"Showdown blinds are {C:attention}Cerulean Bell {}(stacks with above)"}, ch_c_Roland_Showdown_Cerulean = {"Showdown blinds are {C:attention}Cerulean Bell {}(stacks with above)"},
ch_c_Roland_Showdown_Crimson = {"Showdown blinds are {C:attention}Crimson Heart {}(stacks with above)"}, ch_c_Roland_Showdown_Crimson = {"Showdown blinds are {C:attention}Crimson Heart {}(stacks with above)"},

View file

@ -12,12 +12,12 @@ priority = -2
target = "game.lua" target = "game.lua"
pattern = "self.GAME.round_resets.blind_choices.Boss = get_new_boss()" pattern = "self.GAME.round_resets.blind_choices.Boss = get_new_boss()"
position = "before" position = "before"
payload = """ payload = '''
if G.GAME.modifiers.Roland_blossom_deck then if G.GAME.modifiers.Roland_blossom_deck then
self.GAME.round_resets.blind_choices.Small = get_new_boss() self.GAME.round_resets.blind_choices.Small = get_new_boss()
self.GAME.round_resets.blind_choices.Big = get_new_boss() self.GAME.round_resets.blind_choices.Big = get_new_boss()
end end
""" '''
match_indent = true match_indent = true
[[patches]] [[patches]]
@ -25,12 +25,12 @@ match_indent = true
target = "functions/common_events.lua" target = "functions/common_events.lua"
pattern = "G.GAME.round_resets.blind_choices.Boss = get_new_boss()" pattern = "G.GAME.round_resets.blind_choices.Boss = get_new_boss()"
position = "before" position = "before"
payload = """ payload = '''
if G.GAME.modifiers.Roland_blossom_deck then if G.GAME.modifiers.Roland_blossom_deck then
G.GAME.round_resets.blind_choices.Small = get_new_boss() G.GAME.round_resets.blind_choices.Small = get_new_boss()
G.GAME.round_resets.blind_choices.Big = get_new_boss() G.GAME.round_resets.blind_choices.Big = get_new_boss()
end end
""" '''
match_indent = true match_indent = true
[[patches]] [[patches]]
@ -85,49 +85,3 @@ payload = """if next(SMODS.find_card "j_Roland_misfortune") then
card:set_edition "e_negative" card:set_edition "e_negative"
end""" end"""
match_indent = true match_indent = true
[[patches]]
[patches.pattern]
target = "card.lua"
pattern = """if self.ability.name == 'Throwback' then
self.ability.x_mult = 1 + G.GAME.skips*self.ability.extra
end
if self.ability.name == "Driver's License" then"""
position = "at"
payload = """if self.ability.name == 'Throwback' then
self.ability.x_mult = 1 + G.GAME.skips*self.ability.extra
end
if self.ability.name == "Driver's License" and not (self.edition and self.edition.Roland_frozen) then"""
match_indent = true
[[patches]]
[patches.pattern]
target = "card.lua"
pattern = """if self.ability.name == "Steel Joker" then"""
position = "at"
payload = """if self.ability.name == "Steel Joker" and not (self.edition and self.edition.Roland_frozen) then"""
match_indent = true
[[patches]]
[patches.pattern]
target = "card.lua"
pattern = """if self.ability.name == "Cloud 9" then"""
position = "at"
payload = """if self.ability.name == "Cloud 9" and not (self.edition and self.edition.Roland_frozen) then"""
match_indent = true
[[patches]]
[patches.pattern]
target = "card.lua"
pattern = """if self.ability.name == "Stone Joker" then"""
position = "at"
payload = """if self.ability.name == "Stone Joker" and not (self.edition and self.edition.Roland_frozen) then"""
match_indent = true
[[patches]]
[patches.regex]
target = "card.lua"
pattern = "math\\.floor\\(\\(G\\.GAME\\.dollars \\+ \\(G\\.GAME\\.dollar_buffer or 0\\)\\)/self\\.ability\\.extra\\.dollars\\)"
position = "at"
payload = "G.P_CENTERS.j_bootstraps:mult(self)"
match_indent = true

View file

@ -3,20 +3,20 @@
"id": "Roland", "id": "Roland",
"name": "Roland", "name": "Roland",
"prefix": "Roland", "prefix": "Roland",
"version": "2.9.28",
"badge_colour": "8BE9FD",
"display_name": "Roland",
"main_file": "src/main.lua",
"badge_text_colour": "44475A",
"description": "Adds mechanics that are meant to have interesting interactions with the base game. Not meant to be balanced, but not entirely broken either.",
"provides": [],
"conflicts": [],
"author": [ "author": [
"Emik" "Emik"
], ],
"version": "2.0.0~dev",
"badge_colour": "8BE9FD",
"main_file": "src/main.lua",
"badge_text_colour": "44475A",
"display_name": "Roland",
"description": "Adds several disconnected funny ideas I had in my head that I couldn't resist implementing in the game.",
"provides": [],
"conflicts": [],
"dependencies": [ "dependencies": [
"Steamodded (>=1.0.0~BETA-1606b)", "Steamodded (>=1.*)",
"Lovely (>=0.6)", "Lovely (>=0.6)",
"Bakery (>=3.2.0~*)" "Bakery (>=1.3.2~*)"
] ]
} }

1
refs/Cryptid Symbolic link
View file

@ -0,0 +1 @@
../../Cryptid/

View file

@ -1,4 +1,4 @@
local f, q = (... or require "lib.shared")[1], (... or require "lib.shared")[2] local f, q = unpack(... or require "lib.shared")
local function save(ret) local function save(ret)
q(save_run) q(save_run)
@ -8,30 +8,45 @@ end
local back = (function() local back = (function()
local x = 0 local x = 0
---@param tbl SMODS.Back|{is_alt: (fun(self: self): boolean), alt_apply?: fun(self: SMODS.Back|table, back: Back|table), alt_calculate?: fun(self: SMODS.Back|table, back: Back|table, context: CalcContext|table): table?, boolean?} ---@param tbl SMODS.Back|{is_alt: function, alt_apply?: fun(self: SMODS.Back|table, back: Back|table), alt_calculate?: fun(self: SMODS.Back|table, back: Back|table, context: CalcContext|table): table?, boolean?}
return function(tbl) return function(tbl)
local key = tbl.key local key = tbl.key
local apply = tbl.apply local apply = tbl.apply
local calculate = tbl.calculate local calculate = tbl.calculate
function tbl:apply(...) function tbl:apply(...)
local _ = apply and G.GAME.selected_sleeve ~= "sleeve_Roland_" .. key and save(apply(self, ...)) ---@diagnostic disable-next-line: redundant-return-value
return apply and G.GAME.selected_sleeve ~= "sleeve_Roland_" .. key and save(apply(self, ...))
end end
function tbl:calculate(...) function tbl:calculate(...)
return (calculate and G.GAME.selected_sleeve ~= "sleeve_Roland_" .. key) and calculate(self, ...) or nil ---@diagnostic disable-next-line: return-type-mismatch
return calculate and G.GAME.selected_sleeve ~= "sleeve_Roland_" .. key and calculate(self, ...)
end end
tbl.pos = {x = x, y = 0} tbl.pos = {x = x, y = 0}
tbl.is_alt = f.fals
tbl.atlas = "back" tbl.atlas = "back"
x = x + 1
local back = q(SMODS.Back(tbl))
local _ = CardSleeves and CardSleeves.Sleeve { -- This API intends that you share the same functions for apply/calculate,
-- meaning that this may be called to check if a deck/sleeve combo is used even
-- when we are from the deck context. We add this to prevent accidental calls to nil.
function tbl:is_alt()
return false
end
SMODS.Back(tbl)
x = x + 1
---@diagnostic disable-next-line: undefined-global
if not CardSleeves then
return
end
---@diagnostic disable-next-line: undefined-global
CardSleeves.Sleeve {
key = key, key = key,
pos = tbl.pos, pos = tbl.pos,
atlas = "sleeve", atlas = "back",
config = tbl.config and f(tbl.config):table() or nil, config = tbl.config and f(tbl.config):table() or nil,
loc_vars = function(self, ...) loc_vars = function(self, ...)
local ret = tbl.loc_vars and tbl.loc_vars(self, ...) or {} local ret = tbl.loc_vars and tbl.loc_vars(self, ...) or {}
@ -48,8 +63,6 @@ local back = (function()
return self.get_current_deck_key() == "b_Roland_" .. key return self.get_current_deck_key() == "b_Roland_" .. key
end, end,
} }
return back
end end
end)() end)()
@ -60,20 +73,12 @@ SMODS.Atlas {
py = 95, py = 95,
} }
local _ = CardSleeves and SMODS.Atlas {
key = "sleeve",
path = "sleeve.png",
px = 73,
py = 95,
}
back { back {
key = "blossom", key = "blossom",
pronouns = "any_all", pronouns = "any_all",
config = {extra = {alt_times = 4, times = 2}}, config = {extra = {times = 2}},
attributes = {"boss_blind"},
loc_vars = function(self, _, _) loc_vars = function(self, _, _)
return {vars = {self.config.extra.alt_times}} return {vars = {self.config.extra.times}}
end, end,
apply = function(_, _) apply = function(_, _)
G.GAME.modifiers.Roland_blossom_deck = true G.GAME.modifiers.Roland_blossom_deck = true
@ -89,41 +94,78 @@ back {
return return
end end
local count = self:is_alt() and local count = self:is_alt() and G.GAME.round_resets.ante or self.config.extra.times
G.GAME.round_resets.ante * self.config.extra.alt_times or
self.config.extra.times
f(count):each(function(i) for _ = 1, count do
local _ = i == 1 and G.GAME.blind:disable() G.GAME.blind:disable()
q { q {
delay = 0.4, delay = 0.4,
func = function() func = function()
local _ = i ~= 1 and G.GAME.blind:disable()
SMODS.calculate_effect({message = localize "ph_boss_disabled"}, card) SMODS.calculate_effect({message = localize "ph_boss_disabled"}, card)
G.GAME.blind:wiggle() G.GAME.blind:wiggle()
play_sound "timpani" play_sound "timpani"
end, end,
} }
end
end,
}
back {
key = "go",
pronouns = "he_him",
config = {extra = {times = 1, alt_times = 5}},
loc_vars = function(self, info_queue, _)
local _ = info_queue and table.insert(info_queue, G.P_CENTERS.j_credit_card)
return {vars = {self.config.extra.alt_times - self.config.extra.times}}
end,
apply = function(self)
q(function()
local count = self:is_alt() and self.config.extra.alt_times or self.config.extra.times
for _ = 1, count do
local c = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_credit_card", "Roland_go")
c:add_to_deck()
c:start_materialize()
G.jokers:emplace(c)
end
end) end)
end, end,
calculate = function(_, card, context)
if not context.starting_shop then
return
end
ease_dollars(-G.GAME.dollars)
attention_text {
text = localize "k_nope_ex",
backdrop_colour = G.C.MONEY,
offset = {x = 0, y = 0},
silent = true,
major = card,
align = "cm",
scale = 1.3,
hold = 1.4,
}
end,
} }
back { back {
key = "swapper", key = "swapper",
pronouns = "he_him", pronouns = "he_him",
attributes = {"spectral", "tarot"},
apply = function(self) apply = function(self)
local modifiers = G.GAME.modifiers local modifiers = G.GAME.modifiers
modifiers.Roland_swapper_deck = true modifiers.Roland_swapper_deck = true
modifiers.Roland_alt_swapper_deck = modifiers.Roland_alt_swapper_deck or self:is_alt() modifiers.Roland_alt_swapper_deck = modifiers.Roland_alt_swapper_deck or self:is_alt()
end, end,
calculate = f.noop, calculate = function() end,
} }
local swapper = {Spectral = "Tarot", Tarot = "Spectral"} local swapper = {Spectral = "Tarot", Tarot = "Spectral"}
local orig_create_card = create_card local orig_create_card = create_card
---@diagnostic disable-next-line: lowercase-global
function create_card(_type, ...) function create_card(_type, ...)
if not G.GAME.modifiers.Roland_swapper_deck then if not G.GAME.modifiers.Roland_swapper_deck then
return orig_create_card(_type, ...) return orig_create_card(_type, ...)

View file

@ -1,16 +1,16 @@
local f, q = (... or require "lib.shared")[1], (... or require "lib.shared")[2] local f, q = unpack(... or require "lib.shared")
local blind = (function() local blind = (function()
local y = 0 local y = 0
---@param tbl SMODS.Blind|{idea?: string} ---@param tbl SMODS.Blind
---@return SMODS.Blind ---@return SMODS.Blind
return function(tbl) return function(tbl)
tbl.idea = tbl.idea and "Roland_" .. tbl.idea or nil
tbl.pos = {x = 0, y = y} tbl.pos = {x = 0, y = y}
tbl.atlas = "blind" tbl.atlas = "blind"
local ret = SMODS.Blind(tbl)
y = y + 1 y = y + 1
return q(SMODS.Blind(tbl)) return ret
end end
end)() end)()
@ -23,19 +23,37 @@ SMODS.Atlas {
atlas_table = "ANIMATION_ATLAS", atlas_table = "ANIMATION_ATLAS",
} }
SMODS.Sound {key = "kick", path = "kick.ogg"} SMODS.Sound {
key = "kick",
path = "kick.ogg",
}
local function common_rank() local function common_rank()
local tally, to_name = {}, {} local tally = {}
local to_name = {}
f(G.playing_cards):where(SMODS.has_no_rank, false):each(function(v) --- Tallies up a card area's cards.
---@param card_area CardArea
local function tally_up(card_area)
f(card_area.cards):where(function(v)
return not SMODS.has_no_rank(v)
end):each(function(v)
local id = v:get_id() local id = v:get_id()
to_name[id] = v.base.value to_name[id] = v.base.value
tally[id] = (tally[id] or 0) + 1 tally[id] = (tally[id] or 0) + 1
end) end)
end
local max_key = f(tally, pairs):fold({-1 / 0, -1 / 0}, function(a, v, k) tally_up(G.deck)
return (v > a[1] or v == a[1] and k > a[2]) and {v, k} or a tally_up(G.hand)
tally_up(G.discard)
local max_key = f(tally):fold({-1 / 0, -1 / 0}, function(a, v, k)
if v > a[1] or k > a[2] and v == a[1] then
return {v, k}
end
return a
end)[2] end)[2]
return max_key, to_name[max_key] return max_key, to_name[max_key]
@ -52,35 +70,7 @@ end
local function has_enhancement(card) local function has_enhancement(card)
local e = SMODS.get_enhancements(card) local e = SMODS.get_enhancements(card)
return e and next(e) return not not (e and next(e))
end
local function set_freeze(state)
local function copy(x)
return type(x) == "table" and f(x):map(copy):table() or x
end
---@param card Card|{ Roland_blizzard: true|nil }
return function(card)
if not card.Roland_blizzard and card.edition and card.edition.Roland_frozen then
return
end
local last_edition = card.Roland_blizzard
card.Roland_blizzard = state and (copy(card.edition) or true) or nil
q {
delay = 0.1,
func = function()
local edition = state and {Roland_frozen = true} or last_edition or card.Roland_blizzard
card:set_edition(edition ~= true and edition or nil)
end,
}
end
end
local function sort_by_enhancement(v1, v2)
return has_enhancement(v1) and not has_enhancement(v2)
end end
local function hsv_to_rgb(h, s, v) local function hsv_to_rgb(h, s, v)
@ -122,23 +112,28 @@ blind {
boss_colour = HEX "0291fbff", boss_colour = HEX "0291fbff",
pronouns = "she_her", pronouns = "she_her",
config = {draw = 5}, config = {draw = 5},
defeat = function()
G.GAME.Roland_nimble_disabled = nil
end,
disable = function()
G.GAME.Roland_nimble_disabled = true
end,
drawn_to_hand = function(self) drawn_to_hand = function(self)
local function force_hand() local function force_hand()
if is_locked() then if is_locked() then
return false return false
end end
f(G.hand.cards):take(self.config.draw):each(function(v) f(G.hand.cards, ipairs):take(self.config.draw):each(function(v)
G.hand:add_to_highlighted(v, true) G.hand:add_to_highlighted(v, true)
end) end)
G.FUNCS.play_cards_from_highlighted() G.FUNCS.play_cards_from_highlighted(nil)
self:disable()
end end
local g = G.GAME if not G.GAME.Roland_nimble_disabled then
G.GAME.Roland_nimble_disabled = true
if not g.blind.disabled and not g.Roland_nimble_disabled then
g.Roland_nimble_disabled = true
q {func = force_hand, blocking = false} q {func = force_hand, blocking = false}
end end
end, end,
@ -153,14 +148,29 @@ blind {
boss_colour = HEX "ff7f3dff", boss_colour = HEX "ff7f3dff",
pronouns = "any_all", pronouns = "any_all",
disable = function() disable = function()
G.FUNCS.draw_from_hand_to_deck() q(function()
f(G.hand.cards):each(function(v, i)
draw_card(G.hand, G.deck, i / #G.hand.cards * 100, "up", false, v)
end)
end)
q(function() q(function()
pseudoshuffle(G.deck.cards, pseudoseed "RolandFalseShuffle") pseudoshuffle(G.deck.cards, pseudoseed "RolandFalseShuffle")
end) end)
end, end,
calculate = function(_, b, context) calculate = function(_, b, context)
local _ = not b.disabled and context.drawing_cards and table.sort(G.deck.cards, sort_by_enhancement) if b.disabled or not context.drawing_cards then
return
end
b:wiggle()
table.sort(
G.deck.cards,
function(v1, v2)
return has_enhancement(v1) and not has_enhancement(v2)
end
)
end, end,
in_pool = function() in_pool = function()
return G.playing_cards and f(G.playing_cards):any(has_enhancement) return G.playing_cards and f(G.playing_cards):any(has_enhancement)
@ -174,7 +184,13 @@ blind {
pronouns = "he_they", pronouns = "he_they",
disable = function() disable = function()
-- Ensures that this runs after 'set_blind' since it also gets added to queue. -- Ensures that this runs after 'set_blind' since it also gets added to queue.
q {delay = 0.8, trigger = "after", func = G.FUNCS.draw_from_discard_to_deck} q {
delay = 0.8,
trigger = "after",
func = function()
G.FUNCS.draw_from_discard_to_deck()
end,
}
end, end,
set_blind = function() set_blind = function()
-- Allows the background to ease in first before drawing cards. -- Allows the background to ease in first before drawing cards.
@ -207,7 +223,7 @@ blind {
local cards_added = {} local cards_added = {}
local count = #G.hand.highlighted local count = #G.hand.highlighted
f(G.hand.highlighted):take(count):each(function(v, i) f(G.hand.highlighted, ipairs):take(count):each(function(v, i)
local copy = copy_card(v) local copy = copy_card(v)
copy:add_to_deck() copy:add_to_deck()
table.insert(G.hand, copy) table.insert(G.hand, copy)
@ -216,42 +232,12 @@ blind {
draw_card(G.hand, G.discard, i / count * 100, "down", false, copy, nil, nil, true) draw_card(G.hand, G.discard, i / count * 100, "down", false, copy, nil, nil, true)
end) end)
if not next(cards_added) then
return
end
b:wiggle() b:wiggle()
b.triggered = true b.triggered = true
playing_card_joker_effects(cards_added) playing_card_joker_effects(cards_added)
end, end,
} }
blind {
key = "blizzard",
idea = "redstoad",
boss = {min = 3},
boss_colour = HEX "102a41ff",
pronouns = "it_its",
defeat = function(self)
self.cards():where "Roland_blizzard":each(set_freeze())
G.GAME.blind.disabled = true
end,
disable = function(self)
self:defeat()
end,
calculate = function(self, b, context)
return not b.disabled and
context.hand_drawn and
self.cards()
:where("Roland_blizzard", false)
:where("facing", "front")
:each(set_freeze(true)) or nil
end,
cards = function()
return f(G):where(getmetatable, CardArea):flatmap("cards", ipairs)
end,
}
blind { blind {
key = "tranquilizer", key = "tranquilizer",
boss = {min = 6}, boss = {min = 6},
@ -269,6 +255,9 @@ blind {
local _, name = common_rank() local _, name = common_rank()
return {vars = {localize(name or "Ace", "ranks")}} return {vars = {localize(name or "Ace", "ranks")}}
end, end,
disable = function(self)
self.disabled = true
end,
calculate = function(self, b, context) calculate = function(self, b, context)
if not context.card_added and if not context.card_added and
not context.drawing_cards and not context.drawing_cards and
@ -299,12 +288,9 @@ blind {
b:set_text() b:set_text()
end end
end, end,
disable = function()
G.GAME.blind.disabled = true
end,
recalc_debuff = function(self, card) recalc_debuff = function(self, card)
local id, _ = common_rank() local id, _ = common_rank()
local ret = not G.GAME.blind.disabled and id == card:get_id() local ret = not self.disabled and id == card:get_id()
self.triggered = ret self.triggered = ret
return ret return ret
end, end,
@ -312,7 +298,7 @@ blind {
blind { blind {
key = "improbable", key = "improbable",
boss = {min = 3}, boss = {min = 4},
boss_colour = HEX "009966ff", boss_colour = HEX "009966ff",
pronouns = "it_its", pronouns = "it_its",
mult = 2, mult = 2,
@ -327,46 +313,21 @@ blind {
if cry_prob then if cry_prob then
local orig_cry_prob = cry_prob local orig_cry_prob = cry_prob
---@diagnostic disable-next-line: lowercase-global
function cry_prob(...) function cry_prob(...)
return G.GAME.modifiers.Roland_improbable and 0 or orig_cry_prob(...) return G.GAME.modifiers.Roland_improbable and 0 or orig_cry_prob(...)
end end
end end
function SMODS.current_mod:calculate(context) function SMODS.current_mod:calculate(context)
local _ = type(G.calccontext) == "function" and G.calccontext(context) if context.setting_blind and G.GAME.blind.name == "bl_mp_nemesis" then
local modifiers = G.GAME.modifiers
if type(G.calc) == "function" then modifiers.Roland_martingale_seed = (modifiers.Roland_martingale_seed or 0) + 1
G.calc = {G.calc}
end end
if type(G.calc) == "table" then local _ = type(G.calc) == "function" and G.calc(f(context):keys():string())
local str
f(context):keys():map(f.index_into(G.calc)):where(type, "function"):each(function(v)
str = str or f(context):keys():string()
v(str)
end)
local _ = not str and type(G.calc[1]) == "function" and G.calc[1](f(context):keys():string())
end
local _ = context.before and
f {"v_Roland_ceres", "v_Roland_neptune"}
:where(f.index_into(G.GAME.used_vouchers))
:map(f.index_into(G.P_CENTERS))
:where("config.extra.hand_type", context.scoring_name)
:pun "SMODS.Voucher"
:each(function(v, _)
SMODS.smart_level_up_hand(nil, v.config.extra.hand_type, nil, v.config.extra.amount)
end)
local improbable, orig = G.GAME.modifiers.Roland_improbable, G.GAME.probabilities local improbable, orig = G.GAME.modifiers.Roland_improbable, G.GAME.probabilities
local _ = context.end_of_round and
context.main_eval and
not context.blueprint and
G.P_TAGS.tag_Roland_invisible.increment()
-- Normally unreachable since we set it to nil ourselves, -- Normally unreachable since we set it to nil ourselves,
-- but other mods may want to use this modifier. -- but other mods may want to use this modifier.
if improbable == false then if improbable == false then
@ -421,6 +382,7 @@ end
local orig_draw = Card.draw local orig_draw = Card.draw
---@diagnostic disable-next-line: duplicate-set-field
function Card:draw(...) function Card:draw(...)
if equinox() and if equinox() and
not SMODS.Mods.Roland.config.equinox_assist and not SMODS.Mods.Roland.config.equinox_assist and
@ -434,6 +396,7 @@ end
local orig_draw_self = UIElement.draw_self local orig_draw_self = UIElement.draw_self
---@diagnostic disable-next-line: duplicate-set-field
function UIElement:draw_self(...) function UIElement:draw_self(...)
if equinox() and if equinox() and
not self.config.button and not self.config.button and
@ -449,7 +412,6 @@ local venerable_visage = blind {
boss = {showdown = true}, boss = {showdown = true},
boss_colour = HEX "f6f6f2ff", boss_colour = HEX "f6f6f2ff",
pronouns = "any_all", pronouns = "any_all",
dollars = 8,
calculate = function(self, b, context) calculate = function(self, b, context)
if b.disabled then if b.disabled then
return return
@ -462,10 +424,7 @@ local venerable_visage = blind {
end) end)
end end
if b.Roland_vitriol or if b.Roland_vitriol or not context.end_of_round or not next(G.deck.cards) or not next(G.hand.cards) then
not context.end_of_round or
G.GAME.chips == G.GAME.blind.chips or
not (next(G.deck.cards) or next(G.hand.cards)) then
return return
end end
@ -481,31 +440,15 @@ local venerable_visage = blind {
} }
end, end,
vitriol = function(b) vitriol = function(b)
local vitriol = SMODS.Mods.Roland.config.vitriol
local resize_to_w, resize_to_h = 320, 200
local is_fullscreen = love.window.getFullscreen()
if vitriol then
love.window.setFullscreen(false)
delay(1.5)
end
local function jitter()
local x, y = love.window.getDesktopDimensions()
return pseudorandom(pseudoseed "RolandVenerableVisageX", 0, x) - x / 2,
pseudorandom(pseudoseed "RolandVenerableVisageY", 0, y) - y / 2
end
if type(b) == "table" and type(b.wiggle) == "function" then if type(b) == "table" and type(b.wiggle) == "function" then
b:wiggle() b:wiggle()
end end
f(G.playing_cards):each(function(v, k)
return v.area ~= G.hand and draw_card(v.area, G.hand, k * 100 / #G.playing_cards, "up", true, v)
end)
local fail = G.localization.descriptions.Blind.bl_Roland_venerable_visage.fail local fail = G.localization.descriptions.Blind.bl_Roland_venerable_visage.fail
local speed = 0.1
SMODS.draw_cards(#G.deck.cards)
play_sound("gong", 0.6)
attention_text { attention_text {
text = pseudorandom_element(fail, pseudoseed "RolandVenerableVisage"), text = pseudorandom_element(fail, pseudoseed "RolandVenerableVisage"),
@ -515,55 +458,52 @@ local venerable_visage = blind {
hold = 2, hold = 2,
} }
f {0.15, 0.3, 0.45, 0.6}:each(function(v) delay(1)
play_sound("gong", v)
end)
---@type number, number, table f(G.playing_cards):each(function(v)
local w, h, flags = love.window.getMode()
local len = #G.playing_cards
if vitriol then
love.window.setMode(resize_to_w, resize_to_h)
love.resize(resize_to_w, resize_to_h)
end
local x, y = love.window.getPosition()
f(G.playing_cards):each(function(v, i)
q { q {
trigger = "before", trigger = "before",
delay = 6 / len, delay = speed,
func = function() func = function()
if vitriol then
local x_random, y_random = jitter()
love.window.setPosition(x + x_random * i / len, y + y_random * i / len)
end
v:start_dissolve() v:start_dissolve()
v:shatter() v:shatter()
b:wiggle()
end, end,
} }
end) end)
local _ = vitriol and q { delay(1)
trigger = "before",
delay = 1.5, f(G.P_CARDS):each(function(v)
q {
delay = speed,
func = function() func = function()
love.window.setPosition(x, y) G.playing_card = (G.playing_card and G.playing_card + 1) or 1
love.window.setMode(w, h, flags)
love.resize(w, h) local card = Card(
love.window.setFullscreen(is_fullscreen) b and b.T.x or 0,
b and b.T.y or 0,
G.CARD_W,
G.CARD_H,
v,
G.P_CENTERS.m_Bakery_Curse or G.P_CENTERS.c_base,
{playing_card = G.playing_card}
)
table.insert(G.playing_cards, card)
G.deck:emplace(card)
play_sound "card1"
card:add_to_deck()
end, end,
} }
end)
delay(1.5) delay(1)
end, end,
} }
local orig_game_draw = Game.draw local orig_game_draw = Game.draw
---@diagnostic disable-next-line: duplicate-set-field
function Game.draw(...) function Game.draw(...)
orig_game_draw(...) orig_game_draw(...)
local boss_colour = venerable_visage.boss_colour local boss_colour = venerable_visage.boss_colour

View file

@ -1,4 +1,4 @@
local f, q = (... or require "lib.shared")[1], (... or require "lib.shared")[2] local f, q = unpack(... or require "lib.shared")
local function bans() local function bans()
return {banned_cards = {}, banned_tags = {}, banned_other = {}} return {banned_cards = {}, banned_tags = {}, banned_other = {}}
@ -30,6 +30,10 @@ local function is_center_banned_from_pastry(v)
return arcana_boosters[v.key] or pastries_targets[v.set] and is_banned_from_pastry(v) return arcana_boosters[v.key] or pastries_targets[v.set] and is_banned_from_pastry(v)
end end
local function is_joker(v)
return v.set == "Joker" and not ({j_joker = true, j_Roland_msjoker = true})[v.key]
end
local function is_showdown_except(key) local function is_showdown_except(key)
---@param v SMODS.Blind ---@param v SMODS.Blind
---@param k string ---@param k string
@ -39,68 +43,27 @@ local function is_showdown_except(key)
end end
end end
local pastries, amber, cerulean, crimson, verdant, violet = local function is_vanilla(v)
bans(), bans(), bans(), bans(), bans(), bans() return v.set and not v.mod and f(G.P_CENTERS):concat(G.P_CARDS, G.P_TAGS):any(function(c)
return c.mod and not c.in_pool and c.set == v.set
SMODS.Challenge {
key = "Glass",
rules = {custom = {{id = "Roland_Glass1"}, {id = "Roland_Glass2"}}},
pronouns = "they_them",
config = {extra = {odds = 2}},
calculate = function(self, context)
if not context.after or context.blueprint_card then
return
end
f(G.play.cards):where(function(v)
return not v:get_seal(true) and
SMODS.pseudorandom_probability(self, self.key, 1, self.config.extra.odds, self.key)
end):each(function(v)
v:set_seal "Roland_glass"
end) end)
end, end
local jokerful, vanillas, pastries, amber, cerulean, crimson, verdant, violet, final
= bans(), bans(), bans(), bans(), bans(), bans(), bans(), bans(), bans()
SMODS.Challenge {
key = "Jokerful",
rules = {custom = {{id = "Roland_Jokerful"}}},
restrictions = jokerful,
pronouns = "he_him",
} }
SMODS.Challenge { SMODS.Challenge {
key = "Go", key = "Ornate",
rules = {custom = {{id = "Roland_Go"}}}, rules = {custom = {{id = "Roland_Ornate"}}},
jokers = {{id = "j_credit_card"}}, restrictions = vanillas,
pronouns = "he_him", pronouns = "any_all",
calculate = function(self, context)
if context.starting_shop then
ease_dollars(-G.GAME.dollars)
attention_text {
text = localize "k_nope_ex",
backdrop_colour = G.C.MONEY,
offset = {x = 0, y = 0},
silent = true,
major = self,
align = "cm",
scale = 1.3,
hold = 1.4,
}
end
end,
}
local spin_to_win = SMODS.Challenge {
key = "Spin_To_Win",
jokers = f(4):map(f.const {id = "j_joker", eternal = true}):table(),
restrictions = {banned_cards = {{id = "c_Roland_coolheaded"}}},
pronouns = "they_them",
apply = function(self)
self.rotation = 0
end,
calculate = function(self, context)
if not context.card_added or context.card.config.center.key ~= "j_Bakery_Spinner" then
return
end
context.card.ability.extra.rotation = self.rotation
self.rotation = self.rotation + 1
end,
rotation = 0,
} }
SMODS.Challenge { SMODS.Challenge {
@ -110,12 +73,12 @@ SMODS.Challenge {
pronouns = "she_them", pronouns = "she_them",
} }
local finalizers = {
SMODS.Challenge { SMODS.Challenge {
key = "Eternally_Amber", key = "Eternally_Amber",
rules = {custom = {{id = "Roland_Eternally_Amber"}, {id = "Roland_Showdown_Amber"}}}, rules = {custom = {{id = "Roland_Eternally_Amber"}, {id = "Roland_Showdown_Amber"}}},
jokers = {{id = "j_Roland_amber"}},
restrictions = amber, restrictions = amber,
pronouns = "they_them",
calculate = function(_, context) calculate = function(_, context)
local function slide(pitch) local function slide(pitch)
q(function() q(function()
@ -147,14 +110,11 @@ SMODS.Challenge {
end, end,
} }
end, end,
} },
SMODS.Challenge { SMODS.Challenge {
key = "Eternally_Cerulean", key = "Eternally_Cerulean",
rules = {custom = {{id = "Roland_Eternally_Cerulean"}, {id = "Roland_Showdown_Cerulean"}}}, rules = {custom = {{id = "Roland_Eternally_Cerulean"}, {id = "Roland_Showdown_Cerulean"}}},
jokers = {{id = "j_Roland_cerulean"}},
restrictions = cerulean, restrictions = cerulean,
pronouns = "she_her",
calculate = function(_, context) calculate = function(_, context)
if not context.hand_drawn then if not context.hand_drawn then
return return
@ -174,22 +134,20 @@ SMODS.Challenge {
G.hand:add_to_highlighted(v) G.hand:add_to_highlighted(v)
end) end)
end, end,
} },
SMODS.Challenge { SMODS.Challenge {
key = "Eternally_Crimson", key = "Eternally_Crimson",
rules = {custom = {{id = "Roland_Eternally_Crimson"}, {id = "Roland_Showdown_Crimson"}}}, rules = {custom = {{id = "Roland_Eternally_Crimson"}, {id = "Roland_Showdown_Crimson"}}},
jokers = {{id = "j_Roland_crimson"}},
restrictions = crimson, restrictions = crimson,
pronouns = "she_her",
calculate = function(_, context) calculate = function(_, context)
local _ = context.blind_defeated and f(G.jokers.cards):each(function(v) local _ = context.blind_defeated and f(G.jokers.cards):each(function(v)
v.ability.Roland_crimson_heart_chosen = nil v.ability.Roland_crimson_heart_chosen = nil
end) end)
local mod = G.GAME.modifiers local mod = G.GAME.modifiers
mod.Roland_Eternally_Crimson = context.setting_blind and true or mod.Roland_Eternally_Crimson mod.Roland_Eternally_Crimson = not context.setting_blind and mod.Roland_Eternally_Crimson or nil
local debuff = context.debuff_card local debuff = context.debuff_card
---@type (SMODS.Joker|{ability?: {Roland_crimson_heart_chosen: boolean?}, debuff: boolean?})[]
local cards = G.jokers.cards local cards = G.jokers.cards
if debuff and if debuff and
@ -217,7 +175,9 @@ SMODS.Challenge {
local _ = v.debuff and SMODS.recalc_debuff(v) local _ = v.debuff and SMODS.recalc_debuff(v)
end) end)
jokers = next(jokers) and jokers or f(cards):where("debuff", false):map(function(v) jokers = next(jokers) and jokers or f(cards):where(function(v)
return not v.debuff
end):map(function(v)
local _ = not prev[v] and table.insert(jokers, v) local _ = not prev[v] and table.insert(jokers, v)
return v return v
end):table() end):table()
@ -234,14 +194,11 @@ SMODS.Challenge {
mod.Roland_Eternally_Crimson = not context.hand_drawn and mod.Roland_Eternally_Crimson = not context.hand_drawn and
mod.Roland_Eternally_Crimson or nil mod.Roland_Eternally_Crimson or nil
end, end,
} },
SMODS.Challenge { SMODS.Challenge {
key = "Eternally_Verdant", key = "Eternally_Verdant",
rules = {custom = {{id = "Roland_Eternally_Verdant"}, {id = "Roland_Showdown_Verdant"}}}, rules = {custom = {{id = "Roland_Eternally_Verdant"}, {id = "Roland_Showdown_Verdant"}}},
jokers = {{id = "j_Roland_verdant"}},
restrictions = verdant, restrictions = verdant,
pronouns = "she_her",
calculate = function(_, context) calculate = function(_, context)
if context.setting_blind then if context.setting_blind then
G.GAME.modifiers.Roland_Eternally_Verdant = true G.GAME.modifiers.Roland_Eternally_Verdant = true
@ -257,27 +214,44 @@ SMODS.Challenge {
end) end)
end end
end, end,
} },
SMODS.Challenge { SMODS.Challenge {
key = "Eternally_Violet", key = "Eternally_Violet",
rules = {custom = {{id = "Roland_Eternally_Violet"}, {id = "Roland_Showdown_Violet"}}}, rules = {custom = {{id = "Roland_Eternally_Violet"}, {id = "Roland_Showdown_Violet"}}},
jokers = {{id = "j_joker"}, {id = "j_Roland_violet"}},
restrictions = violet, restrictions = violet,
pronouns = "she_they",
calculate = function(_, context) calculate = function(_, context)
if not context.setting_blind then if context.setting_blind then
return G.GAME.blind.chips = G.GAME.blind.chips * (6 / G.GAME.blind.mult)
end
local mult = G.GAME.blind.mult < 6 and G.GAME.blind.mult or 1
G.GAME.blind.chips = G.GAME.blind.chips * 6 / mult
G.GAME.blind.chip_text = number_format(G.GAME.blind.chips) G.GAME.blind.chip_text = number_format(G.GAME.blind.chips)
end
end,
},
}
SMODS.Challenge {
key = "Showdown",
rules = {custom = {
{id = "Roland_Eternally_Amber"},
{id = "Roland_Eternally_Cerulean"},
{id = "Roland_Eternally_Crimson"},
{id = "Roland_Eternally_Verdant"},
{id = "Roland_Eternally_Violet"},
{id = "Roland_Showdown"},
}},
restrictions = final,
calculate = function(_, context)
f(finalizers):each(function(v)
v:calculate(context)
end)
end, end,
} }
q(function() q(function()
f { f {
{G.P_CENTERS, is_joker, jokerful.banned_cards},
{G.P_TAGS, is_vanilla, vanillas.banned_tags},
{G.P_BLINDS, is_vanilla, vanillas.banned_other},
{G.P_CENTERS, is_vanilla, vanillas.banned_cards},
{G.P_TAGS, is_banned_from_pastry, pastries.banned_tags}, {G.P_TAGS, is_banned_from_pastry, pastries.banned_tags},
{G.P_BLINDS, is_banned_from_pastry, pastries.banned_other}, {G.P_BLINDS, is_banned_from_pastry, pastries.banned_other},
{G.P_CENTERS, is_center_banned_from_pastry, pastries.banned_cards}, {G.P_CENTERS, is_center_banned_from_pastry, pastries.banned_cards},
@ -286,17 +260,8 @@ q(function()
{G.P_BLINDS, is_showdown_except "bl_final_heart", crimson.banned_other}, {G.P_BLINDS, is_showdown_except "bl_final_heart", crimson.banned_other},
{G.P_BLINDS, is_showdown_except "bl_final_leaf", verdant.banned_other}, {G.P_BLINDS, is_showdown_except "bl_final_leaf", verdant.banned_other},
{G.P_BLINDS, is_showdown_except "bl_final_vessel", violet.banned_other}, {G.P_BLINDS, is_showdown_except "bl_final_vessel", violet.banned_other},
{G.P_BLINDS, is_showdown_except "bl_Roland_venerable_visage", final.banned_other},
}:each(function(v) }:each(function(v)
f(v[1]):where(v[2]):each(adder(v[3])) f(v[1]):where(v[2]):each(adder(v[3]))
end) end)
end) end)
q(function()
if not G.P_CENTERS.j_Bakery_Spinner then
return true
end
f(spin_to_win.jokers):each(function(v)
v.id = "j_Bakery_Spinner"
end)
end)

View file

@ -1,32 +1,19 @@
local f, q = (... or require "lib.shared")[1], (... or require "lib.shared")[2] local f, q = unpack(... or require "lib.shared")
local mod = SMODS.current_mod local mod = SMODS.current_mod
local cool_phones = SMODS.Mods.Roland.config.cool_phones
SMODS.Atlas { SMODS.Atlas {
key = "charm", key = "charm",
path = cool_phones and "phorm.png" or "charm.png", path = "charm.png",
px = 68, px = 68,
py = 68, py = 68,
} }
local _ = cool_phones and SMODS.Sound {
key = "phone",
path = "phone.ogg",
}
local charm = (function() local charm = (function()
local x = 0 local x = 0
---@param tbl SMODS.Joker|{alerted?: boolean, artist?: string, equip?: fun(self: self, card: Card)} ---@param tbl SMODS.Joker|{alerted?: boolean}
return function(tbl) return function(tbl)
q { q(function()
blocking = false,
no_delete = true,
func = function()
if not Bakery_API or not Bakery_API.Charm then
return false
end
local current_mod = SMODS.current_mod local current_mod = SMODS.current_mod
SMODS.current_mod = mod SMODS.current_mod = mod
@ -35,155 +22,122 @@ local charm = (function()
tbl.unlocked = true tbl.unlocked = true
tbl.discovered = true tbl.discovered = true
tbl.pos = {x = x, y = 0} tbl.pos = {x = x, y = 0}
tbl.atlas = "charm"
tbl.artist = not cool_phones and "Roland_bakersdozenbagels" or nil
x = x + 1 x = x + 1
tbl.atlas = "charm"
local orig_equip = tbl.equip local charm = Bakery_API.Charm(tbl)
function tbl.equip(...)
local _ = cool_phones and play_sound("Roland_phone", 1, 0.5)
if orig_equip then
return orig_equip(...)
end
end
local charm = Bakery_API.credit(Bakery_API.Charm(tbl))
charm:inject() charm:inject()
charm:process_loc_text() charm:process_loc_text()
SMODS.current_mod = current_mod SMODS.current_mod = current_mod
end, end, true)
}
end end
end)() end)()
local function can_discard_zero() local function add_to_consumable_ability_by(n)
return G.GAME.current_round.discards_left > 0 and local function new(v)
not next(G.hand.highlighted) and return type(v) == "number" and v + n or type(v) == "table" and f(v):map(new):table() or v
G.hand.config.card_limit > 0 and
G.GAME.Bakery_charm == "BakeryCharm_Roland_cocacola"
end end
charm { ---@param card Card
key = "hand", return function(card)
pronouns = "he_him", local ability = card.ability or {}
config = {extra = {hands = -2, hand_size = 5}},
attributes = {"hands", "hand_size", "passive"},
loc_vars = function(_, _, card)
local extra = card.ability.extra
return {vars = {extra.hand_size, extra.hands}}
end,
equip = function(_, card)
local extra = card.ability.extra
local round = G.GAME.round_resets
ease_hands_played(extra.hands)
G.hand:change_size(extra.hand_size)
round.hands = round.hands + extra.hands
end,
unequip = function(_, card)
local extra = card.ability.extra
local round = G.GAME.round_resets
ease_hands_played(-extra.hands)
G.hand:change_size(-extra.hand_size)
round.hands = round.hands - extra.hands
end,
}
charm { local function go(key)
key = "cocacola", ---@type { [string]: number }|number
pronouns = "he_they", local value = ability[key]
attributes = {"passive", "tarot", "spectral"}, --print(key)
config = {extra = {discard = 2, hand_size = 1, refund = 0}}, if not value then
loc_vars = function(_, _, card)
local extra = card.ability.extra
return {vars = {extra.discard, extra.hand_size}}
end,
calculate = function(_, card, context)
if not context.end_of_round then
return return
end end
local extra = card.ability.extra if type(value) == "number" then
G.hand:change_size(extra.refund) ability[key] = value + n
extra.refund = 0 return
end,
}
local orig_can_discard = G.FUNCS.can_discard
function G.FUNCS.can_discard(e, ...)
if can_discard_zero() then
e.config.button, e.config.colour = "Roland_cocacola", G.C.RED
else
return orig_can_discard(e, ...)
end
end end
G.FUNCS.Roland_cocacola = function() if type(value) ~= "table" then
stop_use() return
G.CONTROLLER.interrupt.focus = true end
G.CONTROLLER:save_cardarea_focus "hand"
f(G.playing_cards):map "ability":each(function(v) local new_value = f(value):map(new):table()
v.forced_selection = nil
f(new_value):each(function(v, k)
ability[k] = v
end) end)
G.card_area_focus_reset = (G.CONTROLLER.focused.target or {}).area == G.hand and ability[key] = new_value
{area = G.hand, rank = G.CONTROLLER.focused.target.rank} or G.card_area_focus_reset
SMODS.calculate_context {pre_discard = true, full_hand = G.hand.highlighted}
G.GAME.current_round.discards_used = G.GAME.current_round.discards_used + 1
local c = G.Bakery_charm_area.cards[1]
local extra = c.ability.extra or {}
juice_card(c)
ease_discard(extra.discard - 1)
G.hand:change_size(-extra.hand_size)
extra.refund = extra.refund + extra.hand_size
local _ = G.GAME.modifiers.discard_cost and ease_dollars(-G.GAME.modifiers.discard_cost)
G.STATE = G.STATES.DRAW_TO_HAND
q {
trigger = "immediate",
func = function()
if G.SCORING_COROUTINE then
return false
end end
local center_key = (card.config or {}).center_key
if center_key and center_key:sub(1, 2) == "c_" then
f {"extra", "consumeable"}:each(go)
end
end
end
charm {
key = "wii",
pronouns = "they_them",
calculate = function(_, card, context)
if not context.skip_blind then
return
end
local message = localize {type = "variable", key = "b_Roland_entering_shop"}
SMODS.calculate_effect({card = card, message = message, sound = "whoosh1"}, card)
q(function()
G.blind_prompt_box = G.blind_prompt_box and G.blind_prompt_box:remove()
G.blind_select = G.blind_select and G.blind_select:remove()
G.round_eval = G.round_eval and G.round_eval:remove()
G.GAME.current_round.jokers_purchased = 0
G.STATE = G.STATES.SHOP
G.GAME.shop_free = nil
G.GAME.shop_d6ed = nil
G.STATE_COMPLETE = false G.STATE_COMPLETE = false
return true play_sound("whoosh1", 1.33333, 0.8)
play_sound("whoosh1", 0.66666, 0.8)
end)
end, end,
} }
charm {
key = "flexible",
pronouns = "any_all",
}
local orig_showman = SMODS.showman
---@diagnostic disable-next-line: duplicate-set-field
function SMODS.showman(...)
return G.GAME.Bakery_charm == "BakeryCharm_Roland_flexible" or orig_showman(...)
end end
charm { charm {
key = "fat", key = "fat",
pronouns = "he_they", pronouns = "he_they",
attributes = {"passive"}, -- config = {extra = {mod = 1}},
config = {extra = {mod = 2}}, -- loc_vars = function(_, _, card)
loc_vars = function(_, _, card) -- return {vars = {card.ability.extra.mod}}
return {vars = {card.ability.extra.mod}} -- end,
end, -- equip = function(_, card)
equip = function(_, card) -- SMODS.change_booster_limit(card.ability.extra.mod)
SMODS.change_booster_limit(card.ability.extra.mod) -- end,
end, -- unequip = function(card)
unequip = function(_, card) -- SMODS.change_booster_limit(-card.ability.extra.mod)
SMODS.change_booster_limit(-card.ability.extra.mod) -- end,
end,
} }
local orig_init = Card.init local orig_init = Card.init
---@diagnostic disable-next-line: duplicate-set-field
function Card:init(X, Y, W, H, card, center, params, ...) function Card:init(X, Y, W, H, card, center, params, ...)
if G.GAME.Bakery_charm ~= "BakeryCharm_Roland_fat" or center.set ~= "Booster" then if G.GAME.Bakery_charm ~= "BakeryCharm_Roland_fat" or center.set ~= "Booster" then
return orig_init(self, X, Y, W, H, card, center, params, ...) return orig_init(self, X, Y, W, H, card, center, params, ...)
end end
local key = f {"mini", "jumbo", "normal"}:fold(center.key, function(a, v) local key = center.key:gsub("normal", "mega"):gsub("jumbo", "mega"):gsub("mini", "mega")
return a:gsub(v, "mega")
end)
if G.P_CENTERS[key] then if G.P_CENTERS[key] then
return orig_init(self, X, Y, W, H, card, G.P_CENTERS[key], params, ...) return orig_init(self, X, Y, W, H, card, G.P_CENTERS[key], params, ...)
@ -206,64 +160,49 @@ function Card:init(X, Y, W, H, card, center, params, ...)
end end
charm { charm {
key = "flexible", key = "cocacola",
pronouns = "any_all", pronouns = "he_they",
} equip = function()
f(G.consumeables.cards):each(add_to_consumable_ability_by(1))
local orig_showman = SMODS.showman end,
unequip = function()
function SMODS.showman(...) f(G.consumeables.cards):each(add_to_consumable_ability_by(-1))
return G.GAME.Bakery_charm == "BakeryCharm_Roland_flexible" or orig_showman(...)
end
charm {
key = "wii",
pronouns = "they_them",
attributes = {"skip"},
config = {extra = {active = true}},
calculate = function(_, card, context)
if context.prevent_tag_trigger and card.ability.extra.active then
return {prevent_trigger = true}
end
if not context.skip_blind then
return
end
card.ability.extra.active = true
local message = localize {type = "variable", key = "b_Roland_entering_shop"}
SMODS.calculate_effect({card = card, message = message, sound = "whoosh1"}, card)
q(function()
G.blind_prompt_box = G.blind_prompt_box and G.blind_prompt_box:remove()
G.blind_select = G.blind_select and G.blind_select:remove()
G.round_eval = G.round_eval and G.round_eval:remove()
G.GAME.current_round.jokers_purchased = 0
G.GAME.shop_free = nil
G.GAME.shop_d6ed = nil
play_sound("whoosh1", 1.33333, 0.8)
play_sound("whoosh1", 0.66666, 0.8)
q(function()
G.STATE = G.STATES.SHOP
G.STATE_COMPLETE = false
delay(1)
q(function()
card.ability.extra.active = false
end)
end)
end)
end, end,
} }
local orig_apply_to_run = Tag.apply_to_run local orig_set_ability = Card.set_ability
function Tag:apply_to_run(...) ---@diagnostic disable-next-line: duplicate-set-field
if G.GAME.Bakery_charm == "BakeryCharm_Roland_wii" and function Card:set_ability(center, initial, delay_sprites, ...)
G.Bakery_charm_area.cards[1].ability.extra.active then local ret = orig_set_ability(self, center, initial, delay_sprites, ...)
return
if G.GAME.Bakery_charm == "BakeryCharm_Roland_cocacola" then
add_to_consumable_ability_by(1)(self)
end end
return orig_apply_to_run(self, ...) return ret
end end
charm {
key = "hand",
pronouns = "he_him",
config = {extra = {hands = -2, hand_size = 5}},
loc_vars = function(_, _, card)
local extra = card.ability.extra
return {vars = {extra.hand_size, extra.hands}}
end,
equip = function(_, card)
local extra = card.ability.extra
local round = G.GAME.round_resets
ease_hands_played(extra.hands)
G.hand:change_size(extra.hand_size)
round.hands = round.hands + extra.hands
end,
unequip = function(_, card)
local extra = card.ability.extra
local round = G.GAME.round_resets
ease_hands_played(-extra.hands)
G.hand:change_size(-extra.hand_size)
round.hands = round.hands - extra.hands
end,
}

View file

@ -1,327 +0,0 @@
local f, q = (... or require "lib.shared")[1], (... or require "lib.shared")[2]
SMODS.Shader {
key = "frozen",
path = "frozen.fs",
}
---@param suffix string
local function frozen_sound(suffix)
local key = "frozen" .. suffix
return SMODS.Sound {key = key, path = key .. ".ogg"}
end
frozen_sound "_click"
local frozen_blocklist = {CardSleeve = true}
local frozen_sounds = f(4):map(frozen_sound):map "key":table()
local needs_chip_mult_override = {
Bull = true,
Erosion = true,
Misprint = true,
TierList = true,
["Blue Joker"] = true,
["Abstract Joker"] = true,
["Fortune Teller"] = true,
}
local current_round_overrides = {
Castle = "castle_card",
Farmer = "farmer_card",
Tuxedo = "tuxedo_card",
["Go Fish"] = "fish_rank",
["The Idol"] = "idol_card",
["Mail-In Rebate"] = "mail_card",
Obelisk = "most_played_poker_hand",
["Ancient Joker"] = "ancient_card",
Wherewolf = "Bakery_Wherewolf_card",
j_Roland_suitable = "Roland_suitable",
}
SMODS.current_mod.frozen_chip_mult = needs_chip_mult_override
SMODS.current_mod.frozen_current_round = current_round_overrides
local function freeze(card)
---@generic T
---@param x T
---@return T
local function copy(x)
return type(x) == "table" and f(x):map(copy):table() or x
end
if frozen_blocklist[(card.ability or {}).name] then
return card.ability
end
(card.ability or {}).Roland_crimson = not not (card.ability or {}).Roland_crimson
card.Roland_frozen_ability = card.Roland_frozen_ability or copy(card.ability)
card.ability = card.Roland_frozen_ability and copy(card.Roland_frozen_ability) or card.ability
card.Roland_frozen = card.Roland_frozen or {probability = SMODS.get_probability_vars(card, 1, 1)}
local ability, ret = card.ability, card.Roland_frozen_ability
ability.extra = ability.extra == nil and {} or ability.extra
if type(ability) ~= "table" then
return ret
end
ability.seal = ability.seal or {}
if not ability.name or not G.GAME.current_round then
return ret
end
local key = current_round_overrides[ability.name]
if not key then
return ret
end
card.Roland_frozen_current_round = card.Roland_frozen_current_round or copy(G.GAME.current_round[key])
G.GAME.current_round[key] = copy(card.Roland_frozen_current_round)
return ret
end
local function hook_estate()
---@param card Card|{ Roland_frozen: {estate: integer} }
local function estate_pos(card)
if card.area ~= G.jokers and card.area.config.type ~= "title" then
return 1
end
card.Roland_frozen.estate = card.Roland_frozen.estate or card.rank or 1
return card.Roland_frozen.estate
end
local estate = G.P_CENTERS.j_Bakery_Estate
local orig_calculate = estate.calculate
function estate:calculate(card, context, ...)
if not (card or {}).Roland_frozen or not (card.edition or {}).Roland_frozen then
return orig_calculate(self, card, context, ...)
end
if not context.joker_main then
return
end
local joker_count = estate_pos(card)
local extra = card.ability.extra
return {chips = extra.chips * joker_count, mult = extra.mult * joker_count}
end
local orig_loc_vars = estate.loc_vars
function estate:loc_vars(info_queue, card, ...)
if not (card or {}).Roland_frozen or not (card.edition or {}).Roland_frozen then
return orig_loc_vars(self, info_queue, card, ...)
end
local joker_count = estate_pos(card)
local extra = card.ability.extra or {}
return {vars = {extra.chips * joker_count, extra.mult * joker_count}}
end
end
local function hook_proxy()
---@param card Card|{ Roland_frozen: {proxy: string} }
local function get_proxied_joker(card)
if not G.jokers or not G.jokers.cards then
return
end
card.Roland_frozen.proxy = card.Roland_frozen.proxy or
(Bakery_API.get_proxied_joker() or card).config.center.key
---@param v Card
local function eq(v)
return v.config.center.key == card.Roland_frozen.proxy
end
return f(G.jokers.cards):any(eq)
end
local proxy = G.P_CENTERS.j_Bakery_Proxy
local orig_calculate = proxy.calculate
function proxy:calculate(card, context, ...)
if not (card or {}).Roland_frozen or not (card.edition or {}).Roland_frozen then
return orig_calculate(self, card, context, ...)
else
return SMODS.blueprint_effect(card, get_proxied_joker(card), context)
end
end
local orig_loc_vars = proxy.loc_vars
function proxy:loc_vars(info_queue, card, ...)
if not (card or {}).Roland_frozen or not (card.edition or {}).Roland_frozen then
return orig_loc_vars(self, info_queue, card, ...)
end
local other = get_proxied_joker(card)
local var = (other and other ~= card) and localize {
type = "name_text",
set = other.config.center.set,
key = other.config.center.key,
} or localize "k_none"
return {vars = {var}}
end
end
local function hook_scribe()
local scribe = G.P_CENTERS.c_Bakery_Scribe
local orig_can_use = scribe.can_use
function scribe.can_use(...)
return orig_can_use(...) and
(SMODS.Mods.Roland.config.scribable_basket or f(G.jokers.highlighted):all(function(v)
return v.config.center.key ~= "j_Roland_basket"
end))
end
end
SMODS.Edition {
key = "frozen",
shader = "frozen",
sound = {sound = "Roland_frozen", per = 1, vol = 0.8},
attributes = {"passive", "scaling", "mod_chance"},
pronouns = "any_all",
weight = 8,
extra_cost = 4,
in_shop = true,
apply_to_float = false,
calculate = function(_, card, context)
local _ = not context.fix_probability and not context.mod_probability and freeze(card)
end,
on_remove = function(card)
card.Roland_frozen, card.Roland_frozen_ability, card.Roland_frozen_current_round = nil, nil, nil
end,
get_weight = function(self)
return G.GAME.edition_rate * self.weight
end,
}
local orig_play_sound = play_sound
function play_sound(sound, pitch, ...)
if sound ~= "Roland_frozen" and sound ~= "Roland_frozen_click" then
return orig_play_sound(sound, pitch, ...)
end
local overriden_sound = sound == "Roland_frozen" and
pseudorandom_element(frozen_sounds, pseudoseed "Roland_frozen") or
sound
local added_pitch = pseudorandom(pseudoseed "Roland_frozen_pitch") / 4 + 0.8
return orig_play_sound(overriden_sound, pitch + added_pitch, ...)
end
local orig_click = Card.click
function Card:click(...)
local highlight = self.highlighted
local ret = orig_click(self, ...)
if self.edition and self.edition.Roland_frozen and highlight ~= self.highlighted then
play_sound("Roland_frozen_click", highlight and 0 or 0.5, 0.6)
end
return ret
end
local orig_calculate_joker = Card.calculate_joker
---@param self Card|{Roland_frozen_ability?: table}
function Card:calculate_joker(context, ...)
local is_frozen = self.edition and self.edition.Roland_frozen
if is_frozen and self.ability.name == "Loyalty Card" then
return (context.joker_main and self.ability.loyalty_remaining == 0) and
{xmult = self.ability.extra.Xmult} or nil
end
if is_frozen and self.ability.name == "Turtle Bean" then
return
end
local ret = {orig_calculate_joker(self, context, ...)}
if not is_frozen or type(ret[1]) ~= "table" then
return unpack(ret)
end
local ability = freeze(self)
if not needs_chip_mult_override[ability.name] then
return unpack(ret)
end
return f {mult = "mult_mod", chips = "chip_mod", xmult = "xmult", x_mult = "x_mult"}:map(function(v)
local key = "Roland_frozen_" .. v
ability[key] = ability[key] or ret[1][v]
return ability[key]
end):where(f.id):table()
end
local orig_calculate_dollar_bonus = Card.calculate_dollar_bonus
---@param self Card|{Roland_frozen_ability?: table}
function Card:calculate_dollar_bonus(...)
if not self.edition or not self.edition.Roland_frozen then
return orig_calculate_dollar_bonus(self, ...)
end
local ability = freeze(self)
if ability.name == "Satellite" then
ability.Roland_frozen_planets_used = ability.Roland_frozen_planets_used or
orig_calculate_dollar_bonus(self, ...)
return self.ability.extra * ability.Roland_frozen_planets_used
end
return orig_calculate_dollar_bonus(self, ...)
end
local orig_get_probability_vars = SMODS.get_probability_vars
function G.P_CENTERS.j_bootstraps:mult(card)
local ability = card.Roland_frozen_ability
local ret = math.floor((G.GAME.dollars + (G.GAME.dollar_buffer or 0)) / card.ability.extra.dollars)
if not ability then
return ret
end
ability.Roland_bootstraps_mult = ability.Roland_bootstraps_mult or ret
return ability.Roland_bootstraps_mult
end
function SMODS.get_probability_vars(trigger_obj, ...)
local numerator, denominator = orig_get_probability_vars(trigger_obj, ...)
return trigger_obj and (trigger_obj.Roland_frozen or {}).probability or numerator, denominator
end
q {
blocking = false,
no_delete = true,
func = function()
local orig_flip_double_sided = (Bakery_API or {}).flip_double_sided
if not orig_flip_double_sided then
return false
end
function Bakery_API.flip_double_sided(card, ...)
if not card.edition or not card.edition.Roland_frozen then
return orig_flip_double_sided(card, ...)
end
end
local _ = G.P_CENTERS.j_Bakery_Estate and hook_estate()
local _ = G.P_CENTERS.c_Bakery_Scribe and hook_scribe()
local _ = G.P_CENTERS.j_Bakery_Proxy and hook_proxy()
end,
}

File diff suppressed because it is too large Load diff

View file

@ -3,101 +3,10 @@
---@license MPL-2.0 ---@license MPL-2.0
---@version 1.0.0 ---@version 1.0.0
--- ---
---@alias FFrom (fun<K, V>(iter: table<K, V>, fpairs?: fun(t: table<K, V>): (fun(table: table<K, V>, index?: K): K?, V?)): F | { [K]: V })|(fun(iter: number, fpairs?: number, step?: number): F | { [number]: number })|(fun<V>(iter: string): (fun(table: { [string]: V }): V))|(fun<V>(iter: string): (fun(table: { [string]: V }): V))|(fun<K, V>(iter: fun(): K, V): F | { [K]: V })|(fun(iter: false): fun(): false)|(fun(iter: true): fun(): true)|(fun(iter: nil): F)
---@class F ---@class F
local f = {} local f = {}
if not f then if not f then
---@generic I, O
---@param first string|fun(v: I): O
---@return fun(v: I): O
---@nodiscard
function f.chain(first)
error {first}
end
---@generic I, T, O
---@param first string|fun(v: I): T
---@param second string|fun(v: T): O
---@return fun(v: I): O
---@nodiscard
function f.chain(first, second)
error {first, second}
end
---@generic I, T1, T2, O
---@param first string|fun(v: I): T1
---@param second string|fun(v: T1): T2
---@param third string|fun(v: T2): O
---@return fun(v: I): O
---@nodiscard
function f.chain(first, second, third)
error {first, second, third}
end
---@generic I, T1, T2, T3, O
---@param first string|fun(v: I): T1
---@param second string|fun(v: T1): T2
---@param third string|fun(v: T2): T3
---@param fourth string|fun(v: T3): O
---@return fun(v: I): O
---@nodiscard
function f.chain(first, second, third, fourth)
error {first, second, third, fourth}
end
---@generic I, T1, T2, T3, T4, O
---@param first string|fun(v: I): T1
---@param second string|fun(v: T1): T2
---@param third string|fun(v: T2): T3
---@param fourth string|fun(v: T3): T4
---@param fifth string|fun(v: T4): O
---@return fun(v: I): O
---@nodiscard
function f.chain(first, second, third, fourth, fifth)
error {first, second, third, fourth, fifth}
end
---@generic I, O
---@param all { [1]: (string|fun(v: I): O) }
---@return fun(v: I): O
---@nodiscard
function f.chain(all)
error(all)
end
---@generic I, T, O
---@param all { [1]: (string|fun(v: I): T), [2]: (string|fun(v: T): O) }
---@return fun(v: I): O
---@nodiscard
function f.chain(all)
error(all)
end
---@generic I, T1, T2, O
---@param all { [1]: (string|fun(v: I): T1), [2]: (string|fun(v: T1): T2), [3]: (string|fun(v: T2): O) }
---@return fun(v: I): O
---@nodiscard
function f.chain(all)
error(all)
end
---@generic I, T1, T2, T3, O
---@param all { [1]: (string|fun(v: I): T1), [2]: (string|fun(v: T1): T2), [3]: (string|fun(v: T2): T3), [4]: (string|fun(v: T3): O) }
---@return fun(v: I): O
---@nodiscard
function f.chain(all)
error(all)
end
---@generic I, T1, T2, T3, T4, O
---@param all { [1]: (string|fun(v: I): T1), [2]: (string|fun(v: T1): T2), [3]: (string|fun(v: T2): T3), [4]: (string|fun(v: T3): T4), [4]: (string|fun(v: T4): O) }
---@return fun(v: I): O
---@nodiscard
function f.chain(all)
error(all)
end
---@generic K, V ---@generic K, V
---@param self F | { [K]: V } ---@param self F | { [K]: V }
---@return K?, V? ---@return K?, V?
@ -109,74 +18,39 @@ end
---@type F ---@type F
local none local none
---@generic T: F|function|string|nil
---@param func T
---@return T
---@nodiscard
local function autofunc(func)
return type(func) == "string" and f.indices(func) or func or f.id
end
---@generic K, V ---@generic K, V
---@param tbl table<K, V> ---@param tbl table<K, V>
---@param fpairs? fun(t: table<K, V>): (fun(table: table<K, V>, index?: K): K, V) ---@param fpairs? fun(t: table<K, V>): (fun(table: table<K, V>, index?: K): K, V)
---@return fun(tbl: table<K, V>, key: K): K, V ---@return fun(tbl: table<K, V>, key: K): K, V
---@return table<K, V> ---@return K
---@return V ---@return V
---@nodiscard
local function autopairs(tbl, fpairs) local function autopairs(tbl, fpairs)
return (fpairs or (tbl[#tbl] and ipairs or pairs))(tbl) return (fpairs or tbl[#tbl] and ipairs or pairs)(tbl)
end
---@param any any
---@return boolean
local function is_f(any)
return type(any) == "table" and any.from == f.from and any.new == f.new
end end
--- Always returns nil. --- Always returns nil.
---@return nil ---@return nil
---@nodiscard
function f.noop() function f.noop()
end end
--- Always returns false. --- Always returns false.
---@return false ---@return false
---@nodiscard
function f.fals() function f.fals()
return false return false
end end
--- Always returns true. --- Always returns true.
---@return true ---@return true
---@nodiscard
function f.tru() function f.tru()
return true return true
end end
--- Returns the arguments.
---@generic T
---@param ... T
---@return T
---@nodiscard
function f.id(...)
return ...
end
---@generic T
---@param value T
---@return fun(T): boolean
---@nodiscard
function f.eq(value)
return function(v)
return value == v
end
end
---@generic T
---@param value T
---@return fun(T): boolean
---@nodiscard
function f.nq(value)
return function(v)
return value ~= v
end
end
---@generic T ---@generic T
---@param v T ---@param v T
---@return fun(): T ---@return fun(): T
@ -195,158 +69,67 @@ function f.const(v)
end end
end end
---@param i integer
---@return fun(...: any): any
---@nodiscard
function f.arg(i)
return function(...)
return ({...})[i]
end
end
---@generic K, V ---@generic K, V
---@param v K ---@param v K
---@return fun(x: { [K]: V }): V ---@return fun(x: { [K]: V }): V
---@nodiscard ---@nodiscard
function f.index(v) function f.index(v)
return function(x) return function(x)
return type(x) == "table" and x[v] or x return x[v]
end end
end end
---@generic K, V
---@param v { [K]: V }
---@return fun(x: K): V
---@nodiscard
function f.index_into(v)
return type(v) == "table" and function(x)
return v[x]
end or f.const(v)
end
---@generic V
---@param v string
---@return fun(x: { [string]: V }): V
---@nodiscard
function f.indices(v)
return function(x)
if type(x) ~= "table" then
return x
end
for i in v:gmatch "[^.]+" do
x = x[i]
end
return x
end
end
---@param any any
---@return boolean
---@nodiscard
function f.isf(any)
return type(any) == "table" and any.from == f.from and any.new == f.new
end
f[true and "chain"] = function(...)
local ret
for _, v in ipairs {...} do
if type(v) == "table" then
for _, vv in ipairs(v) do
local copy = ret
vv = autofunc(vv)
ret = ret and function(...)
return vv(copy(...))
end or vv
end
else
local copy = ret
v = autofunc(v)
ret = ret and function(...)
return v(copy(...))
end or v
end
end
return ret or f.noop
end
---@generic K, V ---@generic K, V
---@param fnext? fun(): K?, V? ---@param fnext? fun(): K?, V?
---@return F|{ [K]: V } ---@return F|{ [K]: V }
---@nodiscard ---@nodiscard
function f.new(fnext) function f.new(fnext)
-- Iterating over `f` is far easier, but we do this for performance sake. local ret = {next = fnext or f.noop}
return {
all = f.all, for k, v in pairs(f) do
any = f.any, ret[k] = v
arg = f.arg, end
chain = f.chain,
concat = f.concat, return ret
const = f.const,
count = f.count,
each = f.each,
eq = f.eq,
fals = f.fals,
flatmap = f.flatmap,
fold = f.fold,
from = f.from,
id = f.id,
index = f.index,
index_into = f.index_into,
indices = f.indices,
isf = f.isf,
keys = f.keys,
map = f.map,
new = f.new,
next = fnext or f.noop,
noop = f.noop,
nq = f.nq,
peek = f.peek,
pun = f.pun,
skip = f.skip,
slice = f.slice,
string = f.string,
swap = f.swap,
table = f.table,
take = f.take,
tru = f.tru,
values = f.values,
where = f.where,
}
end end
--- Creates an enumeration. --- Creates an enumeration.
---@type FFrom ---@generic K, V
function f.from(iter, fpairs, step) ---@param tbl table<K, V>
if iter == nil then ---@param fpairs? fun(t: table<K, V>): (fun(table: table<K, V>, index?: K): K?, V?)
return none ---@param step? nil
elseif iter == true then ---@return F | { [K]: V }
---@overload fun(tbl: number, fpairs?: number, step?: number): F | { [number]: number }
---@overload fun(tbl: string): (fun(table: { [string]: V }): V)
---@overload fun(tbl: false): fun(): false
---@overload fun(tbl: true): fun(): true
---@overload fun(tbl: nil): F
function f.from(tbl, fpairs, step)
if tbl == true then
return f.tru return f.tru
elseif iter == false then elseif tbl == false then
return f.fals return f.fals
elseif tbl == nil then
return none
end end
local t = type(iter) local tbl_type = type(tbl)
if t == "string" then if tbl_type == "string" then
return f.indices(iter) return f.index(tbl)
elseif t == "number" then elseif tbl_type == "number" then
local ik, is, start = 0, step or 1, fpairs and iter or 1 local ik = 0
local start = fpairs and tbl or 1
local stop = not fpairs and iter or local stop = not fpairs and tbl or
(type(fpairs) == "number") and fpairs or error("Invalid argument type for 'fpairs': " .. type(fpairs)) (type(fpairs) == "number") and fpairs or error("Invalid argument type for 'fpairs': " .. type(fpairs))
if start ~= stop and (is == 0 or ((is < 0) == (start < stop))) then if step and step ~= 0 and ((step < 0) == (start < stop)) then
return none return none
end end
return f.new(function() return f.new(function()
local iv = start + ik * is local iv = tbl + ik * (step or 1)
ik = ik + 1 ik = ik + 1
if start > stop and iv >= stop or if start > stop and iv >= stop or
@ -355,25 +138,17 @@ function f.from(iter, fpairs, step)
return ik, iv return ik, iv
end end
end) end)
elseif t == "function" then elseif tbl_type ~= "table" then
return f.new(iter) error("Invalid argument type for 'tbl': " .. type(tbl))
elseif t == "thread" then
return f.new(function()
local s, k, v = coroutine.resume(iter)
if s then
return k, v
end end
end)
else local next, context, k, v = autopairs(tbl, fpairs)
local next, context, k, v = autopairs(iter, type(fpairs) == "function" and fpairs or nil)
return f.new(function() return f.new(function()
k, v = next(context, k) k, v = next(context, k)
return k, v return k, v
end) end)
end end
end
---@generic K, V ---@generic K, V
---@param self F|{ [K]: V } ---@param self F|{ [K]: V }
@ -383,63 +158,44 @@ end
function f:concat(...) function f:concat(...)
local fsi = 0 local fsi = 0
local fs = {...} local fs = {...}
local sum, last = 0, 0
for i = 1, #fs do for i = 1, #fs do
fs[i] = f.isf(fs[i]) and fs[i] or f.from(fs[i]) if not is_f(fs[i]) then ---@diagnostic disable-next-line: assign-type-mismatch
fs[i] = f.from(fs[i])
end
end end
return f.new(function() return f.new(function()
if fsi == 0 then if fsi == 0 then
local k, v = self:next() local k, v = self:next()
last = type(k) == "number" and math.max(k, last) or last
if k ~= nil then if k ~= nil then
return k, v return k, v
end end
fsi, sum, last = 1, last, 0 fsi = 1
end end
while fsi <= #fs do while fsi <= #fs do
local k, v = fs[fsi]:next() local k, v = fs[fsi]:next()
last = type(k) == "number" and math.max(k, last) or last
if k ~= nil then if k ~= nil then
return type(k) == "number" and k + sum or k, v
end
fsi, sum, last = fsi + 1, sum + last, 0
end
end)
end
---@generic K, V
---@param self F|{ [K]: V }
---@param func fun(v: V, k: K): any
---@return F|{ [K]: V }
---@nodiscard
function f:peek(func)
func = autofunc(func)
return f.new(function()
local k, v = self:next()
if k ~= nil then
func(v, k)
return k, v return k, v
end end
fsi = fsi + 1
end
end) end)
end end
---@generic K, V, U ---@generic K, V, U
---@param self F|{ [K]: V } ---@param self F|{ [K]: V }
---@param func fun(v: V, k: K): U ---@param func F|fun(v: V, k: K): U
---@return F|{ [K]: U } ---@return F|{ [K]: U }
---@overload fun(self: F|{ [K]: V }, func: string): F|{ [K]: U }
---@nodiscard ---@nodiscard
---@overload fun(self: F|{ [K]: V }, func: string): F|{ [K]: U }
function f:map(func) function f:map(func)
func = autofunc(func) func = type(func) == "string" and f.index(func) or func
return f.new(function() return f.new(function()
local k, v = self:next() local k, v = self:next()
@ -452,15 +208,15 @@ end
---@generic K, V, U ---@generic K, V, U
---@param self F|{ [K]: V } ---@param self F|{ [K]: V }
---@param func fun(v: V, k: K): { [any]: U } ---@param func F|fun(v: V, k: K): { [any]: U }
---@param fpairs? fun(t: table<K, V>): (fun(table: table<K, V>, index?: K): K, V) ---@param fpairs? fun(t: table<K, V>): (fun(table: table<K, V>, index?: K): K, V)
---@return F|{ [K]: U } ---@return F|{ [K]: U }
---@overload fun(self: F|{ [K]: V }, func: string, fpairs?: fun(t: table<K, V>): (fun(table: table<K, V>, index?: K): K, V)): F|{ [K]: U }
---@nodiscard ---@nodiscard
---@overload fun(self: F|{ [K]: V }, func: string): F|{ [K]: U }
function f:flatmap(func, fpairs) function f:flatmap(func, fpairs)
-- local i = 0 -- local i = 0
local vt, vk, vv, vp local vt, vk, vv, vp
func = autofunc(func) func = type(func) == "string" and f.index(func) or func
return f.new(function() return f.new(function()
if vk then if vk then
@ -502,13 +258,12 @@ end
---@generic K, V ---@generic K, V
---@param self F|{ [K]: V } ---@param self F|{ [K]: V }
---@param func fun(v: V, k: K): boolean ---@param func F|fun(v: V, k: K): boolean
---@param is? any
---@return F|{ [K]: V } ---@return F|{ [K]: V }
---@nodiscard ---@nodiscard
---@overload fun(self: F|{ [K]: V }, func: string, is?: any): F|{ [K]: V } ---@overload fun(self: F|{ [K]: V }, func: string): F|{ [K]: V }
function f:where(func, is) function f:where(func)
func = autofunc(func) func = type(func) == "string" and f.index(func) or func
return f.new(function() return f.new(function()
local k, v local k, v
@ -520,17 +275,9 @@ function f:where(func, is)
return return
end end
if is == nil then
if func(v, k) then if func(v, k) then
return k, v return k, v
end end
elseif is == false then
if not func(v, k) then
return k, v
end
elseif is == func(v, k) then
return k, v
end
end end
end) end)
end end
@ -569,20 +316,6 @@ function f:values()
end) end)
end end
---@generic K, V
---@param self F|{ [K]: V }
---@return F|{ [V]: K }
---@nodiscard
function f:swap()
return f.new(function()
local k, v = self:next()
if k ~= nil then
return v, k
end
end)
end
---@generic K, V ---@generic K, V
---@param self F|{ [K]: V } ---@param self F|{ [K]: V }
---@param skip? integer ---@param skip? integer
@ -665,12 +398,12 @@ end
---@generic K, V ---@generic K, V
---@param self F|{ [K]: V } ---@param self F|{ [K]: V }
---@param func fun(v: V, k: K): any ---@param func F|fun(v: V, k: K): boolean
---@return boolean|V ---@return boolean|V
---@overload fun(self: F|{ [K]: V }, func: string?): boolean|V
---@nodiscard ---@nodiscard
---@overload fun(self: F|{ [K]: V }, func: string): boolean
function f:any(func) function f:any(func)
func = autofunc(func) func = type(func) == "string" and f.index(func) or func
for k, v in self.next do for k, v in self.next do
if not func or func(v, k) then if not func or func(v, k) then
@ -683,12 +416,12 @@ end
---@generic K, V ---@generic K, V
---@param self F|{ [K]: V } ---@param self F|{ [K]: V }
---@param func fun(v: V, k: K): any ---@param func F|fun(v: V, k: K): boolean
---@return boolean|V ---@return boolean|V
---@overload fun(self: F|{ [K]: V }, func: string?): boolean|V
---@nodiscard ---@nodiscard
---@overload fun(self: F|{ [K]: V }, func: string): boolean
function f:all(func) function f:all(func)
func = autofunc(func) func = type(func) == "string" and f.index(func) or func
for k, v in self.next do for k, v in self.next do
if not func or not func(v, k) then if not func or not func(v, k) then
@ -701,13 +434,13 @@ end
---@generic K, V ---@generic K, V
---@param self F|{ [K]: V } ---@param self F|{ [K]: V }
---@param func fun(v: V, k: K): any ---@param func F|fun(v: V, k: K): boolean
---@return integer ---@return integer
---@overload fun(self: F|{ [K]: V }, func: string): integer
---@nodiscard ---@nodiscard
---@overload fun(self: F|{ [K]: V }, func: string): integer
function f:count(func) function f:count(func)
local ret = 0 local ret = 0
func = autofunc(func) func = type(func) == "string" and f.index(func) or func
for k, v in self.next do for k, v in self.next do
if not func or func(v, k) then if not func or func(v, k) then
@ -718,15 +451,6 @@ function f:count(func)
return ret return ret
end end
---@generic K, V, T
---@param self F|{ [K]: V }
---@param _ `T`
---@return F|{ [K]: `T` }
---@nodiscard
function f:pun(_)
return self
end
---@generic K, V ---@generic K, V
---@param self F|{ [K]: V } ---@param self F|{ [K]: V }
---@return string ---@return string
@ -767,7 +491,7 @@ end
---@generic K, V ---@generic K, V
---@param self F|{ [K]: V } ---@param self F|{ [K]: V }
---@param func? fun(v: V, k: K): nil ---@param func? fun(v: V, k: K)
function f:each(func) function f:each(func)
for k, v in self.next do for k, v in self.next do
if func then if func then
@ -778,11 +502,4 @@ end
none = f.new() none = f.new()
---@type F|FFrom return f.from
local ret = (setmetatable or f.const(f.from))(f, {
__call = function(_, ...)
return f.from(...)
end,
})
return ret

View file

@ -1,76 +0,0 @@
---@meta
---@alias Attributes "mult"|"chips"|"xmult"|"xchips"|"score"|"xscore"|"blindsize"|"xblindsize"|"balance"|"swap"|"retrigger"|"scaling"|"reset"|"suit"|"diamonds"|"hearts"|"spades"|"clubs"|"hand_type"|"rank"|"ace"|"two"|"three"|"four"|"five"|"six"|"seven"|"eight"|"nine"|"ten"|"jack"|"queen"|"king"|"face"|"economy"|"generation"|"destroy_card"|"hands"|"discard"|"hand_size"|"chance"|"joker_slot"|"mod_chance"|"copying"|"full_deck"|"passive"|"joker"|"tarot"|"planet"|"spectral"|"enhancements"|"seals"|"editions"|"tag"|"skip"|"modify_card"|"perma_bonus"|"prevents_death"|"boss_blind"|"reroll"|"on_sell"|"sell_value"|"food"|"space"|"bakery_double_sided"|"bakery_usable"|"bakery_werewolf"
---@type Card[]
CardArea.cards = CardArea.cards
--- @overload fun(tbl: SMODS.Joker): SMODS.GameObject
Bakery_API.Charm = Bakery_API.Charm
--- @generic T: SMODS.GameObject
--- @param obj T
--- @return T
function Bakery_API.credit(obj)
error(obj)
end
---@type table
Balatest = Balatest
--- @type { constants?: { TEN: table }, new: (fun(self: self, arr?: number[], sign?: number, noNormalize?: boolean): table), pow: (fun(x: number, y: number): number) }
Big = Big
--- @type table?
CardSleeves = CardSleeves
--- @type table|fun(obj: SMODS.Back): SMODS.Back
CardSleeves.Sleeve = CardSleeves.Sleeve
--- @type {aliases: { [string]: [string] }}
Cryptid = Cryptid
--- @type fun(area: CardArea, ...: ...): Card
create_card_for_shop = create_card_for_shop
--- @type boolean|table
G.Bakery_charm_area.cards[1].ability.extra = G.Bakery_charm_area.cards[1].ability.extra
SMODS.Mods.Roland.config = require "config"
---@type userdata|{getWidth: fun(self: self): number}
SMODS.Atlas.image_data = SMODS.Atlas.image_data
--- @type table
Talisman = Talisman
--- @type fun(obj: any): number
to_number = to_number
-- This exists to remove the @deprecated warning.
---Returns the elements from the given `list`. This function is equivalent to
---```lua
--- return list[i], list[i+1], ···, list[j]
---```
---
---
---[View documents](command:extension.lua.doc?["en-us/52/manual.html/pdf-unpack"])
---
---@generic T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
---@param list {
--- [1]?: T1,
--- [2]?: T2,
--- [3]?: T3,
--- [4]?: T4,
--- [5]?: T5,
--- [6]?: T6,
--- [7]?: T7,
--- [8]?: T8,
--- [9]?: T9,
--- [10]?: T10,
---}
---@param i? integer
---@param j? integer
---@return T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
---@nodiscard
function unpack(list, i, j)
error {list, i, j}
end

View file

@ -1,59 +1,3 @@
local f = assert(SMODS.load_file "src/lib/funky.lua")() or require "lib.funky"
---@param v string
---@return Card|Tag
local function add(v)
if not G.P_TAGS[v] then
return SMODS.add_card {no_edition = true, key = v}
end
local tag = Tag(v)
if tag.name == "Orbital Tag" then
local hands = f(G.GAME.hands):where "visible":keys():table()
tag.ability.orbital_hand = pseudorandom_element(hands, pseudoseed "Roland_c_orbital_tag")
end
add_tag(tag)
return tag
end
local function simplify(x)
return type(x) == "string" and x:lower():gsub("%s", ""):gsub("_", ""):gsub("^the", "") or x
end
local function flatten(v)
return type(v) ~= "table" and {v} or (f.isf(v) and v:table() or v)
end
---@param it string
local function find(it)
if (G.P_CENTERS[it] or {}).config then
return it
end
local match = simplify(it)
local pool = f(G.P_CENTER_POOLS):any(f.chain(f.arg(2), simplify, f.eq(match)))
if type(pool) == "table" and next(pool) then
return pseudorandom_element(pool, pseudoseed "Roland_c").key
end
if Cryptid and Cryptid.aliases and Cryptid.aliases[it] then
return Cryptid.aliases[it]
end
return f(G.localization.descriptions)
:flatmap(flatten)
:where(type, "table")
:where(f.chain(f.arg(2), f.index_into(G.P_CENTERS), "config"))
:where(function(v, k)
return match == simplify(k) or match == simplify(v.name)
end)
:keys()
:any()
end
local function protect(fun) local function protect(fun)
return function() return function()
local res, ret = pcall(fun) local res, ret = pcall(fun)
@ -62,83 +6,73 @@ local function protect(fun)
sendErrorMessage(tostring(ret), "Roland") sendErrorMessage(tostring(ret), "Roland")
end end
return not res or ret ~= false and not (SMODS.current_mod and ret ~= true) return not res or ret ~= false
end end
end end
local function protect_ev(fun) local function protect_ev(fun)
if type(fun) == "function" then if type(fun) == "table" then
return Event {func = protect(fun)} fun.func = protect(fun.func)
elseif type(fun) ~= "table" then fun = getmetatable(fun) == Event and fun or Event(fun)
error("Expected a function or table, got a " .. type(fun), 3) elseif type(fun) == "function" then
elseif type(fun.is) == "function" and fun:is(SMODS.GameObject) then fun = Event {func = protect(fun)}
return Event { else
blocking = false, error("Expected a function or event, got a " .. type(fun), 3)
no_delete = true,
func = protect(function()
if not Bakery_API or not Bakery_API.credit then
return false
end end
Bakery_API.credit(fun) return fun
end), end
}
else -- This exists to remove the @deprecated warning.
fun.func = protect(fun.func or fun[1]) if false then
return getmetatable(fun) == Event and fun or Event(fun) ---Returns the elements from the given `list`. This function is equivalent to
---```lua
--- return list[i], list[i+1], ···, list[j]
---```
---
---
---[View documents](command:extension.lua.doc?["en-us/52/manual.html/pdf-unpack"])
---
---@generic T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
---@param list {
--- [1]?: T1,
--- [2]?: T2,
--- [3]?: T3,
--- [4]?: T4,
--- [5]?: T5,
--- [6]?: T6,
--- [7]?: T7,
--- [8]?: T8,
--- [9]?: T9,
--- [10]?: T10,
---}
---@param i? integer
---@param j? integer
---@return T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
---@nodiscard
function unpack(list, i, j)
error {list, i, j}
end end
end end
local f = assert(SMODS.load_file "src/lib/funky.lua")() or require "lib.funky"
--- Queues an event to be run. --- Queues an event to be run.
--- Note that events added this way implicitly `return true` unless you explicitly `return false`. --- Note that events added this way implicitly `return true` unless you explicitly `return false`, unlike the vanilla ones.
--- For `front`; boolean `true` to add the event to the front of the queue, rather than the end. --- @param fun (fun():false|nil)|Event The event or a function to run turn into an event.
--- @generic T: fun():boolean?|Event --- @param front boolean|nil `true` to add the event to the front of the queue, rather than the end.
--- @param fun T|Event|{front?: boolean} The table or function to turn into an event. local function q(fun, front)
--- @return T fun G.E_MANAGER:add_event(protect_ev(fun), nil, front)
local function q(fun)
local ev = protect_ev(fun)
G.E_MANAGER:add_event(ev, nil, ev.front)
return fun
end end
--- Determines if a center is allowed to be usable. --- Determines if a center is allowed to be usable.
---@return boolean ---@return boolean
local function u() local function u()
return not ((G.play and next(G.play.cards) or G.CONTROLLER.locked or G.GAME.STOP_USE and G.GAME.STOP_USE > 0) and return not ((G.play and #G.play.cards > 0 or G.CONTROLLER.locked or
(G.GAME.STOP_USE and G.GAME.STOP_USE > 0)) and
G.STATE ~= G.STATES.HAND_PLAYED and G.STATE ~= G.STATES.HAND_PLAYED and
G.STATE ~= G.STATES.DRAW_TO_HAND and G.STATE ~= G.STATES.DRAW_TO_HAND and
G.STATE ~= G.STATES.PLAY_TAROT) G.STATE ~= G.STATES.PLAY_TAROT)
end end
--- Creates one or more cards. return {f, q, u}
---@param ... any
---@return Card|Tag|(Card|Tag[])
local function c(...)
local cards = f {...}
:flatmap(flatten)
:where(type, "string")
:map(string.lower)
:map(find)
:where(f.id)
:map(add)
:values()
:table()
return #cards > 1 and cards or cards[1]
end
return {f, q, u, setmetatable({}, {
__call = function(_, ...)
return c(...)
end,
__index = function(_, k)
return c(k)
end,
__newindex = function(_, k, v)
local n = tonumber(v)
for _ = 1, type(n) == "number" and n or 1 do
c(k)
end
end,
})}

View file

@ -1,130 +1,73 @@
local qol = assert(SMODS.load_file "src/lib/shared.lua")() or require "lib.shared" local qol = assert(SMODS.load_file "src/lib/shared.lua")() or require "lib.shared"
local f, q = qol[1], qol[2]
q { qol[1] {"challenge", "spectral", "tweaks", "blind", "charm", "joker", "back", "seal"}:each(function(v)
front = true,
no_delete = true,
blocking = false,
func = function()
local contributors = (Bakery_API or {}).contributors
if not contributors then
return false
end
-- Special shoutout to all contributors. <3
local credits = {
aster = {
name = "asterSSH",
fg = HEX "f8f8f2ff",
bg = HEX "bd93f9ff",
},
bakersdozenbagels = {
name = "BakersDozenBagels",
fg = HEX "362708ff",
bg = HEX "edd198ff",
},
char = {
name = "char (@irregulester)",
fg = HEX "f8f8f2ff",
bg = HEX "ff79c6ff",
},
ghostlyfield = {
name = "ghostlyfield",
fg = HEX "ffffffff",
bg = HEX "b290e6ff",
},
hamester = {
name = "Hamester",
fg = HEX "ffffffff",
bg = HEX "ffa100ff",
},
redstoad = {
name = "RedsToad",
fg = HEX "ffffffff",
bg = HEX "da4044ff",
},
}
f(credits):each(function(v, k)
contributors["Roland_" .. k] = v
-- G.ARGS.LOC_COLOURS["Bakery_credit_fg_Roland_" .. k] = v.fg
-- G.ARGS.LOC_COLOURS["Bakery_credit_bg_Roland_" .. k] = v.bg
end)
if not SMODS.Mods.DebugPlus or not SMODS.Mods.Roland.config.import_funky then
return
end
_G.f, _G.q, _G.u, _G.c = unpack(qol)
end,
}
f {
"challenge",
"spectral",
"edition",
"tweaks",
"blind",
"charm",
"joker",
"tarot",
"back",
"seal",
"tag",
"voucher",
}:each(function(v)
assert(SMODS.load_file("src/" .. v .. ".lua"))(qol) assert(SMODS.load_file("src/" .. v .. ".lua"))(qol)
end) end)
if Balatest then if Balatest then
f {"joker", "blind", "spectral"}:each(function(v) qol[1] {"joker", "blind", "spectral"}:each(function(v)
assert(SMODS.load_file("src/tests/" .. v .. ".tests.lua"))(qol) assert(SMODS.load_file("src/tests/" .. v .. ".tests.lua"))(qol)
end) end)
end end
local function toggle(id) qol[2](function()
return create_toggle { ---@type table<string, {name: string, fg: table, bg: table}>
label = localize {type = "variable", key = "b_Roland_" .. id}, local contributors = Bakery_API.contributors
ref_table = SMODS.Mods.Roland.config,
ref_value = id,
scale = 1.5,
}
end
local animated = SMODS.Mods.Roland.config.animated_icon contributors.Roland_aster = {
name = "asterSSH",
fg = HEX "f8f8f2ff",
bg = HEX "bd93f9ff",
}
contributors.Roland_char = {
name = "char (@irregulester)",
fg = HEX "f8f8f2ff",
bg = HEX "ff79c6ff",
}
end, true)
SMODS.Atlas { SMODS.Atlas {
px = 256, px = 256,
py = 256, py = 256,
key = "modicon", key = "modicon",
fps = animated and 12 or nil, path = "icon.png",
frames = animated and 12 or nil,
path = animated and "icon.png" or "unicon.png",
atlas_table = animated and "ANIMATION_ATLAS" or "ASSET_ATLAS",
} }
---@diagnostic disable-next-line: duplicate-set-field
function SMODS.current_mod.config_tab() function SMODS.current_mod.config_tab()
return { return {
n = G.UIT.ROOT, n = G.UIT.ROOT,
config = {minw = 1, minh = 1, align = "tl", padding = 0.1, colour = G.C.BLACK}, config = {r = 0.1, minw = 4, minh = 4, align = "tr", padding = 0.2, colour = G.C.BLACK},
nodes = {{ nodes = {{
n = G.UIT.C, n = G.UIT.C,
config = {minw = 1, minh = 1, align = "tl", padding = 0.1, colour = G.C.CLEAR}, config = {minw = 1, minh = 1, align = "tr", padding = 0.2, colour = G.C.CLEAR},
nodes = { nodes = {
toggle "animated_icon", create_toggle {
toggle "cool_phones", label = localize {type = "variable", key = "b_Roland_equinox_assist"},
toggle "faster_planets", ref_table = SMODS.Mods.Roland.config,
-- toggle "illusion_seal", ref_value = "equinox_assist",
-- toggle "no_wild_debuff", scale = 2,
toggle "vitriol", },
toggle "equinox_assist", SMODS.Mods.DebugPlus and UIBox_button {
SMODS.Mods.DebugPlus and toggle "import_funky", label = {localize {type = "variable", key = "b_Roland_debug_export"}},
G.P_CENTERS.c_Bakery_Scribe and toggle "scribable_basket", func = G.P_CENTERS.j_Roland_escapey.debug_export,
Talisman and toggle "harsh_ante_scaling", button = "Roland_debug_export",
colour = G.C.MULT,
col = true,
scale = 5,
minw = 5,
},
}, },
}}, }},
} }
end end
SMODS.current_mod.qol = qol function SMODS.current_mod.optional_features()
return {cardareas = {deck = true, unscored = true}}
end
function G.FUNCS.Roland_debug_export(_, tbl)
local to = tbl or _G
to.f, to.q, to.u = unpack(qol)
end

View file

@ -1,4 +1,4 @@
local f, q = (... or require "lib.shared")[1], (... or require "lib.shared")[2] local f, q = unpack(... or require "lib.shared")
SMODS.Atlas { SMODS.Atlas {
px = 71, px = 71,
@ -11,9 +11,8 @@ SMODS.Seal {
key = "glass", key = "glass",
atlas = "seal", atlas = "seal",
pos = {x = 0, y = 0}, pos = {x = 0, y = 0},
attributes = {"destroy_card", "tag"},
badge_colour = HEX "a6a6a6ff", badge_colour = HEX "a6a6a6ff",
pronouns = "they_them", pronouns = "he_him",
calculate = function(_, card, context) calculate = function(_, card, context)
local function eq(v) local function eq(v)
return v == card return v == card

View file

@ -1,4 +1,6 @@
local f, q, u = (... or require "lib.shared")[1], (... or require "lib.shared")[2], (... or require "lib.shared")[3] local f, q, u = unpack(... or require "lib.shared")
local negative = {key = "e_negative_consumable", set = "Edition", config = {extra = 1}}
local spectral = (function() local spectral = (function()
local x = 0 local x = 0
@ -6,13 +8,19 @@ local spectral = (function()
---@param tbl SMODS.Consumable|{artist?: string} ---@param tbl SMODS.Consumable|{artist?: string}
---@return SMODS.Consumable ---@return SMODS.Consumable
return function(tbl) return function(tbl)
tbl.artist = tbl.artist and "Roland_" .. tbl.artist or nil
tbl.pos = {x = x, y = 0}
tbl.atlas = "spectral"
tbl.set = "Spectral"
tbl.cost = 4 tbl.cost = 4
tbl.set = "Spectral"
tbl.atlas = "spectral"
tbl.pos = {x = x, y = 0}
tbl.artist = tbl.artist and "Roland_" .. tbl.artist or nil
local ret = SMODS.Consumable(tbl)
x = x + 1 x = x + 1
return q(SMODS.Consumable(tbl))
q(function()
Bakery_API.credit(ret)
end)
return ret
end end
end)() end)()
@ -30,10 +38,41 @@ SMODS.Sound {
spectral { spectral {
key = "afterimage", key = "afterimage",
pronouns = "she_they", pronouns = "he_they",
artist = "aster", artist = "aster",
config = {extra = {amount = 1, hand = -2}},
loc_vars = function(_, info_queue, card)
table.insert(info_queue, negative)
return {vars = {card.ability.extra.amount, card.ability.extra.hand}}
end,
can_use = function(_, card)
return u() and card.ability.extra.amount == #Bakery_API.get_highlighted()
end,
use = function(_, card)
f(Bakery_API.get_highlighted()):each(function(v)
q {
delay = 0.1,
func = function()
v:set_edition {negative = true}
v:juice_up(0.5, 0.5)
end,
}
end)
q {
delay = 0.1,
func = function()
G.hand:change_size(card.ability.extra.hand)
Bakery_API.unhighlight_all()
end,
}
end,
}
spectral {
key = "dual",
pronouns = "they_them",
config = {extra = {amount = 2}}, config = {extra = {amount = 2}},
attributes = {"seal", "modify_card", "spectral"},
loc_vars = function(_, _, card) loc_vars = function(_, _, card)
return {vars = {card.ability.extra.amount}} return {vars = {card.ability.extra.amount}}
end, end,
@ -41,13 +80,13 @@ spectral {
return u() and #G.hand.cards > 0 return u() and #G.hand.cards > 0
end, end,
use = function(_, card, _) use = function(_, card, _)
local cards = f(G.hand.cards):table() local cards = f(G.hand.cards, ipairs):table()
pseudoshuffle(cards, pseudoseed "RolandDual") pseudoshuffle(cards, pseudoseed "RolandDual")
f(cards):take(card.ability.extra.amount):each(function(v) f(cards):take(card.ability.extra.amount):each(function(v)
local seal local seal
for _ = 1, 64 do for _ = 1, 31 do
seal = SMODS.poll_seal {guaranteed = true} seal = SMODS.poll_seal {guaranteed = true}
if seal ~= "Roland_glass" then if seal ~= "Roland_glass" then
@ -61,72 +100,9 @@ spectral {
} }
spectral { spectral {
key = "primal", key = "mirror",
pronouns = "he_him", pronouns = "he_him",
artist = "aster", config = {extra = {amount = 1}},
attributes = {"enhancements", "modify_card", "spectral"},
loc_vars = function(_, info_queue)
table.insert(info_queue, G.P_CENTERS.m_stone)
end,
can_use = function()
return next(G.hand.cards) and u()
end,
use = function(_, card)
q {
trigger = "after",
delay = 0.4,
func = function()
card:juice_up(0.3, 0.5)
play_sound("tarot1")
end,
}
f(G.hand.cards):each(function(v, k)
local percent = 1.15 - (k - 0.999) / (#G.hand.cards - 0.998) * 0.3
q {
trigger = "after",
delay = 0.15,
func = function()
v:flip()
v:juice_up(0.3, 0.3)
play_sound("card1", percent)
return true
end,
}
end)
f(G.hand.cards):each(function(v)
q(function()
v:set_ability(G.P_CENTERS.m_stone)
end)
end)
f(G.hand.cards):each(function(v, k)
local percent = 0.85 + (k - 0.999) / (#G.hand.cards - 0.998) * 0.3
q {
trigger = "after",
delay = 0.15,
func = function()
v:flip()
v:juice_up(0.3, 0.3)
play_sound("tarot2", percent, 0.6)
return true
end,
}
end)
delay(0.5)
end,
}
spectral {
key = "refract",
pronouns = "it_its",
artist = "aster",
config = {extra = {amount = 2}},
attributes = {"seal", "modify_card", "spectral"},
loc_vars = function(_, info_queue, card) loc_vars = function(_, info_queue, card)
table.insert(info_queue, G.P_SEALS.Roland_glass) table.insert(info_queue, G.P_SEALS.Roland_glass)
return {vars = {card.ability.extra.amount}} return {vars = {card.ability.extra.amount}}
@ -141,21 +117,20 @@ spectral {
end, end,
} }
spectral { local void = spectral {
key = "void", key = "void",
pronouns = "it_its", pronouns = "it_its",
artist = "aster", artist = "aster",
soul_rate = 0.003, soul_rate = 0.003,
soul_set = "Spectral", soul_set = "Spectral",
config = {extra = {amount = 2}}, config = {extra = {amount = 2}},
attributes = {"destroy_card", "generation", "spectral"},
loc_vars = function(_, info_queue, card) loc_vars = function(_, info_queue, card)
table.insert(info_queue, {key = "e_negative_consumable", set = "Edition", config = {extra = 1}}) table.insert(info_queue, negative)
table.insert(info_queue, G.P_CENTERS.c_cryptid) table.insert(info_queue, G.P_CENTERS.c_cryptid)
return {vars = {card.ability.extra.amount}} return {vars = {card.ability.extra.amount}}
end, end,
can_use = function() can_use = function()
return next(G.playing_cards) and u() return #G.playing_cards > 1 and not not u()
end, end,
use = function(_, card) use = function(_, card)
local function destructible(v) local function destructible(v)
@ -181,15 +156,25 @@ spectral {
f(cards):each(destroy) f(cards):each(destroy)
f(G.jokers.cards):each(calculate_joker) f(G.jokers.cards):each(calculate_joker)
f(card.ability.extra.amount):each(function() for _ = 1, card.ability.extra.amount do
local cryptid = create_card(nil, G.consumeables, nil, nil, nil, nil, "c_cryptid", "void") local cryptid = create_card(nil, G.consumeables, nil, nil, nil, nil, "c_cryptid", "void")
cryptid:set_edition({negative = true}, true) cryptid:set_edition({negative = true}, true)
cryptid:add_to_deck() cryptid:add_to_deck()
G.consumeables:emplace(cryptid) G.consumeables:emplace(cryptid)
end) end
end end
play_sound("Roland_void", 1, 0.7) play_sound("Roland_void", 1, 0.7)
q {delay = 0.28, timer = "REAL", trigger = "after", func = void}
q {
delay = 0.28,
timer = "REAL",
trigger = "after",
func = void,
}
end, end,
} }
q(function()
void.hidden = not SMODS.Mods.Cryptid
end)

View file

@ -1,142 +0,0 @@
local f, q = (... or require "lib.shared")[1], (... or require "lib.shared")[2]
SMODS.Atlas {
px = 34,
py = 34,
key = "tag",
path = "tag.png",
}
local function apply_frozen(tag, context)
return function()
q {
delay = 0.4,
trigger = "after",
func = function()
play_sound "tarot1"
tag:juice_up(0.3, 0.5)
end,
}
q {
delay = 0.15,
trigger = "after",
func = function()
context.card:flip()
play_sound("card1", 0.85)
context.card:juice_up(0.3, 0.3)
end,
}
delay(0.2)
q {
delay = 0.1,
trigger = "after",
func = function()
context.card:set_edition {Roland_frozen = true}
end,
}
q {
delay = 0.15,
trigger = "after",
func = function()
context.card:flip()
play_sound("tarot2", 0.85, 0.6)
context.card:juice_up(0.3, 0.3)
end,
}
delay(0.1)
end
end
SMODS.Tag {
key = "freeze",
atlas = "tag",
pronouns = "any_all",
min_ante = 1,
pos = {x = 0, y = 0},
config = {amount = 5},
loc_vars = function(self, info_queue, tag)
info_queue[#info_queue + 1] = G.P_CENTERS.e_Roland_frozen
tag.ability = tag.ability or {}
return {vars = {tag.ability.amount or self.config.amount}}
end,
apply = function(self, tag, context)
tag.ability = tag.ability or {}
if not tag.triggered and context.type == "Bakery_play_hand_late" and tag.ability.amount == 0 then
tag.triggered = true
tag:yep("X", G.C.RED, f.tru)
end
if tag.triggered or
(tag.ability.amount or self.config.amount) <= 0 or
((context.card or {}).edition or {}).Roland_frozen or
context.type ~= "Bakery_score_card" then
return
end
tag.ability.amount = (tag.ability.amount or self.config.amount) - 1
return {func = apply_frozen(tag, context)}
end,
}
SMODS.Tag {
key = "invisible",
atlas = "tag",
pronouns = "any_all",
min_ante = 2,
pos = {x = 1, y = 0},
config = {invis_rounds = 0, total_rounds = 2},
loc_vars = function(self, _, tag)
tag.ability = tag.ability or f(self.config):table()
local main_end = {}
local _ = f((G.jokers or {}).cards or {}):map "edition":any "negative" and
localize {type = "other", key = "remove_negative", nodes = main_end, vars = {}}
return {
vars = {
tag.ability.total_rounds or self.config.total_rounds,
tag.ability.invis_rounds or self.config.invis_rounds,
},
main_end = main_end[1],
}
end,
increment = function(self)
f(G.GAME.tags):where("key", "tag_Roland_invisible"):each(function(v)
v.ability = v.ability or {}
v.ability.invis_rounds = (v.ability.invis_rounds or self.config.invis_rounds) + 1
v:juice_up()
end)
end,
apply = function(self, tag)
local modifiers = G.GAME.modifiers
tag.ability = tag.ability or {}
local ab = tag.ability
ab.invis_rounds = ab.invis_rounds or self.config.invis_rounds
ab.total_rounds = ab.total_rounds or self.config.total_rounds
if tag.triggered or
not next(G.jokers.cards) or
ab.invis_rounds < ab.total_rounds or
#G.jokers.cards + (modifiers.Roland_invisible or 0) >= G.jokers.config.card_limit then
return
end
tag.triggered = true
modifiers.Roland_invisible = (modifiers.Roland_invisible or 0) + 1
tag:yep("!", G.C.GREY, function()
local copied = pseudorandom_element(G.jokers.cards, pseudoseed "Roland_invisible")
local copy = copy_card(copied, nil, nil, nil, copied.edition and copied.edition.negative)
G.jokers:emplace(copy)
copy:add_to_deck()
modifiers.Roland_invisible = (modifiers.Roland_invisible or 0) - 1
return true
end)
end,
}

View file

@ -1,45 +0,0 @@
local f, q, u = (... or require "lib.shared")[1], (... or require "lib.shared")[2], (... or require "lib.shared")[3]
SMODS.Atlas {
px = 71,
py = 95,
key = "tarot",
path = "tarot.png",
}
q(SMODS.Consumable {
key = "coolheaded",
pos = {x = 0, y = 0},
config = {extra = {amount = 1}},
cost = 3,
set = "Tarot",
atlas = "tarot",
artist = "Roland_aster",
attributes = {"editions", "modify_card", "spectral"},
loc_vars = function(_, info_queue, card)
table.insert(info_queue, G.P_CENTERS.e_Roland_frozen)
return {vars = {card.ability.extra.amount}}
end,
can_use = function(_, card)
if not u() then
return false
end
local highlighted = Bakery_API.get_highlighted()
return #G.jokers.highlighted + #highlighted == card.ability.extra.amount and
f(G.jokers.highlighted):concat(highlighted):all(function(v)
return not v.edition
end)
end,
use = function()
f(G.jokers.highlighted):concat(Bakery_API.get_highlighted()):each(function(v)
q {
delay = 0.1,
func = function()
v:set_edition {Roland_frozen = true}
end,
}
end)
end,
})

View file

@ -1,4 +1,4 @@
local f = (... or require "lib.shared")[1] local f = unpack(... or require "lib.shared")
if not Balatest then if not Balatest then
return return
@ -192,9 +192,9 @@ Balatest.TestPlay {
} }
Balatest.TestPlay { Balatest.TestPlay {
category = {"blind", "mitotic"}, category = {"blind", "xerox"},
name = "mitotic", name = "xerox",
blind = "bl_Roland_mitotic", blind = "bl_Roland_xerox",
execute = function() execute = function()
Balatest.discard {"2S"} Balatest.discard {"2S"}
end, end,
@ -204,9 +204,9 @@ Balatest.TestPlay {
} }
Balatest.TestPlay { Balatest.TestPlay {
category = {"blind", "mitotic"}, category = {"blind", "xerox"},
name = "mitotic_disabled", name = "xerox_disabled",
blind = "bl_Roland_mitotic", blind = "bl_Roland_xerox",
jokers = {"j_chicot"}, jokers = {"j_chicot"},
execute = function() execute = function()
Balatest.discard {"2S"} Balatest.discard {"2S"}

View file

@ -2,6 +2,330 @@ if not Balatest then
return return
end end
Balatest.TestPlay {
category = {"joker", "escapey", "escape"},
name = "escapey_none",
jokers = {"j_Roland_escapey"},
execute = function() end,
assert = function()
Balatest.assert(not G.jokers.cards[1].config.center:Bakery_can_use(G.jokers.cards[1]))
end,
}
Balatest.TestPlay {
category = {"joker", "escapey", "escape"},
name = "escapey_one_consumable",
jokers = {"j_Roland_escapey"},
consumeables = {"c_strength"},
execute = function()
Balatest.q(function()
G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}}
end)
Balatest.wait()
end,
assert = function()
Balatest.assert_eq(G.GAME.hands["High Card"].level, 2)
end,
}
Balatest.TestPlay {
category = {"joker", "escapey", "escape"},
name = "escapey_two_consumables",
jokers = {"j_Roland_escapey"},
consumeables = {"c_strength", "c_strength"},
execute = function()
Balatest.q(function()
G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}}
end)
Balatest.wait()
end,
assert = function()
Balatest.assert_eq(G.GAME.hands["High Card"].level, 3)
end,
}
Balatest.TestPlay {
category = {"joker", "escapey", "escape"},
name = "escapey_one_tag",
jokers = {"j_Roland_escapey"},
no_auto_start = true,
execute = function()
Balatest.skip_blind("tag_investment")
Balatest.q(function()
G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}}
end)
Balatest.wait()
end,
assert = function()
Balatest.assert_eq(G.GAME.hands["High Card"].level, 2)
end,
}
Balatest.TestPlay {
category = {"joker", "escapey", "escape"},
name = "escapey_two_tags",
jokers = {"j_Roland_escapey"},
no_auto_start = true,
execute = function()
Balatest.skip_blind("tag_investment")
Balatest.skip_blind("tag_investment")
Balatest.q(function()
G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}}
end)
Balatest.wait()
end,
assert = function()
Balatest.assert_eq(G.GAME.hands["High Card"].level, 3)
end,
}
Balatest.TestPlay {
category = {"joker", "escapey", "escape"},
name = "escapey_consumables_and_tags",
jokers = {"j_Roland_escapey"},
consumeables = {"c_strength"},
no_auto_start = true,
execute = function()
Balatest.skip_blind("tag_investment")
Balatest.skip_blind("tag_investment")
Balatest.q(function()
G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}}
end)
Balatest.wait()
end,
assert = function()
Balatest.assert_eq(G.GAME.hands["High Card"].level, 2)
end,
}
Balatest.TestPlay {
category = {"joker", "escapey", "escape"},
name = "escapey_partial_selected_consumables_and_tags",
jokers = {"j_Roland_escapey"},
consumeables = {"c_strength", "c_strength"},
no_auto_start = true,
execute = function()
Balatest.skip_blind("tag_investment")
Balatest.skip_blind("tag_investment")
Balatest.q(function()
G.consumeables:add_to_highlighted(G.consumeables.cards[1])
end)
Balatest.wait()
Balatest.q(function()
G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}}
end)
Balatest.wait()
end,
assert = function()
Balatest.assert_eq(G.GAME.hands["High Card"].level, 2)
end,
}
Balatest.TestPlay {
category = {"joker", "escapey", "escape"},
name = "escapey_full_selected_consumables_and_tags",
jokers = {"j_Roland_escapey"},
consumeables = {"c_strength"},
no_auto_start = true,
execute = function()
Balatest.skip_blind("tag_investment")
Balatest.skip_blind("tag_investment")
Balatest.q(function()
G.consumeables:add_to_highlighted(G.consumeables.cards[1])
end)
Balatest.wait()
Balatest.q(function()
G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}}
end)
Balatest.wait()
end,
assert = function()
Balatest.assert_eq(G.GAME.hands["High Card"].level, 3)
end,
}
Balatest.TestPlay {
category = {"joker", "escapey", "fuse"},
name = "escapey_fusion",
jokers = {"j_Roland_escapey", "j_Roland_escapey"},
execute = function()
Balatest.q(function()
G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}}
end)
Balatest.wait()
end,
assert = function()
Balatest.assert_eq(#G.jokers.cards, 1)
Balatest.assert_eq(G.jokers.cards[1].sell_cost, 8)
Balatest.assert_eq(G.jokers.cards[1].ability.extra.levels, 2)
end,
}
Balatest.TestPlay {
category = {"joker", "escapey", "fuse"},
name = "escapey_consumable_takes_precedence",
jokers = {"j_Roland_escapey", "j_Roland_escapey"},
consumeables = {"c_strength"},
execute = function()
Balatest.q(function()
G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}}
end)
Balatest.wait()
end,
assert = function()
Balatest.assert_eq(#G.jokers.cards, 2)
Balatest.assert_eq(G.GAME.hands["High Card"].level, 2)
end,
}
Balatest.TestPlay {
category = {"joker", "escapey", "fuse"},
name = "escapey_tag_takes_precedence",
jokers = {"j_Roland_escapey", "j_Roland_escapey"},
no_auto_start = true,
execute = function()
Balatest.skip_blind("tag_investment")
Balatest.q(function()
G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}}
end)
Balatest.wait()
end,
assert = function()
Balatest.assert_eq(#G.jokers.cards, 2)
Balatest.assert_eq(G.GAME.hands["High Card"].level, 2)
end,
}
Balatest.TestPlay {
category = {"joker", "escapey", "fuse"},
name = "escapey_fusion_takes_precedence",
jokers = {"j_Roland_escapey", "j_Roland_escapey"},
consumeables = {"c_strength"},
execute = function()
Balatest.q(function()
G.consumeables:add_to_highlighted(G.consumeables.cards[1])
end)
Balatest.wait()
Balatest.q(function()
G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}}
end)
Balatest.wait()
end,
assert = function()
Balatest.assert_eq(#G.jokers.cards, 1)
Balatest.assert_eq(G.jokers.cards[1].sell_cost, 8)
Balatest.assert_eq(G.jokers.cards[1].ability.extra.levels, 2)
end,
}
Balatest.TestPlay {
category = {"joker", "escapey", "fuse"},
name = "escapey_scribe_fusion",
jokers = {"j_Roland_escapey"},
execute = function()
if not G.P_CENTERS.c_Bakery_Scribe then
sendWarnMessage("escapey_scribe_fusion cannot run without c_Bakery_Scribe, skipping test.")
return
end
Balatest.q(function()
local scribe = create_card(nil, G.consumeables, nil, nil, nil, nil, "c_Bakery_Scribe", "balatest")
scribe:add_to_deck()
G.consumeables:emplace(scribe)
end)
Balatest.wait()
Balatest.q(function()
G.jokers:add_to_highlighted(G.jokers.cards[1])
end)
Balatest.wait()
Balatest.use(function() return G.consumeables.cards[1] end)
Balatest.q(function()
G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[1]}}
end)
Balatest.wait()
end,
assert = function()
if not G.P_CENTERS.c_Bakery_Scribe then
return
end
Balatest.assert_eq(#G.jokers.cards, 1)
Balatest.assert_eq(G.jokers.cards[1].sell_cost, 8)
Balatest.assert_eq(G.jokers.cards[1].ability.extra.levels, 1)
end,
}
Balatest.TestPlay {
category = {"joker", "escapey", "fuse"},
name = "escapey_scribe_fusion_alt",
jokers = {"j_Roland_escapey"},
execute = function()
if not G.P_CENTERS.c_Bakery_Scribe then
sendWarnMessage("escapey_scribe_fusion_alt cannot run without c_Bakery_Scribe, skipping test.")
return
end
Balatest.q(function()
local scribe = create_card(nil, G.consumeables, nil, nil, nil, nil, "c_Bakery_Scribe", "balatest")
scribe:add_to_deck()
G.consumeables:emplace(scribe)
end)
Balatest.wait()
Balatest.q(function()
G.jokers:add_to_highlighted(G.jokers.cards[1])
end)
Balatest.wait()
Balatest.use(function() return G.consumeables.cards[1] end)
Balatest.q(function()
G.jokers:add_to_highlighted(G.jokers.cards[2])
G.FUNCS.Bakery_use_joker {config = {ref_table = G.jokers.cards[2]}}
end)
Balatest.wait()
end,
assert = function()
if not G.P_CENTERS.c_Bakery_Scribe then
return
end
Balatest.assert_eq(#G.jokers.cards, 1)
Balatest.assert_eq(G.jokers.cards[1].sell_cost, 8)
Balatest.assert_eq(G.jokers.cards[1].ability.extra.levels, 1)
end,
}
Balatest.TestPlay { Balatest.TestPlay {
category = {"joker", "martingale"}, category = {"joker", "martingale"},
name = "martingale_oops", name = "martingale_oops",
@ -10,7 +334,7 @@ Balatest.TestPlay {
Balatest.play_hand {"2S"} Balatest.play_hand {"2S"}
end, end,
assert = function() assert = function()
Balatest.assert_chips(7) Balatest.assert_chips(7 * 2)
end, end,
} }
@ -23,6 +347,6 @@ Balatest.TestPlay {
Balatest.play_hand {"2S"} Balatest.play_hand {"2S"}
end, end,
assert = function() assert = function()
Balatest.assert_chips(1 / 0) Balatest.assert_chips(7 * math.pow(2, 32))
end, end,
} }

View file

@ -2,6 +2,22 @@ if not Balatest then
return return
end end
Balatest.TestPlay {
category = {"spectral", "afterimage"},
name = "afterimage",
consumeables = {"c_Roland_afterimage"},
deck = {cards = {{s = "S", r = "2"}}},
execute = function()
Balatest.highlight({"2S"})
Balatest.use(G.consumeables.cards[1])
Balatest.end_round()
end,
assert = function()
Balatest.assert_eq(G.hand.config.card_limit, 51)
Balatest.assert(G.deck.cards[1].edition.negative)
end,
}
Balatest.TestPlay { Balatest.TestPlay {
category = {"spectral", "void"}, category = {"spectral", "void"},
name = "spectral", name = "spectral",

View file

@ -1,40 +1,54 @@
local f, q = (... or require "lib.shared")[1], (... or require "lib.shared")[2] local f, q = unpack(... or require "lib.shared")
SMODS.Joker:take_ownership("joker", {cost = 1}, true)
local orig_set_debuff = Card.set_debuff
function Card:set_debuff(should_debuff, ...) SMODS.Joker:take_ownership("joker", {cost = 1}, true)
if self.ability.Roland_crimson ~= nil then local orig_can_highlight = CardArea.can_highlight
self.ability.Roland_crimson = not not should_debuff local orig_set_debuff = Card.set_debuff
return local orig_highlight = Card.highlight
local orig_copy_card = copy_card
---@diagnostic disable-next-line: duplicate-set-field
function CardArea:can_highlight(...)
if self ~= G.consumeables then
return orig_can_highlight(self, ...)
end end
if SMODS.get_enhancements(self).m_wild and SMODS.Mods.Roland.config.no_wild_debuff then --- Allows more flexibility when using the Escapey joker to delete specific consumables.
self.config.highlighted_limit = 1 / 0
return true
end
---@diagnostic disable-next-line: duplicate-set-field
function Card:set_debuff(...)
if self.config and self.config.center_key == "m_wild" then
self.debuff = false self.debuff = false
else else
orig_set_debuff(self, should_debuff, ...) orig_set_debuff(self, ...)
end
end
---@diagnostic disable-next-line: duplicate-set-field
function Card:highlight(is_highlighted, ...)
self.highlighted = is_highlighted
if not G.CONTROLLER.HID.controller then
return orig_highlight(self, is_highlighted, ...)
end end
end end
local orig_use_consumeable = Card.use_consumeable local orig_use_consumeable = Card.use_consumeable
---@diagnostic disable-next-line: duplicate-set-field
function Card:use_consumeable(area, copier, ...) function Card:use_consumeable(area, copier, ...)
if SMODS.Mods.Roland.config.faster_planets and self.ability.consumeable.hand_type then
set_consumeable_usage(self)
level_up_hand(copier or self, self.ability.consumeable.hand_type, true)
return
end
local seal_spectrals = {["Deja Vu"] = true, Medium = true, Talisman = true, Trance = true} local seal_spectrals = {["Deja Vu"] = true, Medium = true, Talisman = true, Trance = true}
if not seal_spectrals[self.ability.name] then if not seal_spectrals[self.ability.name] then
return orig_use_consumeable(self, area, copier, ...) return orig_use_consumeable(self, area, copier, ...)
end end
local card = copier or self
f(Bakery_API.get_highlighted()):each(function(v) f(Bakery_API.get_highlighted()):each(function(v)
q(function() q(function()
play_sound "tarot1" play_sound "tarot1"
local card = (copier or self)
card:juice_up(0.3, 0.5) card:juice_up(0.3, 0.5)
end) end)
@ -47,83 +61,55 @@ function Card:use_consumeable(area, copier, ...)
} }
end) end)
q {delay = 0.7, trigger = "after", func = Bakery_API.unhighlight_all} q {
delay = 0.7,
trigger = "after",
func = function()
Bakery_API.unhighlight_all()
end,
}
end end
local orig_use_card = G.FUNCS.use_card ---@diagnostic disable-next-line: lowercase-global
function copy_card(other, new_card, ...)
local ret = orig_copy_card(other, new_card, ...)
function G.FUNCS.use_card(e, ...) if new_card and new_card.edition and new_card.edition.key == "e_negative" then
local ref = e.config.ref_table or {} --- Fixes an issue where using 'c_death' will make negative
local consumeable = (ref.ability or {}).consumeable or {} --- cards do the inverse of what they're supposed to do.
new_card.ability.card_limit = math.max(new_card.ability.card_limit, 1)
if not SMODS.Mods.Roland.config.faster_planets or
(not consumeable.hand_type and not consumeable.hand_types) then
return orig_use_card(e, ...)
end end
local normal = ref.area ~= G.pack_cards or not G.GAME.pack_choices or G.GAME.pack_choices <= 0 return ret
ref.from_area = ref.from_area or ref.area end
play_sound("tarot1", percent, 0.6)
discover_card(ref.config.center)
ref:use_consumeable(ref.area)
SMODS.calculate_context {area = ref.area, consumeable = ref, using_consumeable = true}
q(function() q(function()
ref:remove() local orig_can_highlight_area = Bakery_API.can_highlight_area
---@diagnostic disable-next-line: duplicate-set-field
function Bakery_API.can_highlight_area(area, ...)
return area == G.consumeables or orig_can_highlight_area(area, ...)
end
end) end)
if normal then
return
end
G.GAME.pack_choices = G.GAME.pack_choices - 1
local _ = G.GAME.pack_choices <= 0 and G.FUNCS.end_consumeable()
end
local orig_create_card_for_shop = create_card_for_shop local orig_create_card_for_shop = create_card_for_shop
---@diagnostic disable-next-line: lowercase-global
function create_card_for_shop(...) function create_card_for_shop(...)
---@type Card
local ret = orig_create_card_for_shop(...) local ret = orig_create_card_for_shop(...)
if not SMODS.Mods.Roland.config.illusion_seal or if not G.GAME.used_vouchers.v_illusion or
not G.GAME.used_vouchers.v_illusion or
not ({Default = true, Enhanced = true})[(((ret or {}).config or {}).center or {}).set] or not ({Default = true, Enhanced = true})[(((ret or {}).config or {}).center or {}).set] or
pseudorandom(pseudoseed "Roland_illusion") <= 0.8 then pseudorandom(pseudoseed "Roland_illusion") <= 0.8 then
return ret return ret
end end
local seal = SMODS.poll_seal {type_key = "Roland_illusion_seal", guaranteed = true} local seal = SMODS.poll_seal {
type_key = "Roland_illusion_seal",
guaranteed = true,
}
ret:set_seal(seal, true, true) ret:set_seal(seal, true, true)
return ret return ret
end end
local orig_get_blind_amount = get_blind_amount
---@param ante number
---@return table|number
local function blind(ante)
return ante == 39 and 1e294 or (to_number or f.id)(orig_get_blind_amount(ante))
end
local function no_harsh_ante_scaling()
return not Talisman or not SMODS.Mods.Roland.config.harsh_ante_scaling
end
function get_blind_amount(ante, ...)
local loop = 39
if ante < loop or no_harsh_ante_scaling() then
return orig_get_blind_amount(ante, ...)
end
if (ante - 9) / 15 >= loop then
return 1 / 0
end
local rem = tonumber(blind((ante % loop) + 1))
return ante / 15 >= loop and Big:new(f(blind(ante - (loop * 15))):map(f.const(10)):table()) or
(ante / 9 >= loop and Big:new(f(ante / loop - 8):map(f.const(10)):concat {rem}:table()) or
(ante / 2 >= loop and Big:new {rem, ante / loop} or
(Big.constants and Big.constants.TEN or Big:new {10}):pow(rem)))
end

View file

@ -1,44 +0,0 @@
local voucher = (function()
local x = 0
---@param tbl SMODS.Voucher|{attributes: Attributes[]}
---@return SMODS.Voucher
return function(tbl)
tbl.pos = {x = x, y = 0}
tbl.atlas = "voucher"
tbl.cost = 10
x = x + 1
return SMODS.Voucher(tbl)
end
end)()
SMODS.Atlas {
px = 71,
py = 95,
key = "voucher",
path = "voucher.png",
}
voucher {
key = "ceres",
pronouns = "it_its",
config = {extra = {amount = 1, hand_type = "Flush House"}},
attributes = {"planet", "passive", "hand_type", "space"},
loc_vars = function(_, _, card)
return {vars = {card.ability.extra.amount}}
end,
in_pool = function(self)
return G.GAME.hands[self.config.extra.hand_type].visible
end,
}
voucher {
key = "neptune",
pronouns = "it_its",
requires = {"v_Roland_ceres"},
config = {extra = {amount = 2, hand_type = "Straight Flush"}},
attributes = {"planet", "passive", "hand_type", "space"},
loc_vars = function(_, _, card)
return {vars = {card.ability.extra.amount}}
end,
}