├── README.md ├── assets ├── 1x │ ├── Partners.png │ └── icon.png └── 2x │ ├── Partners.png │ └── icon.png ├── config.lua ├── localization ├── en-us.lua └── zh_CN.lua ├── lovely.toml ├── manifest.json └── partner.lua /README.md: -------------------------------------------------------------------------------- 1 | # Partner 2 | Partner is a vanilla content API mod that adds a new card type to the game. In short, you can accept a partner to accompany you at the beginning of each game. Initially 16 base partners with hand-crafted pixel art are provided. You can also create your own partner by browsing the tutorial or viewing the Partner code. 3 | 4 | ![logo-o](https://github.com/user-attachments/assets/ab06cfd7-918a-40fd-9ed0-17af7b5b9929) 5 | 6 | ## Requirements 7 | [Steamodded](https://github.com/Steamodded/smods) `Steamodded (>=1.0.0~BETA-0323b)` 8 | 9 | [Lovely](https://github.com/ethangreen-dev/lovely-injector) `Lovely (>=0.6)` 10 | 11 | ## Features 12 | Initially provide 16 basic Partners. 13 | 14 | If you have a Joker that corresponds to the Partner you accept, the Partner will become more powerful with a Buff form! The initial 16 basic Partners all have a Buff form. 15 | 16 | | Partner | Base form | Buff form | 17 | |----------|------------------------------------|--------------------------------------------------| 18 | | Jimbo | Gain +2 Chips | Gain +4 Chips | 19 | | Mute | Retrigger 1 additional time | Retrigger 2 additional times | 20 | | Punch | Gain +0.5 Mult | Gain +1 Mult | 21 | | Hatch | Sell value +$1 | Sell value +$2 | 22 | | Steal | Gain +2 Hands | Gain +4 Hands | 23 | | Pale | Earn $1 | Earn $2 | 24 | | Fantasy | Add an extra option of Joker card | Extra option does not take up the optional count | 25 | | Divine | Stock an extra Arcana Pack | Stock an extra free Mega Arcana Pack | 26 | | Plate | Skip blind will increase $1 | Skip blind will increase $2 | 27 | | Batter | Skip pack will increase 1 Mult | Skip pack will increase 2 Mult | 28 | | Bargain | Destroy up to 1 card | Destroy up to 5 cards | 29 | | Literacy | Give Negative edition | Give Negative edition and free | 30 | | Jump | Gain +X0.5 Mult | Gain +X1 Mult | 31 | | Vote | Retrigger 1 additional time | Retrigger 2 additional times | 32 | | Bleed | Give X1.5 Mult | Give X3 Mult | 33 | | Blaze | Upgrade 1 level | Upgrade 2 levels | 34 | 35 | The unlocking condition for all basic Partners are used the corresponding Joker to win on Gold Stake difficulty. 36 | -------------------------------------------------------------------------------- /assets/1x/Partners.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Icecanno/Partner-API/3fef7e3666ad8d2453426e73caee43d4734a55fe/assets/1x/Partners.png -------------------------------------------------------------------------------- /assets/1x/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Icecanno/Partner-API/3fef7e3666ad8d2453426e73caee43d4734a55fe/assets/1x/icon.png -------------------------------------------------------------------------------- /assets/2x/Partners.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Icecanno/Partner-API/3fef7e3666ad8d2453426e73caee43d4734a55fe/assets/2x/Partners.png -------------------------------------------------------------------------------- /assets/2x/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Icecanno/Partner-API/3fef7e3666ad8d2453426e73caee43d4734a55fe/assets/2x/icon.png -------------------------------------------------------------------------------- /config.lua: -------------------------------------------------------------------------------- 1 | return { 2 | enable_partner = true, 3 | enable_speech_bubble = true, 4 | temporary_unlock_all = false, 5 | } 6 | -------------------------------------------------------------------------------- /localization/en-us.lua: -------------------------------------------------------------------------------- 1 | return { 2 | descriptions = { 3 | Partner={ 4 | pnr_partner_joker={ 5 | name = "Jimbo", 6 | text = { 7 | "Gain {C:chips}+#2#{} Chips", 8 | "per hand played", 9 | "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", 10 | }, 11 | unlock={ 12 | "Used {C:attention}Joker", 13 | "to win on {C:attention}Gold", 14 | "{C:attention}Stake{} difficulty", 15 | }, 16 | }, 17 | pnr_partner_mime={ 18 | name = "Mute", 19 | text = { 20 | "Retrigger {C:attention}first{} card", 21 | "{C:attention}held in hand", 22 | "{C:attention}#1#{} additional time(s)", 23 | }, 24 | unlock={ 25 | "Used {C:attention}Mime", 26 | "to win on {C:attention}Gold", 27 | "{C:attention}Stake{} difficulty", 28 | }, 29 | }, 30 | pnr_partner_raised_fist={ 31 | name = "Punch", 32 | text = { 33 | "if played hand", 34 | "contains a {C:attention}Pair{}", 35 | "the rank of {C:attention}lowest{}", 36 | "ranked card gives {C:mult}+#1#{} Mult", 37 | "and increase this value", 38 | }, 39 | unlock={ 40 | "Used {C:attention}Raised Fist", 41 | "to win on {C:attention}Gold", 42 | "{C:attention}Stake{} difficulty", 43 | }, 44 | }, 45 | pnr_partner_egg={ 46 | name = "Hatch", 47 | text = { 48 | "Add {C:money}$#1#{} of {C:attention}sell value", 49 | "to every {C:attention}Joker{}", 50 | "at end of round", 51 | }, 52 | unlock={ 53 | "Used {C:attention}Egg", 54 | "to win on {C:attention}Gold", 55 | "{C:attention}Stake{} difficulty", 56 | }, 57 | }, 58 | pnr_partner_burglar={ 59 | name = "Steal", 60 | text = { 61 | "When {C:attention}Boss Blind{}", 62 | "is selected", 63 | "gain {C:blue}+#1#{} Hands", 64 | }, 65 | unlock={ 66 | "Used {C:attention}Burglar", 67 | "to win on {C:attention}Gold", 68 | "{C:attention}Stake{} difficulty", 69 | }, 70 | }, 71 | pnr_partner_faceless={ 72 | name = "Pale", 73 | text = { 74 | "Earn {C:money}$#1#{} for each", 75 | "discarded {C:attention}face card{}", 76 | }, 77 | unlock={ 78 | "Used {C:attention}Faceless Joker", 79 | "to win on {C:attention}Gold", 80 | "{C:attention}Stake{} difficulty", 81 | }, 82 | }, 83 | pnr_partner_hallucination={ 84 | name = "Fantasy", 85 | text = { 86 | "Add an extra option of", 87 | "{C:attention}Joker{} when any", 88 | "{C:attention}Booster Pack{} is opened", 89 | }, 90 | unlock={ 91 | "Used {C:attention}Hallucination", 92 | "to win on {C:attention}Gold", 93 | "{C:attention}Stake{} difficulty", 94 | }, 95 | }, 96 | pnr_partner_fortune_teller={ 97 | name = "Divine", 98 | text = { 99 | "Shop stocks an", 100 | "extra {C:attention,T:p_arcana_normal_1}Arcana Pack{}", 101 | "every {C:attention}#2#{C:inactive} [#1#]{} rounds", 102 | }, 103 | unlock={ 104 | "Used {C:attention}Fortune Teller", 105 | "to win on {C:attention}Gold", 106 | "{C:attention}Stake{} difficulty", 107 | }, 108 | }, 109 | pnr_partner_golden={ 110 | name = "Plate", 111 | text = { 112 | "Earn {C:money}$#1#{} at", 113 | "end of round", 114 | "increase this value", 115 | "when {C:attention}Blind{} is skipped", 116 | }, 117 | unlock={ 118 | "Used {C:attention}Golden Joker", 119 | "to win on {C:attention}Gold", 120 | "{C:attention}Stake{} difficulty", 121 | }, 122 | }, 123 | pnr_partner_baseball={ 124 | name = "Batter", 125 | text = { 126 | "{C:green}Uncommon{} Jokers", 127 | "each give {C:mult}+#1#{} Mult", 128 | "increase this value when", 129 | "{C:attention}Booster Pack{} is skipped", 130 | }, 131 | unlock={ 132 | "Used {C:attention}Baseball Card", 133 | "to win on {C:attention}Gold", 134 | "{C:attention}Stake{} difficulty", 135 | }, 136 | }, 137 | pnr_partner_trading={ 138 | name = "Bargain", 139 | text = { 140 | "If {C:attention}last discard{} of round", 141 | "has only one card", 142 | "Lose {C:money}$#1#{} and destroy it", 143 | }, 144 | unlock={ 145 | "Used {C:attention}Trading Card", 146 | "to win on {C:attention}Gold", 147 | "{C:attention}Stake{} difficulty", 148 | }, 149 | }, 150 | pnr_partner_flash={ 151 | name = "Literacy", 152 | text = { 153 | "Once per round:", 154 | "next shop joker", 155 | "becomes {C:dark_edition,T:e_negative}Negative{}", 156 | "after {C:attention}first{} reroll", 157 | }, 158 | unlock={ 159 | "Used {C:attention}Flash Card", 160 | "to win on {C:attention}Gold", 161 | "{C:attention}Stake{} difficulty", 162 | }, 163 | }, 164 | pnr_partner_throwback={ 165 | name = "Jump", 166 | text = { 167 | "Click to spend {C:money}$#3#{}", 168 | "to gain {X:mult,C:white}X#2#{} Mult", 169 | "resets after hand played", 170 | "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", 171 | }, 172 | unlock={ 173 | "Used {C:attention}Throwback", 174 | "to win on {C:attention}Gold", 175 | "{C:attention}Stake{} difficulty", 176 | }, 177 | }, 178 | pnr_partner_hanging_chad={ 179 | name = "Vote", 180 | text = { 181 | "Retrigger {C:attention}first{}", 182 | "scoring card", 183 | "{C:attention}#1#{} additional time(s)", 184 | }, 185 | unlock={ 186 | "Used {C:attention}Hanging Chad", 187 | "to win on {C:attention}Gold", 188 | "{C:attention}Stake{} difficulty", 189 | }, 190 | }, 191 | pnr_partner_bloodstone={ 192 | name = "Bleed", 193 | text = { 194 | "First played {C:hearts}Heart", 195 | "card gives {X:mult,C:white} X#1# {} Mult", 196 | "when scored", 197 | }, 198 | unlock={ 199 | "Used {C:attention}Bloodstone", 200 | "to win on {C:attention}Gold", 201 | "{C:attention}Stake{} difficulty", 202 | }, 203 | }, 204 | pnr_partner_burnt={ 205 | name = "Blaze", 206 | text = { 207 | "{C:green}#1# in #2#{} chance to", 208 | "upgrade level of the", 209 | "{C:attention}discarded{} poker hand", 210 | }, 211 | unlock={ 212 | "Used {C:attention}Burnt Joker", 213 | "to win on {C:attention}Gold", 214 | "{C:attention}Stake{} difficulty", 215 | }, 216 | }, 217 | }, 218 | Other={ 219 | partner_benefits={ 220 | name="Partner Benefits", 221 | text={ 222 | "Corresponding Joker", 223 | "will provide {C:dark_edition}benefits{}", 224 | }, 225 | }, 226 | }, 227 | }, 228 | misc={ 229 | dictionary={ 230 | b_partners="Partners", 231 | b_partner_agree="Accept", 232 | b_partner_random="Random", 233 | b_partner_skip="Skip", 234 | k_partner="Partner", 235 | k_partner_benefit="Buff", 236 | k_enable_partner="Enable Partner", 237 | k_enable_speech_bubble="Enable Speech Bubble", 238 | k_temporary_unlock_all="Temporary Unlock All", 239 | ml_partner_unique_ability={ 240 | "Cards may enhance the abilities of", 241 | "corresponding partners", 242 | }, 243 | }, 244 | quips={ 245 | pnr_1={ 246 | "Hello,", 247 | "My friend!", 248 | }, 249 | pnr_2={ 250 | "I will stay", 251 | "with you!", 252 | }, 253 | pnr_3={ 254 | "I think you will have an", 255 | "outstanding performance!", 256 | }, 257 | pnr_4={ 258 | "I believe you can", 259 | "win this run!", 260 | }, 261 | pnr_5={ 262 | "Professional partner", 263 | "always come prepared", 264 | }, 265 | pnr_6={ 266 | "Credits:", 267 | "Brookling", 268 | "Snowylight", 269 | "Betmma", 270 | }, 271 | pnr_partner_mime_1={ 272 | ":)" 273 | }, 274 | pnr_partner_mime_2={ 275 | ":D" 276 | }, 277 | pnr_partner_mime_3={ 278 | ":P" 279 | }, 280 | pnr_partner_mime_4={ 281 | ":I" 282 | }, 283 | pnr_partner_mime_5={ 284 | "XD" 285 | }, 286 | pnr_partner_mime_6={ 287 | ":l" 288 | }, 289 | pnr_partner_throwback_1={ 290 | "Cool Beans" 291 | }, 292 | pnr_partner_throwback_2={ 293 | "Remember, if there's a blind", 294 | "you're not digging the vibe of:", 295 | "SAY NO AND SKIP!" 296 | }, 297 | pnr_partner_throwback_3={ 298 | "Totally Tubular!" 299 | }, 300 | pnr_partner_throwback_4={ 301 | "Gnarly!" 302 | }, 303 | pnr_partner_throwback_5={ 304 | "Let's make some", 305 | "bodacious plays!" 306 | }, 307 | pnr_partner_throwback_6={ 308 | "Hey-yo!", 309 | "Freshest partner in town,", 310 | "reporting for duty!" 311 | }, 312 | }, 313 | }, 314 | } 315 | -------------------------------------------------------------------------------- /localization/zh_CN.lua: -------------------------------------------------------------------------------- 1 | return { 2 | descriptions = { 3 | Partner={ 4 | pnr_partner_joker={ 5 | name = "金宝伙伴", 6 | text = { 7 | "每次出牌", 8 | "获得{C:chips}+#2#{}筹码", 9 | "{C:inactive}(当前{C:chips}+#1#{C:inactive}筹码)", 10 | }, 11 | unlock={ 12 | "使用{C:attention}小丑", 13 | "在{C:attention}金注", 14 | "难度下获胜", 15 | }, 16 | }, 17 | pnr_partner_mime={ 18 | name = "哑剧伙伴", 19 | text = { 20 | "令手中{C:attention}第一张{}牌", 21 | "额外触发{C:attention}#1#{}次", 22 | }, 23 | unlock={ 24 | "使用{C:attention}哑剧演员", 25 | "在{C:attention}金注", 26 | "难度下获胜", 27 | }, 28 | }, 29 | pnr_partner_raised_fist={ 30 | name = "拳头伙伴", 31 | text = { 32 | "打出牌包含{C:attention}对子{}时", 33 | "令手中点数{C:attention}最小{}牌", 34 | "给予{C:mult}+#1#{}倍率", 35 | "并永久提升此数值", 36 | }, 37 | unlock={ 38 | "使用{C:attention}致胜之拳", 39 | "在{C:attention}金注", 40 | "难度下获胜", 41 | }, 42 | }, 43 | pnr_partner_egg={ 44 | name = "鸡蛋伙伴", 45 | text = { 46 | "每回合结束", 47 | "令拥有的所有{C:attention}小丑{}", 48 | "{C:attention}售价{}增加{C:money}$#1#{}", 49 | }, 50 | unlock={ 51 | "使用{C:attention}鸡蛋", 52 | "在{C:attention}金注", 53 | "难度下获胜", 54 | }, 55 | }, 56 | pnr_partner_burglar={ 57 | name = "窃贼伙伴", 58 | text = { 59 | "选择{C:attention}Boss盲注{}时", 60 | "出牌次数{C:blue}+#1#{}", 61 | }, 62 | unlock={ 63 | "使用{C:attention}窃贼", 64 | "在{C:attention}金注", 65 | "难度下获胜", 66 | }, 67 | }, 68 | pnr_partner_faceless={ 69 | name = "无面伙伴", 70 | text = { 71 | "每弃掉一张{C:attention}人头牌{}", 72 | "获得{C:money}$#1#{}", 73 | }, 74 | unlock={ 75 | "使用{C:attention}无面小丑", 76 | "在{C:attention}金注", 77 | "难度下获胜", 78 | }, 79 | }, 80 | pnr_partner_hallucination={ 81 | name = "幻觉伙伴", 82 | text = { 83 | "令任意{C:attention}补充包{}增加", 84 | "额外的{C:attention}小丑牌{}选项", 85 | }, 86 | unlock={ 87 | "使用{C:attention}幻觉", 88 | "在{C:attention}金注", 89 | "难度下获胜", 90 | }, 91 | }, 92 | pnr_partner_fortune_teller={ 93 | name = "占卜伙伴", 94 | text = { 95 | "每经过{C:attention}#2#{C:inactive}[#1#]{}个回合", 96 | "额外上架{C:attention,T:p_arcana_normal_1}秘术包{}", 97 | }, 98 | unlock={ 99 | "使用{C:attention}占卜师", 100 | "在{C:attention}金注", 101 | "难度下获胜", 102 | }, 103 | }, 104 | pnr_partner_golden={ 105 | name = "黄金伙伴", 106 | text = { 107 | "每回合结束", 108 | "获得{C:money}$#1#{}", 109 | "跳过{C:attention}盲注{}时", 110 | "永久提升此数值", 111 | }, 112 | unlock={ 113 | "使用{C:attention}黄金小丑", 114 | "在{C:attention}金注", 115 | "难度下获胜", 116 | }, 117 | }, 118 | pnr_partner_baseball={ 119 | name = "棒球伙伴", 120 | text = { 121 | "令所有{C:green}罕见{}小丑", 122 | "给予{C:mult}+#1#{}倍率", 123 | "跳过{C:attention}补充包{}时", 124 | "永久提升此数值", 125 | }, 126 | unlock={ 127 | "使用{C:attention}棒球卡", 128 | "在{C:attention}金注", 129 | "难度下获胜", 130 | }, 131 | }, 132 | pnr_partner_trading={ 133 | name = "交易伙伴", 134 | text = { 135 | "每回合{C:attention}最后一次{}弃牌", 136 | "若只有一张牌", 137 | "消耗{C:money}$#1#{}将其摧毁", 138 | }, 139 | unlock={ 140 | "使用{C:attention}交易卡", 141 | "在{C:attention}金注", 142 | "难度下获胜", 143 | }, 144 | }, 145 | pnr_partner_flash={ 146 | name = "闪示伙伴", 147 | text = { 148 | "每个回合一次:", 149 | "在你重掷后", 150 | "为商店的{C:attention}第一张{}小丑", 151 | "添加{C:dark_edition,T:e_negative}负片{}", 152 | }, 153 | unlock={ 154 | "使用{C:attention}闪示卡", 155 | "在{C:attention}金注", 156 | "难度下获胜", 157 | }, 158 | }, 159 | pnr_partner_throwback={ 160 | name = "回溯伙伴", 161 | text = { 162 | "点击花费{C:money}$#3#{}", 163 | "获得{X:mult,C:white}X#2#{}倍率", 164 | "每次出牌后重置", 165 | "{C:inactive}(当前为{X:mult,C:white}X#1#{C:inactive}倍率)", 166 | }, 167 | unlock={ 168 | "使用{C:attention}回溯", 169 | "在{C:attention}金注", 170 | "难度下获胜", 171 | }, 172 | }, 173 | pnr_partner_hanging_chad={ 174 | name = "选票伙伴", 175 | text = { 176 | "令{C:attention}第一张{}计分牌", 177 | "额外触发{C:attention}#1#{}次", 178 | }, 179 | unlock={ 180 | "使用{C:attention}未断选票", 181 | "在{C:attention}金注", 182 | "难度下获胜", 183 | }, 184 | }, 185 | pnr_partner_bloodstone={ 186 | name = "血石伙伴", 187 | text = { 188 | "令第一张{C:hearts}红桃{}计分牌", 189 | "给予{X:mult,C:white}X#1#{}倍率", 190 | }, 191 | unlock={ 192 | "使用{C:attention}血石", 193 | "在{C:attention}金注", 194 | "难度下获胜", 195 | }, 196 | }, 197 | pnr_partner_burnt={ 198 | name = "烧焦伙伴", 199 | text = { 200 | "{C:green}#1#/#2#{}几率升级", 201 | "弃掉的{C:attention}牌型等级{}", 202 | }, 203 | unlock={ 204 | "使用{C:attention}烧焦小丑", 205 | "在{C:attention}金注", 206 | "难度下获胜", 207 | }, 208 | }, 209 | }, 210 | Other={ 211 | partner_benefits={ 212 | name="伙伴增益", 213 | text={ 214 | "对应小丑会", 215 | "{C:dark_edition}增益{}伙伴", 216 | }, 217 | }, 218 | }, 219 | }, 220 | misc={ 221 | dictionary={ 222 | b_partners="伙伴", 223 | b_partner_agree="选择伙伴", 224 | b_partner_random="随机伙伴", 225 | b_partner_skip="忽略伙伴", 226 | k_partner="伙伴", 227 | k_partner_benefit="词条", 228 | k_enable_partner="启用伙伴", 229 | k_enable_speech_bubble="启用对话气泡", 230 | k_temporary_unlock_all="临时解锁全部", 231 | ml_partner_unique_ability={ 232 | "小丑可能会强化", 233 | "对应伙伴的能力", 234 | }, 235 | }, 236 | quips={ 237 | pnr_1={ 238 | "你好,", 239 | "我的朋友!", 240 | }, 241 | pnr_2={ 242 | "我会一直", 243 | "和你在一起的!", 244 | }, 245 | pnr_3={ 246 | "相信你能有", 247 | "亮眼的表现!", 248 | }, 249 | pnr_4={ 250 | "这局我觉得", 251 | "你能赢!", 252 | }, 253 | pnr_5={ 254 | "专业伙伴", 255 | "总是有备而来", 256 | }, 257 | pnr_6={ 258 | "鸣谢:", 259 | "Brookling", 260 | "Snowylight", 261 | "Betmma", 262 | }, 263 | pnr_partner_mime_1={ 264 | ":)" 265 | }, 266 | pnr_partner_mime_2={ 267 | ":D" 268 | }, 269 | pnr_partner_mime_3={ 270 | ":P" 271 | }, 272 | pnr_partner_mime_4={ 273 | ":I" 274 | }, 275 | pnr_partner_mime_5={ 276 | "XD" 277 | }, 278 | pnr_partner_mime_6={ 279 | ":l" 280 | }, 281 | pnr_partner_throwback_1={ 282 | "太酷啦!" 283 | }, 284 | pnr_partner_throwback_2={ 285 | "如果你不喜欢", 286 | "某些盲注", 287 | "说不然后跳过!" 288 | }, 289 | pnr_partner_throwback_3={ 290 | "明智的选择!" 291 | }, 292 | pnr_partner_throwback_4={ 293 | "冲啊!" 294 | }, 295 | pnr_partner_throwback_5={ 296 | "让我们", 297 | "一起摇摆!" 298 | }, 299 | pnr_partner_throwback_6={ 300 | "芜湖!", 301 | "最机灵的伙伴", 302 | "来报到啦!" 303 | }, 304 | }, 305 | }, 306 | } 307 | -------------------------------------------------------------------------------- /lovely.toml: -------------------------------------------------------------------------------- 1 | [manifest] 2 | version = "1.0.2b" 3 | dump_lua = true 4 | priority = 0 5 | 6 | [[patches]] 7 | [patches.pattern] 8 | target = "functions/state_events.lua" 9 | pattern = '''-- context.final_scoring_step calculations''' 10 | position = "before" 11 | payload = ''' 12 | 13 | if G.GAME.selected_partner_card then 14 | local ret = G.GAME.selected_partner_card:calculate_partner({full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, partner_main = true}) 15 | if ret then 16 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 17 | end 18 | end 19 | 20 | ''' 21 | match_indent = false 22 | overwrite = false 23 | 24 | [[patches]] 25 | [patches.pattern] 26 | target = "functions/state_events.lua" 27 | pattern = '''-- context.after calculations''' 28 | position = "before" 29 | payload = ''' 30 | 31 | if G.GAME.selected_partner_card then 32 | local ret = G.GAME.selected_partner_card:calculate_partner({full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, partner_after = true}) 33 | if ret then 34 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 35 | end 36 | end 37 | 38 | ''' 39 | match_indent = false 40 | overwrite = false 41 | 42 | [[patches]] 43 | [patches.pattern] 44 | target = "functions/state_events.lua" 45 | pattern = '''-- TARGET: effects before scoring starts''' 46 | position = "before" 47 | payload = ''' 48 | 49 | if G.GAME.selected_partner_card then 50 | local ret = G.GAME.selected_partner_card:calculate_partner({full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, partner_before = true}) 51 | if ret then 52 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 53 | end 54 | end 55 | 56 | ''' 57 | match_indent = false 58 | overwrite = false 59 | 60 | [[patches]] 61 | [patches.pattern] 62 | target = "functions/state_events.lua" 63 | pattern = '''-- TARGET: main end_of_round evaluation''' 64 | position = "before" 65 | payload = ''' 66 | 67 | if G.GAME.selected_partner_card then 68 | local ret = G.GAME.selected_partner_card:calculate_partner({partner_end_of_round = true, game_over = game_over}) 69 | if ret then 70 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 71 | end 72 | end 73 | 74 | ''' 75 | match_indent = false 76 | overwrite = false 77 | 78 | [[patches]] 79 | [patches.pattern] 80 | target = "functions/state_events.lua" 81 | pattern = '''-- TARGET: setting_blind effects''' 82 | position = "before" 83 | payload = ''' 84 | 85 | if G.GAME.selected_partner_card then 86 | local ret = G.GAME.selected_partner_card:calculate_partner({partner_setting_blind = true, blind = G.GAME.round_resets.blind}) 87 | if ret then 88 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 89 | end 90 | end 91 | 92 | ''' 93 | match_indent = false 94 | overwrite = false 95 | 96 | [[patches]] 97 | [patches.pattern] 98 | target = "functions/state_events.lua" 99 | pattern = '''-- TARGET: pre_discard''' 100 | position = "before" 101 | payload = ''' 102 | 103 | if G.GAME.selected_partner_card then 104 | local ret = G.GAME.selected_partner_card:calculate_partner({partner_pre_discard = true, full_hand = G.hand.highlighted, hook = hook}) 105 | if ret then 106 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 107 | end 108 | end 109 | 110 | ''' 111 | match_indent = false 112 | overwrite = false 113 | 114 | [[patches]] 115 | [patches.pattern] 116 | target = "functions/state_events.lua" 117 | pattern = '''SMODS.calculate_context({discard = true, other_card = G.hand.highlighted[i], full_hand = G.hand.highlighted}, effects)''' 118 | position = "before" 119 | payload = ''' 120 | 121 | if G.GAME.selected_partner_card then 122 | local ret = G.GAME.selected_partner_card:calculate_partner({partner_discard = true, other_card = G.hand.highlighted[i], full_hand = G.hand.highlighted}) 123 | if ret then 124 | if ret.remove then 125 | removed = true 126 | end 127 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 128 | end 129 | end 130 | 131 | ''' 132 | match_indent = false 133 | overwrite = false 134 | 135 | [[patches]] 136 | [patches.pattern] 137 | target = "functions/state_events.lua" 138 | pattern = '''for _, area in ipairs(SMODS.get_card_areas('jokers')) do''' 139 | position = "before" 140 | payload = ''' 141 | if G.GAME.selected_partner_card then 142 | local ret = G.GAME.selected_partner_card:calculate_partner_cash() 143 | if ret then 144 | add_round_eval_row({dollars = ret, bonus = true, name = "partner", pitch = pitch, card = G.GAME.selected_partner_card}) 145 | pitch = pitch + 0.06 146 | dollars = dollars + ret 147 | end 148 | end 149 | ''' 150 | match_indent = false 151 | overwrite = false 152 | 153 | [[patches]] 154 | [patches.pattern] 155 | target = "functions/common_events.lua" 156 | pattern = '''elseif config.name == 'interest' then''' 157 | position = "before" 158 | payload = ''' 159 | elseif string.find(config.name, "partner") then 160 | table.insert(left_text, {n=G.UIT.O, config={object = DynaText({string = localize{type = "name_text", set = config.card.config.center.set, key = config.card.config.center.key}, colours = {G.C.FILTER}, shadow = true, pop_in = 0, scale = 0.6*scale, silent = true})}}) 161 | ''' 162 | match_indent = false 163 | overwrite = false 164 | 165 | [[patches]] 166 | [patches.pattern] 167 | target = "functions/state_events.lua" 168 | pattern = '''-- Calculate context.other_joker effects''' 169 | position = "before" 170 | payload = ''' 171 | 172 | if G.GAME.selected_partner_card then 173 | local ret = G.GAME.selected_partner_card:calculate_partner({partner_other_main = true, other_card = _card, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands}) 174 | if ret then 175 | table.insert(effects, {individual = ret}) 176 | end 177 | end 178 | 179 | ''' 180 | match_indent = false 181 | overwrite = false 182 | 183 | [[patches]] 184 | [patches.pattern] 185 | target = "card.lua" 186 | pattern = '''SMODS.calculate_context({open_booster = true, card = self})''' 187 | position = "before" 188 | payload = ''' 189 | 190 | if G.GAME.selected_partner_card then 191 | local ret = G.GAME.selected_partner_card:calculate_partner({partner_open_booster = true, card = self}) 192 | if ret then 193 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 194 | end 195 | end 196 | 197 | ''' 198 | match_indent = false 199 | overwrite = false 200 | 201 | [[patches]] 202 | [patches.pattern] 203 | target = "game.lua" 204 | pattern = '''if not nosave_shop then SMODS.calculate_context({starting_shop = true}) end''' 205 | position = "before" 206 | payload = ''' 207 | 208 | if G.GAME.selected_partner_card then 209 | local ret = G.GAME.selected_partner_card:calculate_partner({partner_starting_shop = true}) 210 | if ret then 211 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 212 | end 213 | end 214 | 215 | ''' 216 | match_indent = false 217 | overwrite = false 218 | 219 | [[patches]] 220 | [patches.pattern] 221 | target = "functions/button_callbacks.lua" 222 | pattern = '''SMODS.calculate_context({reroll_shop = true, cost = reroll_cost})''' 223 | position = "before" 224 | payload = ''' 225 | 226 | if G.GAME.selected_partner_card then 227 | local ret = G.GAME.selected_partner_card:calculate_partner({partner_reroll_shop = true, cost = reroll_cost}) 228 | if ret then 229 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 230 | end 231 | end 232 | 233 | ''' 234 | match_indent = false 235 | overwrite = false 236 | 237 | [[patches]] 238 | [patches.pattern] 239 | target = "functions/button_callbacks.lua" 240 | pattern = '''SMODS.calculate_context({ending_shop = true})''' 241 | position = "before" 242 | payload = ''' 243 | 244 | if G.GAME.selected_partner_card then 245 | local ret = G.GAME.selected_partner_card:calculate_partner({partner_ending_shop = true}) 246 | if ret then 247 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 248 | end 249 | end 250 | 251 | ''' 252 | match_indent = false 253 | overwrite = false 254 | 255 | [[patches]] 256 | [patches.pattern] 257 | target = "functions/button_callbacks.lua" 258 | pattern = '''SMODS.calculate_context({skip_blind = true})''' 259 | position = "before" 260 | payload = ''' 261 | 262 | if G.GAME.selected_partner_card then 263 | local ret = G.GAME.selected_partner_card:calculate_partner({partner_skip_blind = true}) 264 | if ret then 265 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 266 | end 267 | end 268 | 269 | ''' 270 | match_indent = false 271 | overwrite = false 272 | 273 | [[patches]] 274 | [patches.pattern] 275 | target = "functions/button_callbacks.lua" 276 | pattern = '''SMODS.calculate_context({skipping_booster = true, booster = booster_obj})''' 277 | position = "before" 278 | payload = ''' 279 | 280 | if G.GAME.selected_partner_card then 281 | local ret = G.GAME.selected_partner_card:calculate_partner({partner_skipping_booster = true, booster = booster_obj}) 282 | if ret then 283 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 284 | end 285 | end 286 | 287 | ''' 288 | match_indent = false 289 | overwrite = false 290 | 291 | [[patches]] 292 | [patches.pattern] 293 | target = "functions/button_callbacks.lua" 294 | pattern = '''SMODS.calculate_context({selling_card = true, card = card})''' 295 | position = "before" 296 | payload = ''' 297 | 298 | if G.GAME.selected_partner_card then 299 | local ret = G.GAME.selected_partner_card:calculate_partner({partner_selling_card = true, card = card}) 300 | if ret then 301 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 302 | end 303 | end 304 | 305 | ''' 306 | match_indent = false 307 | overwrite = false 308 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "partner", 3 | "name": "Partner", 4 | "display_name": "partner", 5 | "author": ["baimao"], 6 | "description": "Add interesting partners to your Balatro. Thanks to brookling, snowylight, betmma", 7 | "prefix": "partner", 8 | "main_file": "partner.lua", 9 | "priority": -10, 10 | "badge_colour": "A64E91", 11 | "version": "1.0.2b", 12 | "dependencies": [ 13 | "Steamodded (>=1.0.0~BETA-0323b)", 14 | "Lovely (>=0.6)", 15 | ], 16 | } 17 | -------------------------------------------------------------------------------- /partner.lua: -------------------------------------------------------------------------------- 1 | -- Extend Page 2 | 3 | Partner_API = SMODS.current_mod 4 | 5 | Partner_API.Partner = SMODS.Center:extend{ 6 | unlocked = true, 7 | discovered = false, 8 | no_quips = false, 9 | individual_quips = false, 10 | config = {}, 11 | set = "Partner", 12 | class_prefix = "pnr", 13 | required_params = {"key", "atlas", "pos"}, 14 | pre_inject_class = function(self) 15 | G.P_CENTER_POOLS[self.set] = {} 16 | end, 17 | set_card_type_badge = function(self, card, badges) 18 | local display_info = {localize("k_partner")} 19 | if self.badges_info and next(self.badges_info) then 20 | for _, v in pairs(self.badges_info) do 21 | if localize(v) ~= "ERROR" then 22 | display_info[#display_info+1] = localize(v) 23 | else 24 | display_info[#display_info+1] = v 25 | end 26 | end 27 | end 28 | badges[#badges+1] = create_badge(display_info, G.C.DARK_EDITION, G.C.WHITE) 29 | end, 30 | generate_ui = function(self, info_queue, card, desc_nodes, specific_vars, full_UI_table) 31 | SMODS.Center.generate_ui(self, info_queue, card, desc_nodes, specific_vars, full_UI_table) 32 | if self.config.extra.related_card then 33 | if type(self.config.extra.related_card) == "table" then 34 | for k, v in pairs(self.config.extra.related_card) do 35 | if next(SMODS.find_card(v)) then 36 | local main_end = {{n=G.UIT.C, config={align = "bm"}, nodes={ 37 | {n=G.UIT.O, config={object = DynaText({string = {"<"..localize{type = "name_text", set = G.P_CENTERS[v].set, key = v}.." "..localize("k_partner_benefit")..">"}, colours = {G.C.DARK_EDITION}, float = true, scale = 0.3})}}, 38 | }}} 39 | desc_nodes[#desc_nodes+1] = main_end 40 | end 41 | end 42 | else 43 | if next(SMODS.find_card(self.config.extra.related_card)) then 44 | local main_end = {{n=G.UIT.C, config={align = "bm"}, nodes={ 45 | {n=G.UIT.O, config={object = DynaText({string = {"<"..localize{type = "name_text", set = G.P_CENTERS[self.config.extra.related_card].set, key = self.config.extra.related_card}.." "..localize("k_partner_benefit")..">"}, colours = {G.C.DARK_EDITION}, float = true, scale = 0.3})}}, 46 | }}} 47 | desc_nodes[#desc_nodes+1] = main_end 48 | end 49 | end 50 | end 51 | end 52 | } 53 | 54 | -- Collection Page 55 | 56 | Partner_API.custom_collection_tabs = function() 57 | local tally = 0 58 | for _, v in pairs(G.P_CENTER_POOLS["Partner"]) do 59 | if v:is_unlocked() then 60 | tally = tally + 1 61 | end 62 | end 63 | return {UIBox_button({button = "your_collection_partners", label = {localize("b_partners")}, count = {tally = tally, of = #G.P_CENTER_POOLS["Partner"]}, minw = 5, id = "your_collection_partners"})} 64 | end 65 | 66 | G.FUNCS.your_collection_partners = function(e) 67 | G.SETTINGS.paused = true 68 | G.FUNCS.overlay_menu{ 69 | definition = create_UIBox_your_collection_partners(), 70 | } 71 | end 72 | 73 | function create_UIBox_your_collection_partners() 74 | local deck_tables = {} 75 | G.your_collection = {} 76 | for j = 1, 2 do 77 | G.your_collection[j] = CardArea(G.ROOM.T.x, G.ROOM.T.h, 3.6*G.CARD_W, 0.7*G.CARD_H, {card_limit = 4, type = "title", highlight_limit = 0, collection = true}) 78 | table.insert(deck_tables, {n=G.UIT.R, config={align = "cm", padding = 0.07, no_fill = true}, nodes={ 79 | {n=G.UIT.O, config={object = G.your_collection[j]}} 80 | }}) 81 | end 82 | local partner_options = {} 83 | for i = 1, math.ceil(#G.P_CENTER_POOLS["Partner"]/(4*#G.your_collection)) do 84 | table.insert(partner_options, localize("k_page").." "..tostring(i).."/"..tostring(math.ceil(#G.P_CENTER_POOLS["Partner"]/(4*#G.your_collection)))) 85 | end 86 | for i = 1, 4 do 87 | for j = 1, #G.your_collection do 88 | local center = G.P_CENTER_POOLS["Partner"][i+(j-1)*4] 89 | if not center then break end 90 | local card = Card(G.your_collection[j].T.x+G.your_collection[j].T.w/2, G.your_collection[j].T.y, G.CARD_W*46/71, G.CARD_H*58/95, nil, center) 91 | --card.sticker = get_joker_win_sticker(center) 92 | G.your_collection[j]:emplace(card) 93 | end 94 | end 95 | INIT_COLLECTION_CARD_ALERTS() 96 | local t = create_UIBox_generic_options({back_func = "your_collection_other_gameobjects", infotip = localize("ml_partner_unique_ability"), contents = { 97 | {n=G.UIT.R, config={align = "cm", r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes=deck_tables}, 98 | {n=G.UIT.R, config={align = "cm"}, nodes={ 99 | create_option_cycle({options = partner_options, w = 4.5, cycle_shoulders = true, opt_callback = "your_collection_partner_page", current_option = 1, colour = G.C.RED, no_pips = true, focus_args = {snap_to = true, nav = "wide"}}) 100 | }} 101 | }}) 102 | return t 103 | end 104 | 105 | G.FUNCS.your_collection_partner_page = function(args) 106 | if not args or not args.cycle_config then return end 107 | for j = 1, #G.your_collection do 108 | for i = #G.your_collection[j].cards, 1, -1 do 109 | local c = G.your_collection[j]:remove_card(G.your_collection[j].cards[i]) 110 | c:remove() 111 | c = nil 112 | end 113 | end 114 | for i = 1, 4 do 115 | for j = 1, #G.your_collection do 116 | local center = G.P_CENTER_POOLS["Partner"][i+(j-1)*4+(4*#G.your_collection*(args.cycle_config.current_option-1))] 117 | if not center then break end 118 | local card = Card(G.your_collection[j].T.x+G.your_collection[j].T.w/2, G.your_collection[j].T.y, G.CARD_W*46/71, G.CARD_H*58/95, G.P_CARDS.empty, center) 119 | --card.sticker = get_joker_win_sticker(center) 120 | G.your_collection[j]:emplace(card) 121 | end 122 | end 123 | INIT_COLLECTION_CARD_ALERTS() 124 | end 125 | 126 | -- UI Page 127 | 128 | function Partner_API.Partner:is_unlocked() 129 | return self.unlocked or Partner_API.config.temporary_unlock_all or G.PROFILES[G.SETTINGS.profile].all_unlocked 130 | end 131 | 132 | Partner_API.config_tab = function() 133 | return {n=G.UIT.ROOT, config = {align = "cm", padding = 0.05, colour = G.C.CLEAR}, nodes={ 134 | create_toggle({label = localize("k_enable_partner"), ref_table = Partner_API.config, ref_value = "enable_partner"}), 135 | create_toggle({label = localize("k_enable_speech_bubble"), ref_table = Partner_API.config, ref_value = "enable_speech_bubble"}), 136 | create_toggle({label = localize("k_temporary_unlock_all"), ref_table = Partner_API.config, ref_value = "temporary_unlock_all"}), 137 | }} 138 | end 139 | 140 | local Card_set_sprites_ref = Card.set_sprites 141 | function Card:set_sprites(_center, _front) 142 | Card_set_sprites_ref(self, _center, _front) 143 | if _center and _center.set == "Partner" and not _center:is_unlocked() then 144 | self.children.center.atlas = G.ASSET_ATLAS["partner_Partner"] 145 | self.children.center:set_sprite_pos({x = 0, y = 4}) 146 | end 147 | end 148 | 149 | local generate_card_ui_ref = generate_card_ui 150 | function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, hide_desc, main_start, main_end, card) 151 | if _c and _c.set == "Partner" and _c:is_unlocked() and card_type and card_type == "Locked" and (specific_vars and not specific_vars.no_name or not specific_vars) then card_type = "Partner" end 152 | if _c and _c.set == "Partner" and _c:is_unlocked() and badges then badges.card_type = "Partner" end 153 | return generate_card_ui_ref(_c, full_UI_table, specific_vars, card_type, badges, hide_desc, main_start, main_end, card) 154 | end 155 | 156 | local Card_update_ref = Card.update 157 | function Card:update(dt) 158 | Card_update_ref(self, dt) 159 | if self.ability.set == "Partner" and not self.states.drag.is then 160 | if self.T.x+self.T.w > G.ROOM.T.w then 161 | self.T.x = G.ROOM.T.w-self.T.w 162 | elseif self.T.x < 0 then 163 | self.T.x = 0 164 | end 165 | if self.T.y+self.T.h > G.ROOM.T.h then 166 | self.T.y = G.ROOM.T.h-self.T.h 167 | elseif self.T.y < 0 then 168 | self.T.y = 0 169 | end 170 | end 171 | end 172 | 173 | local create_UIBox_card_unlock_ref = create_UIBox_card_unlock 174 | function create_UIBox_card_unlock(card_center) 175 | local ret = create_UIBox_card_unlock_ref(card_center) 176 | local title = ret.nodes[1].nodes[1].nodes[1].nodes[1].nodes[1].nodes[1].nodes[1].nodes[1].config 177 | if card_center.set == "Partner" then 178 | title.object:remove() 179 | title.object = DynaText({string = {localize("k_partner")}, colours = {G.C.BLUE}, shadow = true, rotate = true, bump = true, pop_in = 0.3, pop_in_rate = 2, scale = 1.2}) 180 | end 181 | return ret 182 | end 183 | 184 | local create_UIBox_notify_alert_ref = create_UIBox_notify_alert 185 | function create_UIBox_notify_alert(_achievement, _type) 186 | local ret = create_UIBox_notify_alert_ref(_achievement, _type) 187 | local title = ret.nodes[1].nodes[1].nodes[2].nodes[1].nodes[1].config 188 | if _type == "Partner" then 189 | title.text = localize("k_partner") 190 | end 191 | return ret 192 | end 193 | 194 | function Card:add_partner_speech_bubble(forced_key) 195 | if not Partner_API.config.enable_speech_bubble then return end 196 | if self.children.speech_bubble then self.children.speech_bubble:remove() end 197 | local align = nil 198 | if self.T.x+self.T.w/2 > G.ROOM.T.w/2 then align = "cl" end 199 | self.config.speech_bubble_align = {align = align or "cr", offset = {x=align and -0.1 or 0.1,y=0}, parent = self} 200 | self.children.speech_bubble = UIBox{ 201 | definition = G.UIDEF.partner_speech_bubble(forced_key), 202 | config = self.config.speech_bubble_align 203 | } 204 | self.children.speech_bubble:set_role{role_type = "Minor", xy_bond = "Strong", r_bond = "Weak", major = self} 205 | self.children.speech_bubble.states.visible = false 206 | local hold_time = (G.SETTINGS.GAMESPEED*4) or 4 207 | G.E_MANAGER:add_event(Event({trigger = "after", delay = hold_time, blockable = false, blocking = false, func = function() 208 | self:remove_partner_speech_bubble() 209 | return true end})) 210 | end 211 | 212 | function G.UIDEF.partner_speech_bubble(forced_key) 213 | local text = {} 214 | localize{type = "quips", key = forced_key or "pq_1", nodes=text} 215 | local row = {} 216 | for k, v in ipairs(text) do 217 | row[#row+1] = {n=G.UIT.R, config={align = "cl"}, nodes=v} 218 | end 219 | local t = {n=G.UIT.ROOT, config = {align = "cm", minh = 1, r = 0.3, padding = 0.07, minw = 1, colour = G.C.JOKER_GREY, shadow = true}, nodes={ 220 | {n=G.UIT.C, config={align = "cm", minh = 1, r = 0.2, padding = 0.1, minw = 1, colour = G.C.WHITE}, nodes={ 221 | {n=G.UIT.C, config={align = "cm", minh = 1, r = 0.2, padding = 0.03, minw = 1, colour = G.C.WHITE}, nodes=row} 222 | }} 223 | }} 224 | return t 225 | end 226 | 227 | function Card:partner_say_stuff(n, not_first) 228 | if not Partner_API.config.enable_speech_bubble then return end 229 | self.talking = true 230 | if not not_first then 231 | G.E_MANAGER:add_event(Event({trigger = "after", delay = 0.1, func = function() 232 | if self.children.speech_bubble then self.children.speech_bubble.states.visible = true end 233 | self:partner_say_stuff(n, true) 234 | return true end})) 235 | else 236 | if n <= 0 then self.talking = false; return end 237 | play_sound("voice"..math.random(1, 11), G.SPEEDFACTOR*(math.random()*0.2+1), 0.5) 238 | self:juice_up(0.6, 1) 239 | G.E_MANAGER:add_event(Event({trigger = "after", blockable = false, blocking = false, delay = 0.13, func = function() 240 | self:partner_say_stuff(n-1, true) 241 | return true end})) 242 | end 243 | end 244 | 245 | function Card:remove_partner_speech_bubble() 246 | if self.children.speech_bubble then self.children.speech_bubble:remove(); self.children.speech_bubble = nil end 247 | end 248 | 249 | local Card_draw_ref = Card.draw 250 | function Card:draw(layer) 251 | Card_draw_ref(self, layer) 252 | if self.children.speech_bubble then 253 | self.children.speech_bubble:draw() 254 | end 255 | end 256 | 257 | local Card_move_ref = Card.move 258 | function Card:move(dt) 259 | Card_move_ref(self, dt) 260 | if self.children.speech_bubble then 261 | local align = nil 262 | if self.T.x+self.T.w/2 > G.ROOM.T.w/2 then align = "cl" end 263 | self.children.speech_bubble:set_alignment({type = align or "cr", offset = {x=align and -0.1 or 0.1,y=0}, parent = self}) 264 | end 265 | end 266 | 267 | -- New Run Page 268 | 269 | G.FUNCS.run_setup_partners_option = function(e) 270 | G.SETTINGS.paused = true 271 | G.FUNCS.overlay_menu{ 272 | definition = create_UIBox_partners_option(), 273 | config = {no_esc = true} 274 | } 275 | end 276 | 277 | function create_UIBox_partners_option() 278 | G.GAME.viewed_partner = G.P_CENTER_POOLS["Partner"][G.PROFILES[G.SETTINGS.profile].MEMORY.partner] or G.P_CENTER_POOLS["Partner"][1] 279 | local partner_selection, partner_selection_cycle = create_partner_selection() 280 | G.partner_area = CardArea(G.ROOM.T.x, G.ROOM.T.h, G.CARD_W*46/71, G.CARD_H*58/95, {card_limit = 2, type = "title", highlight_limit = 0}) 281 | local center = G.GAME.viewed_partner 282 | local card = Card(G.partner_area.T.x+G.partner_area.T.w/2-G.CARD_W*23/71, G.partner_area.T.y+G.partner_area.T.h/2-G.CARD_H*29/95, G.CARD_W*46/71, G.CARD_H*58/95, G.P_CARDS.empty, center) 283 | local minw = 3 284 | local UI_table = G.GAME.viewed_partner:is_unlocked() and generate_card_ui(G.GAME.viewed_partner, nil, nil, "Partner") or generate_card_ui(G.GAME.viewed_partner, nil, nil, "Locked") 285 | local partner_main = {n=G.UIT.ROOT, config={align = "cm", minw = minw, minh = 2, id = G.GAME.viewed_partner.name, colour = G.C.CLEAR}, nodes={desc_from_rows(UI_table.main, true, minw-0.2)}} 286 | --card.sticker = get_joker_win_sticker(center) 287 | card.states.hover.can = false 288 | G.partner_area:emplace(card) 289 | local t = create_UIBox_generic_options({no_back = true, contents = { 290 | {n=G.UIT.R, config={align = "cm", minw = 2.5, padding = 0.15, r = 0.1, colour = G.C.L_BLACK}, nodes={ 291 | {n=G.UIT.C, config={align = "cm", padding = 0}, nodes={ 292 | partner_selection, 293 | partner_selection_cycle 294 | }}, 295 | {n=G.UIT.C, config={align = "tm", minw = 3, minh = 1, r = 0.1, colour = G.C.BLACK, padding = 0.15, emboss = 0.05}, nodes={ 296 | {n=G.UIT.R, config={align = "cm", emboss = 0.1, r = 0.1, minw = 2, minh = 0.5}, nodes={ 297 | {n=G.UIT.O, config={id = nil, func = "RUN_SETUP_check_partner_name", object = Moveable()}}, 298 | }}, 299 | {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ 300 | {n=G.UIT.O, config={id = G.GAME.viewed_partner.name, func = "RUN_SETUP_check_partner_card", object = G.partner_area}}, 301 | }}, 302 | {n=G.UIT.R, config={align = "cm", colour = G.C.WHITE, emboss = 0.1, r = 0.1}, nodes={ 303 | {n=G.UIT.O, config={id = G.GAME.viewed_partner.name, func = "RUN_SETUP_check_partner", object = UIBox{definition = partner_main, config = {offset = {x=0,y=0}}}}} 304 | }} 305 | }}, 306 | }}, 307 | {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ 308 | {n=G.UIT.C, config={minw = 2.72, minh = 0.8, r = 0.1, hover = true, button = "skip_partner", colour = G.C.FILTER, align = "cm", emboss = 0.1}, nodes={ 309 | {n=G.UIT.R, config={align = "cm"}, nodes={ 310 | {n=G.UIT.T, config={text = localize("b_partner_skip"), scale = 0.5, colour = G.C.WHITE}} 311 | }}, 312 | }}, 313 | {n=G.UIT.C, config={align = "cm", minw = 0.2}, nodes={}}, 314 | {n=G.UIT.C, config={minw = 2.72, minh = 0.8, r = 0.1, hover = true, button = "random_partner", colour = G.C.BLUE, align = "cm", emboss = 0.1}, nodes={ 315 | {n=G.UIT.R, config={align = "cm"}, nodes={ 316 | {n=G.UIT.T, config={text = localize("b_partner_random"), scale = 0.5, colour = G.C.WHITE}} 317 | }}, 318 | }}, 319 | {n=G.UIT.C, config={align = "cm", minw = 0.2}, nodes={}}, 320 | {n=G.UIT.C, config={minw = 3.33, minh = 0.8, r = 0.1, hover = true, button = "select_partner", func = "select_partner_button", align = "cm", emboss = 0.1}, nodes={ 321 | {n=G.UIT.R, config={align = "cm"}, nodes={ 322 | {n=G.UIT.T, config={text = localize("b_partner_agree"), scale = 0.5, colour = G.C.WHITE}} 323 | }}, 324 | }}, 325 | }}, 326 | }}) 327 | return t 328 | end 329 | 330 | function create_partner_selection() 331 | local partner_tables = {} 332 | G.partner_selection = {} 333 | for i = 1, 2 do 334 | local row = {n=G.UIT.R, config={colour = G.C.LIGHT}, nodes={}} 335 | for j = 1, 4 do 336 | G.partner_selection[j+(i-1)*4] = CardArea(G.ROOM.T.x, G.ROOM.T.h, G.CARD_W*46/71, G.CARD_H*58/95, {card_limit = 2, type = "title", highlight_limit = 0}) 337 | table.insert(row.nodes, {n=G.UIT.O, config={object = G.partner_selection[j+(i-1)*4]}}) 338 | end 339 | table.insert(partner_tables, row) 340 | end 341 | local partner_options = {} 342 | for i = 1, math.ceil(#G.P_CENTER_POOLS["Partner"]/(#G.partner_selection)) do 343 | table.insert(partner_options, localize("k_page").." "..tostring(i).."/"..tostring(math.ceil(#G.P_CENTER_POOLS["Partner"]/(#G.partner_selection)))) 344 | end 345 | local viewed_partner = 1 346 | for k, v in pairs(G.P_CENTER_POOLS["Partner"]) do 347 | if v.name == G.GAME.viewed_partner.name then 348 | viewed_partner = math.ceil(k/(#G.partner_selection)) 349 | break 350 | end 351 | end 352 | for i = 1, #G.partner_selection do 353 | local center = G.P_CENTER_POOLS["Partner"][i+(#G.partner_selection*(viewed_partner-1))] 354 | if not center then break end 355 | local card = Card(G.partner_selection[i].T.x+G.partner_selection[i].T.w/2-G.CARD_W*23/71, G.partner_selection[i].T.y+G.partner_selection[i].T.h/2-G.CARD_H*29/95, G.CARD_W*46/71, G.CARD_H*58/95, empty, center) 356 | card.no_ui = true; card.config.card.no_ui = true 357 | card.ability.fake_partner = true 358 | G.partner_selection[i]:emplace(card) 359 | end 360 | local t, tt = {n=G.UIT.R, config={align = "cm", r = 0.1, minh = 3.6, colour = G.C.BLACK, emboss = 0.05}, nodes=partner_tables}, 361 | {n=G.UIT.R, config={align = "cm"}, nodes={ 362 | create_option_cycle({options = partner_options, w = 2.5, cycle_shoulders = true, opt_callback = "your_selection_partner_page", current_option = viewed_partner, colour = G.C.RED, no_pips = true, focus_args = {snap_to = true, nav = "wide"}}) 363 | }} 364 | return t, tt 365 | end 366 | 367 | G.FUNCS.your_selection_partner_page = function(args) 368 | if not args or not args.cycle_config then return end 369 | for j = 1, #G.partner_selection do 370 | for i = #G.partner_selection[j].cards, 1, -1 do 371 | local c = G.partner_selection[j]:remove_card(G.partner_selection[j].cards[i]) 372 | c:remove() 373 | c = nil 374 | end 375 | end 376 | for j = 1, #G.partner_selection do 377 | local center = G.P_CENTER_POOLS["Partner"][j+(#G.partner_selection*(args.cycle_config.current_option-1))] 378 | if not center then break end 379 | local card = Card(G.partner_selection[j].T.x+G.partner_selection[j].T.w/2-G.CARD_W*23/71, G.partner_selection[j].T.y+G.partner_selection[j].T.h/2-G.CARD_H*29/95, G.CARD_W*46/71, G.CARD_H*58/95, G.P_CARDS.empty, center) 380 | card.no_ui = true; card.config.card.no_ui = true 381 | card.ability.fake_partner = true 382 | G.partner_selection[j]:emplace(card) 383 | end 384 | end 385 | 386 | G.FUNCS.RUN_SETUP_check_partner_card = function(e) 387 | if e.config.object and G.GAME.viewed_partner.name ~= e.config.id then 388 | local c = G.partner_area:remove_card(G.partner_area.cards[1]) 389 | c:remove() 390 | c = nil 391 | local center = G.GAME.viewed_partner 392 | local card = Card(G.partner_area.T.x+G.partner_area.T.w/2-G.CARD_W*23/71, G.partner_area.T.y+G.partner_area.T.h/2-G.CARD_H*29/95, G.CARD_W*46/71, G.CARD_H*58/95, G.P_CARDS.empty, center) 393 | card.states.hover.can = false 394 | G.partner_area:emplace(card) 395 | e.config.id = G.GAME.viewed_partner.name 396 | end 397 | end 398 | 399 | G.FUNCS.RUN_SETUP_check_partner_name = function(e) 400 | if e.config.object and G.GAME.viewed_partner.name ~= e.config.id then 401 | local partner_name = G.GAME.viewed_partner:is_unlocked() and localize{type = "name_text", set = "Partner", key = G.GAME.viewed_partner.key} or localize("k_locked") 402 | e.config.object:remove() 403 | e.config.object = UIBox{ 404 | definition = {n=G.UIT.ROOT, config={align = "cm", colour = G.C.CLEAR}, nodes={ 405 | {n=G.UIT.O, config={id = G.GAME.viewed_partner.name, func = "RUN_SETUP_check_partner_name", object = DynaText({string = partner_name, maxw = 4, colours = {G.C.WHITE}, shadow = true, bump = true, scale = 0.5, pop_in = 0, silent = true})}}, 406 | }}, 407 | config = {offset = {x=0,y=0}, align = "cm", parent = e} 408 | } 409 | e.config.id = G.GAME.viewed_partner.name 410 | end 411 | end 412 | 413 | G.FUNCS.RUN_SETUP_check_partner = function(e) 414 | if G.GAME.viewed_partner.name ~= e.config.id then 415 | local minw = 3 416 | local UI_table = G.GAME.viewed_partner:is_unlocked() and generate_card_ui(G.GAME.viewed_partner, nil, nil, "Partner") or generate_card_ui(G.GAME.viewed_partner, nil, nil, "Locked") 417 | local partner_main = {n=G.UIT.ROOT, config={align = "cm", minw = minw, minh = 2, id = G.GAME.viewed_partner.name, colour = G.C.CLEAR}, nodes={desc_from_rows(UI_table.main, true, minw-0.2)}} 418 | e.config.object:remove() 419 | e.config.object = UIBox{ 420 | definition = partner_main, 421 | config = {offset = {x=0,y=0}, align = "cm", parent = e} 422 | } 423 | e.config.id = G.GAME.viewed_partner.name 424 | end 425 | end 426 | 427 | G.FUNCS.skip_partner = function() 428 | G.FUNCS.exit_overlay_menu() 429 | G.GAME.skip_partner = true 430 | end 431 | 432 | G.FUNCS.random_partner = function() 433 | local center = pseudorandom_element(G.P_CENTER_POOLS["Partner"], pseudoseed(os.time())) 434 | G.GAME.viewed_partner = center 435 | for k, v in pairs(G.P_CENTER_POOLS["Partner"]) do 436 | if v == G.GAME.viewed_partner then 437 | G.PROFILES[G.SETTINGS.profile].MEMORY.partner = k 438 | end 439 | end 440 | end 441 | 442 | G.FUNCS.select_partner_button = function(e) 443 | if G.GAME.viewed_partner and G.GAME.viewed_partner:is_unlocked() then 444 | e.config.colour = G.C.GREEN 445 | e.config.button = "select_partner" 446 | else 447 | e.config.colour = G.C.UI.BACKGROUND_INACTIVE 448 | e.config.button = nil 449 | end 450 | end 451 | 452 | G.FUNCS.select_partner = function() 453 | G.FUNCS.exit_overlay_menu() 454 | G.E_MANAGER:add_event(Event({func = function() 455 | G.GAME.selected_partner = G.GAME.viewed_partner.key 456 | G.GAME.selected_partner_card = Card(G.deck.T.x+G.deck.T.w-G.CARD_W*0.6, G.deck.T.y-G.CARD_H*1.6, G.CARD_W*46/71, G.CARD_H*58/95, G.P_CARDS.empty, G.GAME.viewed_partner) 457 | G.GAME.selected_partner_card:juice_up(0.3, 0.5) 458 | return true end})) 459 | end 460 | 461 | local run_setup_option_ref = G.UIDEF.run_setup_option 462 | function G.UIDEF.run_setup_option(type) 463 | local t = run_setup_option_ref(type) 464 | if type == "New Run" then 465 | t.nodes[#t.nodes].nodes[#t.nodes[#t.nodes].nodes] = {n=G.UIT.C, config={align = "cm", minw = 2.4}, nodes={ 466 | type == "New Run" and create_toggle{col = true, label = localize("k_partner"), label_scale = 0.28, w = 0, scale = 0.7, ref_table = Partner_API.config, ref_value = "enable_partner"} or nil 467 | }} 468 | end 469 | return t 470 | end 471 | 472 | -- Galdur Compat 473 | local run_setup_option_new_model_ref = G.UIDEF.run_setup_option_new_model 474 | function G.UIDEF.run_setup_option_new_model(type) 475 | local t = run_setup_option_new_model_ref(type) 476 | t.nodes[#t.nodes].nodes[2].nodes[#t.nodes[#t.nodes].nodes[2].nodes+1] = {n=G.UIT.C, config={align = "cm", minw = 2.4}, nodes={ 477 | type == "New Run" and create_toggle{col = true, label = localize("k_partner"), label_scale = 0.28, w = 0, scale = 0.7, ref_table = Partner_API.config, ref_value = "enable_partner"} or nil 478 | }} 479 | return t 480 | end 481 | 482 | local Game_start_run_ref = Game.start_run 483 | function Game:start_run(args) 484 | Game_start_run_ref(self, args) 485 | if not G.GAME.selected_partner and not G.GAME.skip_partner and Partner_API.config.enable_partner then 486 | G.E_MANAGER:add_event(Event({func = function() 487 | G.FUNCS.run_setup_partners_option() 488 | return true end})) 489 | elseif G.GAME.selected_partner then 490 | G.E_MANAGER:add_event(Event({func = function() 491 | local center = nil 492 | for k, v in pairs(G.P_CENTER_POOLS["Partner"]) do 493 | if v.key == G.GAME.selected_partner then center = v end 494 | end 495 | G.GAME.selected_partner_card = Card(G.deck.T.x+G.deck.T.w-G.CARD_W*0.6, G.deck.T.y-G.CARD_H*1.6, G.CARD_W*46/71, G.CARD_H*58/95, G.P_CARDS.empty, center) 496 | G.GAME.selected_partner_card:juice_up(0.3, 0.5) 497 | if G.GAME.selected_partner_table then 498 | for k, v in pairs(G.GAME.selected_partner_table) do 499 | G.GAME.selected_partner_card.ability.extra[k] = v 500 | end 501 | G.GAME.selected_partner_table = nil 502 | end 503 | return true end})) 504 | end 505 | end 506 | 507 | -- Controller Page 508 | 509 | local Controller_queue_R_cursor_press_ref = Controller.queue_R_cursor_press 510 | function Controller:queue_R_cursor_press(x, y) 511 | Controller_queue_R_cursor_press_ref(self, x, y) 512 | if self.locks.frame then return end 513 | self.partner_R_cursor_queue = {x = x, y = y} 514 | end 515 | 516 | local Controller_update_ref = Controller.update 517 | function Controller:update(dt) 518 | Controller_update_ref(self, dt) 519 | if self.partner_R_cursor_queue then 520 | self:partner_R_cursor_press(self.partner_R_cursor_queue.x, self.partner_R_cursor_queue.y) 521 | self.partner_R_cursor_queue = nil 522 | end 523 | if not self.cursor_up.partner_R_handled then 524 | if self.cursor_down.partner_R_target then 525 | if (not self.cursor_down.partner_R_target.click_timeout or self.cursor_down.partner_R_target.click_timeout*G.SPEEDFACTOR > self.cursor_up.partner_R_time - self.cursor_down.partner_R_time) then 526 | if Vector_Dist(self.cursor_down.partner_R_T, self.cursor_up.partner_R_T) < G.MIN_CLICK_DIST then 527 | if self.cursor_down.partner_R_target.states.click.can then 528 | self.clicked.partner_R_target = self.cursor_down.partner_R_target 529 | self.clicked.partner_R_handled = false 530 | end 531 | end 532 | end 533 | end 534 | self.cursor_up.partner_R_handled = true 535 | end 536 | if not self.clicked.partner_R_handled then 537 | if self.clicked.partner_R_target then 538 | self.clicked.partner_R_target:partner_R_click() 539 | self.clicked.partner_R_handled = true 540 | end 541 | end 542 | end 543 | 544 | function Controller:partner_R_cursor_press(x, y) 545 | if ((self.locked) and (not G.SETTINGS.paused or G.screenwipe)) or (self.locks.frame) then return end 546 | local x = x or self.cursor_position.x 547 | local y = y or self.cursor_position.y 548 | self.cursor_down.partner_R_T = {x = x/(G.TILESCALE*G.TILESIZE), y = y/(G.TILESCALE*G.TILESIZE)} 549 | self.cursor_down.partner_R_time = G.TIMERS.TOTAL 550 | self.cursor_down.partner_R_target = nil 551 | local press_node = (self.HID.touch and self.cursor_hover.target) or self.hovering.target or self.focused.target 552 | if press_node then 553 | self.cursor_down.partner_R_target = press_node.states.click.can and press_node or press_node:can_drag() or nil 554 | end 555 | if self.cursor_down.partner_R_target == nil then 556 | self.cursor_down.partner_R_target = G.ROOM 557 | end 558 | end 559 | 560 | local love_mousereleased_ref = love.mousereleased 561 | function love.mousereleased(x, y, button) 562 | love_mousereleased_ref(x, y, button) 563 | if button == 2 then G.CONTROLLER:partner_R_cursor_release(x, y) end 564 | end 565 | 566 | local Controller_button_release_update_ref = Controller.button_release_update 567 | function Controller:button_release_update(button, dt) 568 | Controller_button_release_update_ref(self, button, dt) 569 | if not self.held_button_times[button] then return end 570 | if button == "b" then 571 | self:partner_R_cursor_release() 572 | end 573 | end 574 | 575 | function Controller:partner_R_cursor_release(x, y) 576 | if ((self.locked) and (not G.SETTINGS.paused or G.screenwipe)) or (self.locks.frame) then return end 577 | local x = x or self.cursor_position.x 578 | local y = y or self.cursor_position.y 579 | self.cursor_up.partner_R_T = {x = x/(G.TILESCALE*G.TILESIZE), y = y/(G.TILESCALE*G.TILESIZE)} 580 | self.cursor_up.partner_R_time = G.TIMERS.TOTAL 581 | self.cursor_up.partner_R_handled = false 582 | end 583 | 584 | function Node:partner_R_click() end 585 | 586 | -- Hook Page 587 | 588 | local save_run_ref = save_run 589 | function save_run() 590 | if G.GAME.selected_partner_card and G.GAME.selected_partner_card.ability then 591 | G.GAME.selected_partner_table = G.GAME.selected_partner_table or {} 592 | for k, v in pairs(G.GAME.selected_partner_card.ability.extra) do 593 | G.GAME.selected_partner_table[k] = v 594 | end 595 | end 596 | save_run_ref() 597 | end 598 | 599 | local Card_click_ref = Card.click 600 | function Card:click() 601 | Card_click_ref(self) 602 | if self.ability.set == "Partner" and self.ability.fake_partner then 603 | G.GAME.viewed_partner = self.config.center 604 | for k, v in pairs(G.P_CENTER_POOLS["Partner"]) do 605 | if v == G.GAME.viewed_partner then 606 | G.PROFILES[G.SETTINGS.profile].MEMORY.partner = k 607 | end 608 | end 609 | end 610 | if G.GAME.selected_partner_card and G.GAME.selected_partner_card == self then 611 | if self.children.speech_bubble then 612 | self:remove_partner_speech_bubble() 613 | elseif not G.GAME.partner_click_deal then 614 | G.GAME.partner_click_deal = true 615 | local ret = G.GAME.selected_partner_card:calculate_partner({partner_click = true}) 616 | if ret then 617 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 618 | end 619 | G.E_MANAGER:add_event(Event({func = function() 620 | G.GAME.partner_click_deal = nil 621 | return true end})) 622 | end 623 | end 624 | end 625 | 626 | function Card:partner_R_click() 627 | if G.GAME.selected_partner_card and G.GAME.selected_partner_card == self then 628 | if not G.GAME.partner_R_click_deal then 629 | G.GAME.partner_R_click_deal = true 630 | local ret = G.GAME.selected_partner_card:calculate_partner({partner_R_click = true}) 631 | if ret then 632 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 633 | end 634 | G.E_MANAGER:add_event(Event({func = function() 635 | G.GAME.partner_R_click_deal = nil 636 | return true end})) 637 | end 638 | end 639 | end 640 | 641 | -- Talisman Compat 642 | to_big = to_big or function(a) 643 | return a 644 | end 645 | 646 | to_number = to_number or function(a) 647 | return a 648 | end 649 | 650 | function Card:calculate_partner(context) 651 | if not context then return end 652 | local obj = self.config.center 653 | if self.ability.set == "Partner" and obj.calculate and type(obj.calculate) == "function" then 654 | local ret = obj:calculate(self, context) 655 | self:general_partner_speech(context) 656 | if ret then return ret end 657 | end 658 | end 659 | 660 | function Card:general_partner_speech(context) 661 | if not context or self.config.center.no_quips then return end 662 | if context.partner_setting_blind and G.GAME.round == 1 then 663 | if self.config.center.individual_quips then 664 | G.E_MANAGER:add_event(Event({func = function() 665 | local max_quips = 0 666 | for k, v in pairs(G.localization.misc.quips) do 667 | if string.find(k, self.config.center.key) then 668 | max_quips = max_quips + 1 669 | end 670 | end 671 | self:add_partner_speech_bubble(self.config.center.key.."_"..math.random(1, max_quips)) 672 | self:partner_say_stuff(5) 673 | return true end})) 674 | else 675 | G.E_MANAGER:add_event(Event({func = function() 676 | self:add_partner_speech_bubble("pnr_"..math.random(1,6)) 677 | self:partner_say_stuff(5) 678 | return true end})) 679 | end 680 | end 681 | if context.partner_setting_blind and context.blind.boss and G.GAME.round_resets.ante == G.GAME.win_ante then 682 | if self.config.center.individual_quips then 683 | G.E_MANAGER:add_event(Event({func = function() 684 | local max_quips = 0 685 | for k, v in pairs(G.localization.misc.quips) do 686 | if string.find(k, self.config.center.key) then 687 | max_quips = max_quips + 1 688 | end 689 | end 690 | self:add_partner_speech_bubble(self.config.center.key.."_"..math.random(1, max_quips)) 691 | self:partner_say_stuff(5) 692 | return true end})) 693 | else 694 | G.E_MANAGER:add_event(Event({func = function() 695 | self:add_partner_speech_bubble("dq_1") 696 | self:partner_say_stuff(5) 697 | return true end})) 698 | end 699 | end 700 | end 701 | 702 | function Card:calculate_partner_cash() 703 | local obj = self.config.center 704 | if self.ability.set == "Partner" and obj.calculate_cash and type(obj.calculate_cash) == "function" then 705 | local ret = obj:calculate_cash(self) 706 | if ret then return ret end 707 | end 708 | end 709 | 710 | local SMODS_calculate_context_ref = SMODS.calculate_context 711 | function SMODS.calculate_context(context, return_table) 712 | local _ret = SMODS_calculate_context_ref(context, return_table) 713 | if G.GAME.selected_partner_card then 714 | local ret = G.GAME.selected_partner_card:calculate_partner(context) 715 | if ret then 716 | SMODS.trigger_effects({{individual = ret}}, G.GAME.selected_partner_card) 717 | end 718 | end 719 | return _ret 720 | end 721 | 722 | local SMODS_calculate_repetitions_ref = SMODS.calculate_repetitions 723 | SMODS.calculate_repetitions = function(card, context, reps) 724 | local reps = SMODS_calculate_repetitions_ref(card, context, reps) 725 | if G.GAME.selected_partner_card then 726 | local ret = G.GAME.selected_partner_card:calculate_partner(context) 727 | if ret then 728 | SMODS.insert_repetitions(reps, ret, card) 729 | end 730 | end 731 | return reps 732 | end 733 | 734 | local SMODS_calculate_card_areas_ref = SMODS.calculate_card_areas 735 | function SMODS.calculate_card_areas(_type, context, return_table, args) 736 | local flags = SMODS_calculate_card_areas_ref(_type, context, return_table, args) 737 | if G.GAME.selected_partner_card then 738 | if _type == "individual" and args and args.main_scoring then 739 | local ret = G.GAME.selected_partner_card:calculate_partner(context) 740 | if ret then 741 | return_table[#return_table+1] = {individual = ret} 742 | end 743 | end 744 | end 745 | return flags 746 | end 747 | 748 | -- Localization Page 749 | 750 | function Partner_API.process_loc_text() 751 | G.localization.descriptions.Partner = G.localization.descriptions.Partner or {} 752 | end 753 | 754 | -- Atlas Page 755 | 756 | SMODS.Atlas{ 757 | key = "modicon", 758 | px = 34, 759 | py = 34, 760 | path = "icon.png" 761 | } 762 | 763 | SMODS.Atlas{ 764 | key = "Partner", 765 | px = 46, 766 | py = 58, 767 | path = "Partners.png" 768 | } 769 | 770 | -- Register Page 771 | 772 | Partner_API.Partner{ 773 | key = "joker", 774 | name = "Joker Partner", 775 | unlocked = false, 776 | discovered = true, 777 | pos = {x = 0, y = 0}, 778 | loc_txt = {}, 779 | atlas = "Partner", 780 | config = {extra = {related_card = "j_joker", chips = 0, chip_mod = 2}}, 781 | loc_vars = function(self, info_queue, card) 782 | local benefits = 1 783 | if next(SMODS.find_card("j_joker")) then benefits = 2 end 784 | return { vars = {card.ability.extra.chips, card.ability.extra.chip_mod*benefits} } 785 | end, 786 | calculate = function(self, card, context) 787 | if context.partner_main and card.ability.extra.chips >= 1 then 788 | return { 789 | message = localize{type = "variable", key = "a_chips", vars = {card.ability.extra.chips}}, 790 | chip_mod = card.ability.extra.chips, 791 | colour = G.C.CHIPS 792 | } 793 | end 794 | if context.partner_before then 795 | local benefits = 1 796 | if next(SMODS.find_card("j_joker")) then benefits = 2 end 797 | card.ability.extra.chips = card.ability.extra.chips + card.ability.extra.chip_mod*benefits 798 | card_eval_status_text(card, "extra", nil, nil, nil, {message = localize("k_upgrade_ex"), colour = G.C.CHIPS}) 799 | end 800 | end, 801 | check_for_unlock = function(self, args) 802 | for _, v in pairs(G.P_CENTER_POOLS["Joker"]) do 803 | if v.key == "j_joker" then 804 | if get_joker_win_sticker(v, true) >= 8 then 805 | return true 806 | end 807 | break 808 | end 809 | end 810 | end, 811 | } 812 | 813 | Partner_API.Partner{ 814 | key = "mime", 815 | name = "Mime Partner", 816 | unlocked = false, 817 | discovered = true, 818 | pos = {x = 1, y = 0}, 819 | loc_txt = {}, 820 | atlas = "Partner", 821 | individual_quips = true, 822 | config = {extra = {related_card = "j_mime", repetitions = 1}}, 823 | loc_vars = function(self, info_queue, card) 824 | local benefits = 1 825 | if next(SMODS.find_card("j_mime")) then benefits = 2 end 826 | return { vars = {card.ability.extra.repetitions*benefits} } 827 | end, 828 | calculate = function(self, card, context) 829 | if context.repetition and context.other_card and G.hand.cards[1] and context.other_card == G.hand.cards[1] and next(context.card_effects[1]) then 830 | local benefits = 1 831 | if next(SMODS.find_card("j_mime")) then benefits = 2 end 832 | return { 833 | message = localize("k_again_ex"), 834 | repetitions = card.ability.extra.repetitions*benefits, 835 | card = card 836 | } 837 | end 838 | end, 839 | check_for_unlock = function(self, args) 840 | for _, v in pairs(G.P_CENTER_POOLS["Joker"]) do 841 | if v.key == "j_mime" then 842 | if get_joker_win_sticker(v, true) >= 8 then 843 | return true 844 | end 845 | break 846 | end 847 | end 848 | end, 849 | } 850 | 851 | Partner_API.Partner{ 852 | key = "raised_fist", 853 | name = "Fist Partner", 854 | unlocked = false, 855 | discovered = true, 856 | pos = {x = 2, y = 0}, 857 | loc_txt = {}, 858 | atlas = "Partner", 859 | config = {extra = {related_card = "j_raised_fist", mult = 1, mult_mod = 0.5}}, 860 | loc_vars = function(self, info_queue, card) 861 | return { vars = {card.ability.extra.mult} } 862 | end, 863 | calculate = function(self, card, context) 864 | if context.individual and context.other_card and G.hand.cards[1] and context.poker_hands and next(context.poker_hands["Pair"]) then 865 | local min_id = 15 866 | local raised_card = nil 867 | for i = 1, #G.hand.cards do 868 | if min_id >= G.hand.cards[i].base.id and not SMODS.has_no_rank(G.hand.cards[i]) then 869 | min_id = G.hand.cards[i].base.id 870 | raised_card = G.hand.cards[i] 871 | end 872 | end 873 | if context.other_card == raised_card then 874 | if context.other_card.debuff then 875 | return { 876 | message = localize("k_debuffed"), 877 | colour = G.C.RED, 878 | card = card, 879 | } 880 | else 881 | return { 882 | mult = card.ability.extra.mult, 883 | card = card 884 | } 885 | end 886 | end 887 | end 888 | if context.partner_before and context.poker_hands and next(context.poker_hands["Pair"]) then 889 | local benefits = 1 890 | if next(SMODS.find_card("j_raised_fist")) then benefits = 2 end 891 | card.ability.extra.mult = card.ability.extra.mult + card.ability.extra.mult_mod*benefits 892 | card_eval_status_text(card, "extra", nil, nil, nil, {message = localize("k_upgrade_ex"), colour = G.C.MULT}) 893 | end 894 | end, 895 | check_for_unlock = function(self, args) 896 | for _, v in pairs(G.P_CENTER_POOLS["Joker"]) do 897 | if v.key == "j_raised_fist" then 898 | if get_joker_win_sticker(v, true) >= 8 then 899 | return true 900 | end 901 | break 902 | end 903 | end 904 | end, 905 | } 906 | 907 | Partner_API.Partner{ 908 | key = "egg", 909 | name = "Egg Partner", 910 | unlocked = false, 911 | discovered = true, 912 | pos = {x = 3, y = 0}, 913 | loc_txt = {}, 914 | atlas = "Partner", 915 | config = {extra = {related_card = "j_egg", sell_cost_mod = 1}}, 916 | loc_vars = function(self, info_queue, card) 917 | local benefits = 1 918 | if next(SMODS.find_card("j_egg")) then benefits = 2 end 919 | return { vars = {card.ability.extra.sell_cost_mod*benefits} } 920 | end, 921 | calculate = function(self, card, context) 922 | if context.partner_end_of_round then 923 | local benefits = 1 924 | if next(SMODS.find_card("j_egg")) then benefits = 2 end 925 | for k, v in ipairs(G.jokers.cards) do 926 | v.ability.extra_value = (v.ability.extra_value or 0) + card.ability.extra.sell_cost_mod*benefits 927 | v:set_cost() 928 | end 929 | card_eval_status_text(card, "extra", nil, nil, nil, {message = localize("k_val_up"), colour = G.C.MONEY}) 930 | end 931 | end, 932 | check_for_unlock = function(self, args) 933 | for _, v in pairs(G.P_CENTER_POOLS["Joker"]) do 934 | if v.key == "j_egg" then 935 | if get_joker_win_sticker(v, true) >= 8 then 936 | return true 937 | end 938 | break 939 | end 940 | end 941 | end, 942 | } 943 | 944 | Partner_API.Partner{ 945 | key = "burglar", 946 | name = "Burglar Partner", 947 | unlocked = false, 948 | discovered = true, 949 | pos = {x = 0, y = 1}, 950 | loc_txt = {}, 951 | atlas = "Partner", 952 | config = {extra = {related_card = "j_burglar", hands_played_mod = 2}}, 953 | loc_vars = function(self, info_queue, card) 954 | local benefits = 1 955 | if next(SMODS.find_card("j_burglar")) then benefits = 2 end 956 | return { vars = {card.ability.extra.hands_played_mod*benefits} } 957 | end, 958 | calculate = function(self, card, context) 959 | if context.partner_setting_blind and context.blind.boss then 960 | local benefits = 1 961 | if next(SMODS.find_card("j_burglar")) then benefits = 2 end 962 | G.E_MANAGER:add_event(Event({func = function() 963 | ease_hands_played(card.ability.extra.hands_played_mod*benefits) 964 | card_eval_status_text(card, "extra", nil, nil, nil, {message = localize{type = "variable", key = "a_hands", vars = {card.ability.extra.hands_played_mod*benefits}}}) 965 | return true end})) 966 | end 967 | end, 968 | check_for_unlock = function(self, args) 969 | for _, v in pairs(G.P_CENTER_POOLS["Joker"]) do 970 | if v.key == "j_burglar" then 971 | if get_joker_win_sticker(v, true) >= 8 then 972 | return true 973 | end 974 | break 975 | end 976 | end 977 | end, 978 | } 979 | 980 | Partner_API.Partner{ 981 | key = "faceless", 982 | name = "Faceless Partner", 983 | unlocked = false, 984 | discovered = true, 985 | pos = {x = 1, y = 1}, 986 | loc_txt = {}, 987 | atlas = "Partner", 988 | config = {extra = {related_card = "j_faceless", discard_dollars = 1}}, 989 | loc_vars = function(self, info_queue, card) 990 | local benefits = 1 991 | if next(SMODS.find_card("j_faceless")) then benefits = 2 end 992 | return { vars = {card.ability.extra.discard_dollars*benefits} } 993 | end, 994 | calculate = function(self, card, context) 995 | if context.partner_discard and context.other_card and context.other_card:is_face() then 996 | local benefits = 1 997 | if next(SMODS.find_card("j_faceless")) then benefits = 2 end 998 | ease_dollars(card.ability.extra.discard_dollars*benefits) 999 | card_eval_status_text(card, "extra", nil, nil, nil, {message = localize("$")..card.ability.extra.discard_dollars*benefits, colour = G.C.MONEY}) 1000 | end 1001 | end, 1002 | check_for_unlock = function(self, args) 1003 | for _, v in pairs(G.P_CENTER_POOLS["Joker"]) do 1004 | if v.key == "j_faceless" then 1005 | if get_joker_win_sticker(v, true) >= 8 then 1006 | return true 1007 | end 1008 | break 1009 | end 1010 | end 1011 | end, 1012 | } 1013 | 1014 | Partner_API.Partner{ 1015 | key = "hallucination", 1016 | name = "Hallucination Partner", 1017 | unlocked = false, 1018 | discovered = true, 1019 | pos = {x = 2, y = 1}, 1020 | loc_txt = {}, 1021 | atlas = "Partner", 1022 | config = {extra = {related_card = "j_hallucination"}}, 1023 | loc_vars = function(self, info_queue, card) 1024 | return { vars = {} } 1025 | end, 1026 | calculate = function(self, card, context) 1027 | if context.partner_open_booster then 1028 | G.E_MANAGER:add_event(Event({func = function() 1029 | --thats too unbalanced 1030 | --local _planet = nil 1031 | --for k, v in pairs(G.P_CENTER_POOLS.Planet) do 1032 | --if v.config.hand_type == G.GAME.last_hand_played then 1033 | --_planet = v.key 1034 | --end 1035 | --end 1036 | local _card = create_card("Joker", G.pack_cards, nil, nil, nil, nil, nil, "hal_pnr") 1037 | _card:add_to_deck() 1038 | G.pack_cards:emplace(_card) 1039 | if next(SMODS.find_card("j_hallucination")) then G.GAME.pack_choices = G.GAME.pack_choices + 1 end 1040 | return true end})) 1041 | card_eval_status_text(card, "extra", nil, nil, nil, {message = localize("k_plus_joker"), colour = G.C.GREEN}) 1042 | end 1043 | end, 1044 | check_for_unlock = function(self, args) 1045 | for _, v in pairs(G.P_CENTER_POOLS["Joker"]) do 1046 | if v.key == "j_hallucination" then 1047 | if get_joker_win_sticker(v, true) >= 8 then 1048 | return true 1049 | end 1050 | break 1051 | end 1052 | end 1053 | end, 1054 | } 1055 | 1056 | Partner_API.Partner{ 1057 | key = "fortune_teller", 1058 | name = "Fortune Partner", 1059 | unlocked = false, 1060 | discovered = true, 1061 | pos = {x = 3, y = 1}, 1062 | loc_txt = {}, 1063 | atlas = "Partner", 1064 | config = {extra = {related_card = "j_fortune_teller", round = 3, rounds = 3}}, 1065 | loc_vars = function(self, info_queue, card) 1066 | info_queue[#info_queue+1] = G.P_CENTERS.p_arcana_normal_1 1067 | return { vars = {card.ability.extra.rounds, card.ability.extra.round} } 1068 | end, 1069 | calculate = function(self, card, context) 1070 | if context.partner_setting_blind then 1071 | card.ability.extra.rounds = card.ability.extra.rounds - 1 1072 | if card.ability.extra.rounds <= 0 then 1073 | card.ability.extra.rounds = 0 1074 | end 1075 | end 1076 | if context.partner_starting_shop and card.ability.extra.rounds <= 0 then 1077 | card.ability.extra.rounds = card.ability.extra.round 1078 | G.E_MANAGER:add_event(Event({func = function() 1079 | local key = "p_arcana_normal_"..(math.random(1, 4)) 1080 | if next(SMODS.find_card("j_fortune_teller")) then key = "p_arcana_mega_"..(math.random(1, 2)) end 1081 | local _card = Card(G.shop_booster.T.x+G.shop_booster.T.w/2, G.shop_booster.T.y, G.CARD_W*1.27, G.CARD_H*1.27, G.P_CARDS.empty, G.P_CENTERS[key], {bypass_discovery_center = true, bypass_discovery_ui = true}) 1082 | create_shop_card_ui(_card, "Booster", G.shop_booster) 1083 | _card:start_materialize() 1084 | G.shop_booster:emplace(_card) 1085 | if next(SMODS.find_card("j_fortune_teller")) then _card.ability.couponed = true; _card:set_cost() end 1086 | return true end})) 1087 | card_eval_status_text(card, "extra", nil, nil, nil, {message = localize("k_booster"), colour = G.C.PURPLE}) 1088 | end 1089 | end, 1090 | check_for_unlock = function(self, args) 1091 | for _, v in pairs(G.P_CENTER_POOLS["Joker"]) do 1092 | if v.key == "j_fortune_teller" then 1093 | if get_joker_win_sticker(v, true) >= 8 then 1094 | return true 1095 | end 1096 | break 1097 | end 1098 | end 1099 | end, 1100 | } 1101 | 1102 | Partner_API.Partner{ 1103 | key = "golden", 1104 | name = "Golden Partner", 1105 | unlocked = false, 1106 | discovered = true, 1107 | pos = {x = 0, y = 2}, 1108 | loc_txt = {}, 1109 | atlas = "Partner", 1110 | config = {extra = {related_card = "j_golden", dollars = 2, dollars_mod = 1}}, 1111 | loc_vars = function(self, info_queue, card) 1112 | return { vars = {card.ability.extra.dollars} } 1113 | end, 1114 | calculate = function(self, card, context) 1115 | if context.partner_skip_blind then 1116 | local benefits = 1 1117 | if next(SMODS.find_card("j_golden")) then benefits = 2 end 1118 | card.ability.extra.dollars = card.ability.extra.dollars + card.ability.extra.dollars_mod*benefits 1119 | card_eval_status_text(card, "extra", nil, nil, nil, {message = localize("k_upgrade_ex"), colour = G.C.MONEY}) 1120 | end 1121 | end, 1122 | calculate_cash = function(self, card) 1123 | return card.ability.extra.dollars 1124 | end, 1125 | check_for_unlock = function(self, args) 1126 | for _, v in pairs(G.P_CENTER_POOLS["Joker"]) do 1127 | if v.key == "j_golden" then 1128 | if get_joker_win_sticker(v, true) >= 8 then 1129 | return true 1130 | end 1131 | break 1132 | end 1133 | end 1134 | end, 1135 | } 1136 | 1137 | Partner_API.Partner{ 1138 | key = "baseball", 1139 | name = "Baseball Partner", 1140 | unlocked = false, 1141 | discovered = true, 1142 | pos = {x = 1, y = 2}, 1143 | loc_txt = {}, 1144 | atlas = "Partner", 1145 | config = {extra = {related_card = "j_baseball", mult = 3, mult_mod = 1}}, 1146 | loc_vars = function(self, info_queue, card) 1147 | return { vars = {card.ability.extra.mult} } 1148 | end, 1149 | calculate = function(self, card, context) 1150 | if context.partner_other_main and context.other_card then 1151 | if context.other_card.ability.set == "Joker" and context.other_card.config.center.rarity == 2 then 1152 | return { 1153 | mult = card.ability.extra.mult, 1154 | card = card 1155 | } 1156 | end 1157 | end 1158 | if context.partner_skipping_booster then 1159 | local benefits = 1 1160 | if next(SMODS.find_card("j_baseball")) then benefits = 2 end 1161 | card.ability.extra.mult = card.ability.extra.mult + card.ability.extra.mult_mod*benefits 1162 | card_eval_status_text(card, "extra", nil, nil, nil, {message = localize("k_upgrade_ex"), colour = G.C.MULT}) 1163 | end 1164 | end, 1165 | check_for_unlock = function(self, args) 1166 | for _, v in pairs(G.P_CENTER_POOLS["Joker"]) do 1167 | if v.key == "j_baseball" then 1168 | if get_joker_win_sticker(v, true) >= 8 then 1169 | return true 1170 | end 1171 | break 1172 | end 1173 | end 1174 | end, 1175 | } 1176 | 1177 | Partner_API.Partner{ 1178 | key = "trading", 1179 | name = "Trading Partner", 1180 | unlocked = false, 1181 | discovered = true, 1182 | pos = {x = 2, y = 2}, 1183 | loc_txt = {}, 1184 | atlas = "Partner", 1185 | config = {extra = {related_card = "j_trading", discard_dollars = 2}}, 1186 | loc_vars = function(self, info_queue, card) 1187 | return { vars = {card.ability.extra.discard_dollars} } 1188 | end, 1189 | calculate = function(self, card, context) 1190 | local benefits = 1 1191 | if next(SMODS.find_card("j_trading")) then benefits = 5 end 1192 | if context.partner_discard and G.GAME.current_round.discards_left <= 1 and #context.full_hand <= 1*benefits then 1193 | if context.other_card == context.full_hand[#context.full_hand] then 1194 | ease_dollars(-card.ability.extra.discard_dollars*#context.full_hand) 1195 | card_eval_status_text(card, "dollars", -card.ability.extra.discard_dollars*#context.full_hand) 1196 | end 1197 | return { remove = true } 1198 | end 1199 | end, 1200 | check_for_unlock = function(self, args) 1201 | for _, v in pairs(G.P_CENTER_POOLS["Joker"]) do 1202 | if v.key == "j_trading" then 1203 | if get_joker_win_sticker(v, true) >= 8 then 1204 | return true 1205 | end 1206 | break 1207 | end 1208 | end 1209 | end, 1210 | } 1211 | 1212 | Partner_API.Partner{ 1213 | key = "flash", 1214 | name = "Flash Partner", 1215 | unlocked = false, 1216 | discovered = true, 1217 | pos = {x = 3, y = 2}, 1218 | loc_txt = {}, 1219 | atlas = "Partner", 1220 | config = {extra = {related_card = "j_flash", first_reroll = false}}, 1221 | loc_vars = function(self, info_queue, card) 1222 | info_queue[#info_queue+1] = G.P_CENTERS.e_negative 1223 | return { vars = {} } 1224 | end, 1225 | calculate = function(self, card, context) 1226 | if context.partner_reroll_shop and not card.ability.extra.first_reroll then 1227 | for i = 1, #G.shop_jokers.cards do 1228 | if not G.shop_jokers.cards[i].edition and G.shop_jokers.cards[i].ability.set == "Joker" then 1229 | card.ability.extra.first_reroll = true 1230 | card_eval_status_text(card, "extra", nil, nil, nil, {message = localize{type = "name_text", key = "e_negative", set = "Edition"}, colour = G.C.DARK_EDITION}) 1231 | G.shop_jokers.cards[i]:set_edition({negative = true}, true) 1232 | if next(SMODS.find_card("j_flash")) then G.shop_jokers.cards[i].ability.couponed = true; G.shop_jokers.cards[i]:set_cost() end 1233 | break 1234 | end 1235 | end 1236 | end 1237 | if context.partner_ending_shop and card.ability.extra.first_reroll then 1238 | card.ability.extra.first_reroll = false 1239 | end 1240 | end, 1241 | check_for_unlock = function(self, args) 1242 | for _, v in pairs(G.P_CENTER_POOLS["Joker"]) do 1243 | if v.key == "j_flash" then 1244 | if get_joker_win_sticker(v, true) >= 8 then 1245 | return true 1246 | end 1247 | break 1248 | end 1249 | end 1250 | end, 1251 | } 1252 | 1253 | Partner_API.Partner{ 1254 | key = "throwback", 1255 | name = "Throwback Partner", 1256 | unlocked = false, 1257 | discovered = true, 1258 | pos = {x = 0, y = 3}, 1259 | loc_txt = {}, 1260 | atlas = "Partner", 1261 | individual_quips = true, 1262 | config = {extra = {related_card = "j_throwback", xmult = 1, xmult_mod = 0.5, cost = 2}}, 1263 | loc_vars = function(self, info_queue, card) 1264 | local benefits = 1 1265 | if next(SMODS.find_card("j_throwback")) then benefits = 2 end 1266 | return { vars = {card.ability.extra.xmult, card.ability.extra.xmult_mod*benefits, card.ability.extra.cost} } 1267 | end, 1268 | calculate = function(self, card, context) 1269 | if context.partner_main and card.ability.extra.xmult > 1 then 1270 | return { 1271 | message = localize{type = "variable", key = "a_xmult", vars = {card.ability.extra.xmult}}, 1272 | Xmult_mod = card.ability.extra.xmult, 1273 | } 1274 | end 1275 | if context.partner_after and card.ability.extra.xmult > 1 then 1276 | G.E_MANAGER:add_event(Event({func = function() 1277 | card.ability.extra.xmult = 1 1278 | return true end})) 1279 | card_eval_status_text(card, "extra", nil, nil, nil, {message = localize("k_reset"), colour = G.C.RED}) 1280 | end 1281 | if context.partner_click and ((to_big(G.GAME.dollars) - to_big(G.GAME.bankrupt_at)) >= to_big(card.ability.extra.cost)) then 1282 | G.GAME.partner_click_deal = true 1283 | local benefits = 1 1284 | if next(SMODS.find_card("j_throwback")) then benefits = 2 end 1285 | card.ability.extra.xmult = card.ability.extra.xmult + card.ability.extra.xmult_mod*benefits 1286 | ease_dollars(-card.ability.extra.cost) 1287 | card_eval_status_text(card, "dollars", -card.ability.extra.cost) 1288 | G.E_MANAGER:add_event(Event({func = function() 1289 | G.GAME.partner_click_deal = nil 1290 | return true end})) 1291 | end 1292 | end, 1293 | check_for_unlock = function(self, args) 1294 | for _, v in pairs(G.P_CENTER_POOLS["Joker"]) do 1295 | if v.key == "j_throwback" then 1296 | if get_joker_win_sticker(v, true) >= 8 then 1297 | return true 1298 | end 1299 | break 1300 | end 1301 | end 1302 | end, 1303 | } 1304 | 1305 | Partner_API.Partner{ 1306 | key = "hanging_chad", 1307 | name = "Chad Partner", 1308 | unlocked = false, 1309 | discovered = true, 1310 | pos = {x = 1, y = 3}, 1311 | loc_txt = {}, 1312 | atlas = "Partner", 1313 | config = {extra = {related_card = "j_hanging_chad", repetitions = 1}}, 1314 | loc_vars = function(self, info_queue, card) 1315 | local benefits = 1 1316 | if next(SMODS.find_card("j_hanging_chad")) then benefits = 2 end 1317 | return { vars = {card.ability.extra.repetitions*benefits} } 1318 | end, 1319 | calculate = function(self, card, context) 1320 | if context.repetition and context.other_card and context.scoring_hand and context.other_card == context.scoring_hand[1] then 1321 | local benefits = 1 1322 | if next(SMODS.find_card("j_hanging_chad")) then benefits = 2 end 1323 | return { 1324 | message = localize("k_again_ex"), 1325 | repetitions = card.ability.extra.repetitions*benefits, 1326 | card = card 1327 | } 1328 | end 1329 | end, 1330 | check_for_unlock = function(self, args) 1331 | for _, v in pairs(G.P_CENTER_POOLS["Joker"]) do 1332 | if v.key == "j_hanging_chad" then 1333 | if get_joker_win_sticker(v, true) >= 8 then 1334 | return true 1335 | end 1336 | break 1337 | end 1338 | end 1339 | end, 1340 | } 1341 | 1342 | Partner_API.Partner{ 1343 | key = "bloodstone", 1344 | name = "Blood Partner", 1345 | unlocked = false, 1346 | discovered = true, 1347 | pos = {x = 2, y = 3}, 1348 | loc_txt = {}, 1349 | atlas = "Partner", 1350 | config = {extra = {related_card = "j_bloodstone", xmult = 1.5}}, 1351 | loc_vars = function(self, info_queue, card) 1352 | local benefits = 1 1353 | if next(SMODS.find_card("j_bloodstone")) then benefits = 2 end 1354 | return { vars = {card.ability.extra.xmult*benefits} } 1355 | end, 1356 | calculate = function(self, card, context) 1357 | if context.individual and context.other_card and context.scoring_hand then 1358 | local first_heart = nil 1359 | for i = 1, #context.scoring_hand do 1360 | if context.scoring_hand[i]:is_suit("Hearts") then 1361 | first_heart = context.scoring_hand[i] 1362 | break 1363 | end 1364 | end 1365 | if context.other_card == first_heart then 1366 | local benefits = 1 1367 | if next(SMODS.find_card("j_bloodstone")) then benefits = 2 end 1368 | return { 1369 | x_mult = card.ability.extra.xmult*benefits, 1370 | colour = G.C.RED, 1371 | card = card 1372 | } 1373 | end 1374 | end 1375 | end, 1376 | check_for_unlock = function(self, args) 1377 | for _, v in pairs(G.P_CENTER_POOLS["Joker"]) do 1378 | if v.key == "j_bloodstone" then 1379 | if get_joker_win_sticker(v, true) >= 8 then 1380 | return true 1381 | end 1382 | break 1383 | end 1384 | end 1385 | end, 1386 | } 1387 | 1388 | Partner_API.Partner{ 1389 | key = "burnt", 1390 | name = "Burnt Partner", 1391 | unlocked = false, 1392 | discovered = true, 1393 | pos = {x = 3, y = 3}, 1394 | loc_txt = {}, 1395 | atlas = "Partner", 1396 | config = {extra = {related_card = "j_burnt", odd = 4, upgrade_mod = 1}}, 1397 | loc_vars = function(self, info_queue, card) 1398 | return { vars = {""..(G.GAME and G.GAME.probabilities.normal or 1), card.ability.extra.odd} } 1399 | end, 1400 | calculate = function(self, card, context) 1401 | if context.partner_pre_discard and pseudorandom("burnt_pnr") < G.GAME.probabilities.normal/card.ability.extra.odd then 1402 | local benefits = 1 1403 | if next(SMODS.find_card("j_burnt")) then benefits = 2 end 1404 | local text, disp_text = G.FUNCS.get_poker_hand_info(context.full_hand) 1405 | card_eval_status_text(card, "extra", nil, nil, nil, {message = localize("k_upgrade_ex")}) 1406 | update_hand_text({sound = "button", volume = 0.7, pitch = 0.8, delay = 0.3}, {handname = localize(text, "poker_hands"), chips = G.GAME.hands[text].chips, mult = G.GAME.hands[text].mult, level = G.GAME.hands[text].level}) 1407 | level_up_hand(card, text, nil, card.ability.extra.upgrade_mod*benefits) 1408 | update_hand_text({sound = "button", volume = 0.7, pitch = 1.1, delay = 0}, {mult = 0, chips = 0, handname = "", level = ""}) 1409 | end 1410 | end, 1411 | check_for_unlock = function(self, args) 1412 | for _, v in pairs(G.P_CENTER_POOLS["Joker"]) do 1413 | if v.key == "j_burnt" then 1414 | if get_joker_win_sticker(v, true) >= 8 then 1415 | return true 1416 | end 1417 | break 1418 | end 1419 | end 1420 | end, 1421 | } 1422 | --------------------------------------------------------------------------------