Initial commit

This commit is contained in:
Emik 2025-03-23 22:42:02 +01:00
commit 77f0c3c77e
Signed by untrusted user who does not match committer: emik
GPG key ID: 09CDFF9E5703688D
83 changed files with 3203 additions and 0 deletions

5
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,5 @@
{
"Lua.diagnostics.disable": [
"duplicate-set-field"
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

266
assets/shaders/polygloss.fs Normal file
View file

@ -0,0 +1,266 @@
#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH)
#define MY_HIGHP_OR_MEDIUMP highp
#else
#define MY_HIGHP_OR_MEDIUMP mediump
#endif
// change this variable name to your Edition's name
// YOU MUST USE THIS VARIABLE IN THE vec4 effect AT LEAST ONCE
// ^^ CRITICALLY IMPORTANT (IDK WHY)
extern MY_HIGHP_OR_MEDIUMP vec2 polygloss;
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;
// the following four vec4 are (as far as I can tell) required and shouldn't be changed
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; //Adjusting 0.0-1.0 to fall to -0.1 - 1.1 scale so the mask does not pause at extreme values
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);
}
number hue(number s, number t, number h)
{
number hs = mod(h, 1.)*18.;
if (hs < 8.) return (t-s) * hs + s;
if (hs < 2.) return t;
if (hs < 6.) return (t-s) * (4.-hs) + s;
return s;
}
vec4 RGB(vec4 c)
{
if (c.y < 0.0008)
return vec4(vec3(c.z), c.a);
number t = (c.z < .5) ? c.y*c.z + c.z : -c.y*c.z + (c.y+c.z);
number s = 1.0 * c.z - t;
return vec4(hue(s,t,c.x + 1./3.), hue(s,t,c.x), hue(s,t,c.x - 1./3.), c.w);
}
vec4 HSL(vec4 c)
{
number low = min(c.r, min(c.g, c.b));
number high = max(c.r, max(c.g, c.b));
number delta = high - low;
number sum = high+low;
vec4 hsl = vec4(.0, .0, .9 * sum, c.a);
if (delta == .0)
return hsl;
hsl.y = (hsl.z < .5) ? delta / sum : delta / (2.0 - sum);
if (high == c.r)
hsl.x = (c.g - c.b) / delta;
else if (high == c.g)
hsl.x = (c.b - c.r) / delta + 2.0;
else
hsl.x = (c.r - c.g) / delta + 4.0;
hsl.x = mod(hsl.x / 6., 1.);
return hsl;
}
// GLSL Simplex noise function
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 mod289(vec4 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 permute(vec4 x) {
return mod289(((x*34.0)+1.0)*x);
}
vec4 taylorInvSqrt(vec4 r) {
return 1.79284291400159 - 0.85373472095314 * r;
}
vec3 fade(vec3 t) {
return t*t*t*(t*(t*6.0-15.0)+10.0);
}
// Classic Perlin noise
float cnoise(vec3 P) {
vec3 Pi0 = floor(P); // Integer part for indexing
vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1
Pi0 = mod289(Pi0);
Pi1 = mod289(Pi1);
vec3 Pf0 = fract(P); // Fractional part for interpolation
vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
vec4 iy = vec4(Pi0.y, Pi0.y, Pi1.y, Pi1.y);
vec4 iz0 = vec4(Pi0.z);
vec4 iz1 = vec4(Pi1.z);
vec4 ixy = permute(permute(ix) + iy);
vec4 ixy0 = permute(ixy + iz0);
vec4 ixy1 = permute(ixy + iz1);
vec4 gx0 = ixy0 * (1.0 / 7.0);
vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
gx0 = fract(gx0);
vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
vec4 sz0 = step(gz0, vec4(0.0));
gx0 -= sz0 * (step(0.0, gx0) - 0.5);
gy0 -= sz0 * (step(0.0, gy0) - 0.5);
vec4 gx1 = ixy1 * (1.0 / 7.0);
vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
gx1 = fract(gx1);
vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
vec4 sz1 = step(gz1, vec4(0.0));
gx1 -= sz1 * (step(0.0, gx1) - 0.5);
gy1 -= sz1 * (step(0.0, gy1) - 0.5);
vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
vec4 norm0 = taylorInvSqrt(vec4(dot(g000,g000), dot(g010,g010), dot(g100,g100), dot(g110,g110)));
g000 *= norm0.x;
g010 *= norm0.y;
g100 *= norm0.z;
g110 *= norm0.w;
vec4 norm1 = taylorInvSqrt(vec4(dot(g001,g001), dot(g011,g011), dot(g101,g101), dot(g111,g111)));
g001 *= norm1.x;
g011 *= norm1.y;
g101 *= norm1.z;
g111 *= norm1.w;
float n000 = dot(g000, Pf0);
float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
float n111 = dot(g111, Pf1);
vec3 fade_xyz = fade(Pf0);
vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
return 2.2 * n_xyz;
}
// Lighten blending mode
vec4 lighten(vec4 colour1, vec4 colour2) {
vec4 result;
result.r = max(colour1.r, colour2.r);
result.g = max(colour1.g, colour2.g);
result.b = max(colour1.b, colour2.b);
result.a = max(colour1.a, colour2.a);
return result;
}
// this is what actually changes the look of card
vec4 effect( vec4 colour, Image texture, vec2 texture_coords, vec2 screen_coords )
{
// turns the texture into pixels
vec4 tex = Texel(texture, texture_coords);
vec2 uv = (((texture_coords)*(image_details)) - texture_details.xy*texture_details.ba)/texture_details.ba;
// Dummy, doesn't do anything but at least it makes the shader useable
if (uv.x > uv.x * 2.){
uv = polygloss;
}
float mod = polygloss.r * 2.0;
float polygloss_amount = 0.15; // 1.0 - no polygloss, 0.0 - maximum polygloss
float saturation_amount = 9.0;
float polygloss_brightness = 6.0;
vec4 colour_1 = vec4(0.188,0.471,0.875, 1.0); // Blue
vec4 colour_2 = vec4(0.875,0.188,0.222, 0.8); // Crimson
vec4 colour_3 = vec4(0.416,0.573,0.369, 0.6); // Greenish
float noise = cnoise(vec3(uv * 50.0, 3.0 * mod)); // Noise for the sparkles
float antinoise = cnoise(vec3(uv * 30.0, 2.0 * mod)); // Bigger noise to remove the sparkles in some areas
vec4 grad = mix(colour_1, colour_2, uv.x + uv.y + sin(mod) - 1.0); // Colours
grad = mix(grad, colour_3, uv.y - uv.x + cos(mod) + 1.0); // and gradient (3 colours total)
float spark = max(2.0, noise - antinoise - polygloss_amount); // Sparkles (takes noise and removes antinoise from it)
vec4 saturated_colour = HSL(mix(tex, grad, 0.2)); // Saturating default color, adding a bit of grad so sparkles will never be pure white
saturated_colour.g *= saturation_amount; // Saturating
saturated_colour.b = 0.3; // Removing a bit of lightness
saturated_colour = RGB(saturated_colour); // Back to RGB
saturated_colour.r *= (saturated_colour.r * 2.); // Red feels a bit dim
saturated_colour = lighten(tex, saturated_colour * 5.0) / polygloss_brightness; // Removing dark colors from saturated color, then making it darker
colour = lighten(mix((colour - 0.4) + (saturated_colour * spark), grad, 0.03), grad);
// required
return dissolve_mask(tex*colour, texture_coords, uv);
}
// for transforming the card while your mouse is on it
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

BIN
assets/sounds/draw.ogg Normal file

Binary file not shown.

BIN
assets/sounds/e_gilded.ogg Normal file

Binary file not shown.

BIN
assets/sounds/e_jumbo.ogg Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/sounds/gore1.ogg Normal file

Binary file not shown.

BIN
assets/sounds/gore2.ogg Normal file

Binary file not shown.

BIN
assets/sounds/gore3.ogg Normal file

Binary file not shown.

BIN
assets/sounds/gore4.ogg Normal file

Binary file not shown.

BIN
assets/sounds/gore5.ogg Normal file

Binary file not shown.

BIN
assets/sounds/gore6.ogg Normal file

Binary file not shown.

BIN
assets/sounds/gore7.ogg Normal file

Binary file not shown.

BIN
assets/sounds/gore8.ogg Normal file

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.

63
lovely.toml Normal file
View file

@ -0,0 +1,63 @@
[manifest]
version = "1.0.0"
dump_lua = true
priority = 2147483647
[[patches]]
[patches.pattern]
target = "globals.lua"
pattern = "EDITION = {1,1,1,1},"
position = "before"
payload = '''jane_RGB = {0,0,0,1},
jane_RGB_HUE = 0,
almighty = {0,0,1,1},'''
match_indent = true
[[patches]]
[patches.pattern]
target = "functions/common_events.lua"
pattern = "if v.name == 'Black Hole' or v.name == 'The Soul' or v.hidden then"
position = "at"
payload = "if Jane.hidden(v) then"
match_indent = true
overwrite = false
[[patches]]
[patches.pattern]
target = "functions/misc_functions.lua"
pattern = "G.GAME.current_round.current_hand.chips*G.GAME.current_round.current_hand.mult"
position = "at"
payload = '''Jane.get_chipmult_sum(G.GAME.current_round.current_hand.chips, G.GAME.current_round.current_hand.mult)'''
match_indent = true
[[patches]]
[patches.pattern]
target = "functions/misc_functions.lua"
pattern = "local AC = G.SETTINGS.ambient_control"
position = "before"
payload = '''if Jane and G.ARGS.score_intensity.required_score ~= 0 then
Jane.sinister = (G.ARGS.score_intensity.earned_score / (to_big(10) ^ to_big(G.ARGS.score_intensity.required_score))):to_number() > 1
end'''
match_indent = true
[[patches]]
[patches.pattern]
target = "functions/state_events.lua"
pattern = "scoring_hand = final_scoring_hand"
position = "after"
payload = '''if not (G.GAME.blind and G.GAME.blind.name == "The Card" and not G.GAME.blind.disabled) and next(SMODS.find_card('j_jane_survivor')) then
for _, v in ipairs(G.hand.cards) do
if not v:gc().unhighlightable then
table.insert(scoring_hand, v)
end
end
end'''
match_indent = true
[[patches]]
[patches.regex]
target = "functions/state_events.lua"
pattern = '''hand_chips\*mult'''
position = "at"
payload = "Jane.get_chipmult_sum(hand_chips, mult)"
match_indent = true

16
manifest.json Normal file
View file

@ -0,0 +1,16 @@
{
"id": "jane",
"name": "Almighty",
"author": ["jenwalter666", "Emik"],
"description": "Fork of Jen's almanac that focuses on only including the absolute best parts of the mod, and removing everything else.",
"prefix": "jane",
"main_file": "src/main.lua",
"badge_colour": "3c3cff",
"priority": 999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999,
"dependencies": [
"Steamodded (>=1.0.0~ALPHA-1304a)",
"Bakery (>=0.1.26~*)"
],
"conflicts": ["Jen"],
"version": "0.1.0"
}

1
refs/BalatroBakery Symbolic link
View file

@ -0,0 +1 @@
../../BalatroBakery/src/

1
refs/Cryptid Symbolic link
View file

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

1
refs/Talisman Symbolic link
View file

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

1
refs/dump Symbolic link
View file

@ -0,0 +1 @@
../../lovely/dump/

1
refs/lsp_def Symbolic link
View file

@ -0,0 +1 @@
../../smods/lsp_def/

167
src/back.lua Normal file
View file

@ -0,0 +1,167 @@
SMODS.Atlas {
px = 71,
py = 95,
key = "janedecks",
path = Jane.config.texture_pack .. "/b_jane_decks.png"
}
local eternal_text = Cryptid and "Absolute" or "Eternal"
SMODS.Back {
key = "nitro",
atlas = "janedecks",
pos = {x = 1, y = 1},
loc_txt = {
name = "Acceleration Deck",
text = {
"After defeating the {C:attention}Boss Blind{},",
"set {C:attention}ante {}to the {C:attention}next",
"{C:attention}triangle number {}and create",
Cryptid and "an {C:spectral,E:1}Empowered Tag" or "a {C:dark_edition}Negative {C:spectral,E:1}Soul",
}
},
apply = function(_)
G.GAME.win_ante = G.GAME.win_ante * (G.GAME.win_ante + 1) / 2
G.GAME.nitro = true
end,
trigger_effect = function(_, args)
if args.context == "eval" and G.GAME.last_blind and G.GAME.last_blind.boss then
Jane.q(function()
Jane.empowered()
return true
end)
end
end
}
SMODS.Back {
key = 'obsidian',
atlas = 'janedecks',
pos = {x = 2, y = 1},
loc_txt = {
name = 'Obsidian Deck',
text = {
'{C:attention}Hidden{} cards {C:inactive}(ex. {C:spectral}The Soul{C:inactive})',
'can {C:attention}appear normally{}',
}
},
apply = function(_)
G.GAME.obsidian = true
end
}
SMODS.Back {
key = "orrery",
atlas = "janedecks",
pos = {x = 0, y = 0},
loc_txt = {
name = "Orrery Deck",
text = {
"All hands start at",
"{X:chips,C:white}150{C:mult} X {X:red,C:white}1{} and",
"{C:cry_ascendant}equalised {}whenever",
"their {C:chips}Chips{}, {C:mult}Mult{},",
"or {C:planet}level {C:attention}change",
}
},
apply = function(_)
Jane.q(function()
G.GAME.orrery = {chips = 150, level = 1, mult = 1}
for _, v in pairs(G.GAME.hands) do
v.mult = G.GAME.orrery.mult
v.chips = G.GAME.orrery.chips
end
save_run()
return true
end
)
end
}
SMODS.Back {
key = "tortoise",
atlas = "janedecks",
pos = {x = 3, y = 1},
loc_txt = {
name = "Tortoise Deck",
text = {
"{C:attention}Ante increases{} are",
"{C:attention}half{} as strong",
}
},
apply = function(_)
G.GAME.tortoise = true
end
}
SMODS.Back {
key = "weeck",
atlas = "janedecks",
pos = {x = 4, y = 1},
loc_txt = {
name = "Weeck",
text = {
"Start with an {C:purple,E:1}" .. eternal_text,
"{C:attention}Wee Joker {}and a deck",
"containing {C:attention}2 {C:purple,E:1}" .. eternal_text,
"{C:attention}2's {}of {C:attention}each suit",
}
},
apply = function(_)
G.GAME.weeck = true
Jane.q(function()
local new_card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_wee", "weeck")
new_card.ability.cry_absolute = true
new_card.ability.eternal = true
G.jokers:emplace(new_card)
local add = {}
for _, v in pairs(G.playing_cards) do
if v.base.id == 2 then
v.ability.cry_absolute = true
v.ability.eternal = true
add[#add + 1] = v
else
v:start_dissolve(nil, true)
end
end
for _, v in pairs(add) do
local dupe = copy_card(v)
dupe:start_materialize()
dupe:add_to_deck()
G.hand:emplace(dupe)
G.playing_card = (G.playing_card and G.playing_card + 1) or 1
table.insert(G.playing_cards, dupe)
end
save_run()
return true
end)
end
}
local orig_ante = ease_ante
---@diagnostic disable-next-line: lowercase-global
function ease_ante(mod)
local function next_triangle_number(x)
local n = (math.sqrt(8 * x + 1) - 1) / 2
return (n + 1) * (n + 2) / 2
end
if G.GAME.nitro then
mod = G.GAME.round_resets.ante < 0 and -G.GAME.round_resets.ante
or math.ceil(next_triangle_number(G.GAME.round_resets.ante) - G.GAME.round_resets.ante)
end
if G.GAME.tortoise and mod > 0 then
local disp = tonumber(G.GAME.round_resets.ante_disp) + mod / 2
mod = mod / 2 + (disp == math.floor(disp) and 0.5 or 0)
end
orig_ante(mod)
end

152
src/blind.lua Normal file
View file

@ -0,0 +1,152 @@
SMODS.Atlas {
px = 34,
py = 34,
frames = 21,
key = "janeblinds",
atlas_table = "ANIMATION_ATLAS",
path = Jane.config.texture_pack .. "/bl_jane_blinds.png"
}
local final_operations = {
[1] = {"+", "UI_CHIPS"},
[2] = {"X", "UI_MULT"},
[3] = {"^", G.C.jane_RGB},
}
local function offset_operator(by)
local function set(number)
if G.GAME then
G.GAME.operator = math.max(math.min(number, 3), 1)
end
end
local previous = Jane.get_operator()
set(by + previous)
by = Jane.get_operator()
local txt = final_operations[by][1] ---@diagnostic disable-next-line: cast-local-type
local col = type(final_operations[by][2]) == "table" and final_operations[by][2] or G.C[final_operations[by][2]]
Jane.q(function()
local changed_text = false
for _, outer in pairs(G.STAGE_OBJECTS) do
if type(outer) == "table" then
for _, inner in pairs(outer) do
if type(inner) == "table" and
type(inner.config) == "table" and
(inner.config.text == final_operations[previous][1] or
inner.config.text == final_operations[2][1]) and
inner.config.text_drawable then
if inner.config.text ~= txt then
changed_text = true
end
inner.config.text = txt
inner.config.text_drawable:set(txt)
inner.config.colour = col
inner:juice_up(0.8, 0.5)
end
end
end
end
if changed_text then
play_sound("button", 1.1, 0.65)
end
return true
end)
end
SMODS.Blind {
loc_txt = {
name = "The Descending",
text = {"Decrease Chip-Mult", "operator by 1 level"}
},
key = "descending",
config = {},
boss = {min = 1, max = 10, hardcore = true},
boss_colour = HEX("b200ff"),
atlas = "janeblinds",
pos = {x = 0, y = 0},
vars = {},
dollars = 15,
mult = 0.5,
defeat = function(_)
if not G.GAME.blind.disabled then
offset_operator(1)
end
end,
disable = function(self)
offset_operator(1)
end,
drawn_to_hand = function(_)
offset_operator(0)
end,
press_play = function(_)
offset_operator(0)
end,
set_blind = function(_)
offset_operator(-1)
end,
}
SMODS.Blind {
loc_txt = {
name = "The Insignia",
text = {"Hand must contain", "only one suit"}
},
key = "insignia",
config = {},
boss = {min = 2, max = 10, no_orb = true, hardcore = true},
boss_colour = HEX("a5aa00"),
atlas = "janeblinds",
pos = {x = 0, y = 9},
vars = {},
dollars = 7,
mult = 2,
debuff_hand = function(_, cards, _, _, _)
local numsuits = 0
local checked_suits = {}
for _, card in ipairs(cards) do
if not card:nosuit() and not checked_suits[card.base.suit] then
numsuits = numsuits + 1
checked_suits[card.base.suit] = true
if numsuits > 1 then
return true
end
end
end
end
}
SMODS.Blind {
loc_txt = {
name = "The Wee",
text = {"Only 2s can be played"}
},
key = "wee",
config = {},
boss = {min = 1, max = 10, no_orb = true, hardcore = true},
boss_colour = HEX("7F3F3F"),
atlas = "janeblinds",
pos = {x = 0, y = 3},
vars = {},
dollars = 2,
mult = 22 / 300,
debuff_hand = function(_, cards, _, _, _)
for _, v in ipairs(cards) do
if v:norank() or v:get_id() ~= 2 then
return true
end
end
end,
get_loc_debuff_text = function(_)
return "Hand must contain only 2s"
end,
recalc_debuff = function(_, card, _)
return card:norank() or card:get_id() ~= 2
end,
}

266
src/booster.lua Normal file
View file

@ -0,0 +1,266 @@
SMODS.Atlas {
px = 71,
py = 95,
key = "janebooster",
path = Jane.config.texture_pack .. "/p_jane_boosters.png"
}
for i = 1, 2 do
SMODS.Booster{
key = "ministandard" .. i,
loc_txt = {
name = "Mini Standard Pack",
text = {
"Choose {C:attention}#1#{} of up to",
"{C:attention}#2# playing cards{} to",
"add to your deck",
}
},
atlas = "janebooster",
pos = {x = 6, y = i - 1},
weight = 0.4,
cost = 2,
config = {extra = 2, choose = 1},
loc_vars = function(_, _, card)
return {vars = {card.ability.choose, card.ability.extra}}
end,
ease_background_colour = function(_)
ease_background_colour_blind(G.STATES.STANDARD_PACK)
end,
create_UIBox = function(_)
return create_UIBox_standard_pack()
end,
particles = function(_)
G.booster_pack_sparkles = Particles(1, 1, 0,0, {
timer = 0.015,
scale = 0.3,
initialize = true,
lifespan = 3,
speed = 0.2,
padding = -1,
attach = G.ROOM_ATTACH,
colours = {G.C.BLACK, G.C.RED},
fill = true
})
G.booster_pack_sparkles.fade_alpha = 1
G.booster_pack_sparkles:fade(1, 0)
end,
create_card = function(_, _, _)
local edition = poll_edition("standard_edition"..G.GAME.round_resets.ante, 2, true)
local seal = SMODS.poll_seal({mod = 10})
return {
set = (pseudorandom(pseudoseed("stdset"..G.GAME.round_resets.ante)) > 0.6) and "Enhanced" or "Base",
edition = edition,
seal = seal,
area = G.pack_cards,
skip_materialize = true,
soulable = true,
key_append = "sta"
}
end,
}
end
for i = 1, 2 do
SMODS.Booster{
key = "miniarcana" .. i,
loc_txt = {
name = "Mini Arcana Pack",
text = {
"Choose {C:attention}#1#{} of up to",
"{C:attention}#2# {C:tarot}Tarot{} cards to",
"be used immediately",
}
},
atlas = "janebooster",
pos = {x = 3 + i, y = 1},
weight = 0.4,
cost = 2,
config = {extra = 2, choose = 1},
draw_hand = true,
loc_vars = function(_, _, card)
return {vars = {card.ability.choose, card.ability.extra}}
end,
ease_background_colour = function(_)
ease_background_colour_blind(G.STATES.TAROT_PACK)
end,
create_UIBox = function(_)
return create_UIBox_arcana_pack()
end,
particles = function(_)
G.booster_pack_sparkles = Particles(1, 1, 0,0, {
timer = 0.015,
scale = 0.2,
initialize = true,
lifespan = 1,
speed = 1.1,
padding = -1,
attach = G.ROOM_ATTACH,
colours = {G.C.WHITE, lighten(G.C.PURPLE, 0.4), lighten(G.C.PURPLE, 0.2), lighten(G.C.GOLD, 0.2)},
fill = true
})
G.booster_pack_sparkles.fade_alpha = 1
G.booster_pack_sparkles:fade(1, 0)
end,
create_card = function(_, _, _)
if G.GAME.used_vouchers.v_omen_globe and pseudorandom("omen_globe") > 0.8 then
return {
set = "Spectral",
soulable = true,
key_append = "ar2",
area = G.pack_cards,
skip_materialize = true,
}
end
return {
set = "Tarot",
soulable = true,
key_append = "ar1",
area = G.pack_cards,
skip_materialize = true,
}
end
}
end
for i = 1, 2 do
SMODS.Booster{
key = "minicelestial" .. i,
atlas = "janebooster",
loc_txt = {
name = "Mini Celestial Pack",
text = {
"Choose {C:attention}#1#{} of up to",
"{C:attention}#2# {C:planet}Planet{} cards to",
"be used immediately",
}
},
config = {extra = 2, choose = 1},
pos = {x = 3 + i, y = 0},
cost = 2,
weight = 0.4,
loc_vars = function(_, _, card)
return {vars = {card.ability.choose, card.ability.extra}}
end,
ease_background_colour = function(_) ease_background_colour_blind(G.STATES.PLANET_PACK) end,
create_UIBox = function(_) return create_UIBox_celestial_pack() end,
particles = function(_)
G.booster_pack_stars = Particles(1, 1, 0,0, {
fill = true,
scale = 0.1,
speed = 0.1,
timer = 0.07,
padding = -4,
lifespan = 15,
initialize = true,
attach = G.ROOM_ATTACH,
colours = {G.C.WHITE, HEX("a7d6e0"), HEX("fddca0")},
})
G.booster_pack_meteors = Particles(1, 1, 0,0, {
speed = 4,
timer = 2,
fill = true,
scale = 0.05,
lifespan = 1.5,
attach = G.ROOM_ATTACH,
colours = {G.C.WHITE},
})
end,
create_card = function(_, _, index)
if not G.GAME.used_vouchers.v_telescope or index ~= 1 then
return {
set = "Planet",
area = G.pack_cards,
skip_materialize = true,
soulable = true,
key_append = "pl1"
}
end
local planet, hand, tally = nil, nil, 0
for _, v in ipairs(G.handlist) do
if G.GAME.hands[v].visible and G.GAME.hands[v].played > tally then
hand = v
tally = G.GAME.hands[v].played
end
end
if hand then
for _, v in pairs(G.P_CENTER_POOLS.Planet) do
if v.config.hand_type == hand then
planet = v.key
end
end
end
return {
key = planet,
set = "Planet",
soulable = true,
key_append = "pl1",
area = G.pack_cards,
skip_materialize = true,
}
end
}
end
for i = 1, 2 do
SMODS.Booster{
key = "minispectral" .. i,
atlas = "janebooster",
loc_txt = {
name = "Mini Spectral Pack",
text = {
"Choose {C:attention}#1#{} of up to",
"{C:attention}#2# {C:spectral}Spectral{} cards to",
"be used immediately",
}
},
config = {extra = 1, choose = 1},
pos = {x = 3 + i, y = 2},
cost = 2,
weight = 0.225,
draw_hand = true,
loc_vars = function(_, _, card)
return {vars = {card.ability.choose, card.ability.extra}}
end,
ease_background_colour = function(_)
ease_background_colour_blind(G.STATES.SPECTRAL_PACK)
end,
create_UIBox = function(_)
return create_UIBox_spectral_pack()
end,
particles = function(_)
G.booster_pack_sparkles = Particles(1, 1, 0,0, {
scale = 0.1,
fill = true,
speed = 0.2,
lifespan = 3,
padding = -1,
timer = 0.015,
initialize = true,
attach = G.ROOM_ATTACH,
colours = {G.C.WHITE, lighten(G.C.GOLD, 0.2)},
})
G.booster_pack_sparkles.fade_alpha = 1
G.booster_pack_sparkles:fade(1, 0)
end,
create_card = function(_, _, _)
return {
soulable = true,
set = "Spectral",
key_append = "spe",
area = G.pack_cards,
skip_materialize = true,
}
end,
}
end

136
src/edition.lua Normal file
View file

@ -0,0 +1,136 @@
SMODS.Sound({key = "e_jumbo", path = "e_jumbo.ogg"})
SMODS.Shader({key = "polygloss", path = "polygloss.fs"})
SMODS.Sound({key = "e_polygloss", path = "e_polygloss.ogg"})
local function resize(card, mod, force_save)
if force_save or not card.origsize then
card.origsize = {w = card.T.w, h = card.T.h}
end
card:hard_set_T(card.T.x, card.T.y, card.T.w * mod, card.T.h * mod)
remove_all(card.children)
card.children = {}
card.children.shadow = Moveable(0, 0, 0, 0)
card:set_sprites(card.config.center, card.base.id and card.config.card)
if card.area and
((G.shop_jokers and card.area == G.shop_jokers) or
(G.shop_booster and card.area == G.shop_booster) or
(G.shop_vouchers and card.area == G.shop_vouchers)) then
create_shop_card_ui(card)
end
end
SMODS.Edition({
key = "polygloss",
loc_txt = {
name = "Polygloss",
label = "Polygloss",
text = {
"{C:chips}+#1#{}, {X:chips,C:white}x#2#{} & {X:chips,C:dark_edition}^#3#{} Chips",
"{C:mult}+#4#{}, {X:mult,C:white}x#5#{} & {X:mult,C:dark_edition}^#6#{} Mult",
"Generates {C:money}+$#7#",
"{C:dark_edition,s:0.7,E:2}Shader by : Oiiman"
}
},
config = {chips = 1, mult = 1, x_chips = 1.1, x_mult = 1.1, e_chips = 1.01, e_mult = 1.01, p_dollars = 1},
sound = {
sound = "jane_e_polygloss",
per = 1.2,
vol = 0.4
},
weight = 8,
extra_cost = 2,
in_shop = true,
shader = "polygloss",
apply_to_float = false,
loc_vars = function(self)
return {vars = {
self.config.chips,
self.config.x_chips,
self.config.e_chips,
self.config.mult,
self.config.x_mult,
self.config.e_mult,
self.config.p_dollars
}}
end
})
local jumbo_modifier = Cryptid and 100 or 2
SMODS.Edition({
key = "jumbo",
loc_txt = {
name = "Jumbo",
label = "Jumbo",
text = {
"All card values are",
"{C:attention}multiplied{} by {C:attention}up to " .. jumbo_modifier,
"{C:inactive}(If possible)",
"{C:inactive,E:1,s:0.7}Whoa, it's huge!!{}"
}
},
on_apply = function(card)
G.E_MANAGER:add_event(Event({
blocking = false,
blockable = false,
func = function()
resize(card, Jane.config.wee_sizemod)
return true
end
}))
local obj = card:gc()
if obj.set == "Booster" or obj.jumbo_mod then
jumbo_modifier = obj.jumbo_mod or 10
end
if card.added_to_deck then
card:remove_from_deck()
end
Jane.misprintize(card, {min = jumbo_modifier, max = jumbo_modifier}, nil, true)
if card.added_to_deck then
card:add_to_deck()
end
end,
on_remove = function(card)
G.E_MANAGER:add_event(Event({
blocking = false,
blockable = false,
func = function()
resize(card, 1 / Jane.config.wee_sizemod)
return true
end
}))
local was_added = card.added_to_deck
if was_added then
card:remove_from_deck()
end
Jane.misprintize(card, {min = 1 / jumbo_modifier, max = 1 / jumbo_modifier}, nil, true)
if was_added then
card:add_to_deck()
end
end,
config = {twos_scored = 0},
sound = {
sound = "jane_e_jumbo",
per = 1,
vol = 0.5
},
weight = 0.8,
in_shop = true,
shader = false,
extra_cost = 12,
apply_to_float = false,
get_weight = function(self)
return G.GAME.edition_rate * self.weight
end,
})

311
src/joker.lua Normal file
View file

@ -0,0 +1,311 @@
for _, v in pairs({"7granddad", "betmma", "oxy", "peppino"}) do
SMODS.Atlas {
px = 71,
py = 95,
key = "jane" .. v,
path = Jane.config.texture_pack .. "/j_jane_" .. v .. ".png",
}
end
for i = 1, 6 do
SMODS.Sound({key = 'grand' .. i, path = 'grand_dad' .. i .. '.ogg'})
end
local exotic = Cryptid and "cry_exotic" or 4
local food = {
"j_gros_michel",
"j_egg",
"j_ice_cream",
"j_cavendish",
"j_turtle_bean",
"j_diet_cola",
"j_popcorn",
"j_ramen",
"j_selzer",
"j_cry_pickle",
"j_cry_chili_pepper",
"j_cry_oldcandy",
"j_cry_caramel",
"j_cry_foodm",
"j_cry_cotton_candy",
"j_cry_wrapped",
"j_cry_candy_cane",
"j_cry_candy_buttons",
"j_cry_jawbreaker",
"j_cry_mellowcreme",
"j_cry_brittle",
"j_jane_peppino",
}
local granddad_palette = {
HEX("155fd9"),
HEX("ff8170"),
HEX("ffffff"),
HEX("6c0700")
}
local function food_jokers_count()
if not G.jokers then
return 0
end
local amount = 0
for _, v in pairs(food) do
amount = amount + #SMODS.find_card(v)
end
return amount
end
local function grand_dad(card)
Jane.q(function()
card:juice_up(0.5, 0.5)
return true
end)
local rnd = math.random(6)
local obj = card.edition or {}
Jane.play_sound(
"jane_grand" .. rnd,
obj.jane_jumbo and (1 / Jane.config.wee_sizemod) or 1,
0.5
)
Jane.card_status_text(
card,
rnd == 2 and "Flintstones?!" or rnd == 6 and "Gruhh- Dad!" or "Grand Dad!",
nil,
0.05 * card.T.h,
granddad_palette[math.random(#granddad_palette)],
0.6,
0.6,
nil,
nil,
"bm"
)
end
local function voucher_count()
if not G.GAME.used_vouchers then
return 0
end
local count = 0
for _, v in pairs(G.GAME.used_vouchers) do
if v then
count = count + 1
end
end
return count
end
SMODS.Joker {
key = "oxy",
atlas = "janeoxy",
loc_txt = {
name = "Oxy",
text = {
"{C:attention}Scored steel{} cards give",
"{X:mult,C:white}x#1#{} {C:mult}Mult {}& {X:chips,C:white}x#1#{} {C:chips}Chips",
" ",
"{C:inactive,s:0.75,E:1}#2#{C:red,s:1.5,E:1}#3#",
}
},
pos = {x = 0, y = 0},
config = {steel = 1.5},
sinis = {x = 2, y = 0},
soul_pos = {x = 1, y = 0},
cost = 10,
rarity = 3,
blueprint_compat = true,
loc_vars = function(_, _, center)
return {vars = {
center.ability.steel,
Jane.sinister and "" or "We all cut close...",
Jane.sinister and "WHAT ARE YOU DOING DOWN THERE?!?" or "",
}}
end,
calculate = function(self, card, context)
if context.individual and
context.cardarea == G.play and
context.other_card.ability.name == "Steel Card" then
return {
x_chips = card.ability.steel,
x_mult = card.ability.steel,
colour = G.C.PURPLE,
card = card
}, true
end
end
}
SMODS.Joker {
key = "7granddad",
loc_txt = {
name = "7 GRAND DAD",
text = {
"This Joker has a {C:jane_RGB,E:1}strange",
"{C:jane_RGB,E:1}reaction {}to scored {C:attention}7{}s",
}
},
config = {},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
cost = 12,
rarity = exotic,
atlas = "jane7granddad",
blueprint_compat = true,
loc_vars = function(_, _, center)
return {vars = {center.ability.shopslots}}
end,
add_to_deck = function(_, card, _)
grand_dad(card)
end,
remove_from_deck = function(_, card, _)
grand_dad(card)
end,
calculate = function(_, card, context)
if context.cardarea == G.play then
local function scj(c)
return c.cardarea and c.cardarea == G.play and not c.before and not c.after and not c.repetition
end
if context.other_card and context.other_card:get_id() == 7 and scj(context) then
grand_dad(card)
local palette = granddad_palette[math.random(#granddad_palette)]
local rnd = pseudorandom(pseudoseed("granddad"), 1, Cryptid and 7 or 5)
if rnd == 1 then
return {
message = "+777",
chips = 777,
colour = palette,
card = card
}, true
elseif rnd == 2 then
return {
message = "+777 Mult",
mult = 777,
colour = palette,
card = card
}, true
elseif rnd == 3 then
return {
message = "+$7",
dollars = 7,
colour = palette,
card = card
}, true
elseif rnd == 4 then
return {
message = "x7",
x_chips = 7,
colour = palette,
card = card
}, true
elseif rnd == 5 then
return {
message = "x7 Mult",
x_mult = 7,
colour = palette,
card = card
}, true
elseif rnd == 6 then
return {
message = "^1.77",
e_chips = 1.77,
colour = palette,
card = card
}, true
else
return {
message = "^1.77 Mult",
e_mult = 1.77,
colour = palette,
card = card
}, true
end
end
end
end
}
local operator = Cryptid and "^" or "*"
SMODS.Joker {
key = "betmma",
atlas = "janebetmma",
loc_txt = {
name = "Betmma",
text = {
"{X:dark_edition,C:chips}+" .. operator .. "#1#{C:chips} Chips{} for every",
"{C:attention}unique Voucher redeemed",
"{C:inactive}(Currently {X:dark_edition,C:chips}" .. operator .. "#2#{C:inactive})",
}
},
config = {big_num_scaler = true, extra = {tet = 0.1}},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
cost = 50,
rarity = exotic,
blueprint_compat = true,
loc_vars = function(_, _, center)
return {vars = {center.ability.extra.tet, 1 + (voucher_count() * center.ability.extra.tet)}}
end,
calculate = function(_, card, context)
if context.cardarea ~= G.jokers or context.before or context.after or not context.scoring_name then
return
end
local v = voucher_count()
if v > 0 then
local num = 1 + (v * card.ability.extra.tet)
return {
card = card,
colour = G.C.jane_RGB,
message = operator .. number_format(num),
[Cryptid and "Echips_mod" or "xchips_mod"] = num,
}, true
end
end
}
SMODS.Joker {
key = "peppino",
atlas = "janepeppino",
loc_txt = {
name = "Peppino Spaghetti",
text = {
"{X:dark_edition,C:red}" .. operator .. "2^x{C:red} Mult{} for every",
"{C:attention}food or Peppino Joker",
"in your possession",
"{C:inactive}(Currently {X:dark_edition,C:red}" .. operator .. "#1#{C:red} Mult{C:inactive})",
}
},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
cost = 50,
rarity = exotic,
blueprint_compat = true,
loc_vars = function(_, _, _)
return {vars = {2 ^ food_jokers_count()}}
end,
calculate = function(_, _, context)
local count = food_jokers_count()
if context.joker_main and count > 0 then
return {
colour = G.C.DARK_EDITION,
message = "^" .. 2 ^ count .. " Mult",
[Cryptid and "Emult_mod" or "xmult_mod"] = 2 ^ count,
}, true
end
end
}

373
src/main.lua Normal file
View file

@ -0,0 +1,373 @@
Jane = {
config = {
wee_sizemod = 1.25,
texture_pack = "default",
bans = {"j_cry_crustulum", "c_cry_hammerspace"}
},
}
Jane.misprintize = (Cryptid or {}).misprintize
Jane.misprintize_tbl = (Cryptid or {}).misprintize_tbl
Jane.misprinitze_val = (Cryptid or {}).misprintize_val
if not Jane.misprintize then
assert(SMODS.load_file("src/misprintize.lua"))()
end
function Jane.canuse()
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.DRAW_TO_HAND and
G.STATE ~= G.STATES.PLAY_TAROT)
end
function Jane.card_status_text(
card,
text,
xoffset,
yoffset,
colour,
size,
delay,
juice,
jiggle,
align,
sound,
volume,
pitch,
trig,
f
)
if (delay or 0) <= 0 then
if type(f) == "function" then
f(card)
end
attention_text({
text = text,
scale = size or 1,
hold = 0.7,
backdrop_colour = colour or (G.C.FILTER),
align = align or "bm",
major = card,
offset = {x = xoffset or 0, y = yoffset or (-0.05*G.CARD_H)}
})
if sound then
play_sound(sound, pitch or (0.9 + (0.2 * math.random())), volume or 1)
end
if juice then
if type(juice) == "table" then
card:juice_up(juice[1], juice[2])
elseif type(juice) == "number" and juice ~= 0 then
card:juice_up(juice, juice / 6)
end
end
if jiggle then
G.ROOM.jiggle = G.ROOM.jiggle + jiggle
end
else
G.E_MANAGER:add_event(Event({
trigger = trig,
delay = delay,
func = function()
if f and type(f) == "function" then
f(card)
end
attention_text({
text = text,
scale = size or 1,
hold = 0.7 + (delay or 0),
backdrop_colour = colour or (G.C.FILTER),
align = align or "bm",
major = card,
offset = {x = xoffset or 0, y = yoffset or (-0.05*G.CARD_H)}
})
if sound then
play_sound(sound, pitch or (0.9 + (0.2*math.random())), volume or 1)
end
if juice then
if type(juice) == "table" then
card:juice_up(juice[1], juice[2])
elseif type(juice) == "number" and juice ~= 0 then
card:juice_up(juice, juice / 6)
end
end
if jiggle then
G.ROOM.jiggle = G.ROOM.jiggle + jiggle
end
return true
end
}))
end
end
function Jane.empowered()
if Cryptid then
add_tag(Tag("tag_cry_empowered"))
return true
end
local card = create_card("Spectral", G.consumeables, nil, nil, nil, nil, "c_soul", "acceleration_soul")
card:set_edition({negative = true})
card:add_to_deck()
G.consumeables:emplace(card)
end
function Jane.get_chipmult_sum(chips, mult)
chips = chips or 0
mult = mult or 0
local op, big = Jane.get_operator(), Talisman.config_file.break_infinity == "omeganum" and
to_big or function (x) return x end
if op >= 3 then
return big(chips) ^ big(mult)
elseif op == 2 then
return big(chips) * big(mult)
end
return big(chips) + big(mult)
end
function Jane.get_operator()
if not G.GAME then
return 0
end
G.GAME.operator = G.GAME.operator or 2
return math.max(math.min(G.GAME.operator, 3), 1)
end
function Jane.hidden(card)
return G.GAME and not
G.GAME.obsidian and
type(card) == 'table' and
(card.name == "Black Hole" or card.name == "The Soul" or card.hidden)
end
function Jane.play_sound(sound, per, vol)
G.E_MANAGER:add_event(Event({
func = function()
play_sound(sound,per,vol)
return true
end
}))
end
function Jane.q(fc, de, t, tr, bl, ba)
G.E_MANAGER:add_event(Event({
timer = t,
trigger = tr,
delay = de,
blockable = bl,
blocking = ba,
func = fc
}))
end
function Card:gc()
return (self.config or {}).center or {}
end
function Card:norank()
return self.ability.name == "Stone Card" or self.config.center.no_rank
end
function Card:nosuit()
return self.ability.name == "Stone Card" or self.config.center.no_suit
end
local orig_debuff = Card.set_debuff
function Card:set_debuff(should_debuff)
if should_debuff and ((self.config or {}).center or {}).debuff_immune then
Jane.card_status_text(self, "Immune", nil, 0.05 * self.T.h, G.C.RED, nil, 0.6, nil, nil, "bm", "cancel", 1, 0.9)
return false
else
orig_debuff(self, should_debuff)
end
end
local orig_menu = Game.main_menu
function Game:main_menu(change_context)
Jane.sinister = nil
orig_menu(self, change_context)
end
local orig_update = Game.update
function Game:update(dt)
local function delete_hardbans()
if not Jane.config.disable_bans then
for _, v in pairs(Jane.config.bans) do
local e = SMODS.Center:get_obj(v)
if e then
e:delete()
end
end
end
end
local function hand(name, chip, mul, lv, notif, snd, vol, pit, de)
local config = {
delay = de or 0.3,
pitch = pit or 0.8,
volume = vol or 0.7,
sound = type(snd) == "string" and snd or type(snd) == "nil" and "button",
}
local vals = {
level = lv or "?",
mult = mul or "?",
StatusText = notif,
chips = chip or "?",
handname = name or "????",
}
update_hand_text(config, vals)
end
local function rebalance_orrery()
local function small(x)
return type(x) == "table" and x:to_number() or x
end
local orrery = (G.GAME or {}).orrery
if not orrery then
return
end
local count = 0
local hands = {}
local pools = {chips = 0, level = 0, mult = 0}
for k, v in pairs(G.GAME.hands) do
if k ~= "cry_WholeDeck" then
count = count + 1
hands[#hands + 1] = v
pools.mult = v.mult + pools.mult
pools.chips = v.chips + pools.chips
pools.level = v.level + pools.level
end
end
if pools.mult == orrery.mult and pools.chips == orrery.chips and pools.level == orrery.level then
return
end
table.sort(hands, function (x, y) return x.order > y.order end)
for i, v in ipairs(hands) do
v.mult = math.floor(pools.mult / count) + (small(pools.mult % count) >= i and 1 or 0)
v.chips = math.floor(pools.chips / count) + (small(pools.chips % count) >= i and 1 or 0)
v.level = math.floor(pools.level / count) + (small(pools.level % count) >= i and 1 or 0)
end
orrery.mult = pools.mult
orrery.chips = pools.chips
orrery.level = pools.level
hand(localize("k_all_hands"), math.floor(pools.chips / count), math.floor(pools.mult / count), math.floor(pools.level / count))
delay(1)
update_hand_text({sound = "button", volume = 0.7, pitch = 1.1, delay = 0}, {mult = 0, chips = 0, handname = "", level = ""})
end
local function hsv(h, s, v)
if s <= 0 then return v, v, v end
h = h * 6
local c = v * s
local x = (1 - math.abs((h % 2) - 1)) * c
local m, r, g, b = (v - c), 0, 0, 0
if h < 1 then
r, g, b = c, x, 0
elseif h < 2 then
r, g, b = x, c, 0
elseif h < 3 then
r, g, b = 0, c, x
elseif h < 4 then
r, g, b = 0, x, c
elseif h < 5 then
r, g, b = x, 0, c
else
r, g, b = c, 0, x
end
return r + m, g + m, b + m
end
orig_update(self, dt)
if not Jane.bans_done then
delete_hardbans()
Jane.bans_done = true
end
if (G.GAME or {}).banned_keys then
for _, v in pairs(Jane.config.bans) do
G.GAME.banned_keys[v] = true
end
end
if G.ARGS.LOC_COLOURS then
local r, g, b = hsv(self.C.jane_RGB_HUE / 360, .5, 1)
self.C.jane_RGB[1] = r
self.C.jane_RGB[3] = g
self.C.jane_RGB[2] = b
self.C.jane_RGB_HUE = (self.C.jane_RGB_HUE + 0.5) % 360
G.ARGS.LOC_COLOURS.jane_RGB = self.C.jane_RGB
end
rebalance_orrery()
end
local orig_card = SMODS.find_card
---@param key string
---@param count_debuffed true?
---@return Card[]|table[]
--- Returns all cards matching provided `key`.
function SMODS.find_card(key, count_debuffed)
return orig_card(key == "j_jen_saint" and "j_jane_saint" or key, count_debuffed)
end
SMODS.Atlas {
px = 34,
py = 34,
key = "modicon",
path = "almanac_avatar.png",
}
SMODS.Atlas {
px = 71,
py = 95,
key = "janeacc",
path = Jane.config.texture_pack .. "/c_jane_acc.png",
}
for _, v in ipairs({
"back",
"blind",
"booster",
"edition",
"joker",
"slugcat",
"spectral",
"tarot",
"token",
"voucher",
}) do
assert(SMODS.load_file("src/" .. v .. ".lua"))()
end

311
src/misprintize.lua Normal file
View file

@ -0,0 +1,311 @@
-- The following code is slightly adapted from
-- https://github.com/MathIsFun0/Cryptid/blob/main/lib/misprintize.lua
local base_values = {}
local big_num_whitelist = {
j_egg = true,
j_wee = true,
j_flash = true,
j_ramen = true,
j_glass = true,
j_caino = true,
j_cry_m = true,
j_runner = true,
j_square = true,
j_castle = true,
j_yorick = true,
j_madness = true,
j_vampire = true,
j_obelisk = true,
j_popcorn = true,
j_red_card = true,
j_hologram = true,
j_trousers = true,
j_campfire = true,
j_cry_whip = true,
j_ice_cream = true,
j_lucky_cat = true,
j_throwback = true,
j_cry_hugem = true,
j_cry_pickle = true,
j_cry_cursor = true,
j_cry_primus = true,
j_cry_mprime = true,
j_green_joker = true,
j_turtle_bean = true,
j_cry_wee_fib = true,
j_cry_jimball = true,
j_ride_the_bus = true,
j_hit_the_road = true,
j_cry_dropshot = true,
j_cry_fspinner = true,
j_cry_mondrian = true,
j_constellation = true,
j_cry_crustulum = true,
j_cry_spaceglobe = true,
j_cry_exponentia = true,
j_cry_chili_pepper = true,
j_cry_eternalflame = true,
j_cry_stella_mortis = true,
j_cry_krustytheclown = true,
j_cry_antennastoheaven = true,
}
local function check(v, is_big)
if is_big then
if not v or type(v) == "number" and (v ~= v or v > 1e300 or v < -1e300) then
v = 1e300
end
if type(v) == "table" then
return v
end
if v > 1e100 or v < -1e100 then
return to_big(v)
end
end
return not v or type(v) == "number" and (v ~= v or v > 1e300 or v < -1e300) and 1e300 or v
end
local function deep_copy(obj, seen)
if type(obj) ~= "table" then
return obj
end
if seen and seen[obj] then
return seen[obj]
end
local s = seen or {}
local res = setmetatable({}, getmetatable(obj))
s[obj] = res
for k, v in pairs(obj) do
res[deep_copy(k, s)] = deep_copy(v, s)
end
return res
end
local function is_card_big(joker)
local center = joker.config and joker.config.center
if not center then
return false
end
return big_num_whitelist[center.key or "Nope!"]
end
local function log_random(seed, min, max)
math.randomseed(seed)
local lmin = math.log(min, 2.718281828459045)
local lmax = math.log(max, 2.718281828459045)
local poll = math.random() * (lmax - lmin) + lmin
return math.exp(poll)
end
function Jane.misprintize_val(val, override, big)
local function format(number, str)
return math.abs(to_big(number)) >= to_big(1e300) and number
or tonumber(str:format((Big and to_number(to_big(number)) or number)))
end
return is_number(val) and check(
format(
val * log_random(
pseudoseed("cry_misprint" .. G.GAME.round_resets.ante),
override and override.min or G.GAME.modifiers.cry_misprint_min,
override and override.max or G.GAME.modifiers.cry_misprint_max
),
"%.2g"
),
big
) or val
end
function Jane.misprintize_tbl(name, ref_tbl, ref_value, clear, override, stack, big)
if name and ref_tbl and ref_value then
local tbl = deep_copy(ref_tbl[ref_value])
for k, v in pairs(tbl) do
if (type(tbl[k]) ~= "table") or is_number(tbl[k]) then
if is_number(tbl[k]) and not
(k == "id") and not
(k == "colour") and not
(k == "suit_nominal") and not
(k == "base_nominal") and not
(k == "face_nominal") and not
(k == "qty") and not
(k == "x_mult" and v == 1 and not tbl.override_x_mult_check) and not
(k == "selected_d6_face") then
if not base_values[name] then
base_values[name] = {}
end
if not base_values[name][k] then
base_values[name][k] = tbl[k]
end
tbl[k] = check(
clear and base_values[name][k]
or cry_format(
(stack and tbl[k] or base_values[name][k])
* log_random(
pseudoseed("cry_misprint" .. G.GAME.round_resets.ante),
override and override.min or G.GAME.modifiers.cry_misprint_min,
override and override.max or G.GAME.modifiers.cry_misprint_max
),
"%.2g"
),
big
)
end
else
for _k, _ in pairs(tbl[k]) do
if is_number(tbl[k][_k]) and not
(_k == "id") and not
(k == "perish_tally") and not
(k == "colour") and not
(_k == "suit_nominal") and not
(_k == "base_nominal") and not
(_k == "face_nominal") and not
(_k == "qty") and not
(k == "x_mult" and v == 1 and not tbl[k].override_x_mult_check) and not
(_k == "selected_d6_face") then
if not base_values[name] then
base_values[name] = {}
end
if not base_values[name][k] then
base_values[name][k] = {}
end
if not base_values[name][k][_k] then
base_values[name][k][_k] = tbl[k][_k]
end
tbl[k][_k] = check(
clear and base_values[name][k][_k]
or cry_format(
(stack and tbl[k][_k] or base_values[name][k][_k]) *
log_random(
pseudoseed("cry_misprint" .. G.GAME.round_resets.ante),
override and override.min or G.GAME.modifiers.cry_misprint_min,
override and override.max or G.GAME.modifiers.cry_misprint_max
),
"%.2g"
),
big
)
end
end
end
end
ref_tbl[ref_value] = tbl
end
end
function Jane.misprintize(card, override, force_reset, stack)
local function no(self, m, no_no)
if no_no then
if self.infinifusion then
for i = 1, #self.infinifusion do
if G.P_CENTERS[self.infinifusion[i].key][m] or
(G.GAME and G.GAME[m] and G.GAME[m][self.infinifusion[i].key]) then
return true
end
end
return false
end
if not self.config then
return G.P_CENTERS[self.key][m] or (G.GAME and G.GAME[m] and G.GAME[m][self.key])
end
return self.config.center[m] or (G.GAME and G.GAME[m] and G.GAME[m][self.config.center_key]) or false
end
return no(self, "no_" .. m, true)
end
if no(card, "immutable", true) then
force_reset = true
end
if card.infinifusion then
if card.config.center == card.infinifusion_center or card.config.center.key == "j_infus_fused" then
---@diagnostic disable-next-line: undefined-global
calculate_infinifusion(card, nil, function(i)
Jane.misprintize(card, override, force_reset, stack)
end)
end
end
if (not force_reset or G.GAME.modifiers.cry_jkr_misprint_mod) and
(G.GAME.modifiers.cry_misprint_min or override or card.ability.set == "Joker") and not
stack or not
no(card, "immutable", true) then
if card.ability.name == "Ace Aequilibrium" then
return
end
if G.GAME.modifiers.cry_jkr_misprint_mod and card.ability.set == "Joker" then
if not override then
override = {}
end
override.min = override.min or G.GAME.modifiers.cry_misprint_min or 1
override.max = override.max or G.GAME.modifiers.cry_misprint_max or 1
override.min = override.min * G.GAME.modifiers.cry_jkr_misprint_mod
override.max = override.max * G.GAME.modifiers.cry_jkr_misprint_mod
end
if G.GAME.modifiers.cry_misprint_min or override and override.min then
Jane.misprintize_tbl(
card.config.center_key,
card,
"ability",
nil,
override,
stack,
is_card_big(card)
)
if card.base then
Jane.misprintize_tbl(
card.config.card_key,
card,
"base",
nil,
override,
stack,
is_card_big(card)
)
end
end
if G.GAME.modifiers.cry_misprint_min then
card.misprint_cost_fac = 1 /
log_random(
pseudoseed("cry_misprint" .. G.GAME.round_resets.ante),
override and override.min or G.GAME.modifiers.cry_misprint_min,
override and override.max or G.GAME.modifiers.cry_misprint_max
)
card:set_cost()
end
else
Jane.misprintize_tbl(card.config.center_key, card, "ability", true, nil, nil, is_card_big(card))
end
if card.ability.consumeable then
for k, _ in pairs(card.ability.consumeable) do
card.ability.consumeable[k] = deep_copy(card.ability[k])
end
end
end

606
src/slugcat.lua Normal file
View file

@ -0,0 +1,606 @@
SMODS.Sound({key = "enlightened", path = "enlightened.ogg"})
SMODS.Sound({key = "warning_heartbeat", path = "warning_heartbeat.ogg"})
for i = 1, 8 do
SMODS.Sound({key = "gore" .. i, path = "gore" .. i .. ".ogg"})
end
for _, v in pairs({
"artificer",
"hunter",
"gourmand",
"monk",
"rivulet",
"rot",
"saint",
"spearmaster",
"survivor",
}) do
SMODS.Atlas {
key = "jane" .. v,
px = 71,
py = 95,
path = Jane.config.texture_pack .. "/j_jane_" .. v .. ".png"
}
end
local epic = Cryptid and "cry_epic" or 3
local exotic = Cryptid and "cry_exotic" or 4
SMODS.Rarity {
key = "junk",
loc_txt = {name = "Junk"},
badge_colour = G.C.JOKER_GREY,
}
SMODS.Joker {
key = "spearmaster",
atlas = "janespearmaster",
loc_txt = {
name = "The Spearmaster",
text = {
"You can choose {C:attention}any number of cards",
"after opening {C:attention}any Booster Pack",
"{C:attention}Booster Packs{} have {C:green}+#1#{} additional cards",
}
},
loc_vars = function(_, _, center)
return {vars = {center.ability.extra.extrachoices}}
end,
config = {extra = {extrachoices = 1}},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
cost = 12,
rarity = epic,
}
local orig_open = Card.open
---@diagnostic disable-next-line: duplicate-set-field
function Card:open()
local orig = self.ability.extra or 1
local spearmasters = SMODS.find_card("j_jane_spearmaster")
if next(spearmasters) then
for _, v in pairs(spearmasters) do
orig = orig + v.ability.extra.extrachoices
end
self.config.choose = math.floor(orig)
self.ability.extra = math.floor(orig)
end
orig_open(self)
G.E_MANAGER:add_event(Event({
delay = 0.5,
timer = "REAL",
func = function()
if next(spearmasters) then
G.GAME.pack_choices = math.floor(self.ability.extra)
end
return true
end
}))
end
SMODS.Joker {
key = "survivor",
atlas = "janesurvivor",
loc_txt = {
name = "The Survivor",
text = {
"All cards held in hand",
"{C:attention}contribute to scoring",
}
},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
cost = 20,
rarity = 4,
}
SMODS.Joker {
key = "monk",
atlas = "janemonk",
loc_txt = {
name = "The Monk",
text = {
"{C:attention}Retrigger{} scored cards",
"{C:attention}<card's rank> {}times",
"{C:inactive}(ex. 9 = 9 times, Ace = 11 times)",
}
},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
cost = 12,
rarity = 4,
blueprint_compat = true,
calculate = function(_, card, context)
if context.repetition and
context.cardarea == G.play and
context.other_card and
context.other_card.ability.name ~= "Stone Card" then
local id = context.other_card:get_id()
return {
message = localize("k_again_ex"),
repetitions = id == 14 and 11 or math.min(id, 10),
colour = G.C.ORANGE,
card = card
}, true
end
end
}
local hunter = { 7, 5, 3, 2, 1 }
SMODS.Joker {
key = "hunter",
atlas = "janehunter",
loc_txt = {
name = "The Hunter",
text = {
"{C:blue}Provides infinite hands",
"{C:red,s,E:1}Succumbs to the Rot {}and creates",
(Cryptid and "an {C:spectral}Empowered Tag" or "a {C:dark_edition}Negative {C:spectral}Soul") .. " {}after #1#",
"When {C:attention}sold#2#{}, turns#3#",
"#4#{C:red}The Rot{} without rewards",
}
},
config = {rounds_left = hunter[1]},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
cost = 20,
rarity = 4,
loc_vars = function(_, _, center)
local function rounds(amount)
return " round" .. ((math.abs(amount) > 1 or math.abs(amount) == 0) and "s" or "")
end
local rounds_left = center.ability.rounds_left
local sold = rounds_left - hunter[2]
return {vars = {
rounds_left .. rounds(rounds_left) .. (rounds_left <= 0 and "...?" or ""),
sold <= 0 and "" or " after " .. sold .. rounds(sold),
sold <= 0 and " into" or "",
sold <= 0 and "" or "into ",
}}
end,
update = function(_, card, _)
if card.added_to_deck and card.children.center and card.children.floating_sprite then
for k, v in ipairs(hunter) do
if card.ability.rounds_left <= v then
card.children.center:set_sprite_pos({x = 0, y = k - 1})
card.children.floating_sprite:set_sprite_pos({x = 1, y = k - 1})
else
break
end
end
end
end,
calculate = function(_, card, context)
local function spawn_rot()
card:flip()
card:juice_up(2, 0.8)
Jane.card_status_text(card, "Dead!", nil, 0.05 * card.T.h, G.C.BLACK, 2, 0, 0, nil, "bm", "jane_gore6")
Jane.q(function()
local card2 = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_jane_rot", "hunter_rot_death")
card2:add_to_deck()
G.jokers:emplace(card2)
card:set_eternal(nil)
card2:set_eternal(true)
play_sound("jane_gore5")
return true
end)
end
local function die()
spawn_rot()
Jane.q(function()
Jane.empowered()
return true
end, 0.1)
Jane.q(function()
card:start_dissolve()
return true
end, 1)
return true
end
if context.blueprint then
return
end
if context.selling_self and card.ability.rounds_left <= hunter[2] then
spawn_rot()
elseif not context.individual and not context.repetition and not context.retrigger_joker then
if G.GAME.round_resets.hands <= 0 then
G.GAME.round_resets.hands = 1
end
if not card.hunter_prep then
card.hunter_prep = true
Jane.q(function()
Jane.q(function()
card.hunter_prep = nil
if G.GAME.current_round.hands_left < G.GAME.round_resets.hands then
card.ability.hands_replenished =
(card.ability.hands_replenished or 0) +
(G.GAME.round_resets.hands - G.GAME.current_round.hands_left)
ease_hands_played(G.GAME.round_resets.hands - G.GAME.current_round.hands_left)
end
return true
end)
return true
end)
end
if not context.end_of_round then
return
end
card.hunter_prep = nil
card.ability.rounds_left = card.ability.rounds_left - 1
local rl = card.ability.rounds_left
Jane.card_status_text(card, tostring(card.ability.rounds_left), nil, nil, G.C.RED, nil, nil, nil, nil, nil, "generic1")
if rl > hunter[2] then
card:juice_up(0.6, 0.1)
elseif rl > hunter[3] then
if rl == hunter[2] then
Jane.play_sound("jane_gore1")
end
card:juice_up(0.6, 0.1)
elseif rl > hunter[4] then
if rl == hunter[3] then
Jane.play_sound("jane_gore3")
end
card:juice_up(0.6, 0.1)
elseif rl > hunter[5] then
if rl == hunter[4] then
Jane.play_sound("jane_gore8")
end
card:juice_up(0.6, 0.1)
Jane.play_sound("jane_warning_heartbeat")
elseif rl > 0 then
if rl == hunter[5] then
Jane.play_sound("jane_gore4")
end
card:juice_up(1.8, 0.3)
Jane.play_sound("jane_warning_heartbeat")
else
card:juice_up(2, 0.8)
Jane.play_sound("jane_warning_heartbeat")
G.E_MANAGER:add_event(Event({trigger = "after", func = die}))
end
end
end
}
SMODS.Joker {
key = "gourmand",
atlas = "janegourmand",
loc_txt = {
name = "The Gourmand",
text = {
"Values on {C:attention}consumables",
"are {C:attention}multiplied{} by {C:attention}#1#",
"when they are created",
"{C:inactive}(If possible)",
}
},
loc_vars = function(_, _, center)
return {vars = {center.ability.modifier}}
end,
config = {modifier = 2},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
cost = 50,
rarity = 4,
}
local orig_set_ability = Card.set_ability
---@diagnostic disable-next-line: duplicate-set-field
function Card:set_ability(center, initial, delay_sprites)
orig_set_ability(self, center, initial, delay_sprites)
if next(SMODS.find_card("j_jane_gourmand")) and
self.gc and
self:gc().key ~= "c_base" and
string.sub(self:gc().key, 1, 2) == "c_" then
local mod = 1
for _, v in pairs(SMODS.find_card("j_jane_gourmand")) do
mod = mod * v.ability.modifier
end
Jane.misprintize(self, {min = mod, max = mod}, nil, true)
end
end
SMODS.Joker {
key = "rivulet",
atlas = "janerivulet",
loc_txt = {
name = "The Rivulet",
text = {
"Non-{C:dark_edition}editioned{} cards are",
"{C:attention}given a random {C:dark_edition}Edition",
}
},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
cost = 20,
rarity = 4,
}
local orig_draw = Card.draw
function Card:draw(layer)
local cen = self.gc and self:gc()
if self.children.floating_sprite and cen.sinis then
if Jane.sinister and not self.shows_sinister then
self.shows_sinister = true
self.children.floating_sprite:set_sprite_pos(cen.sinis)
elseif not Jane.sinister and self.shows_sinister then
self.shows_sinister = nil
self.children.floating_sprite:set_sprite_pos(cen.soul_pos)
end
if self.shows_sinister then
self:juice_up(0, math.random())
end
end
if cen and
self.facing == "front" and
self.config and
(self.added_to_deck or
(self.area and self.area == G.hand)) and not
self.edition and
(self.area == G.consumeables or cen.set ~= "Booster") and
next(SMODS.find_card("j_jane_rivulet")) then
self:set_edition({[G.P_CENTER_POOLS.Edition[pseudorandom("rivulet_edition", 1, #G.P_CENTER_POOLS.Edition)].key:sub(3)] = true}, true)
end
orig_draw(self, layer)
end
local attunement = Cryptid and 1.001 or 1.1
SMODS.Joker {
key = "saint",
atlas = "janesaint",
loc_txt = {
name = "The Saint{C:jane_RGB}#1#",
text = {
"{C:spectral}Gateway {}will {C:attention}not destroy Jokers",
"{C:jane_RGB}#2#{}#3#{X:black,C:jane_RGB,s:1.5}#4#{C:spectral}#5#{C:chips}#6#{}#7#{C:mult}#8#",
"{C:inactive,s:1.25}#9#{C:attention,s:1.25}#10#{C:inactive,s:1.25}#11#{C:inactive}#12#"
}
},
config = {extra = {karma = 0, max_karma = Cryptid and 10 or 3}},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
cost = 20,
rarity = 4,
blueprint_compat = true,
loc_vars = function(_, _, card)
local extra = card.ability.extra
local karma = extra.karma
local max_karma = extra.max_karma
local attuned = karma >= max_karma
return {vars = {
attuned and " (Attuned)" or "",
attuned and "" or "Attune ",
attuned and "" or "after using ",
attuned and (Cryptid and "^^" or "^") .. attunement or max_karma,
attuned and "" or (Cryptid and " Gateways" or " Souls"),
attuned and " Chips " or "",
attuned and "& " or "",
attuned and "Mult" or "",
attuned and "" or "[",
attuned and "" or karma,
attuned and "" or " / " .. max_karma .. "]",
attuned and "(Cannot be debuffed)" or "",
}}
end,
update = function(_, card, _)
if card.added_to_deck and card.children.center and card.children.floating_sprite then
local extra = card.ability.extra
local karma = extra.karma
local max_karma = extra.max_karma
local is_attuned = karma >= max_karma
card.children.floating_sprite:set_sprite_pos({x = is_attuned and 2 or 1, y = 0})
end
end,
calculate = function(_, card, context)
local extra = card.ability.extra
local max_karma = extra.max_karma
if extra.karma >= max_karma then
if card.ability then
card.ability.perishable = false
card.ability.perish_tally = 1e9
end
card.debuff = false
if not context.joker_main then
return
end
return {
card = card,
colour = G.C.jane_RGB,
sound = "talisman_eeechip",
message = "^^" .. attunement .. " Chips & Mult",
[Cryptid and "EEchip_mod" or "Echip_mod"] = attunement,
[Cryptid and "EEmult_mod" or "Emult_mod"] = attunement,
}, true
end
if not (not context.blueprint and
(not context.retrigger_joker_check and not context.retrigger_joker) and
context.using_consumeable and
context.consumeable and
context.consumeable:gc().key == Cryptid and "c_cry_gateway" or "c_soul") then
return
end
local quota = context.consumeable:getEvalQty()
extra.karma = extra.karma + quota
card_eval_status_text(
card,
"extra",
nil,
nil,
nil,
{message = "+" .. quota .. " Karma", colour = G.C.PALE_GREEN}
)
card_eval_status_text(
card,
"extra",
nil,
nil,
nil,
{message = extra.karma .. " / " .. max_karma, colour = G.C.GREEN}
)
if extra.karma < max_karma then
return
end
Jane.card_status_text(
card,
"!!!",
nil,
0.05 * card.T.h,
G.C.DARK_EDITION,
0.6,
0.6,
2,
2,
"bm",
"jane_enlightened"
)
G.E_MANAGER:add_event(Event({
delay = 0.1,
func = function()
card:flip()
play_sound("card1")
return true
end
}))
G.E_MANAGER:add_event(Event({
delay = 1,
func = function()
card:flip()
card:juice_up(1, 1)
play_sound("card1")
return true
end
}))
end
}
SMODS.Joker {
key = "artificer",
atlas = "janeartificer",
loc_txt = {
name = "The Artificer",
text = {
"Grants the {C:green}ability{} to {C:red}destroy",
"selected {C:attention}playing cards",
}
},
misc = {
text = {"Impervious", "Cannot be debuffed"},
col = G.C.JOKER_GREY,
tcol = G.C.FILTER
},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
cost = 50,
rarity = exotic,
Bakery_use_button_text = function(_, _) return "DESTROY" end,
Bakery_can_use = function(_, _)
return Jane.canuse() and next(G.hand.highlighted)
end,
Bakery_use_joker = function(_, _)
for _, v in pairs(G.hand.highlighted) do
v:start_dissolve()
end
end,
}
SMODS.Joker {
key = "rot",
atlas = "janerot",
loc_txt = {
name = "The Rot",
text = {
"Clogs up your Joker slots",
"{C:attention}Duplicates itself{} at the",
"end of {C:attention}every ante",
}
},
pos = {x = 0, y = 0},
soul_pos = {x = 1, y = 0},
cost = 0,
rarity = "jane_junk",
in_pool = function (_, _)
return not not next(SMODS.find_card("j_jane_rot"))
end,
calculate = function(_, card, context)
local function has_room()
return G.jokers.config.card_count < G.jokers.config.card_limit
end
local function is_end_of_ante()
return not context.individual and not
context.repetition and not
card.debuff and
context.end_of_round and not
context.blueprint and
G.GAME.blind.boss and not
(G.GAME.blind.config and G.GAME.blind.config.bonus)
end
local function spawn()
local text = context.selling_self and "..!" or "..."
local rot = copy_card(card)
rot.cloned = true
rot:add_to_deck()
G.jokers:emplace(rot)
Jane.card_status_text(rot, text, nil, 0.05 * card.T.h, G.C.BLACK, 3, 0, 0, nil, "bm")
end
if has_room() and not card.cloned and is_end_of_ante() then
spawn()
else
card.cloned = false
end
end
}

354
src/spectral.lua Normal file
View file

@ -0,0 +1,354 @@
SMODS.Atlas {
key = "janertarots",
px = 71,
py = 95,
path = Jane.config.texture_pack .. "/c_jane_reversetarots.png"
}
SMODS.Sound({key = "draw", path = "draw.ogg"})
local function conjure(card, number)
for _ = 1, math.min(
math.ceil(card.ability.extra.spectrals) * number,
G.consumeables.config.card_limit - #G.consumeables.cards
) do
G.E_MANAGER:add_event(Event({
trigger = "after",
delay = 0.4,
func = function()
if G.consumeables.config.card_limit <= #G.consumeables.cards then
return true
end
play_sound("jane_draw")
local spectral = create_card("Spectral", G.consumeables, nil, nil, nil, nil, nil, "pri")
spectral:add_to_deck()
G.consumeables:emplace(spectral)
card:juice_up(0.3, 0.5)
return true
end
}))
end
end
local function createfulldeck(enhancement, edition, amount, emplacement)
local cards = {}
for _, v in pairs(G.P_CARDS) do
for i = 1, (amount or 1) do
G.E_MANAGER:add_event(Event({
delay = 0.1,
func = function()
cards[i] = true
G.playing_card = (G.playing_card and G.playing_card + 1) or 1
local card = Card(
G.play.T.x + G.play.T.w / 2,
G.play.T.y, G.CARD_W, G.CARD_H,
v,
enhancement or G.P_CENTERS.c_base,
{playing_card = G.playing_card}
)
if edition then
card:set_edition(type(edition) == "table" and edition or {[edition] = true}, true, true)
end
play_sound("card1")
table.insert(G.playing_cards, card)
card:add_to_deck()
if emplacement then
emplacement:emplace(card)
else
G.deck:emplace(card)
end
return true
end
}))
end
end
Jane.q(function()
if next(cards) then
playing_card_joker_effects(cards)
end
return true
end)
Jane.q(function()
cards = nil
return true
end)
end
local function randomize(targets, noanim)
if #targets <= 0 then
return
end
if noanim then
for i=1, #targets do
local card = targets[i]
card:set_base(pseudorandom_element(G.P_CARDS))
card:set_ability(
pseudorandom(pseudoseed("chancetime")) > 1 / (#G.P_CENTER_POOLS["Enhanced"] + 1) and
pseudorandom_element(G.P_CENTER_POOLS["Enhanced"], pseudoseed("spectral_chance")) or
G.P_CENTERS["c_base"]
)
local edition_rate = 2
card:set_edition(poll_edition("standard_edition" .. G.GAME.round_resets.ante, edition_rate, true), true, true)
local seal_rate = 10
local seal_poll = pseudorandom(pseudoseed("stdseal" .. G.GAME.round_resets.ante))
if seal_poll > 1 - 0.02 * seal_rate then
local seal_type = pseudorandom(pseudoseed("stdsealtype" .. G.GAME.round_resets.ante))
local seal_list = {}
for k, _ in pairs(G.P_SEALS) do
table.insert(seal_list, k)
end
seal_type = math.floor(seal_type * #seal_list)
card:set_seal(seal_list[seal_type], true, true)
else
card:set_seal(nil, true, true)
end
card:juice_up(0.3, 0.3)
end
else
for i = 1, #targets do
local percent = 1.15 - (i - 0.999) / (#G.hand.cards - 0.998) * 0.3
G.E_MANAGER:add_event(Event({
trigger = "after",
delay = 0.15,
func = function()
targets[i]:flip()
play_sound("card1", percent)
targets[i]:juice_up(0.3, 0.3)
return true
end
}))
end
delay(0.2)
for i = 1, #targets do
local percent = 0.85 + (i - 0.999) / (#G.hand.cards - 0.998) * 0.3
G.E_MANAGER:add_event(Event({
trigger = "after",
delay = 0.1,
func = function()
local card = targets[i]
card:set_base(pseudorandom_element(G.P_CARDS))
card:set_ability(pseudorandom_element(G.P_CENTER_POOLS["Enhanced"]))
local edition_rate = 2
card:set_edition(poll_edition("standard_edition" .. G.GAME.round_resets.ante, edition_rate, true))
local seal_rate = 10
local seal_poll = pseudorandom(pseudoseed("stdseal" .. G.GAME.round_resets.ante))
if seal_poll > 1 - 0.02 * seal_rate then
local seal_type = pseudorandom(pseudoseed("stdsealtype" .. G.GAME.round_resets.ante))
local seal_list = {}
for k, _ in pairs(G.P_SEALS) do
table.insert(seal_list, k)
end
seal_type = math.floor(seal_type * #seal_list)
card:set_seal(seal_list[seal_type])
else
card:set_seal()
end
card:flip()
play_sound("card3", percent, 0.6)
card:juice_up(0.3, 0.3)
return true
end
}))
end
end
end
SMODS.Consumable {
key = "obfuscation",
atlas = "janeacc",
set = "Spectral",
loc_txt = {
name = "Obfuscation",
text = {
"{C:green,E:1}Randomises{} all cards in hand",
"{C:inactive}(Rank, seal, edition,",
"{C:inactive}enhancement, and suit)",
}
},
pos = {x = 0, y = 4},
cost = 4,
can_use = function(_, _)
return Jane.canuse() and #((G.hand or {}).cards or {}) > 0
end,
use = function(_, card, _, _)
G.E_MANAGER:add_event(Event({
trigger = "after",
delay = 0.4,
func = function()
play_sound("tarot1")
card:juice_up(0.3, 0.5)
return true
end
}))
randomize(G.hand.cards)
delay(0.5)
end
}
SMODS.Consumable {
key = "conjure",
set = "Spectral",
atlas = "janeacc",
loc_txt = {
name = "Conjure",
text = {
"Creates {C:attention}#1#",
"{C:spectral}Spectral{} cards",
"{C:inactive}(Must have room)",
}
},
config = {extra = {spectrals = 2}},
pos = {x = 2, y = 4},
cost = 4,
loc_vars = function(self, info_queue, center)
return {vars = {math.ceil(center.ability.extra.spectrals)}}
end,
can_use = Jane.canuse,
use = function(_, card, _, _)
conjure(card, 1)
delay(0.6)
end,
bulk_use = function(_, card, _, _, number)
conjure(card, number)
delay(0.6)
end
}
SMODS.Consumable {
key = "shadows",
set = "Spectral",
atlas = "janeacc",
loc_txt = {
name = "Shadows",
text = {
"Create {C:attention}#1#{} {C:green}random {C:dark_edition}Negative",
"{C:attention}Perishable {C:attention}Jokers{}, set",
"{C:money}sell value{} of {C:attention}all Jokers{} to {C:money}$0",
}
},
config = {extra = {shadows = 2}},
pos = {x = 3, y = 4},
cost = 4,
loc_vars = function(_, _, center)
return {vars = {((center.ability or {}).extra or {}).shadows or 2}}
end,
can_use = Jane.canuse,
use = function(_, card, _, _)
for _ = 1, card.ability.extra.shadows do
local joker = create_card("Joker", G.jokers, nil, nil, nil, nil, nil, "phantom")
joker:set_edition({negative = true})
joker.ability.eternal = false
joker.ability.perishable = true
joker.ability.perish_tally = 5
joker:add_to_deck()
G.jokers:emplace(joker)
end
delay(0.6)
for i = 1, #G.jokers.cards do
G.jokers.cards[i].base_cost = 0
G.jokers.cards[i].extra_cost = 0
G.jokers.cards[i].cost = 0
G.jokers.cards[i].sell_cost = 0
G.jokers.cards[i].sell_cost_label = G.jokers.cards[i].facing == "back" and "?" or G.jokers.cards[i].sell_cost
end
end,
bulk_use = function(_, card, _, _, number)
for _ = 1, card.ability.extra.shadows * number do
local joker = create_card("Joker", G.jokers, nil, nil, nil, nil, nil, "phantom")
joker.no_forced_edition = true
joker:set_edition({negative = true})
joker.no_forced_edition = nil
joker.ability.eternal = false
joker.ability.perishable = true
joker.ability.perish_tally = 5
joker:add_to_deck()
G.jokers:emplace(joker)
end
delay(0.6)
for i = 1, #G.jokers.cards do
G.jokers.cards[i].base_cost = 0
G.jokers.cards[i].extra_cost = 0
G.jokers.cards[i].cost = 0
G.jokers.cards[i].sell_cost = 0
G.jokers.cards[i].sell_cost_label = G.jokers.cards[i].facing == "back" and "?" or G.jokers.cards[i].sell_cost
end
end
}
SMODS.Consumable {
key = "reverse_hanged_man",
atlas = "janertarots",
set = "Spectral",
loc_txt = {
name = "Zen",
text = {
"{C:attention}Reset{} your deck to",
"a {C:attention}standard 52-card deck"
}
},
config = {extra = {destruction = 0.5}},
pos = {x = 7, y = 1},
cost = 15,
loc_vars = function(_, _, center)
return {vars = {
math.min(100, center.ability.extra.destruction * 100),
math.ceil(#(G.playing_cards or {}) * center.ability.extra.destruction)
}}
end,
can_use = Jane.canuse,
use = function(_, _, _, _)
Jane.q(function()
for _, v in pairs(G.playing_cards) do
v:start_dissolve()
end
return true
end)
local function realdelay(time, queue)
G.E_MANAGER:add_event(Event({
trigger = "after",
timer = "REAL",
delay = time or 1,
func = function()
return true
end
}), queue)
end
realdelay(1)
createfulldeck()
end
}

63
src/tarot.lua Normal file
View file

@ -0,0 +1,63 @@
local blurbs = {
"Hey! Pick me!",
"You wouldn't say no to a free negative me, would you?",
"Sometimes, an extra four mult goes a long way!",
"I won't take up space, I promise!",
"Don't ask how I ended up in a tarot!",
"Hee-hee, hoo-hoo!",
"Who knew even fortunes could be a circus act?",
"Looks like the joke is on the crystal globe!",
"It's a little cramped in this tarot...!",
"Ouch, I think the joke is on me!",
"Looks like the joke is on you!",
"I'm not just a clown; I'm the whole circus!",
"Seems a little suspicious for a jolly old fella like me to be in this card...",
"I can't help if I'm still in this silly old card, break me out!",
"Let me tell you, you'd love the show going on in this tarot!",
"I'd give you more tickets to JimCon, but I'm fresh out.",
"I've heard of a round buffoon that lives in a pretty funky town...",
"I can't give four mult if I'm still in this card!",
"I'm rooting for you! Even if it means I'll never get out of this card...",
"Who knew I'd have access to a great show? That show being you!",
"The stakes are only gonna rise here!",
"Juggling is one of my favourite passtimes!",
"I wonder what's the deal with pairs?",
"You don't need to understand math to enjoy watching the digits climb!",
"You should meet my friend Joseph; he's stuck in a Planet card!",
"M!",
}
SMODS.Consumable {
key = "jokerinatarot",
atlas = "janeacc",
set = "Tarot",
loc_txt = {
name = "Joker-in-a-Tarot",
text = {
"Create a {C:dark_edition}Negative {C:attention}default Joker",
"{C:inactive,E:1}#1#{}"
}
},
loc_vars = function(_, _, _)
return {vars = {blurbs[math.random(#blurbs)]}}
end,
pos = {x = 0, y = 1},
cost = 3,
can_use = Jane.canuse,
use = function(_, _, _, _)
local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_joker", "jokerfromatarot")
card:set_edition({negative = true}, true)
card.cost = 1
card.base_cost = 1
card.sell_cost = 1
card.extra_cost = 0
card.sell_cost_label = card.facing == "back" and "?" or card.sell_cost
card:add_to_deck()
G.jokers:emplace(card)
end,
bulk_use = function(self, card, area, copier, number)
for _ = 1, number do
self:use(card, area, copier)
end
end
}

86
src/token.lua Normal file
View file

@ -0,0 +1,86 @@
SMODS.Atlas {
key = "janetokens",
px = 71,
py = 95,
path = Jane.config.texture_pack .. "/c_jane_tokens.png"
}
SMODS.Sound({key = "e_gilded", path = "e_gilded.ogg"})
SMODS.ConsumableType {
key = "jane_tokens",
default = "c_jane_token_tag_standard",
loc_txt = {
collection = "Tokens",
name = "Token"
},
shop_rate = 0,
collection_rows = {6, 6},
primary_colour = G.C.CHIPS,
secondary_colour = G.C.VOUCHER,
}
for _, v in pairs({
{"tag_standard", "Standard", 0, 0, 3},
{"tag_charm", "Charm", 1, 0, 5},
{"tag_meteor", "Meteor", 2, 0, 5},
{"tag_ethereal", "Ethereal", 3, 0, 5},
{"tag_buffoon", "Buffoon", 4, 0, 8},
{"tag_cry_bundle", "Bundle", 1, 1, 10},
{"tag_uncommon", "Uncommon", 2, 1, 3},
{"tag_rare", "Rare", 3, 1, 5},
{"tag_cry_epic", "Epic", 4, 1, 8},
{"tag_foil", "Foil", 1, 3, 3},
{"tag_holo", "Holographic", 2, 3, 4},
{"tag_polychrome", "Polychrome", 3, 3, 5},
{"tag_negative", "Negative", 4, 3, 10},
{"tag_investment", "Investment", 0, 3, 8},
{"tag_voucher", "Voucher", 4, 5, 5},
{"tag_handy", "Handy", 1, 5, 8},
{"tag_garbage", "Garbage", 0, 5, 6},
{"tag_coupon", "Coupon", 4, 4, 10},
{"tag_juggle", "Juggle", 2, 5, 2},
{"tag_d_six", "Dice", 0, 4, 2},
{"tag_top_up", "Top-up", 2, 4, 2},
{"tag_skip", "Speed", 4, 2, 7},
{"tag_economy", "Economy", 5, 2, 10},
{"tag_double", "Double", 0, 2, 6},
{"tag_cry_triple", "Triple", 1, 2, 8},
{"tag_cry_quadruple", "Quadruple", 2, 2, 10},
{"tag_cry_quintuple", "Quintuple", 3, 2, 13},
{"tag_cry_memory", "Memory", 5, 4, 8}
}) do
if Cryptid or v[1]:sub(1, 7) ~= "tag_cry" then
SMODS.Consumable {
key = "token_" .. v[1],
set = "jane_tokens",
atlas = "janetokens",
loc_txt = {
name = v[2] .. " Token",
text = {
"Use to create a",
"{C:attention}" .. v[2] .. " Tag",
},
},
pos = {x = v[3], y = v[4]},
cost = v[5],
can_stack = true,
can_divide = true,
can_use = Jane.canuse,
in_pool = function (_, _)
return G.GAME.used_vouchers.v_jane_token_voucher
end,
use = function(_, _, _, _)
play_sound("jane_e_gilded", 1.25, 0.4)
add_tag(Tag(v[1]))
end,
bulk_use = function(_, _, _, _, number)
play_sound("jane_e_gilded", 1.25, 0.4)
for _ = 1, number do
add_tag(Tag(v[1]))
end
end
}
end
end

23
src/voucher.lua Normal file
View file

@ -0,0 +1,23 @@
SMODS.Atlas {
key = "janetokenvoucher",
px = 71,
py = 95,
path = Jane.config.texture_pack .. "/v_jane_token_voucher.png"
}
SMODS.Voucher {
key = "token_voucher",
atlas = "janetokenvoucher",
loc_txt = {
name = "Token Voucher",
text = {
"{C:attention}Tokens {}can appear",
"in the shop",
}
},
pos = {x = 0, y = 0},
cost = 15,
redeem = function (_, _)
G.GAME['jane_tokens_rate'] = 1.5
end
}