From 5bac8a05fa0f7f2ac70b9754770d7d2b8ae8b6cc Mon Sep 17 00:00:00 2001 From: Jseph Date: Sun, 28 Dec 2025 23:04:55 -0800 Subject: [PATCH 01/10] Check in ininitial work for purchasing reserve cards --- Jseph/purchase_reserve_cards.lua | 138 +++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 Jseph/purchase_reserve_cards.lua diff --git a/Jseph/purchase_reserve_cards.lua b/Jseph/purchase_reserve_cards.lua new file mode 100644 index 0000000..495e879 --- /dev/null +++ b/Jseph/purchase_reserve_cards.lua @@ -0,0 +1,138 @@ +require 'herorealms' +require 'decks' +require 'stdlib' +require 'stdcards' +require 'hardai' +require 'mediumai' +require 'easyai' + +-- Buff that counts how many times the player's deck was shuffled (due to draw or otherwise) +function draw_shuffle_counter_def() + local layout = createLayout({ + name = "Draw Shuffle Counter", + art = "art/t_cunning_of_the_wolf", + text = format("Shuffles: {0}", { getCounter("draw_shuffles") }) + }) + local currentReserve = loc(currentPid, reservePloc) + local reserveSlot = createSlot({ + id = "reserve_card_slot", + expiresArray = { neverExpiry }, + }) + local moveBackToReserve = createCardEffectAbility({ + id = "move_back_to_reserve", + effect = moveTarget(currentReserve).apply(selectTargets().where( + isCardWithSlot("reserve_card_slot").And( + isCardWithSlot("purchased_reserve_card_slot").invert() + ))), + cost = noCost, + trigger = locationChangedCardTrigger, + check = const(1).eq(1), + activations = multipleActivations, + tags = { } + }) + + return createBuffDef({ + id = "draw_shuffle_counter", + name = "Draw Shuffle Counter", + layout = layout, + abilities = { + createAbility({ + id = "draw_shuffle_counter_inc", + trigger = deckShuffledTrigger, + activations = multipleActivations, + effect = incrementCounterEffect("draw_shuffles", const(1)).seq(showTextExpressionEffect( + format("Your deck has been shuffled {0} times.", { getCounter("draw_shuffles") }) + )) + }), + createAbility({ + id = "add_slot_to_reserve_cards", + trigger = autoTrigger, + activations = singleActivation, + effect = addSlotToTarget(reserveSlot).apply(selectLoc(currentReserve)) + }), + }, + cardEffectAbilities = { + moveBackToReserve, + } + }) +end + +-- Skill: pay 1 gold to purchase the first card in your reserve +function purchase_first_reserve_skill_def() + -- TODO implement this basic proof of concept skill. +end + +function setupGame(g) + registerCards(g, { + --purchase_first_reserve_skill_def() + }) + standardSetup(g, { + description = "Custom no heroes game", + playerOrder = { plid1, plid2 }, + ai = ai.CreateKillSwitchAi(createAggressiveAI(), createHardAi2()), + timeoutAi = createTimeoutAi(), + opponents = { { plid1, plid2 } }, + players = { + { + id = plid1, + startDraw = 1, + name = "Player 1", + avatar="assassin", + health = 50, + cards = { + deck = { -- orc deck still in progress + { qty=1, card=dagger_carddef() }, + { qty=1, card=gold_carddef() }, + }, + reserve = { + { qty = 1, card = wizard_treasure_map_carddef() }, + { qty = 1, card = ranger_parrot_carddef() }, + }, + skills = { + --purchase_first_reserve_skill_def() + }, + buffs = { + drawCardsCountAtTurnEndDef(5), + discardCardsAtTurnStartDef(), + fatigueCount(40, 1, "FatigueP1"), + draw_shuffle_counter_def(), + } + } + }, + { + id = plid2, + startDraw = 5, + name = "Player 2", + avatar="assassin", + health = 50, + cards = { + deck = { + { qty=2, card=dagger_carddef() }, + { qty=8, card=gold_carddef() }, + }, + buffs = { + drawCardsCountAtTurnEndDef(5), + discardCardsAtTurnStartDef(), + fatigueCount(40, 1, "FatigueP2") + } + } + } + } + }) +end + +function endGame(g) +end + + function setupMeta(meta) + meta.name = "purchase_reserve_cards" + meta.minLevel = 0 + meta.maxLevel = 0 + meta.introbackground = "" + meta.introheader = "" + meta.introdescription = "" + meta.path = "C:/Users/jseph/hero-realms-lua-scripts/hero-realms-lua-scripts/Jseph/purchase_reserve_cards.lua" + meta.features = { +} + + end \ No newline at end of file From 4f0475721867d418197564adfad84654741e55a5 Mon Sep 17 00:00:00 2001 From: Jseph Date: Mon, 29 Dec 2025 21:24:13 -0800 Subject: [PATCH 02/10] Add skill to purchase reserve cards. Also update lua documentation and add in trickster mod. --- Jseph/purchase_reserve_cards.lua | 63 +- Jseph/trickster.lua | 529 ++++++ Lua_documentation.md | 2857 +++++++++++++++++++++++------- 3 files changed, 2844 insertions(+), 605 deletions(-) create mode 100644 Jseph/trickster.lua diff --git a/Jseph/purchase_reserve_cards.lua b/Jseph/purchase_reserve_cards.lua index 495e879..c776513 100644 --- a/Jseph/purchase_reserve_cards.lua +++ b/Jseph/purchase_reserve_cards.lua @@ -57,14 +57,60 @@ function draw_shuffle_counter_def() }) end --- Skill: pay 1 gold to purchase the first card in your reserve +-- Helper function to create purchase abilities for reserve cards +local function create_purchase_ability(baseCost, index, slot) + local target = selectLoc(loc(currentPid, reservePloc)).take(index).reverse().take(1) + local rawCost = const(baseCost).add(getTurnsPlayed(currentPid).negate()) + local dynamicCost = ifInt(rawCost.gte(const(0)), rawCost, const(0)) + return createAbility({ + id = "purchase_reserve_activate_" .. index, + trigger = uiTrigger, + promptType = showPrompt, + layout = createLayout({ + name = "Purchase Reserve", + art = "art/T_Taxation", + text = format("{0}, : Move {1} to your hand.\n(-1 per turn)", { dynamicCost, getCardNameStringExpression(target) }), + }), + effect = addSlotToTarget(slot).apply(target) + .seq(moveTarget(currentHandLoc).apply(target)), + cost = combineCosts({ expendCost, goldCost(dynamicCost) }), + check = selectLoc(loc(currentPid, reservePloc)).count().gte(index) + }) +end + +-- Skill: pay dynamic gold to purchase one of the first 4 cards in your reserve function purchase_first_reserve_skill_def() - -- TODO implement this basic proof of concept skill. + local slot = createSlot({ id = "purchased_reserve_card_slot", expiresArray = { neverExpiry } }) + + -- Initial layout for the skill itself + local firstCard = selectLoc(loc(currentPid, reservePloc)).take(1) + local rawCost = const(9).add(getTurnsPlayed(currentPid).negate()) + local dynamicCost = ifInt(rawCost.gte(const(0)), rawCost, const(0)) + local mainLayout = createLayout({ + name = "Purchase Reserve", + art = "art/T_Taxation", + text = "Acquire reserve card.\nCost: 7 gold + 2 gold per index - 1 gold per turn.", + }) + + return createSkillDef({ + id = "purchase_first_reserve_skill", + name = "Purchase Reserve", + cardTypeLabel = "Skill", + types = { skillType }, + abilities = { + create_purchase_ability(9, 1, slot), + create_purchase_ability(11, 2, slot), + create_purchase_ability(13, 3, slot), + create_purchase_ability(15, 4, slot), + }, + layout = mainLayout, + layoutPath = "avatars/profit" + }) end function setupGame(g) registerCards(g, { - --purchase_first_reserve_skill_def() + purchase_first_reserve_skill_def() }) standardSetup(g, { description = "Custom no heroes game", @@ -75,21 +121,22 @@ function setupGame(g) players = { { id = plid1, - startDraw = 1, + startDraw = 5, name = "Player 1", avatar="assassin", health = 50, cards = { - deck = { -- orc deck still in progress - { qty=1, card=dagger_carddef() }, - { qty=1, card=gold_carddef() }, + deck = { + { qty=10, card=fire_gem_carddef() }, }, reserve = { { qty = 1, card = wizard_treasure_map_carddef() }, { qty = 1, card = ranger_parrot_carddef() }, + { qty = 1, card = fire_gem_carddef() }, + { qty = 1, card = gold_carddef() }, }, skills = { - --purchase_first_reserve_skill_def() + purchase_first_reserve_skill_def() }, buffs = { drawCardsCountAtTurnEndDef(5), diff --git a/Jseph/trickster.lua b/Jseph/trickster.lua new file mode 100644 index 0000000..64ac0f1 --- /dev/null +++ b/Jseph/trickster.lua @@ -0,0 +1,529 @@ +require 'herorealms' +require 'decks' +require 'stdlib' +require 'stdcards' +require 'hardai' +require 'mediumai' +require 'easyai' + +--[[ Notes +Good art for reshuffle art: art/epicart/erratic_research +Good art for pet monkey: art/epicart/deathbringer, art/classes/druid/nimble_fox, art/epicart/kong +Good art for stack the deck: art/t_sleight_of_hand +]] + +double_or_nothing_layout = [[ + + + + + + + + +]] + +hidden_power_layout = [[ + + + + + + + + + + + + + + + + + + + + + + + + + +]] + +replace_ability_layout = [[ + + + + + + + + + + +]] + +local function show_choices_effect() + return pushChoiceEffect({ + choices = { + { + effect = gainGoldEffect(s.Minus(s.Const(1), s.getCounter("reshuffle_card_count"))) + .seq(animateShuffleDeckEffect(currentPid)) + .seq(shuffleEffect(loc(currentPid, deckPloc))) + .seq(drawCardsEffect(selectTargets().count())), + layout = createLayout({ + name = "Continue", + art = "art/t_prism_rainerpetter", + text = format("{0}: Shuffle your deck then draw {1} card(s)", + {s.Minus(s.getCounter("reshuffle_card_count"), s.Const(1)), + getCounter("reshuffle_card_count")}) + }), + }, + } + }) +end + +local function reshuffle_effect() + return pushTargetedEffect({ + desc = + "Pay X to put X+1 cards back in your deck, shuffle, and then draw X+1 cards.", + min=1, + max=ifInt( + selectLoc(loc(currentPid, handPloc)).count() + .gte(getPlayerGold(currentPid).add(const(1))), + getPlayerGold(currentPid).add(const(1)), + selectLoc(loc(currentPid, handPloc)).count()), + validTargets = selectLoc(loc(currentPid, handPloc)), + targetEffect = moveTarget(loc(currentPid, deckPloc)) + .seq(resetCounterEffect("reshuffle_card_count")) + .seq(incrementCounterEffect("reshuffle_card_count", selectTargets().count())) + .seq(show_choices_effect()) + }) +end + +local function reshuffle_hand_skill() + local cardLayout = createLayout({ + name = "Mulligan", + art = "art/t_prism_rainerpetter", + frame = "frames/alchemist_frames/alchemist_item_cardframe", + text = ": Pay X to put X+1 cards back in your deck, shuffle, and then draw X+1 cards." + }) + --[[local effect_chain = ignoreTarget(showTextEffect("Listing functions!")) + for name, value in pairs(_G) do + if type(value) == "function" and string.find(name, "animate") ~= nil then + effect_chain = effect_chain.seq(ignoreTarget(waitForClickEffect(name,""))).seq(animateShuffleDeckEffect(currentPid)) + end + end]] + card = createSkillDef({ + id = "reshuffle_skill_jseph", + name = "Reshuffle", + abilities = { + createAbility({ + id = "reshuffle_ability_id_jseph", + trigger = uiTrigger, + promptType = showPrompt, + layout = cardLayout, + effect = reshuffle_effect(), + cost = expendCost, + check = selectLoc(loc(currentPid, handPloc)).count() + .gte(const(1)), + activations = multipleActivations, + }) + }, + layout = cardLayout, + layoutPath = "art/t_prism_rainerpetter", + }) + return card +end + +local function sacrifice_from_discard_effect() + return pushTargetedEffect({ + desc = "Sacrifice from discard.", + min=0, + max=1, + validTargets = sacrificeSelector(selectLoc(currentDiscardLoc)), + targetEffect = sacrificeTarget() + }) +end + +local function switch_impl(val, cases) + local len = #cases + local effect = nullEffect() + for i = len, 1, -1 do + local c = cases[i][1] + local e = cases[i][2] + effect = ifElseEffect(val.where(c).count().gte(1),e,effect) + end + return effect +end + +local function hidden_power_card_def() + local cardLayout = createLayout({ + name = "Hidden Power", + art = "art/epicart/reusable_knowledge", + frame = "frames/alchemist_frames/alchemist_item_cardframe", + xmlText = hidden_power_layout, + }) + local kTopCardText = "Top card of the market deck." + local add_faction = function(faction) + return addSlotToTarget(createFactionsSlot({faction},{leavesPlayExpiry, endOfTurnExpiry})) + .apply(selectSource()) + end + local kBonusEffects = { + {isCardFaction(wildFaction), gainCombatEffect(3).seq(add_faction(wildFaction))}, + {isCardFaction(guildFaction), gainGoldEffect(2).seq(add_faction(guildFaction))}, + {isCardFaction(necrosFaction), sacrifice_from_discard_effect().seq(add_faction(necrosFaction))}, + {isCardFaction(imperialFaction), gainHealthEffect(5).seq(add_faction(imperialFaction))}, + } + return createActionDef({ + id = "hidden_power_jseph", + name = "Hidden Power", + tags = {}, + types = {}, + factions = {}, + acquireCost = 0, + abilities = { + createAbility({ + id = "hidden_power_ability_jseph", + effect = noUndoEffect() + .seq(moveTarget(revealLoc).apply(selectLoc(tradeDeckLoc).take(1))) + .seq(waitForClickEffect(kTopCardText, kTopCardText)) + .seq(switch_impl(selectLoc(revealLoc), kBonusEffects)) + .seq(moveTarget(tradeDeckLoc).apply(selectLoc(revealLoc))), + cost = noCost, + trigger = onPlayTrigger, + tags = {gainCombatTag}, + aiPriority = toIntExpression(5), + }) + }, + layout=cardLayout, + }) +end + +local function double_or_nothing_effect() + local slot = createSlot({ + id = "double_or_nothing_discount", + expiresArray = { endOfTurnExpiry } }) + return pushChoiceEffect({ + choices = { + { + effect = gainGoldEffect(1), + layout = createLayout({ + name = "Gold", + art = "art/treasures/t_trick_dice", + text = format("{{{0} gold}}",{1}) + }), + }, + { + effect = randomTarget(const(2), addSlotToTarget(slot) + .seq(noUndoEffect())) + .apply(selectLoc(centerRowLoc)) + .seq(createCardEffect(getCostDiscountBuff("Double or Nothing", 2, selectLoc(centerRowLoc).where(isCardWithSlotString("double_or_nothing_discount"))), currentBuffsLoc)), + + layout = createLayout({ + name = "Discount", + art = "art/treasures/t_trick_dice", + text = "Discount two random cards in the market by until you acquire your next card." + }), + } + } + }) +end + +local function double_or_nothing_card_def() + local cardLayout = createLayout({ + name = "Double or Nothing", + art = "art/treasures/t_trick_dice", + frame = "frames/alchemist_frames/alchemist_item_cardframe", + xmlText = double_or_nothing_layout + }) + return createActionDef({ + id = "double_or_nothing_jseph", + name = "Double or Nothing", + tags = {}, + types = {}, + factions = {}, + acquireCost = 0, + abilities = { + createAbility({ + id = "double_or_nothing_ability_jseph", + effect = double_or_nothing_effect(), + cost = noCost, + trigger = onPlayTrigger, + tags = {gainCombatTag}, + aiPriority = toIntExpression(5), + }) + }, + layout=cardLayout, + }) +end + +local function pet_monkey_effect() + local kMoveText = "Moving to top of market deck." + return pushTargetedEffect({ + desc = "Select a card to swap with the top card of the market deck.", + min=1, + max=1, + validTargets = selectLoc(centerRowLoc), + targetEffect = moveTarget(revealLoc) + .seq(waitForClickEffect(kMoveText, kMoveText)) + .seq(moveTarget(tradeDeckLoc).apply(selectLoc(revealLoc))) + .seq(noUndoEffect()) + }) +end + +local function pet_monkey_card_def() + local cardLayout = createLayout({ + name = "Pet Monkey", + art = "art/epicart/kong", + frame = "frames/alchemist_frames/alchemist_item_cardframe", + text = ": Swap any card on the market row with the top card of the market deck.", + }) + return createChampionDef({ + id = "pet_monkey_jseph", + name = "Pet Monkey", + types = {minionType, championType}, + acquireCost = 0, + health = 2, + isGuard = false, + abilities = { + createAbility({ + id = "pet_monkey_ability_jseph", + trigger = uiTrigger, + cost = expendCost, + activations = multipleActivations, + effect = pet_monkey_effect()}) + }, + layout = cardLayout, + }) +end + +function card_in_current_discard() + return selectTargets().exclude(selectLoc(currentDiscardLoc)).count().eq(0) +end + +function sacrifice_target_and_replace(location) + local equalValue = getCardPrintedCost().eq(selectTargets().sum(getCardPrintedCost())) + return sacrificeTarget().apply(selectTargets()) + .seq(moveTarget(location).apply( + selectLoc(tradeDeckLoc).take(1).where(equalValue).union(selectLoc(tradeDeckLoc).where(equalValue)).take(1))) + .seq(noUndoEffect()) +end + +function stack_the_deck_effect_2(location) + return pushTargetedEffect({ + desc = "Select the second card to sacrifice and replace.", + min=0, + max=1, + validTargets = sacrificeSelector(selectLoc(location).where(getCardPrintedCost().gte(1))), + targetEffect = ignoreTarget(sacrifice_target_and_replace(location)) + }) +end + +function stack_the_deck_effect() + return pushTargetedEffect({ + desc = "Select the first card to sacrifice and replace.", + min=0, + max=1, + validTargets = sacrificeSelector( + selectLoc(loc(oppPid, discardPloc)).union(selectLoc(currentDiscardLoc))).where(getCardPrintedCost().gte(1)), + targetEffect = ignoreTarget( + ifElseEffect( + card_in_current_discard(), + sacrifice_target_and_replace(currentDiscardLoc) + .seq(stack_the_deck_effect_2(loc(oppPid, discardPloc))), + sacrifice_target_and_replace(loc(oppPid, discardPloc)) + .seq(stack_the_deck_effect_2(currentDiscardLoc)))) + }) +end + +function stack_the_deck_ability_def() + local cardLayout = createLayout({ + name = "Stack The Deck", + art = "art/t_angry_skeleton", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText=replace_ability_layout + }) + + return createSkillDef({ + id = "replace_ability_jseph", + name = "Stack The Deck", + types = { skillType }, + layout = cardLayout, + layoutPath = "art/t_angry_skeleton", + abilities = { + createAbility({ + id = "replace_ability_ability_jseph", + trigger = uiTrigger, + activations = singleActivation, + layout = cardLayout, + promptType = showPrompt, + effect = stack_the_deck_effect(), + cost = sacrificeSelfCost, + }), + }, + }) +end + +local function chooseStart() + return cardChoiceSelectorEffect({ + id = "SoG_Choice", + name = "First choice", + trigger = startOfTurnTrigger, + + upperTitle = "Welcome! How would you like to proceed?", + lowerTitle = "", +-- 1.1 choice + effectFirst = moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)).union(selectLoc(loc(currentPid, deckPloc)).union(selectLoc(loc(currentPid, skillsPloc)).union(selectLoc(loc(currentPid, reservePloc)) + .union(selectLoc(loc(currentPid, buffsPloc)).where(isCardType(ogreType).Or(isCardType(orcType)).Or(isCardType(elfType).Or(isCardType(dwarfType).Or(isCardType(smallfolkType)).Or(isCardType(halfDemonType)).Or(isCardType(magicArmorType)).Or(isCardName("smallfolk_hide_buff")))))))))) + .seq(sacrificeTarget().apply(selectLoc(loc(currentPid, skillsPloc)))) + .seq(moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)))) + .seq(setPlayerNameEffect("Trickster", currentPid)) + .seq(setPlayerAvatarEffect("smugglers", currentPid)) + .seq(gainMaxHealthEffect(currentPid, const(56).add(getPlayerMaxHealth(currentPid).negate()))) + .seq(gainHealthEffect(56)) + .seq(createCardEffect(reshuffle_hand_skill(), currentSkillsLoc)) + .seq(createCardEffect(stack_the_deck_ability_def(), currentSkillsLoc)) + .seq(createCardEffect(hidden_power_card_def(), loc(currentPid, asidePloc))) + .seq(createCardEffect(double_or_nothing_card_def(), loc(currentPid, asidePloc))) + .seq(createCardEffect(pet_monkey_card_def(), loc(currentPid, asidePloc))) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(ruby_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(dagger_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) + .seq(shuffleEffect(currentDeckLoc)) + .seq(sacrificeTarget().apply(selectSource())) + .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) + .seq(shuffleEffect(currentDeckLoc)) + .seq(ifElseEffect( + getTurnsPlayed(oppPid).eq(0), + drawCardsEffect(5), + drawCardsEffect(3) + )), + layoutFirst = createLayout({ + name = "Play as Trickster", + art = "art/T_Storm_Siregar", + xmlText=[[ + + + + + + ]] + }), + -- We should reshuffle, that would be a bit more fair here but who cares for testing. + effectSecond = sacrificeTarget().apply(selectSource()), + layoutSecond = createLayout({ + name = "Selected class", + art = "art/T_All_Heroes", + xmlText=[[ + + + + + + ]] + }), + turn = 1, + }) +end + +function setupGame(g) + registerCards(g, { + reshuffle_hand_skill(), + hidden_power_card_def(), + double_or_nothing_card_def(), + pet_monkey_card_def(), + stack_the_deck_ability_def(), + }) + + standardSetup(g, { + description = "Level 3 Trickster Class.", + --This defines the play order, only 2 players is supported at this time + playerOrder = { plid1, plid2 }, + --AI is optional but I always include it to have the option to test with an AI player + -- + ai = ai.CreateKillSwitchAi(createAggressiveAI(), createHardAi2()), + timeoutAi = createTimeoutAi(), + opponents = { { plid1, plid2 } }, + --This player object contains/creates all the information about each player. + --In this example the data is imported from the player/character (ie the class, health, and starting cards) + players = { + { + id = plid1, + init = { + --This is the part where the character data is imported + fromEnv = plid1 + }, + --Uncomment the line below to make this player an AI. One note about AI, it doesnt work in online games so make sure to comment it back out before uploading + --isAi = true, + --This sets how many cards a player has on thier first turn (player 1 normally has only 3) + -- startDraw = 3, + name = "Trickster", + avatar = "smugglers", + cards = { + --Since the starting deck cards/skills are predetermined, only buffs need to be here + buffs = { + --This sets how many cards a player draws at the end of the turn, normally 5 + drawCardsCountAtTurnEndDef(5), + --This handles the case where a player needs to discard a card at the start of their turn (discard effects/skills) + discardCardsAtTurnStartDef(), + --This is used to track the turn counter so the "Enrage" syste, triggers to try and end the game after the set number of turns. + fatigueCount(40, 1, "FatigueP1"), + chooseStart(), + }, + } + }, + { + id = plid2, + --Uncomment the line below to make this player an AI. One note about AI, it doesnt work in online games so make sure to comment it back out before uploading + --isAi = true, + --This sets how many cards a player has on thier first turn (player 1 normally has only 3) + -- startDraw = 5, + init = { + --This is the part where the character data is imported + fromEnv = plid2 + }, + cards = { + --Since the starting deck cards/skills are predetermined, only buffs need to be here + buffs = { + --This sets how many cards a player draws at the end of the turn, normally 5 + drawCardsCountAtTurnEndDef(5), + --This handles the case where a player needs to discard a card at the start of their turn (discard effects/skills) + discardCardsAtTurnStartDef(), + --This is used to track the turn counter so the "Enrage" syste, triggers to try and end the game after the set number of turns. + fatigueCount(40, 1, "FatigueP2"), + chooseStart(), + }, + } + }, + } + }) +end + + +function endGame(g) -- more info on this later +end + + + + function setupMeta(meta) + meta.name = "trickster" + meta.minLevel = 3 + meta.maxLevel = 3 + meta.introbackground = "" + meta.introheader = "" + meta.introdescription = "" + meta.path = "C:/Program Files (x86)/Steam/steamapps/common/Hero Realms/Custom Scripts/trickster.lua" + meta.features = { +} + + end \ No newline at end of file diff --git a/Lua_documentation.md b/Lua_documentation.md index a5f7101..bcc5eb1 100644 --- a/Lua_documentation.md +++ b/Lua_documentation.md @@ -1,6 +1,47 @@ -Lua Effect Documentation v1.1 - -Changes in 1.1 +# Lua Effect Documentation v1.6 +WARNING: There's a special random algorithm inside the engine. It is impossible to use lua or any other random inside the script without breaking a game while running your script. + + +## Changes in 1.6 +1. Coop section added +2. backgrounds list added +# Changes in 1.5 +1. Bug fixed where some layouts were not working with string expressions +2. getCardNameStringExpression(selector) added, returning name of the first card in selector, so you can use it to display card name whereever you want as string expression +3. getFactionCount(selector) added, counting number of distinct factions in selector +4. Reserve should now be working whenever you have any cards in reserve scroller +5. skillSacrificePloc exposed. Contains cards sacrificed from skills scroller (abilities etc) +6. moveToBottomDeckTarget(isHidden, order, playerExpression) added, allowing bottomdecking to any player’s deck +7. createCardFromIdEffect(id, locExpression) added allowing creating a card using card string id. Like “influence” +8. createAbilitySlot(t) now accepts t.displayText to be displayed when buffed card is zoomed in +9. winEndReason, concedeEndReason extra end game reasons exposed over API +10. appendEffect(effect) added, allowing to send an effect to the end of current effects queue (needed for endturneffect) +11. .OrBool(boolExpression) added to allow chaining simple bool check along with card bool expressions. selector.where(isChampion().And(isCardStunned().OrBool(yourBookCheckHere))) +12. player.cards.reserve added. you may put reserve cards there in script init data structure, no extra scripting needed +13. reservePloc exposed via API +14. randomStringExpression(expressions) added, allowing to pick one random from the list provided +15. setPlayerNameExpressionEffect(name, player) added, so you can set player name using string expression +16. noHeal player slot added, when on player - they can’t be healed +17. healOverride player slot added. when on player - they get healed for 5 on their turn start instead of discarding a card +18. healInsteadOfEffect(effect, healAmount) effect added. It heals for healAmount if healOverride is on current player +19. controllerPid added - current card controller. Different from owner - player the card originated from +20. isCardWithSlot(slotKey) card bool expression added +21. isCardWithSlotString(slotKey, stringValue) - checks if card has a slot with a key and certain string value +22. isCardAffordableExt(extraGold) - same as isCardAffordable, but you may also specify amount if virtual gold added to you actual gold (usable for cases like +23. hasCardTag(tag) bool card expression added to check if card has given tag +# Changes in 1.4 +1. divide(intDividendExpression, intDivisorExpression) added +2. selector.avg(intCardExpression) added +3. showTextExpressionEffect added +4. Dynamic layouts - see “Dynamic string generation” section +# Changes in 1.3 +1. getHeroLevel intExpression added +2. getCardPrintedCost intExpression added +3. intExpression players slots added +# Changes in 1.2 +1. More ability triggers +2. Custom ability triggers +# Changes in 1.1 1. IMPORTANT - set of mandatory player buffs changed. Check updated example scripts and update your scripts correspondingly 2. transformTarget now properly changes card face on the board 3. nuUndoEffect renamed to noUndoEffect @@ -16,26 +57,112 @@ Changes in 1.1 13. coop scenarios development support (see coop_pirates_example.lua) 14. new card formatting option in “xmltext” field. See examples below. You may still use “text” for simple things, but for complex formatting - only xmltext 15. new arts available -Goal -To standardize naming of all lua effects to make it clear what kind of effect it is. -At a high level +# Requirements + +**Minimal requirements:** + +- Steam - no other platform supports development mode +- basic knowledge of script programming +- any text editor (notepad works just fine) + +**Optimal requirements:** + +- Steam - no other platform supports development mode +- LUA scripting language experience +- text editor with syntax highlight support (notepad++, sublime, vsCode) +# How to start +## Development +1. Have a text editor installed. Any editor works for this, but better grab one with LUA syntax highlight support. +2. Set up a folder for your scripts on hard drive. For best results, try not to include any special symbols in either directory name/path and file name. Do not rename or move the folder after creation, as path to the script on your hard drive is used for versioning. If your script changes path of file name, it will be recognised as a new script. +3. Put a sample script into that folder +4. in the app, open settings and navigate to game tab. There - set development mode to ON. NOTE: it’s visible only on desktop. +![](https://paper-attachments.dropboxusercontent.com/s_8F32093F9D1BC25CDB861F9978E50FF5798A412C3D7094A6FD010F642CC00894_1688058452469_image.png) + + + +4. Now you have “Develop” menu item in main menu +5. It opens Script Editor screen +6. There, click “Open” button, navigate to the folder you created on step 2 and select a script to use. +7. Only one script is loaded into editor and can be uploaded at a time +![](https://paper-attachments.dropboxusercontent.com/s_8F32093F9D1BC25CDB861F9978E50FF5798A412C3D7094A6FD010F642CC00894_1688059017626_image.png) + +8. You may either click edit icon and script will open in your system default editor. Or you may load script into editor of your choice and it will be reloaded when you get back into script editor screen. +9. If script contains errors, error icon will appear, showing full error text when clicked. +![](https://paper-attachments.dropboxusercontent.com/s_8F32093F9D1BC25CDB861F9978E50FF5798A412C3D7094A6FD010F642CC00894_1688059744029_image.png) + +10. Play button lets you test your script in Pass’n’Play mode. +11. When you think your script is ready for the world - tap upload icon and script upload screen will open +![](https://paper-attachments.dropboxusercontent.com/s_8F32093F9D1BC25CDB861F9978E50FF5798A412C3D7094A6FD010F642CC00894_1688059791833_image.png) + +12. Fill all the values, making sure they are informative enough. +13. Script name will appear on online lobby tiles, and longer description will appear on script selection screen and in in-game menu. +![](https://paper-attachments.dropboxusercontent.com/s_8F32093F9D1BC25CDB861F9978E50FF5798A412C3D7094A6FD010F642CC00894_1688060019008_image.png) + + + +![](https://paper-attachments.dropboxusercontent.com/s_8F32093F9D1BC25CDB861F9978E50FF5798A412C3D7094A6FD010F642CC00894_1688060074122_image.png) + + + + + + + + + + + + + + + + -- all lua functions will start with a lowercase -- card definitions should only call other lua functions, which will delegate to c# as required -Other goals: -- as few top level functions as possible -- try to use methods to free the user from having to remember how to deal with similar related types, e.g. intexpression, intcardexpression etc -Overview + + + + + +14. Choose an image for your scriptget +15. Click underlined EULA link and either accept or decline it. +16. Upload button appears only if you have accepted the terms. +## Using your scripts online +1. After upload, you may challenge anyone with your script. +2. To do that, go to online challenge screen, tap game type selector button and choose “Custom” option +![](https://paper-attachments.dropboxusercontent.com/s_8F32093F9D1BC25CDB861F9978E50FF5798A412C3D7094A6FD010F642CC00894_1688060197533_image.png) + +3. Fill you opponent name, other options and click “Challenge” button +4. On “Select Script” screen, click “My scripts” tile and there you find scripts you uploaded. +## Verification and Favorities + +When you successfully uploaded your script, there’s a chance it still contains bugs, so initially script has “Not verified” status. +To get verified, you need to play a full game to the win on one of the sides. +Only after game finishes gracefully, script is marked as Verified and may now be added Favorities by other players. + +To add script to favorities, tap on ended custom script game and select “Add to Favorities” option. + +![](https://paper-attachments.dropboxusercontent.com/s_8F32093F9D1BC25CDB861F9978E50FF5798A412C3D7094A6FD010F642CC00894_1688060845796_image.png) + + +Note that you can’t fav your own script. + +## Versioning + +After some time, you might want to update your script to balance it or fix some bugs that were not found during initial playtesting. +To do that, just upload your fixed script version from the same hard drive location as original one. +New version needs another round of verification. + +# Script Engine Reference The rule engine is built using a few concepts that are combined together to form the full effect system that is used to implement all the cards and effects in the game -Selectors are used to select cards, e.g. cards in the market row, cards in hand, champions with 3 or more defense etc. +Selectors are used to select cards, e.g. cards in the market row, cards in hand, champions with 3 or more defence etc. Effects are used to change the game state, e.g. sacrifice, add combat etc. @@ -64,7 +191,7 @@ Arkus’s Ally Ability: - Effect: Gain 6 health -Players +## Players Predefined constants to select players: @@ -73,22 +200,32 @@ nextPid - next player to take a turn oppPid - opponent of current player nextAllyPid - next ally player nextOppPid - next opponent +ownerPid - card owner +controllerPid - current card controller + +## End game reason + winEndReason - win + concedeEndReason - concede -Locations + +## Locations To specify a location: loc(player, ploc), where ploc is (ends with Ploc or player loc): -inPlayPloc -discardPloc -handPloc -castPloc -deckPloc -buffsPloc -skillsPloc +inPlayPloc - champions location +discardPloc - discard pile +handPloc +castPloc - played non-champion cards (items and actions) +deckPloc - deck pile +buffsPloc - special location, where buff cards should be created +skillsPloc - skills, abilities and armors around avatar revealPloc - common reveal location for any cards that get revealed to both players myRevealPloc - used when you need to reveal cards only to player +sacrificePloc - where cards go after sacrifice +skillSacrificePloc - where ability and any other cards from skills scroller go when sacrificed +reservePloc - reserve cards chest also, predefined locs without a need to specify player (ends with -Loc): @@ -106,7 +243,7 @@ currentBuffsLoc currentSkillsLoc currentRevealLoc -Examples: +**Examples**: -- these two are the same @@ -114,38 +251,64 @@ Examples: loc(currentPid, inPlayPloc) -Factions +## Factions wildFaction guildFaction necrosFaction imperialFaction -BoolCardExpressions (TODO: check if all expressions are present) -They all start with isCard- +Setting card faction: + + local card = createActionDef({ + id = "dragon_fire", + name = "Dragon Fire", + tags = { galleryCardTag, callToArmsSetTag }, + types = { dragonType }, + factions = { imperialFaction }, + acquireCost = 7, + + +## BoolCardExpressions (TODO: check if all expressions are present) + +They all start with isCard*() and used to check cards state. Mostly used when filtering selectors isCardExpended() isCardStunned() isCardStunnable() isCardChampion() isCardAction() -isCardFaction(Faction.Wild) -isCardType(“Knife”) -> checks type and subtype +isCardFaction(*imperialFaction*) +isCardType(*knifeType*) -> checks type and subtype isCardName(“thief_throwing_knife”) → checks card id isCardAtLoc(ploc) → true if target is in the passed CardLocEnum isGuard() +isCardHasController(playerExpression) +isCardFactionless() +isCardAffordable() - checks if you have enough gold to buy it considering any effective cost changes +isCardSkill() +isCardAbility() +isCardAquirable() - checks if you are able to purchase it if you had enough gold +isCardSelf() - checks if card is the origin of an effect executing +isCardDamaged() +isCardExpended() +isCardWithSlot(slotKey) +isCardWithSlotString(slotKey, stringValue) - checks if card has a slot with a key and certain string value +isCardAffordableExt(extraGold) - same as isCardAffordable, but you may also specify amount if virtual gold added to you actual gold. +hasCardTag(tag) bool card expression added to check if card has given tag operators -.And(isCardXX()), .Or(isCardXX()), .invert() +.And(isCardXX()) - bool and + .Or(isCardXX()) - bool or + .invert() - bool not + .OrBool(boolExpression) - allows to or with a simple boolExpression, not card one Note that these are uppercase, as and/or are keywords in lua -constBoolExpression(value) returns a BoolExpression that always returns the value it was initially passed. - -Usage +**Usage** Bool card expressions are mostly used to filter out data from selectors (see below) -Examples: +**Examples**: -- selects cards from opponent's in play, which are not expended and can be stunned @@ -153,7 +316,8 @@ Examples: selectLoc(loc(oppPid, inPlayPloc)).where(isCardExpended().invert().And(isCardStunnable())) -Selectors TODO: check if there are more predefined selectors +## Selectors +## TODO: check if there are more predefined selectors Selectors are used to start a chain of selecting cards. All selectors start with the selectX: @@ -161,8 +325,11 @@ selectLoc(loc): select cards in a location selectSource(): select source card selectTargets(): selects target from targeted effect or cardeffect ability trigger. selectOppStunnable(): select all opponent’s champions that can be stunned +saveTarget(key) - saves targets under key name +selectSavedTargets(key) - selects saved targets +clearTargets(key) - clears targets for the key -Filtering +**Filtering** selector.where(BoolCardExpression) to filter by a property on each card @@ -173,7 +340,7 @@ Example: selectLoc(loc(oppPid, inPlayPloc)).where(isCardExpended().invert().And(isCardStunnable())) -Chaining +**Chaining** selector1.union(selector2) to return the union of both selectors. @@ -183,7 +350,7 @@ selector1.union(selector2) to return the union of both selectors. selector.exclude(selectSource()) to exclude the source card -Ordering +**Ordering** selector.order(intcardexpression) to order by the provided value .orderDesc(intcardexpression) @@ -193,7 +360,7 @@ selector.order(intcardexpression) to order by the provided value selector.take(n) to take the first X cards .take(intExpression) -Conversions +**Conversions** To Int: .count() @@ -202,14 +369,17 @@ or You can use .take(1).sum(getCardCost()) to get the cost of a single card, e.g. for comparison -Predefined selectors +.avg(intCardExpression) - returns average value of int card values for the selector + +**Predefined selectors** We predefine commonly used selectors in lua selectCurrentChampions() selectNextChampions() +sacrificeSelector(selector) - special selector, to be used to get cards that can be sacrificed. E.g. if you have a noSacrifice slot on a card this selector will exclude it from the list. -Examples +**Examples** to select all stunned champions @@ -224,7 +394,7 @@ action cards in trade row selectLoc(centerRowLoc).where(isCardAction()) -IntExpression TODO: check if we have all int expressions specified here +## IntExpression TODO: check if we have all int expressions specified here Most int expressions start with get- @@ -232,20 +402,28 @@ From selector: selector.count() getCounter(counter) getPlayerHealth(pid) -getPlayerCombat(pid) +getHeroLevel(pid) +getCurrentPlayerCombat() +getOppPlayerCombat() getPlayerGold(pid) getPlayerDamageReceivedThisTurn(pid) getTurnsPlayed(pid) getSacrificedCardsCounter() +getRankedFactionCount(rank) - returns number of faction cards for current player for the given rank. Where rank 1 faction has most cards. +getChampionsPlayedThisTurn() +getFactionCount(selector) - counting number of distinct factions in selector + constant int: const(int) Arithmetic -.add(intExpression), .multiply(intExpression), negate() +.add(intExpression), .multiply(intExpression), .negate() use negate with add to subtract -Conversions: +divide(intDividendExpression, intDivisorExpression) + +Comparison: .gte(intExpression or int), .lte(intExpression or int), .eq(intExpression or int) Used when you need to check for something - returns BoolExpression @@ -258,47 +436,123 @@ Operations: minInt(intExp, intExp) - returns min of two values -StringExpression +## StringExpression There are several places where we can show dynamic strings. To create a dynamic string, just use the format function. -format(“{0}”, { int, intexpressoin etc}) +**String format** +format(“{0}”, { int, intexpression etc}) + + format("{0} {1}.", { randomNameTitle, randomName}) +**Random string expression** +randomStringExpression(expressions) - returns random string expression from provided array -IntCardExpression + local randomNameTitle = randomStringExpression({ + "Mr.", + "Mrs.", + "Ms.", + "Dr.", + "Prof." + }) + +**getCardName** +getCardNameStringExpression(selector) - returns name of the first card from selector + +**getCurrentGender** + + getCurrentGender() - returns either male of female string + +**ifElseString** +ifElseString(boolExpression, yesStringExpression, noStringExpression) + + ifElseString(condition, + toStringExpression("an"), + toStringExpression("a")) + +**S****witch string** +switchString(intExpression, { string items array} +Returns corresponding value based on incoming int + + switchString( + intExprValue, + { + stringItem(8, "Wild"), + stringItem(4, "Imperial"), + stringItem(2, "Necros"), + stringItem(1, "Guild"), + }) +## Dynamic string generation + +Sometimes you might need to calculate text on a card in runtime. + +Any text on a card, a choice or other text fields can now be dynamic. + +Examples: + +Targeted effect: + + local val = minInt(toIntExpression(v), currentHand().count()) + return pushTargetedEffect({ + desc = format("Discard {0} card(s)", { val }), + +Layout: + + layout = layoutCard({ + title = "Heal Myself", + art = "art/cleric_lesser_resurrect", + text = format("{{{0} health}}", { currentV }), + flavor = format("Health: {0}", { getPlayerHealth(currentPid) }) + }) + +Choices: + + layout = createLayout({ + name = "Dispersion", + art = "art/classes/alchemist/dispersion", + frame = "frames/alchemist_frames/alchemist_skill_cardframe", + text = format("Gain {0} ", { getRankedFactionCountInPlay(1).add(getRankedFactionCountInPlay(2)) }) + }), + +Certain effect: + + simpleMessageExpressionEffect(format("{0} Summoning Power", {getCounter("summon_meter")})) +## IntCardExpression These start with getCard- getCardCost() +getCardPrintedCost() getCardHealth() -BoolExpression +## BoolExpression hasClass(playerExpression, heroClass) hasPlayerSlot(playerExpression, slot) constBoolExpression(value) - constant value +isWinner(playerExpression) - checks if player is a winner, useful for after game ends effects -Negate +**Negate** .invert() to negate, e.g. isExpended().invert() returns false if the card is expended (not is a reserved word in lua) -Combining +**Combining** .And(boolExp), .Or(boolExp) -Extended examples TODO: add more examples for selectors +## Extended examples TODO: add more examples for selectors Select all cards in trade row where their cost is greater than the players’ gold -selectTradeRow().where(getCardCost().gt(getPlayerGold(currentPid))) +selectLoc(centerRowLoc).where(getCardCost().gt(getPlayerGold(currentPid))) Note that getCardCost() is a IntCardExpr, the gt() accepts an IntExpr, which should get upgraded to an IntCardExpr, and the entire expression becomes a boolcardexpr -Effects -Simple Effect TODO: check if all available effects are present here +# Effects +## Simple Effect TODO: check if all available effects are present here All simple effects end with the word -Effect. These require no input from user. @@ -319,14 +573,16 @@ Predefined simple effects: Game state effects -- gainCombatEffect(int(expression)) -- gainGoldEffect(int(expression)) -- gainHealthEffect(int(expression)) -- drawCardsEffect(int(expression)) -- drawToLocationEffect(intExpression, locExpression) - draws count cards to location -- hitOpponentEffect(int(expression)) - deals damage to opponent -- hitSelfEffect(int(expression)) - deals damage to self -- oppDiscardEffect(int(expression)) - makes opponent discard cards when their turn starts +- addDisplayDiscardEffect(oppPid, const(1)) - adds a number to discard display. Not actually requires anyone to discard. Used for cards like Distracted Exchange and other similar thief skills +- gainCombatEffect(int/expression) +- gainGoldEffect(int/expression) +- gainHealthEffect(int/expression) +- gainToughnessEffect(int/expression) +- drawCardsEffect(int/expression) +- drawToLocationEffect(intExpression, locExpression, order) - draws count cards to location. Order possible values -1, 0. Use one or another depending on deck you’re trying to bottom card to. Bottoms for deck and for discard differ, because cards facing is opposite. +- hitOpponentEffect(int/expression) - deals damage to opponent +- hitSelfEffect(int/expression) - deals damage to self +- oppDiscardEffect(int/expression) - makes opponent discard cards when their turn starts - createCardEffect(cardId, location) - creates a card at location - endGameEffect(winnerId, endReason) - ends game with a winner and given reason - setMarketLengthEffect(int) - sets length of market row to the value @@ -335,20 +591,21 @@ Game state effects - shuffleEffect(locExr) - shuffles all cards contained in the passed location - shuffleTradeDeckEffect() - shuffles trade deck -Conditionals +**Conditionals** conditions are bool expressions - ifEffect(condition, simpleEffect) - if condition is true, executes the effect - ifElseEffect(condition, yesSimpleEffect, noSimpleEffect) - if condition is true executes yes effect, otherwise no effect -Specials +**Specials** - noUndoEffect() - prevents user from undoing their actions from this point - nullEffect() - does nothing - can be used to do nothing in certain cases +- browseCards({ locExpr = …, header = “Header text”, oppHeader = “Header text for opponent”}) - allows to browse cards from a location until dismissed -Fatigue counter +**Fatigue counter** fatigueCount(startingTurn, delta, name) - fatigue counter @@ -409,17 +666,22 @@ Control flow Animations / text -- animateShuffleEffect(player) - plays deck shuffle animation +- animateShuffleDeckEffect(player) - plays deck shuffle animation - showOpponentTextEffect(text) - shows text to the opponent - hideOpponentTextEffect(text) - hides previously shown opponent text - showCardEffect(layout) - you may build a layout to be show to player to several seconds or until clicked. createLayout("avatars/troll", "Injured Troll I", "{3 combat}", "The troll gets angry") - showTextEffect(text) - shows text on the screen - waitForClickEffect(playerText, oppText)- shows text to corresponding player and waits for click +- waitForClickExpressionEffect(playerStringExpression, oppTextExpression)- same as previous but with string expression support shows text to corresponding player and waits for click. - customAnimationEffect({id, player}) - plays a custom animation with “id” for “player” - cardAbilityAnimationEffect({ id, layout }) - plays card animation with “id” style and “layout” card face +- setPlayerAvatarEffect(avatar, playerExpression) - changes avatar for a hero +- setPlayerNameEffect(name, playerExpression) - changes name of a hero +- showTextExpressionEffect(format, { expressions }) - shows flying text + showTextExpressionEffect(format("10/3 result: {0}", {divide(10, 3)})) -Card Effects TODO: check if all effects are listed +## Card Effects TODO: check if all effects are listed Card effects require a list of cards to be supplied, normally via a selector. They end with -Target @@ -435,7 +697,8 @@ Game state card effects - expendTarget() - expends targets - grantHealthToTarget(intExpression) - grants +X defense to target champions - moveTarget(locExr) - moves targets to location -- moveToTopDeckTarget(isHidden) - moves targets to top of current player’s deck. Set isHidden to true if you don’t want card’s name reflected in log +- moveToBottomDeckTarget(isHidden, order) - order possible values -1, 0. Use one or another depending on deck you’re trying to bottom card to. Bottoms for deck and for discard differ, because cards facing is opposite. +1. moveToBottomDeckTarget(isHidden, order, playerExpression) added, allowing bottomdecking to any player’s deck - nullTarget() - does nothing - playTarget() - plays targets - prepareTarget() - prepares targets @@ -447,7 +710,7 @@ Random card effects - probabilityTarget({ chance, onSuccessTarget, onFailureTarget}) - executes onSuccess target effect with chance probability, otherwise executes onFalureTarget effect. -- randomTarget(cardEffect, intExpression) - passes X random targets to underlying card effect +- randomTarget(intExpression, cardEffect) - passes X random targets to underlying card effect Animation card effects @@ -472,7 +735,7 @@ We can also convert a simple effect into a card effect by ignoring the target: ignoreTarget(simpleeffect) -Targeted Effects +## Targeted Effects Targeted effects can be pushed into the effect queue with pushTargetedEffect(). They will prompt the player to select a target, and then execute the provided card effect on the targets. @@ -495,9 +758,9 @@ Example to prompt the user to sacrifice a card in hand: }) -Special target effects +## Special target effects -promptSplit effect +**promptSplit effect** allows to sort cards from a location. Used for track and channel abilities. @@ -508,25 +771,31 @@ Example: selector = selectLoc(currentRevealLoc), take = const(4), -- number of cards to take for split sort = const(2), -- number of cards to be sorted for ef2 + minTake = const(1), -- number of mandatory cards moved to ef2 ef1 =moveToTopDeckTarget(true), -- effect to be applied to cards left ef2 = discardTarget(), -- effect to be applied to sorted cards header = "Careful Track", -- prompt header description = "Look at the top four cards of your deck. You may put up to two of them into your discard pile, then put the rest back in any order.", + rightPileDesc = "Text explaining right pile rules, e.g. ordering", pile1Name = "Top of Deck", pile2Name = "Discard Pile", eff1Tags = { buytopdeckTag }, eff2Tags = { cheapestTag } })) -Choice Effects +## Choice Effects The user will make a choice, then a SimpleEffect will be executed for the chosen item. + pushChoiceEffect({ choices={ { - id="choicelayoutid", - effect=gainCombatEffect(5) + layout = ... + effect=gainCombatEffect(5), + title = "Extra text displayed over a choice item", + condition = constBoolExpression(true), -- if condition is not fulfilled - choice would be grayed out + tags = { tag1, tag2, tag3 } }, { effect = healPlayerEffect(currentPid, v), @@ -553,16 +822,27 @@ The user will make a choice, then a SimpleEffect will be executed for the chosen } }) +Optionally, you may use pushChoiceEffectWithTitle + + + pushChoiceEffectWithTitle({ + choices = ..., + upperTitle = "Extra text displayed above choice items", + lowerTitle = "Extra text displayed below choice items" + }) + If you are using the field "text" in createLayout ({ }) and want to display the gold/combat/health icon in the description use {X gold} ; {X combat} ; {X health} where X - count of points + +![](https://paper-attachments.dropboxusercontent.com/s_31F66394ABC2465F353C26C1171909EFCCC7A751E53E3C241560ABB665EB7125_1647011256237_ima22ge.png) + + Otherwise, use “xmltext” field. Example: -![](https://paper-attachments.dropbox.com/s_31F66394ABC2465F353C26C1171909EFCCC7A751E53E3C241560ABB665EB7125_1647011256237_ima22ge.png) - layout = createLayout({ title = "Hunter's Cloak", art = "icons/ranger_hunters_cloak", @@ -573,24 +853,9 @@ Example: Note that choice effects can be dynamically generated using thecreateLayout() function, which returns a dynamic layout for the art. All the fields of createLayout accept StringExpression. -Target Player effects (not implemented yet) - -In coop, we might need to target players. - - - pushTargetPlayerEffect({ - mode = targetPlayers, - effect = playerGainHealthEffect(targetPid, 5) - }) - -targetPid will be defined within the context of a target player effect. +## Storytelling effects -mode can only be targetPlayers for now. - - -Storytelling effects - -Speech bubble effect +**Speech bubble effect** Shows a speech bubble for defined player, which may wait to be dismissed or not. showSpeechBubbleEffect({ @@ -599,84 +864,136 @@ Shows a speech bubble for defined player, which may wait to be dismissed or not. waitForClick=constBoolExpression(false) }) -Story tell effect effect with portrait +**Story tell effect effect with portrait** Shows an avatar sayng a string. storyTellEffectWithPortrait("lys__the_unseen", "Delicious…") + leftStoryTellEffectWithPortrait("lys__the_unseen", "Delicious…") -- shows portrait on left side -Story lines effect +**Story lines effect** Shows strings one after another. storyLinesEffect({"You’ve defeated the Necros but lost many allies in the battle.", "You leave the temple, but the surrounding catacombs are like a maze.", "Without the captive Priest for a guide, you quickly become lost." }) -Creating abilities + +**Dramatic pause effect** +Does nothing for seconds. Useful when you need a dramatic pause within character dialog + + dramaticPauseEffect(seconds) +# Creating abilities Abilities link effects with their trigger times. -createAbility({id, trigger, effect, cost, activations, check}) -id: unique id of the ability (within a card) + createAbility({ + id = "uniqueAbilityId", + trigger = startOfTurnTrigger, + effect = nullEffect(), -- a simple effect to execute + cost = noCost, -- noCost by default + activations = singleActivation, -- single by defaule + check = constBoolExpression(true), -- true by default + promptType = showPrompt -- noprompt by default, + allyFactions { guildFaction, wildFaction } -- needs a guild or wild ally in play to activate + }) -trigger: one of +**Ability Triggers** startOfTurnTrigger - endOfTurnTrigger + oppStartOfTurnTrigger endOfOppTurnTrigger oppStartOfTurnTrigger autoTrigger - automatic trigger, like death cultist's expend ability has auto trigger onExpendTrigger - onSacrificeTrigger - triggers this trigger on sacrificed cards - onSacrificeGlobal - triggers all cards with this trigger - onStunTrigger - triggers this trigger on sacrificed cards - onStunGlobal - triggers all cards with this trigger + onSacrificeTrigger - pulls this trigger for sacrificed card only + onSacrificeGlobalTrigger - pulls this trigger for all cards when any card is sacrificed + onStunTrigger - pulls this trigger for stunned card only + onStunGlobalTrigger - pulls this trigger for all cards when any card is stunned uiTrigger - user will need to manually trigger it - onAcquireTrigger - triggers this trigger on sacrificed cards - onAcquireGlobal - triggers all cards with this trigger + onAcquireTrigger - triggers this trigger on acquired card + onAcquireGlobalTrigger - triggers all cards with this trigger onPlayTrigger - triggers every time you play any card, not just the card with the onPlay ability startOfGameTrigger endOfGameTrigger onZeroHealthTrigger - triggers when player's health becomes 0 or below + deckPreShuffleTrigger - triggers before player deck shuffled deckShuffledTrigger - triggers when player deck shuffled onLeavePlayTrigger - triggers when champion leaves InPlay area + onDiscardTrigger - triggers from card gets into discard pile + gainedHealthTrigger - current player gains health (only gains) + oppGainedHealthTrigger - opponent gained health + onMarketChangedTrigger - market changed + onDiscardTrigger - when a card discarded + onCreateTrigger - a card created + oppCardDrawnTrigger - opp drew a card -effect: an effect to run when triggered +**Custom ability triggers** +You may now create custom ability triggers -cost: requirements to use the effect, set if required; to give an ability multiple costs, such as expending and also paying gold, use combineCosts(t), passing it a table that contains each of the desired costs, e.g. combineCosts({ expendCost, goldCost(2) }) + customAbilityTrigger = "customAbilityTrigger" + +You may specify it as a trigger for any ablity. +Then you may fire it at any moment: + + fireAbilityTriggerEffect(customAbilityTrigger) -- fire trigger for all cards in play + + fireAbilityTriggerForTarget(customAbilityTrigger) -- fire trigger for targets - use with either .apply(selector) or as part or targeted effect + +Usage on an ability + + trigger = abilityTrigger(customAbilityTrigger) + +**Cost** + +Requirements to use the effect, set if required; to give an ability multiple costs, such as expending and also paying gold, use combineCosts(t), passing it a table that contains each of the desired costs, e.g. combineCosts({ expendCost, goldCost(2) }) - expendCost - sacrificeSelfCost - goldCost(value) - sacrificeSelfCost -- costAnd({ expendCost, goldCost(value) }) +- combineCosts({ expendCost, goldCost(value) }) -activations: set if required +**Activations** +Set if required - singleActivation: normally used with auto abilities to indicate that the ability can only be activated once per turn. Default. - multipleActivations: used with expend abilities to indicate that it can be activated multiple times per turn. -promptType: set if you want to show confirmation card before executing ability. +**PromptType** +Set if you want to show confirmation card before executing ability. - noPrompt - default - showPrompt - enables prompt -priority: trigger priority - when multiple same trigger abilities fire, they are executed in priority order from highest to lowest. 0 by default. +**PlayAllType** +Determines what happens when player clicks play all with this ability on a card in hand -layout: layout to be displayed when activated on prompt + noPlayPlayType -- card won’t be played as part of play all + blockPlayType -- blocks using play all (elven gift, dark reward) + playFirstPlayType -- has priority when using play all + normalPlayType -- card will be played normally -check: bool expression to decide if the ability can be triggered +**Priority** +Trigger priority - when multiple same trigger abilities fire, they are executed in priority order from highest to lowest. 0 by default. -allyFactions: used for Ally Abilities, which can only activate if a card of the specified faction(s) is also in play; required for createUiAllyAbility() and createAutoAllyAbility() +**Layout** +Layout to be displayed when activated via prompt +**Check** +Bool expression to decide if the ability can be triggereautd -Creating CardEffectAbilities +**AllyFactions** +Used for Ally Abilities, which can only activate if a card of the specified faction(s) is also in play; required for createUiAllyAbility() and createAutoAllyAbility() -Standard Abilities can have the onAcquire trigger, which makes them activate whenever any card is acquired (regardless of destination), which can make it difficult to have the ability affect the acquired card, as the onAcquire ability does not have a reference to it. If you want to create an ability that will always be able to affect the specific card that was acquired, you need to give the Card Def a CardEffectAbility. The createDef() function accepts a cardEffectAbilities table in its table parameter, though the abilities table is still required, even if a card has a CardEffectAbility but no Ability: - card = createDef({ +## Creating CardEffectAbilities + +Standard Abilities can have the onAcquire trigger, which makes them activate whenever *any* card is acquired (regardless of destination), which can make it difficult to have the ability affect the acquired card, as the onAcquire ability does not have a reference to it. If you want to create an ability that will always be able to affect the specific card that was acquired, you need to give the Card Def a CardEffectAbility. The createDef() function accepts a cardEffectAbilities table in its table parameter, though the abilities table is still required, even if a card has a CardEffectAbility but no Ability: + + card = createActionDef({ ... abilities = { }, cardEffectAbilities = { @@ -696,20 +1013,20 @@ There are several possible trigger timings for a CardEffectAbility: - postSelfAcquiredCardTrigger - like postAcquired, but only triggers for the card itself - playedCardTrigger - triggers after a card is played from hand. The trigger will happen after after played triggers, but before auto triggers happen. This way, if the played card trigger expends a champion, auto triggers will not fire. -CardEffectAbilities must use CardEffects, which require specific card targets. The CardEffectAbility will pass the acquired card as the target for the CardEffect assigned to it if the trigger is onAcquire or postAcquire; it will pass the card whose location changed if the trigger is onLocationChanged. You can also use selectTargets() to obtain the targets +CardEffectAbilities must use CardEffects, which require specific card targets. The CardEffectAbility will pass the acquired cmoveard as the target for the CardEffect assigned to it if the trigger is onAcquire or postAcquire; it will pass the card whose location changed if the trigger is onLocationChanged. You can also use selectTargets() to obtain the targets Additionally, for a card that contains a CardEffectAbility with the onLocationChanged trigger, when that card is first created, that ability will trigger, targeting every possible card. You can pass a Simple Effect to the CardEffectAbility, it will be auto converted to a CardEffect. -Creating Card Defs +## Creating Card Defs Finally, we put the abilities into a Card Def to allow us to actually create a card. `createDef({id, name, cardTypeLabel, playLocation, abilities, types, ….})` -id: a unique id for the card +id: a unique id for the card. Better add something, say, your username to the id to avoid breaking your script if we introduce a card with same name. “frostbite_randomnoob” name: Title of card @@ -727,7 +1044,7 @@ healthType: for champions, its either defenseHealthType (default) or healthHealt buffDetails: for global buffs, this creates the display when its clicked for details: - createBuffDetails({ + buffDetails = createBuffDetails({ art = "wizard_spell_components", name = "Soak", text = "+1 cost" @@ -745,7 +1062,7 @@ layout: if we want to dynamically generate the card layout, example code: See layout text chapter below for more formatting info. Alternatively, we can load a card texture as layout, e.g. -`loadLayoutTexture(``"``Textures/death_touch``"``)` +`loadLayoutTexture("Textures/death_touch")` cardTypeLabel: type of card, used only for display. Normally auto filled in. @@ -755,27 +1072,37 @@ Helper methods for creating card defs. These generally just fill in the required createActionDef() - function confused_apparition_carddef() - return createActionDef({ - id="confused_apparition", - name="Confused Apparition", - types={noStealType}, - acquireCost=0, + function dragon_fire_carddef() + local card = createActionDef({ + id = "dragon_fire", + name = "Dragon Fire", + tags = { galleryCardTag, callToArmsSetTag }, + types = { dragonType }, + factions = { imperialFaction }, + acquireCost = 7, abilities = { createAbility({ - id="confused_apparition_auto", - trigger= autoTrigger, - effect = ifEffect(selectLoc(currentInPlayLoc).where(isCardName("weak_skeleton")).count().lte(0), healPlayerEffect(oppPid, 1)) + id = "dragon_fire", + effect = gainCombatEffect(7).seq(drawCardsWithAnimation(1)), + cost = noCost, + trigger = onPlayTrigger, + tags = { gainCombatTag, draw1Tag, aiPlayAllTag }, + aiPriority = toIntExpression(300) + }), + createAbility({ + id = "dragon_fire_sacrifice", + layout = loadLayoutData("layouts/firstkickstarter/dragon_fire_sacrifice"), + effect = getFireballDamageEffect(4, "art/sets/promos1art/dragon_fire", "art/sets/promos1art/dragon_fire", false); + cost = sacrificeSelfCost, + trigger = uiTrigger, + promptType = showPrompt, + tags = { damageTag }, + aiPriority = toIntExpression(-1) }) }, - layout = createLayout({ - name = "Confused Apparition", - art = "art/T_Confused_Apparition", - frame = "frames/Coop_Campaign_CardFrame", - text = "Opponent gains 1 unless you have a Weak Skeleton in play." - }) - }) - end + layout = loadLayoutData("layouts/firstkickstarter/dragon_fire") + } + ) createChampionDef() @@ -806,6 +1133,29 @@ createChampionDef() }) end +createItemDef() + + function thief_trick_dice_carddef() + return createItemDef({ + id = "thief_trick_dice", + name = "Trick Dice", + types = { thiefType, weaponType, diceType }, + acquireCost = 0, + abilities = { + createAbility({ + id = "thief_trick_dice", + effect = drawCardsEffect(2).seq(forceDiscard(1)), + cost = noCost, + trigger = onPlayTrigger, + playAllType = blockPlayType, + tags = { drawTag, discardTag, aiPlayAllTag }, + aiPriority = toIntExpression(200) + }) + }, + layout = ... + }) + end + createBuffDef() createSkillDef() @@ -879,7 +1229,7 @@ createMagicArmorDef() createHeroAbilityDef() -Layout Text formatting +## Layout Text formatting To set up layout text, use xmltext field of layout table. Double square brackets allow multiline code for easier reading. @@ -897,7 +1247,7 @@ To set up layout text, use xmltext field of layout table. Double square brackets ![](https://paper-attachments.dropbox.com/s_3393425E83F8175532A1ABBAD0B8649F1F9A50A4DBF53772E500C4EB4C156AC7_1661177102391_spriteatlas.png) -Sprites: +**Sprites:** {shield} {combat} {combat_1} 1-9 {expend_1} @@ -914,7 +1264,7 @@ Sprites: {wild} {requiresHealth_40} 10 - 50 step 5 -Examples: +**Examples:** @@ -1142,7 +1492,7 @@ Examples: ]], ![](https://paper-attachments.dropbox.com/s_3393425E83F8175532A1ABBAD0B8649F1F9A50A4DBF53772E500C4EB4C156AC7_1664378926283_image.png) -Slots TODO: add slots info +## Slots TODO: add slots info There are two types of slots in the engine: @@ -1152,7 +1502,7 @@ There are two types of slots in the engine: Slots are customizable objects that can be attached to cards and players for various purposes. All slots have a lifespan, determined by expiration field. -Slot Expiry +**Slot Expiry** startOfOwnerTurnExpiry endOfOwnerTurnExpiry @@ -1160,30 +1510,50 @@ Slot Expiry leavesPlayExpiry linkedExpiry neverExpiry + leavesMarketExpiry -Card slots +## Card slots Card slots are used to change card properties in various ways or to set flags for further use. -Adding slot to a card +**Adding slot to a card** - addSlotToTarget(slot) + local slot = createSlot({ + id = noSacrificeSlot, + expiresArray = { neverExpiry } + // optional fields + expireEffect = nullEffect() // effect to execute on expire + }) + + addSlotToTarget(slot).apply(selector) As a card effect, it should either be used as part of targeted effect, or followed by .apply(selector) -Checking slots +**Checking slots** To check slots, tail selector with .where(isCardWithSlot(“stringSlotKey”)) Available slot types: -Factions Slot +*slotNoSacrifice - prevents card from being sacrificed (use sacrificeSelector(selector) to make use of it)* +*slotNoPlay - prevents card from being played* +*slotNoBuy - prevents card from being acquired* +*slotNoStun - card can’t be stunned* +*slotHealthChange - modifies champion’s def* +*slotCostChange - modifies card’s acquire cost* +*slotExpend - card is expended when it has this slot* +*slotFactions - extra factions for a card* +*slotAbility - adds an ability to a card* +*slotNoDamage - card can’t be damaged* +*slotNoAttack - card can’t be directly attacked* + +**Factions Slot** When this slot is attached to a card, it’s considered to have factions specified and this effect falls of when expiration event comes. createFactionsSlot({ necrosFaction, guildFaction }, { leavesPlayExpiry, endOfTurnExpiry }) -Ability Slot +**Ability Slot** When this slot attached to a card, it also has the ability in it. @@ -1197,9 +1567,13 @@ When this slot attached to a card, it also has the ability in it. tags = { } }) - createAbilitySlot(ab, { endOfTurnExpiry }) + createAbilitySlot({ + ability = ab, + expiry = { endOfTurnExpiry }}, + displayText = "Text to be displayed when zoomed on card with slot attached" + }) -Cost change Slot +**Cost change Slot** Modifies cost of a card it attached to. @@ -1207,13 +1581,13 @@ Modifies cost of a card it attached to. if discount is negative, card will cost more. -NoBuy Slot +**NoBuy Slot** When this slot is attached to a card in the market row, a card can’t be acquired even if you have enough gold. createNoBuySlot(expires) -Health change Slot +**Health change Slot** Modifies defense of a card it attached to. @@ -1221,36 +1595,76 @@ Modifies defense of a card it attached to. if discount is negative, card will cost more. -No stun slot +**No stun slot** createNoStunSlot(expiresArray) card with this slot can’t be stunned -No damage slot +**No damage slot** createNoDamageSlot(expiresArray) card with this slot can’t be damaged -Custom slots +**Guard switch slot** + + createGuardSwitchSlot(isGuard, expiresArray) + +card with this slot has guard set by isGuard parameter + +**Custom slots** Custom slots can be used to set some flag on a card - createSlot({ id = "abilityActivated", expiry = endOfTurnExpiry }) + createSlot({ id = "abilityActivated", expiry = { endOfTurnExpiry } }) -Player slots (coming soon) +## Player slots Player slots can be attached to players and then can be checked and used to make decisions. Player slot consists of a key and optional string value. For example, slots are used to mark a player as one who has a champion stunned this turn, so phoenix helm could activate. +**Creating player slot** + + createPlayerSlot({ key = "usedSkillThisTurn", expiry = { endOfTurnExpiry } }) + +**Adding player slot** + + addSlotToPlayerEffect(playerExpression, createPlayerSlot({ key = "usedSkillThisTurn", expiry = { endOfTurnExpiry } })) + +**Removing player slot** + + removeSlotFromPlayerEffect(playerExpression, "usedSkillThisTurn") + +**IntExpression Player slot** +Using this slot, you may store calculable values for a player and then use them for other things. + + createPlayerIntExpressionSlot(key, intExpression, expiresArray) + +You can get total sum of stored values using following function, which is an IntExpression. + + sumIntSlots(playerExpression, key) + +**Internal player slots** +*undamageablePlayerSlot - player can’t be damaged.* +untargetable*PlayerSlot - player can’t be damaged.* +noHealKey - player can’t be healed -Examples: TBD +## Custom indicator -AI +Indicator on player avatar is tied to custom player value. It would appear once value is more than 0 and disappear once value returns to 0. + + gainCustomValueEffect(intExpression) + getCustomValue(pid) - returns intExpression + gainCustomValueEffect(getCustomValue(pid).negate()) - sets value to 0 + +**Warning**: Monk uses this indicator for Tao Lu display. If your script uses custom value and Monk hero is playing it - it will change values unexpectedly. So make sure your general script code is not using it while allowing any hero to play it. + +## Examples: TBD +## AI There are 3 types of AI available in the app: @@ -1260,446 +1674,1224 @@ There are 3 types of AI available in the app: - createHardAi() -Image resources +# Coop +## Intro screen setup +**** +To customize intro screen, add this function to the end of the script and fill it with data required. +Find available backgrounds at corresponding arts section. + + function setupCoopIntro(encounter) + encounter.name = "Script name" + encounter.description = "Complex long description with update date" + encounter.minlevel = 2 + encounter.maxlevel = 24 + encounter.avatar = "avatars/chest" + encounter.heroname = "CustomHeroName" + encounter.backgroundPath = "scenariointrobackgrounds/journeys/confiscate" + encounter.features = { + encounter.feature("avatars/ambushers", "feature text1"), + encounter.feature("avatars/bard_01", "feature text2"), + encounter.feature("avatars/broyelin", "feature text3") + } + end +# Image resources Those can be art, frames, icons and avatars. Virtually all arts except frames are interchangeable. You may use avatars for choices and layouts, but not vice versa. Path should be set as a path, see exact values below. -Art +## Art Art folder is used to generate layouts. It determines the art on the card. Possible values: - art/dark_sign.png - art/gold_female_black.png - art/gold_female_dark.png - art/gold_female_pale.png - art/gold_female_white.png - art/gold_female_white_grayscale.png - art/gold_male_black.png - art/gold_male_dark.png - art/gold_male_pale.png - art/gold_male_white.png - art/Gorg__Orc_Shaman.png - art/T_All_Heroes.png - art/T_Angry_Skeleton.png - art/T_Arkus_Imperial_Dragon.png - art/T_Banshee.png - art/T_Barreling_Fireball.png - art/T_Basilisk.png - art/T_Battle_Cry.png - art/T_Battle_Resurrect.png - art/T_Black_Arrow.png - art/T_Black_Knight.png - art/T_Blazing_Fire.png - art/T_Blazing_Heat.png - art/T_Bless.png - art/T_Bless_Of_Heart.png - art/T_Bless_Of_Iron.png - art/T_Bless_Of_Soul.png - art/T_Bless_Of_Steel.png - art/T_Bless_The_Flock.png - art/T_Blistering_Blaze.png - art/T_Blow_Away.png - art/T_Borg_Ogre_Mercenary.png - art/T_Bounty_Collection.png - art/T_Bribe.png - art/T_Broadsides.png - art/T_Broelyn_Loreweaver.png - art/T_Broelyn_Loreweaver_Old.png - art/T_Calm_Channel.png - art/T_Capsize.png - art/T_Captain_Goldtooth.png - art/T_Careful_Track.png - art/T_Cat_Familiar.png - art/T_Channel.png - art/T_Chaotic_Gust.png - art/T_Charing_Guardian.png - art/T_Cleric_Brightstar_Shield.png - art/T_Cleric_Divine_Resurrect.png - art/T_Cleric_Everburning_Candle.png - art/T_Cleric_Female.png - art/T_Cleric_Hammer_Of_Light.png - art/T_Cleric_Holy_Resurrect.png - art/T_Cleric_Lesser_Resurrect.png - art/T_Cleric_MaleAlternate.png - art/T_Cleric_Mass_Resurrect.png - art/T_Cleric_Minor_Resurrect.png - art/T_Cleric_Phoenix_Helm.png - art/T_Cleric_Redeemed_Ruinos.png - art/T_Cleric_Rightous_Resurrect.png - art/T_Cleric_Shining_Breastplate.png - art/T_Cleric_Talisman_Of_Renewal.png - art/T_Cleric_Veteran_Follower.png - art/T_Close_Ranks.png - art/T_Command.png - art/T_Confused_Apparition.png - art/T_Crashing_Torrent.png - art/T_Crashing_Wave.png - art/T_Cristov_The_Just.png - art/T_Cron_The_Berserker.png - art/T_Crushing_Strength.png - art/T_Cult_Priest.png - art/T_Cunning_Of_The_Wolf.png - art/T_Dagger.png - art/T_Darian_War_Mage.png - art/T_Dark_Energy.png - art/T_Dark_Reward.png - art/T_Death_Cultist.png - art/T_Death_Threat.png - art/T_Death_Touch.png - art/T_Deception.png - art/T_Deep_Channel.png - art/T_Demon.png - art/T_Devastating_Blow.png - art/T_Devil.png - art/T_Devotion.png - art/T_Dire_Wolf.png - art/T_Distracted_Exchange.png - art/T_Divine_Resurrect.png - art/T_Domination.png - art/T_Dragon_Fire.png - art/T_Drench.png - art/T_Edge_Of_The_Moat.png - art/T_Elf.png - art/T_Elixir_Of_Concentration.png - art/T_Elixir_Of_Endurance.png - art/T_Elixir_Of_Fortune.png - art/T_Elixir_Of_Strength.png - art/T_Elixir_Of_Wisdom.png - art/T_Elven_Curse.png - art/T_Elven_Gift.png - art/T_Evangelize.png - art/T_Expansion.png - art/T_Explosive_Fireball.png - art/T_Fairy.png - art/T_Feisty_Orcling.png - art/T_Fierce_Gale.png - art/T_Fighter_Crushing_Blow.png - art/T_Fighter_Double_Bladed_Axe.png - art/T_Fighter_FemaleAlternate.png - art/T_Fighter_Hand_Scythe.png - art/T_Fighter_Helm_Of_Fury.png - art/T_Fighter_Helm_Of_Fury_2.png - art/T_Fighter_Jagged_Spear.png - art/T_Fighter_Male.png - art/T_Fighter_Rallying_Flag.png - art/T_Fighter_Seasoned_Shield_Bearer.png - art/T_Fighter_Sharpening_Stone.png - art/T_Fighter_Spiked_Pauldrons.png - art/T_Fighter_Sweeping_Blow.png - art/T_Fighter_Whirling_Blow.png - art/T_Fireball.png - art/T_Fire_Blast.png - art/T_Fire_Bomb.png - art/T_Fire_Gem.png - art/T_Fire_Staff.png - art/T_Fissure.png - art/T_Flame_Burst.png - art/T_Flawless_Track.png - art/T_Flesh_Ripper.png - art/T_Flood.png - art/T_Follower_A.png - art/T_Follower_B.png - art/T_Giant_Knight.png - art/T_Glittering_Spray.png - art/T_Glittering_Torrent.png - art/T_Glittering_Wave.png - art/T_Goblin.png - art/T_Gold.png - art/T_GrakStormGiant.png - art/T_Granite_Smasher.png - art/T_GroupTackle.png - art/T_Growing_Flame.png - art/T_HalfDemon_AntonisPapantoniou.png - art/T_HalfDemon_HellFire_ShenFei.png - art/T_Headshot.png - art/T_Headshot_1.png - art/T_Heavy_Gust.png - art/T_Heist.png - art/T_HitJob.png - art/T_Holy_Resurrect.png - art/T_Horn_Of_Calling.png - art/T_Hunting_Bow.png - art/T_Ignite.png - art/T_Influence.png - art/T_Intimidation.png - art/T_Knock_Back.png - art/T_Knock_Down.png - art/T_Kraka_High_Priest.png - art/T_Krythos_Master_Vampire.png - art/T_Large_Twister.png - art/T_Lesser_Resurrect.png - art/T_Lesser_Vampire.png - art/T_Life_Drain.png - art/T_Life_Force.png - art/T_Lift.png - art/T_Light_Crossbow.png - art/T_Longshot.png - art/T_Longsword.png - art/T_Lys_The_Unseen.png - art/T_Man_At_Arms.png - art/T_Man_At_Arms_Old.png - art/T_Maroon.png - art/T_Mass_Resurrect.png - art/T_Masterful_Heist.png - art/T_Master_Weyan.png - art/T_Maurader.png - art/T_Midnight_Knight.png - art/T_Mighty_Blow.png - art/T_Minor_Resurrect.png - art/T_Misdirection.png - art/T_Myros_Guild_Mage.png - art/T_Nature_S_Bounty.png - art/T_Orc.png - art/T_Orc_Grunt.png - art/T_Orc_Guardian.png - art/T_Orc_Riot.png - art/T_Paladin_Sword.png - art/T_Parov_The_Enforcer.png - art/T_Pick_Pocket.png - art/T_Pilfer.png - art/T_Pillar_Of_Fire.png - art/T_Piracy.png - art/T_Powerful_Blow.png - art/T_Practiced_Heist.png - art/T_Prayer_Beads.png - art/T_Precision_Blow.png - art/T_Prism_RainerPetter.png - art/T_Profit.png - art/T_Pure_Channel.png - art/T_Quickshot.png - art/T_Raiding_Party.png - art/T_Rake_Master_Assassin.png - art/T_Rally_The_Troops.png - art/T_Rampage.png - art/T_Ranger_Fast_Track.png - art/T_Ranger_Female_Alternate.png - art/T_Ranger_Flashfire_Arrow.png - art/T_Ranger_Honed_Black_Arrow.png - art/T_Ranger_Hunters_Cloak.png - art/T_Ranger_Instinctive_Track.png - art/T_Ranger_Male.png - art/T_Ranger_Pathfinder_Compass.png - art/T_Ranger_Snake_Pet.png - art/T_Ranger_Sureshot_Bracer.png - art/T_Ranger_Twin_Shot.png - art/T_Ranger_Unending_Quiver.png - art/T_Rasmus_The_Smuggler.png - art/T_Rayla_Endweaver.png - art/T_Recruit.png - art/T_Relentless_Track.png - art/T_Resurrect.png - art/T_Righteous_Resurrect.png - art/T_Rolling_Fireball.png - art/T_Ruby.png - art/T_Scorching_Fireball.png - art/T_Searing_Fireball.png - art/T_Searing_Guardian.png - art/T_Seek_Revenge.png - art/T_Serene_Channel.png - art/T_Set_Sail.png - art/T_Shadow_Spell_09.png - art/T_Shadow_Spell_09_Blue.png - art/T_Shadow_Spell_09_Green.png - art/T_Shambling_Dirt.png - art/T_Shield_Bearer.png - art/T_Shining_Spray.png - art/T_Shortsword.png - art/T_Shoulder_Bash.png - art/T_Shoulder_Crush.png - art/T_Shoulder_Smash.png - art/T_Skeleton.png - art/T_Skeleton_Blue.png - art/T_Skeleton_Green.png - art/T_Skillful_Heist.png - art/T_Sleight_Of_Hand.png - art/T_Small_Twister.png - art/T_Smashing_Blow.png - art/T_Smash_And_Grab.png - art/T_Smooth_Heist.png - art/T_Snapshot.png - art/T_Soothing_Torrent.png - art/T_Soothing_Wave.png - art/T_Soul_Channel.png - art/T_Spark.png - art/T_Spell_Components.png - art/T_Spider.png - art/T_Spiked_Mace.png - art/T_Splashing_Wave.png - art/T_Spreading_Blaze.png - art/T_Spreading_Flames.png - art/T_Spreading_Sparks.png - art/T_Steady_Shot.png - art/T_Stone_Golem.png - art/T_Stone_Guardian.png - art/T_Storm_Siregar.png - art/T_Street_Thug.png - art/T_Strength_In_Numbers.png - art/T_Strength_Of_The_Wolf.png - art/T_Sweltering_Heat.png - art/T_Swipe.png - art/T_Taxation.png - art/T_Theft.png - art/T_TheRot.png - art/T_The_Rot.png - art/T_Thief_Blackjack.png - art/T_Thief_Enchanted_Garrote.png - art/T_Thief_Female.png - art/T_Thief_Jewelers_Loupe.png - art/T_Thief_Keen_Throwing_Knife.png - art/T_Thief_Knife_Belt.png - art/T_Thief_Male_Alternate.png - art/T_Thief_Masterful_Heist.png - art/T_Thief_Sacrificial_Dagger.png - art/T_Thief_Shadow_Mask.png - art/T_Thief_Shadow_Mask_2.png - art/T_Thief_Silent_Boots.png - art/T_Throwing_Axe.png - art/T_Throwing_Knife.png - art/T_TimelyHeist.png - art/T_Tithe_Priest.png - art/T_Torgen_Rocksplitter.png - art/T_Track.png - art/T_Triple_Shot.png - art/T_Turn_To_Ash.png - art/T_TwinShot.png - art/T_Tyrannor_The_Devourer.png - art/T_Unify_Apsara.png - art/T_Varrick_The_Necromancer.png - art/T_Venom.png - art/T_Violent_Gale.png - art/T_Walking_Dirt.png - art/T_Weak_Skeleton.png - art/T_Well_Placed_Shot.png - art/T_Whirling_Blow.png - art/T_Wind_Storm.png - art/T_Wind_Tunnel.png - art/T_Wizard_Alchemist_S_Stone.png - art/T_Wizard_Arcane_Wand.png - art/T_Wizard_Blazing_Staff.png - art/T_Wizard_Female_Alternate.png - art/T_Wizard_Magic_Mirror.png - art/T_Wizard_Magic_Mirror_2.png - art/T_Wizard_Male.png - art/T_Wizard_Runic_Robes.png - art/T_Wizard_Runic_Robes_2.png - art/T_Wizard_Serpentine_Staff.png - art/T_Wizard_Silverskull_Amulet.png - art/T_Wizard_Spellcaster_Gloves.png - art/T_Wolf_Form.png - art/T_Wolf_Shaman.png - art/T_Word_Of_Power.png - art/T_Wurm.png - art/T_Wyvern.png - art/Promos1Art/Afterlife.png - art/Promos1Art/Bjorn_the_Centurion.png - art/Promos1Art/Bloodfang.png - art/Promos1Art/Crime_Spree.png - art/Promos1Art/Devotion.png - art/Promos1Art/Dragon_Fire.png - art/Promos1Art/Droga__Guild_Enforcer.png - art/Promos1Art/Galok__the_Vile.png - art/Promos1Art/Gorg__Orc_Shaman.png - art/Promos1Art/Kasha__the_Awakener.png - art/Promos1Art/Legionnaire.png - art/Promos1Art/Mobia__Elf_Lord.png - art/Promos1Art/Raiding_Party.png - art/Promos1Art/Ren__Bounty_Hunter.png - art/Promos1Art/Robbery.png - art/Promos1Art/The_Summoning.png - art/Promos1Art/Valius__Fire_Dragon.png - art/Promos1Art/Zombie.png - art/treasures/T_Bottle_Of_Rum.png - art/treasures/T_Bracers_Of_Brawn.png - art/treasures/T_Brillant_Ruby.png - art/treasures/T_Cleric_Elixir_Blue_Purple.png - art/treasures/T_Cleric_Elixir_Golden.png - art/treasures/T_Cleric_Elixir_Green.png - art/treasures/T_Fat_Cat_Familiar.png - art/treasures/T_Fighter_Elixir_Blue.png - art/treasures/T_Fighter_Elixir_Green.png - art/treasures/T_Fighter_Elixir_Red.png - art/treasures/T_Flaming_Longsword.PNG - art/treasures/T_Green_Potions_Large.png - art/treasures/T_Green_Potions_Medium.png - art/treasures/T_Hook_Weapon.png - art/treasures/T_Horn_Of_Command.png - art/treasures/T_Horn_Of_Need.png - art/treasures/T_Imperial_Sailor.png - art/treasures/T_Lightning_Longsword.png - art/treasures/T_Magic_Scroll_Souveraine.png - art/treasures/T_Parrot.png - art/treasures/T_Pirate_Cutlass.png - art/treasures/T_Ranger_Elixir_Orange.png - art/treasures/T_Ranger_Elixir_Red_Brownish.png - art/treasures/T_Ranger_Elixir_Yellow.png - art/treasures/T_Sharpened_Ruby.png - art/treasures/T_Ship_Bell.png - art/treasures/T_Ship_In_A_Bottle.png - art/treasures/T_Spiked_Mace_Of_Healing.png - art/treasures/T_Spiked_Mace_Of_Inspiration.png - art/treasures/T_Spyglass.png - art/treasures/T_Thief_Elixir_Black.png - art/treasures/T_Thief_Elixir_Red.png - art/treasures/T_Thief_Elixir_White.png - art/treasures/T_Treasure_Map.png - art/treasures/T_Trick_Dice.png - art/treasures/T_Wise_Cat_Familiar.png - art/treasures/T_Wizard_Elixir_Blue.png - art/treasures/T_Wizard_Elixir_Orange.png - art/treasures/T_Wizard_Elixir_Silver.png - - -Frames + art/gorg__orc_shaman + art/t_all_heroes + art/t_all_heroes_2 + art/t_angry_skeleton + art/t_arkus_imperial_dragon + art/t_banshee + art/t_barreling_fireball + art/t_basilisk + art/t_battle_cry + art/t_battle_resurrect + art/t_black_arrow + art/t_black_knight + art/t_blazing_fire + art/t_blazing_heat + art/t_bless + art/t_bless_of_heart + art/t_bless_of_iron + art/t_bless_of_soul + art/t_bless_of_steel + art/t_bless_the_flock + art/t_blistering_blaze + art/t_blow_away + art/t_borg_ogre_mercenary + art/t_bounty_collection + art/t_bribe + art/t_broadsides + art/t_broelyn_loreweaver + art/t_broelyn_loreweaver_old + art/t_calm_channel + art/t_capsize + art/t_captain_goldtooth + art/t_careful_track + art/t_cat_familiar + art/t_cat_familiar_fixed + art/t_channel + art/t_chaotic_gust + art/t_charing_guardian + art/t_cleric_brightstar_shield + art/t_cleric_divine_resurrect + art/t_cleric_everburning_candle + art/t_cleric_female + art/t_cleric_hammer_of_light + art/t_cleric_holy_resurrect + art/t_cleric_lesser_resurrect + art/t_cleric_malealternate + art/t_cleric_mass_resurrect + art/t_cleric_minor_resurrect + art/t_cleric_phoenix_helm + art/t_cleric_redeemed_ruinos + art/t_cleric_rightous_resurrect + art/t_cleric_shining_breastplate + art/t_cleric_talisman_of_renewal + art/t_cleric_veteran_follower + art/t_close_ranks + art/t_command + art/t_confused_apparition + art/t_crashing_torrent + art/t_crashing_wave + art/t_cristov_the_just + art/t_cron_the_berserker + art/t_crushing_strength + art/t_cult_priest + art/t_cunning_of_the_wolf + art/t_dagger + art/t_darian_war_mage + art/t_dark_energy + art/t_dark_reward + art/t_death_cultist + art/t_death_threat + art/t_death_touch + art/t_deception + art/t_deep_channel + art/t_demon + art/t_devastating_blow + art/t_devil + art/t_devotion + art/t_dire_wolf + art/t_distracted_exchange + art/t_divine_resurrect + art/t_domination + art/t_dragon_fire + art/t_drench + art/t_edge_of_the_moat + art/t_elf + art/t_elixir_of_concentration + art/t_elixir_of_endurance + art/t_elixir_of_fortune + art/t_elixir_of_strength + art/t_elixir_of_wisdom + art/t_elven_curse + art/t_elven_gift + art/t_evangelize + art/t_expansion + art/t_explosive_fireball + art/t_fairy + art/t_feisty_orcling + art/t_fierce_gale + art/t_fighter_crushing_blow + art/t_fighter_double_bladed_axe + art/t_fighter_femalealternate + art/t_fighter_hand_scythe + art/t_fighter_helm_of_fury + art/t_fighter_helm_of_fury_2 + art/t_fighter_jagged_spear + art/t_fighter_male + art/t_fighter_rallying_flag + art/t_fighter_seasoned_shield_bearer + art/t_fighter_sharpening_stone + art/t_fighter_spiked_pauldrons + art/t_fighter_sweeping_blow + art/t_fighter_sweeping_blow_fixed + art/t_fighter_whirling_blow + art/t_fighter_whirling_blow_fixed + art/t_fire_blast + art/t_fire_bomb + art/t_fire_gem + art/t_fire_staff + art/t_fireball + art/t_fissure + art/t_flame_burst + art/t_flawless_track + art/t_flesh_ripper + art/t_flood + art/t_follower_a + art/t_follower_b + art/t_giant_knight + art/t_glittering_spray + art/t_glittering_torrent + art/t_glittering_wave + art/t_goblin + art/t_gold + art/t_grakstormgiant + art/t_granite_smasher + art/t_grouptackle + art/t_growing_flame + art/t_halfdemon_antonispapantoniou + art/t_halfdemon_hellfire_shenfei + art/t_headshot + art/t_headshot_1 + art/t_heavy_gust + art/t_heist + art/t_hitjob + art/t_holy_resurrect + art/t_horn_of_calling + art/t_hunting_bow + art/t_ignite + art/t_influence + art/t_intimidation + art/t_knock_back + art/t_knock_down + art/t_kraka_high_priest + art/t_krythos_master_vampire + art/t_large_twister + art/t_lesser_resurrect + art/t_lesser_vampire + art/t_life_drain + art/t_life_force + art/t_lift + art/t_light_crossbow + art/t_longshot + art/t_longsword + art/t_lys_the_unseen + art/t_man_at_arms + art/t_man_at_arms_old + art/t_maroon + art/t_mass_resurrect + art/t_master_weyan + art/t_masterful_heist + art/t_maurader + art/t_midnight_knight + art/t_mighty_blow + art/t_minor_resurrect + art/t_misdirection + art/t_mud_pile + art/t_myros_guild_mage + art/t_nature_s_bounty + art/t_orc + art/t_orc_grunt + art/t_orc_guardian + art/t_orc_riot + art/t_paladin_sword + art/t_parov_the_enforcer + art/t_pick_pocket + art/t_pilfer + art/t_pillar_of_fire + art/t_piracy + art/t_powerful_blow + art/t_practiced_heist + art/t_prayer_beads + art/t_precision_blow + art/t_prism_rainerpetter + art/t_profit + art/t_pure_channel + art/t_quickshot + art/t_raiding_party + art/t_rake_master_assassin + art/t_rally_the_troops + art/t_rampage + art/t_ranger_fast_track + art/t_ranger_female_alternate + art/t_ranger_flashfire_arrow + art/t_ranger_honed_black_arrow + art/t_ranger_hunters_cloak + art/t_ranger_instinctive_track + art/t_ranger_instinctive_track_fixed + art/t_ranger_male + art/t_ranger_pathfinder_compass + art/t_ranger_snake_pet + art/t_ranger_sureshot_bracer + art/t_ranger_twin_shot + art/t_ranger_unending_quiver + art/t_rasmus_the_smuggler + art/t_rayla_endweaver + art/t_recruit + art/t_relentless_track + art/t_resurrect + art/t_righteous_resurrect + art/t_rock_guardian + art/t_rolling_fireball + art/t_ruby + art/t_scorching_fireball + art/t_searing_fireball + art/t_searing_guardian + art/t_seek_revenge + art/t_serene_channel + art/t_set_sail + art/t_shadow_spell_09 + art/t_shadow_spell_09_blue + art/t_shadow_spell_09_green + art/t_shambling_dirt + art/t_shield_bearer + art/t_shining_spray + art/t_shortsword + art/t_shoulder_bash + art/t_shoulder_crush + art/t_shoulder_smash + art/t_skeleton + art/t_skeleton_blue + art/t_skeleton_green + art/t_skillful_heist + art/t_sleight_of_hand + art/t_small_twister + art/t_smash_and_grab + art/t_smashing_blow + art/t_smooth_heist + art/t_snapshot + art/t_soothing_torrent + art/t_soothing_wave + art/t_soul_channel + art/t_spark + art/t_spell_components + art/t_spider + art/t_spiked_mace + art/t_splashing_wave + art/t_spreading_blaze + art/t_spreading_flames + art/t_spreading_sparks + art/t_steady_shot + art/t_stone_golem + art/t_stone_guardian + art/t_storm_siregar + art/t_street_thug + art/t_strength_in_numbers + art/t_strength_of_the_wolf + art/t_sweltering_heat + art/t_swipe + art/t_taxation + art/t_therot + art/t_the_rot + art/t_theft + art/t_thief_blackjack + art/t_thief_enchanted_garrote + art/t_thief_female + art/t_thief_jewelers_loupe + art/t_thief_keen_throwing_knife + art/t_thief_knife_belt + art/t_thief_male_alternate + art/t_thief_masterful_heist + art/t_thief_sacrificial_dagger + art/t_thief_shadow_mask + art/t_thief_shadow_mask_2 + art/t_thief_silent_boots + art/t_throwing_axe + art/t_throwing_knife + art/t_timelyheist + art/t_tithe_priest + art/t_torgen_rocksplitter + art/t_track + art/t_triple_shot + art/t_turn_to_ash + art/t_twinshot + art/t_tyrannor_the_devourer + art/t_unify_apsara + art/t_varrick_the_necromancer + art/t_venom + art/t_violent_gale + art/t_walking_dirt + art/t_weak_skeleton + art/t_well_placed_shot + art/t_whirling_blow + art/t_wind_storm + art/t_wind_tunnel + art/t_wizard_alchemist_s_stone + art/t_wizard_arcane_wand + art/t_wizard_blazing_staff + art/t_wizard_female_alternate + art/t_wizard_magic_mirror + art/t_wizard_magic_mirror_2 + art/t_wizard_male + art/t_wizard_runic_robes + art/t_wizard_runic_robes_2 + art/t_wizard_serpentine_staff + art/t_wizard_silverskull_amulet + art/t_wizard_spellcaster_gloves + art/t_wolf_form + art/t_wolf_shaman + art/t_word_of_power + art/t_wurm + art/t_wyvern + art/catacombs + art/cleric_lesser_resurrect + art/cleric_shining_breastplate + art/dark_sign + art/drak__storm_giant + art/fighter_sweeping_blow_old + art/fighter_whirling_blow_old + art/gold_female_black + art/gold_female_dark + art/gold_female_pale + art/gold_female_white + art/gold_female_white_grayscale + art/gold_male_black + art/gold_male_dark + art/gold_male_pale + art/gold_male_white + art/monsters_in_the_dark + art/ranger_hunters_cloak + art/the_call + art/wizard_fireball + art/ancestry/battle_rage + art/ancestry/bully + art/ancestry/burgle + art/ancestry/crush_you! + art/ancestry/demon_blood + art/ancestry/demonic_strength + art/ancestry/dwarf + art/ancestry/elf + art/ancestry/elven_grace + art/ancestry/elven_wisdom + art/ancestry/friendly_banter + art/ancestry/half-demon + art/ancestry/hammer + art/ancestry/hammer_strike + art/ancestry/hellfire + art/ancestry/hide + art/ancestry/ogre + art/ancestry/orc + art/ancestry/orc_male + art/ancestry/pick + art/ancestry/ragged_blade + art/ancestry/shiny_rock + art/ancestry/smallfolk + art/ancestry/sunstone_brooch + art/ancestry/war_club + art/epicart/absolve + art/epicart/abyss_summoner + art/epicart/aerial_assassin + art/epicart/alchemist_assassin + art/epicart/ambush_party + art/epicart/amnesia + art/epicart/ancient_chant + art/epicart/angel_of_death + art/epicart/angel_of_death_alt + art/epicart/angel_of_light + art/epicart/angel_of_mercy + art/epicart/angel_of_the_gate + art/epicart/angelic_protector + art/epicart/angeline_s_favor + art/epicart/angeline_s_will + art/epicart/anguish_demon + art/epicart/ankylosaurus + art/epicart/apocalypse + art/epicart/arcane_research + art/epicart/arm + art/epicart/army_of_the_apocalypse + art/epicart/ascendant_pyromancer + art/epicart/avenger_of_covenant + art/epicart/avenging_angel + art/epicart/banishment + art/epicart/battle_cry + art/epicart/bellowing_minotaur + art/epicart/bitten + art/epicart/blind_faith + art/epicart/blue_dragon + art/epicart/bodyguard + art/epicart/bounty_hunter + art/epicart/brachiosaurus + art/epicart/brak__fist_of_lashnok + art/epicart/brand__rebel_fighter + art/epicart/brave_squire + art/epicart/breath_of_life + art/epicart/bruger__the_pathfinder + art/epicart/burrowing_wurm + art/epicart/canopy_ranger + art/epicart/carrion_demon + art/epicart/cast_out + art/epicart/cave_troll + art/epicart/ceasefire + art/epicart/chamberlain_kark + art/epicart/champion_of_the_wicked + art/epicart/charged_dragon + art/epicart/chomp + art/epicart/chomp_ + art/epicart/chronicler + art/epicart/citadel_raven + art/epicart/citadel_scholar + art/epicart/consume + art/epicart/corpse_taker + art/epicart/corpsemonger + art/epicart/courageous_soul + art/epicart/crystal_golem + art/epicart/cyan_dragon + art/epicart/dark_assassin + art/epicart/dark_eyes + art/epicart/dark_knight + art/epicart/dark_leader + art/epicart/dark_offering + art/epicart/dark_one_s_apprentice + art/epicart/dark_one_s_fury + art/epicart/dark_prince + art/epicart/deadly_raid + art/epicart/deathbringer + art/epicart/demon_breach + art/epicart/demon_token + art/epicart/demonic_rising + art/epicart/den_mother + art/epicart/devour + art/epicart/dirge_of_scara + art/epicart/disappearing_act + art/epicart/divine_judgment + art/epicart/djinn_of_the_sands + art/epicart/dragonling + art/epicart/drain_essence + art/epicart/draka__dragon_tyrant + art/epicart/draka_s_enforcer + art/epicart/draka_s_fire + art/epicart/drifting_terror + art/epicart/drinker_of_blood + art/epicart/eager_necromancer + art/epicart/elara__the_lycomancer + art/epicart/elder_greatwurm + art/epicart/endbringer_ritualist + art/epicart/enrage + art/epicart/ensnaring_rune + art/epicart/entangling_vines + art/epicart/erase + art/epicart/erratic_research + art/epicart/erwin__architect_of_war + art/epicart/ethereal_dragon + art/epicart/evict + art/epicart/fairy_entrancer + art/epicart/fairy_trickster + art/epicart/faithful_pegasus + art/epicart/feeding_frenzy + art/epicart/feint + art/epicart/fiery_demise + art/epicart/final_task + art/epicart/fire_shaman + art/epicart/fire_spirit + art/epicart/fireball + art/epicart/fires_of_rebellion + art/epicart/flame_spike + art/epicart/flame_strike + art/epicart/flames_of_furios + art/epicart/flanking_maneuver + art/epicart/flash_fire + art/epicart/flood_of_fire + art/epicart/forbidden_research + art/epicart/force_field + art/epicart/force_lance + art/epicart/forced_exile + art/epicart/forcemage_apprentice + art/epicart/forest_dweller + art/epicart/forest_giant + art/epicart/forked_jolt + art/epicart/forked_lightning + art/epicart/frantic_digging + art/epicart/from_beyond + art/epicart/frost_giant + art/epicart/fumble + art/epicart/garbage_golem + art/epicart/gareth_s_automaton + art/epicart/gareth_s_juggernaut + art/epicart/gareth_s_processor + art/epicart/gareth_s_will + art/epicart/gift_of_furios + art/epicart/gladius_the_defender + art/epicart/go_wild + art/epicart/gold_dragon + art/epicart/grave_demon + art/epicart/great_horned_lizard + art/epicart/greater_lightning_wurm + art/epicart/guilt_demon + art/epicart/halt_ + art/epicart/hand_of_angeline + art/epicart/hands_from_below + art/epicart/harbinger_of_anguish + art/epicart/hasty_retreat + art/epicart/heinous_feast + art/epicart/helena_s_chosen + art/epicart/helion__the_dominator + art/epicart/helion_s_fury + art/epicart/herald_of_angeline + art/epicart/herald_of_lashnok + art/epicart/herald_of_scara + art/epicart/high_king + art/epicart/hill_giant + art/epicart/howl + art/epicart/human_token + art/epicart/hunting_pack + art/epicart/hunting_pterosaur + art/epicart/hunting_raptors + art/epicart/hunting_wyvern + art/epicart/hurricane + art/epicart/ice_drake + art/epicart/imperial_cavalry + art/epicart/imperial_commander + art/epicart/infernal_gatekeeper + art/epicart/infest + art/epicart/inheritance_of_the_meek + art/epicart/inner_demon + art/epicart/inner_peace + art/epicart/insurgency + art/epicart/invoke_pact + art/epicart/javelin_thrower + art/epicart/juggernaut + art/epicart/jungle_queen + art/epicart/justice_prevails + art/epicart/kalani__woodreader + art/epicart/keeper_of_secrets + art/epicart/keira__wolf_caller + art/epicart/knight_of_elara + art/epicart/knight_of_glory + art/epicart/knight_of_shadows + art/epicart/knight_of_the_dawn + art/epicart/knight_of_the_dragon + art/epicart/kong + art/epicart/krieg__dark_one_s_chosen + art/epicart/lash + art/epicart/lashnok_s_will + art/epicart/lay_claim + art/epicart/lesser_angel + art/epicart/lesser_arms + art/epicart/lesser_chaos + art/epicart/lesser_demonize + art/epicart/lesser_devil + art/epicart/lesser_digging + art/epicart/lesser_dinosaur + art/epicart/lesser_dragon + art/epicart/lesser_elf + art/epicart/lesser_flame + art/epicart/lesser_giant + art/epicart/lesser_golem + art/epicart/lesser_night_stalker + art/epicart/lesser_planning + art/epicart/lesser_squire + art/epicart/lesson_learned + art/epicart/lightning_elemental + art/epicart/lightning_mage + art/epicart/lightning_storm + art/epicart/lightning_strike + art/epicart/little_devil + art/epicart/lord_of_the_arena + art/epicart/lurking_giant + art/epicart/lycomancy + art/epicart/lying_in_wait + art/epicart/markus__watch_captain + art/epicart/marshal + art/epicart/martial_law + art/epicart/master_forcemage + art/epicart/master_zo + art/epicart/medusa + art/epicart/memory_spirit + art/epicart/mighty_blow + art/epicart/mirage_wielder + art/epicart/mist_guide_herald + art/epicart/mobilize + art/epicart/murderous_necromancer + art/epicart/muse + art/epicart/mystic_researcher + art/epicart/mythic_monster + art/epicart/necromancer_apprentice + art/epicart/necromancer_lord + art/epicart/necrovirus + art/epicart/new_dawn + art/epicart/no_escape + art/epicart/noble_martyr + art/epicart/noble_unicorn + art/epicart/novice_wizard + art/epicart/ogre_mercenary + art/epicart/owl_familiar + art/epicart/pack_alpha + art/epicart/palace_guard + art/epicart/paros__rebel_leader + art/epicart/pelios__storm_lord + art/epicart/plague + art/epicart/plague_zombies + art/epicart/plentiful_dead + art/epicart/polar_shock + art/epicart/preacher + art/epicart/priest_of_kalnor + art/epicart/priestess_of_angeline + art/epicart/psionic_assault + art/epicart/psyche_snare + art/epicart/pyromancer + art/epicart/pyrosaur + art/epicart/quell + art/epicart/rabble_rouser + art/epicart/rage + art/epicart/raging_t_rex + art/epicart/rain_of_fire + art/epicart/rally_the_people + art/epicart/rampaging_cyclops + art/epicart/rampaging_wurm + art/epicart/raxxa__demon_tyrant + art/epicart/raxxa_s_curse + art/epicart/raxxa_s_displeasure + art/epicart/raxxa_s_enforcer + art/epicart/reality_shift + art/epicart/reap_or_sow + art/epicart/reaper + art/epicart/red_mist + art/epicart/rescue_griffin + art/epicart/reset + art/epicart/resurrection + art/epicart/reusable_knowledge + art/epicart/revolt + art/epicart/rift_summoner + art/epicart/rise_of_the_many + art/epicart/rising_storm + art/epicart/ritual_of_scara + art/epicart/royal_escort + art/epicart/royal_intervention + art/epicart/run_riot + art/epicart/runek__dark_duelist + art/epicart/rybas__canopy_sniper + art/epicart/sand_wurm + art/epicart/saren__night_stalker + art/epicart/savage_uprising + art/epicart/scara_s_gift + art/epicart/scara_s_will + art/epicart/scarred_berserker + art/epicart/scarred_cultist + art/epicart/scarred_priestess + art/epicart/scarros__hound_of_draka + art/epicart/scrap_golem + art/epicart/scrap_golem_token + art/epicart/scrap_token + art/epicart/sea_hydra + art/epicart/sea_titan + art/epicart/second_wind + art/epicart/secret_legion + art/epicart/sequester + art/epicart/shadow_imp + art/epicart/shield_of_tarken + art/epicart/shock_trooper + art/epicart/silver_dragon + art/epicart/silver_wing_griffin + art/epicart/silver_wing_guardian + art/epicart/silver_wing_lancer + art/epicart/silver_wing_paladin + art/epicart/silver_wing_savior + art/epicart/siren_s_song + art/epicart/sky_serpent + art/epicart/smash_and_burn + art/epicart/soul_hunter + art/epicart/soul_storm + art/epicart/sparkmage + art/epicart/spawning_demon + art/epicart/spike_trap + art/epicart/spore_beast + art/epicart/stalking_werewolf + art/epicart/stampeding_einiosaurus + art/epicart/stand_alone + art/epicart/standard_bearer + art/epicart/stealthy_predator + art/epicart/steed_of_zaltessa + art/epicart/steel_golem + art/epicart/steel_titan + art/epicart/storm_dragon + art/epicart/strafing_dragon + art/epicart/street_swindler + art/epicart/subjugate + art/epicart/succubus + art/epicart/sun_strike + art/epicart/surprise_attack + art/epicart/teleport + art/epicart/temporal_enforcer + art/epicart/temporal_shift + art/epicart/temporize + art/epicart/terrorize + art/epicart/the_gudgeon + art/epicart/the_people_s_champion + art/epicart/the_risen + art/epicart/thought_plucker + art/epicart/thrasher_demon + art/epicart/thundarus + art/epicart/time_bender + art/epicart/time_thief + art/epicart/time_walker + art/epicart/transfigure + art/epicart/transform + art/epicart/transmogrify + art/epicart/triceratops + art/epicart/trihorror + art/epicart/turn + art/epicart/unquenchable_thirst + art/epicart/urgent_messengers + art/epicart/valentia_justice_bringer + art/epicart/valiant_knight + art/epicart/vampire_lord + art/epicart/vanishing + art/epicart/velden__frost_titan + art/epicart/vilify + art/epicart/village_protector + art/epicart/vital_mission + art/epicart/wake_the_dead + art/epicart/war_lion_of_valentia + art/epicart/war_machine + art/epicart/war_priest + art/epicart/warrior_angel + art/epicart/warrior_golem + art/epicart/watchful_gargoyle + art/epicart/wave_of_transformation + art/epicart/whirlwind + art/epicart/white_dragon + art/epicart/white_knight + art/epicart/wild_roc + art/epicart/winds_of_change + art/epicart/winged_death + art/epicart/winter_fairy + art/epicart/wither + art/epicart/wolf_companion + art/epicart/wolf_s_bite + art/epicart/wolf_s_call + art/epicart/wolf_token + art/epicart/woodland_bushwacker + art/epicart/word_of_summoning + art/epicart/wurm_hatchling + art/epicart/wyvern + art/epicart/zaltessa_s_fire + art/epicart/zannos__corpse_lord + art/epicart/zealous_necromancer + art/epicart/zombie_apocalypse + art/epicart/zombie_token + art/treasures/t_bottle_of_rum + art/treasures/t_bracers_of_brawn + art/treasures/t_cleric_elixir_blue_purple + art/treasures/t_cleric_elixir_golden + art/treasures/t_cleric_elixir_green + art/treasures/t_fighter_elixir_blue + art/treasures/t_fighter_elixir_green + art/treasures/t_fighter_elixir_red + art/treasures/t_green_potions_large + art/treasures/t_green_potions_medium + art/treasures/t_hook_weapon + art/treasures/t_imperial_sailor + art/treasures/t_magic_scroll_souveraine + art/treasures/t_parrot + art/treasures/t_pirate_cutlass + art/treasures/t_ranger_elixir_orange + art/treasures/t_ranger_elixir_red_brownish + art/treasures/t_ranger_elixir_yellow + art/treasures/t_ship_bell + art/treasures/t_ship_in_a_bottle + art/treasures/t_spyglass + art/treasures/t_thief_elixir_black + art/treasures/t_thief_elixir_red + art/treasures/t_thief_elixir_white + art/treasures/t_treasure_map + art/treasures/t_trick_dice + art/treasures/t_wizard_elixir_blue + art/treasures/t_wizard_elixir_orange + art/treasures/t_wizard_elixir_silver + art/treasures/alchemist_diamond + art/treasures/alchemist_kaleidoscope + art/treasures/alchemist_prismatic_cloak + art/treasures/alchemist_rainbow_potion + art/treasures/alchemist_recipe_book + art/treasures/alchemist_silver_scales + art/treasures/alchemist_vial_of_acid + art/treasures/alchemist_waistcoat_of_infinite_pockets + art/treasures/barbarian_caltrops + art/treasures/barbarian_cloak_of_rage + art/treasures/barbarian_double_bladed_hand_axe + art/treasures/barbarian_net_of_thorns + art/treasures/barbarian_pillage + art/treasures/barbarian_roaring_cowl + art/treasures/barbarian_seething_spear + art/treasures/barbarian_whip + art/treasures/bard_captivating_herald + art/treasures/bard_dancing_shoes + art/treasures/bard_enchanted_flute + art/treasures/bard_minstrel_s_gloves + art/treasures/bard_muse_s_paper + art/treasures/bard_music_box + art/treasures/bard_tuning_fork + art/treasures/bard_whistling_rapier + art/treasures/cleric_benediction_beads + art/treasures/cleric_blessed_bracers + art/treasures/cleric_enduring_follower + art/treasures/cleric_holy_water + art/treasures/cleric_mantle_of_rebirth + art/treasures/cleric_morningstar + art/treasures/cleric_motes_of_light + art/treasures/cleric_sacred_censer + art/treasures/cleric_spiked_mace_of_healing + art/treasures/cleric_spiked_mace_of_might + art/treasures/druid_flying_squirrel + art/treasures/druid_great_stag + art/treasures/druid_grizzled_pauldrons + art/treasures/druid_hardy_hedgehog + art/treasures/druid_lore_telling + art/treasures/druid_reclaim_the_forest + art/treasures/druid_sunbird + art/treasures/druid_verdant_boots + art/treasures/druid_wisdom_of_the_woods + art/treasures/fighter_adamantine_breastplate + art/treasures/fighter_chain + art/treasures/fighter_dazzling_ruby + art/treasures/fighter_flaming_longsword + art/treasures/fighter_iron_shield + art/treasures/fighter_javelin + art/treasures/fighter_lightning_longsword + art/treasures/fighter_runic_throwing_axe + art/treasures/fighter_spiked_sabatons + art/treasures/fighter_taunting_talisman + art/treasures/monk_ancestral_circlet + art/treasures/monk_arm_bands_of_defense + art/treasures/monk_dragon_staff + art/treasures/monk_radiant_blossom + art/treasures/monk_staff_of_the_phoenix + art/treasures/monk_tranquil_wind + art/treasures/monk_void_s_eye + art/treasures/monk_wraps_of_illusionary_speed + art/treasures/necromancer_bag_of_bones + art/treasures/necromancer_bone_gauntlets + art/treasures/necromancer_company_of_corpses + art/treasures/necromancer_dread_cauldron + art/treasures/necromancer_greaves_of_grieving + art/treasures/necromancer_preserved_heart + art/treasures/necromancer_soul_cage + art/treasures/necromancer_withered_wand + art/treasures/ranger_death_arrow + art/treasures/ranger_gloves_of_accuracy + art/treasures/ranger_gold_tipped_arrow + art/treasures/ranger_hawk_pet + art/treasures/ranger_horn_of_command + art/treasures/ranger_horn_of_need + art/treasures/ranger_stalking_bow + art/treasures/ranger_tracker_s_boots + art/treasures/ranger_trick_arrow + art/treasures/ranger_veiled_trap + art/treasures/thief_blinding_powder + art/treasures/thief_brillant_ruby + art/treasures/thief_hideaway_cloak + art/treasures/thief_kunai + art/treasures/thief_lockpick + art/treasures/thief_lucky_knife + art/treasures/thief_parrying_dagger + art/treasures/thief_ruby_encrusted_knife + art/treasures/thief_sharpened_ruby + art/treasures/thief_stickyfinger_gloves + art/treasures/wizard_adept_s_components + art/treasures/wizard_clock_of_ages + art/treasures/wizard_combust + art/treasures/wizard_content_familiar + art/treasures/wizard_enchanter_s_hat + art/treasures/wizard_incandescent_sash + art/treasures/wizard_magic_scroll + art/treasures/wizard_prestidigitation_charm + art/treasures/wizard_wand_of_wanting + art/treasures/wizard_wizened_familiar + art/campaign/journeys/imperial_persistence + art/campaign/warinthewild/kasha__the_awakener_cropped + art/campaign/warinthewild/slaughterclaw + art/campaign/warinthewild/slaughterclaw_fixed + art/campaign/warinthewild/slaughterclaw_zoomed + art/campaign/warinthewild/the_summoning + art/classes/alchemist/crucible + art/classes/alchemist/diamond + art/classes/alchemist/dispersion + art/classes/alchemist/dissolve + art/classes/alchemist/fireworks + art/classes/alchemist/reflection + art/classes/alchemist/refraction + art/classes/alchemist/transmogrification + art/classes/alchemist/transmutation + art/classes/alchemist/alchemy_powders + art/classes/alchemist/auric_gloves + art/classes/alchemist/bottled_tempest + art/classes/alchemist/brittle_gas + art/classes/alchemist/fools_gold + art/classes/alchemist/frothing_potion + art/classes/alchemist/instant_transmutation + art/classes/alchemist/major_transmogrification + art/classes/alchemist/minor_transmutation + art/classes/alchemist/perfect_refraction + art/classes/alchemist/philosophers_stone + art/classes/alchemist/polished_philosophers_stone + art/classes/alchemist/prismatic_dispersion + art/classes/alchemist/rainbow_potion + art/classes/alchemist/rapid_transmogrification + art/classes/alchemist/rapid_transmutation + art/classes/alchemist/recalibration_crystal + art/classes/alchemist/recipe_book + art/classes/alchemist/sloshing_potion + art/classes/alchemist/spectrum_spectacles + art/classes/alchemist/swirling_philosophers_stone + art/classes/alchemist/vial_of_acid_gas + art/classes/alchemist/waistcoat_of_infinite_pockets + art/classes/alchemist/wide_diffusion + art/classes/barbarian/amulet_of_stifled_pain + art/classes/barbarian/battle_cry + art/classes/barbarian/battle_roar_incorrect + art/classes/barbarian/battle_roar_l3 + art/classes/barbarian/battle_roar_l5 + art/classes/barbarian/bellow + art/classes/barbarian/bone_axe + art/classes/barbarian/crushed_coin + art/classes/barbarian/disorienting_headbutt + art/classes/barbarian/double_bladed_hand_axe + art/classes/barbarian/earthshaker + art/classes/barbarian/eternal_rage_berserk + art/classes/barbarian/eternal_rage_calm + art/classes/barbarian/explosive_rage_berserk + art/classes/barbarian/explosive_rage_calm + art/classes/barbarian/fiery_rage_berserk + art/classes/barbarian/fiery_rage_calm + art/classes/barbarian/flail + art/classes/barbarian/flaring_rage_berserk + art/classes/barbarian/flaring_rage_calm + art/classes/barbarian/growl + art/classes/barbarian/hand_axe + art/classes/barbarian/headbutt + art/classes/barbarian/inner_rage_berserk + art/classes/barbarian/inner_rage_calm + art/classes/barbarian/plunder + art/classes/barbarian/razor_bracers + art/classes/barbarian/ring_of_rage + art/classes/barbarian/roar + art/classes/barbarian/seething_spear + art/classes/barbarian/serrated_hand_axe + art/classes/barbarian/shattering_headbutt + art/classes/barbarian/smoldering_rage_berserk + art/classes/barbarian/smoldering_rage_calm + art/classes/barbarian/stomping_boots + art/classes/barbarian/terrifying_howl + art/classes/barbarian/terrifying_roar + art/classes/barbarian/war_cry + art/classes/barbarian/wolf_companion + art/classes/bard/bard_battle_march + art/classes/bard/bard_bold_saga + art/classes/bard/bard_brave_story + art/classes/bard/bard_captivating_herald + art/classes/bard/bard_character_female + art/classes/bard/bard_character_male + art/classes/bard/bard_coat_of_encores + art/classes/bard/bard_collecting_cap + art/classes/bard/bard_dancing_blade + art/classes/bard/bard_dancing_shoes + art/classes/bard/bard_enchanted_flute + art/classes/bard/bard_epic_poem + art/classes/bard/bard_flute + art/classes/bard/bard_goblet_of_whimsy + art/classes/bard/bard_golden_harp + art/classes/bard/bard_grand_legend + art/classes/bard/bard_guild_tale + art/classes/bard/bard_harp + art/classes/bard/bard_herald + art/classes/bard/bard_heroic_fable + art/classes/bard/bard_imperial_anthem + art/classes/bard/bard_inspiring_tune + art/classes/bard/bard_intrepid_tale + art/classes/bard/bard_lullaby_harp + art/classes/bard/bard_lute + art/classes/bard/bard_minstrel_s_gloves + art/classes/bard/bard_moving_melody + art/classes/bard/bard_muse_s_paper + art/classes/bard/bard_music_box + art/classes/bard/bard_musical_darts + art/classes/bard/bard_mythic_chronicle + art/classes/bard/bard_necros_dirge + art/classes/bard/bard_rally_hymn + art/classes/bard/bard_rousing_ode + art/classes/bard/bard_silencing_scepter + art/classes/bard/bard_song_of_the_wild + art/classes/bard/bard_songbook + art/classes/bard/bard_stirring_song + art/classes/bard/bard_summoning_drum + art/classes/bard/bard_tuning_fork + art/classes/bard/bard_valiant_verse + art/classes/bard/bard_whistling_rapier + art/classes/druid/animal_strength + art/classes/druid/bear_form + art/classes/druid/bear_strength + art/classes/druid/circlet_of_flowers + art/classes/druid/entangling_roots + art/classes/druid/feral_fox + art/classes/druid/flourishing_staff + art/classes/druid/forest_fury + art/classes/druid/forest_rage + art/classes/druid/forest_vengeance + art/classes/druid/fox + art/classes/druid/gnarled_staff + art/classes/druid/grass_weave_sash + art/classes/druid/grizzly_form + art/classes/druid/hardy_hedgehog + art/classes/druid/heartwood_splinter + art/classes/druid/hedgehog + art/classes/druid/honeycomb + art/classes/druid/lore_telling + art/classes/druid/nimble_fox + art/classes/druid/owl + art/classes/druid/panther_eye_ring + art/classes/druid/polar_bear_form + art/classes/druid/pure_bear_form + art/classes/druid/rabbit + art/classes/druid/reclaim_the_forest + art/classes/druid/soul_of_the_forest + art/classes/druid/spirit_bear_form + art/classes/druid/spirit_grizzly_form + art/classes/druid/spirit_of_the_forest + art/classes/druid/squirrel + art/classes/druid/sunbird + art/classes/druid/ursine_rod + art/classes/druid/way_of_the_forest + art/classes/monk/monk_amulet_of_resolve + art/classes/monk/monk_ancestral_circlet + art/classes/monk/monk_arm_bands_of_defense + art/classes/monk/monk_calm + art/classes/monk/monk_cobra_fang + art/classes/monk/monk_dim_mak + art/classes/monk/monk_dragon_staff + art/classes/monk/monk_favored_technique + art/classes/monk/monk_female + art/classes/monk/monk_flowing_technique + art/classes/monk/monk_fluid_technique + art/classes/monk/monk_focus + art/classes/monk/monk_focused_strike + art/classes/monk/monk_horn_of_ascendance + art/classes/monk/monk_jing + art/classes/monk/monk_magnificent_blossom + art/classes/monk/monk_male + art/classes/monk/monk_masterful_technique + art/classes/monk/monk_practiced_technique + art/classes/monk/monk_precise_technique + art/classes/monk/monk_qi + art/classes/monk/monk_qigong + art/classes/monk/monk_radiant_blossom + art/classes/monk/monk_resplendent_blossom + art/classes/monk/monk_ring_of_1000_palms + art/classes/monk/monk_serene_wind + art/classes/monk/monk_slippers_of_the_crane + art/classes/monk/monk_spring_blossom + art/classes/monk/monk_staff_of_meditation + art/classes/monk/monk_staff_of_the_phoenix + art/classes/monk/monk_stillness_of_water + art/classes/monk/monk_striking_cobra + art/classes/monk/monk_tonfas_of_balance + art/classes/monk/monk_tranquil_wind + art/classes/monk/monk_void_s_eye + art/classes/monk/monk_wraps_of_illusinary_speed + art/classes/monk/monk_wraps_of_strength + art/classes/monk/monk_yin_yang + art/classes/necromancer/anguish_blade + art/classes/necromancer/animate_dead + art/classes/necromancer/bag_of_bones + art/classes/necromancer/bloodrose + art/classes/necromancer/bone_gauntlets + art/classes/necromancer/bone_raising + art/classes/necromancer/bone_waltz + art/classes/necromancer/bone_waltz_v2 + art/classes/necromancer/collection_of_corpses + art/classes/necromancer/company_of_corpses + art/classes/necromancer/corpse_horde + art/classes/necromancer/corpse_raising + art/classes/necromancer/dread_cauldron + art/classes/necromancer/empty_graves + art/classes/necromancer/final_return + art/classes/necromancer/fresh_harvest + art/classes/necromancer/grave_robbery + art/classes/necromancer/greaves_of_grieving + art/classes/necromancer/necromancer_female + art/classes/necromancer/necromancer_male + art/classes/necromancer/onyx_skull + art/classes/necromancer/plague_belt + art/classes/necromancer/preserved_heart + art/classes/necromancer/puzzle_box + art/classes/necromancer/raise_dead + art/classes/necromancer/raise_skeleton + art/classes/necromancer/reanimate + art/classes/necromancer/reawaken + art/classes/necromancer/rod_of_reanimation + art/classes/necromancer/rod_of_spite + art/classes/necromancer/rod_of_unlife + art/classes/necromancer/rotting_crown + art/classes/necromancer/severing_scythe + art/classes/necromancer/skeleton_servant + art/classes/necromancer/skeleton_warrior + art/classes/necromancer/skull_swarm + art/classes/necromancer/soul_cage + art/classes/necromancer/soul_prism + art/classes/necromancer/stitcher_s_kit + art/classes/necromancer/strong_bones + art/classes/necromancer/upgraded_collection_of_corpses + art/classes/necromancer/voidstone + art/classes/necromancer/withered_wand + art/sets/promos1art/afterlife + art/sets/promos1art/bjorn__the_centurion + art/sets/promos1art/bloodfang + art/sets/promos1art/crime_spree + art/sets/promos1art/devotion + art/sets/promos1art/dragon_fire + art/sets/promos1art/droga__guild_enforcer + art/sets/promos1art/galok__the_vile + art/sets/promos1art/gorg__orc_shaman + art/sets/promos1art/kasha__the_awakener + art/sets/promos1art/legionnaire + art/sets/promos1art/mobia__elf_lord + art/sets/promos1art/raiding_party + art/sets/promos1art/ren__bounty_hunter + art/sets/promos1art/robbery + art/sets/promos1art/the_summoning + art/sets/promos1art/valius_fire_dragon_under_the_rockslide + art/sets/promos1art/valius__fire_dragon + art/sets/promos1art/zombie + + +## Frames Frames are used to set a frame for a card layout Possible values: - frames/Cleric_armor_frame.png - frames/Cleric_CardFrame.png - frames/Fighter_armor_frame.png - frames/Generic_CardFrame.png - frames/Generic_Top_CardFrame.png - frames/Guild_Action_CardFrame.png - frames/Guild_Champion_CardFrame.png - frames/HR_CardFrame_Action_Guild.png - frames/HR_CardFrame_Action_Imperial.png - frames/HR_CardFrame_Action_Necros.png - frames/HR_CardFrame_Action_Wild.png - frames/HR_CardFrame_Champion_Guild.png - frames/HR_CardFrame_Champion_Imperial.png - frames/HR_CardFrame_Champion_Necros.png - frames/HR_CardFrame_Champion_Wild.png - frames/HR_CardFrame_Item_Generic.png - frames/Imperial_Action_CardFrame.png - frames/Imperial_Champion_CardFrame.png - frames/Necros_Action_CardFrame.png - frames/Necros_Champion_CardFrame.png - frames/Ranger_armor_frame.png - frames/Ranger_CardFrame.png - frames/Thief_armor_frame.png - frames/Thief_CardFrame.png - frames/Treasure_CardFrame.png - frames/Warrior_CardFrame.png - frames/warrior_top.png - frames/Wild_Action_CardFrame.png - frames/Wild_Champion_CardFrame.png - frames/Wizard_armor_frame.png - frames/Wizard_CardFrame.png - frames/Cleric_Frames/Cleric_Treasure_CardFrame.png - frames/FactionFrames_IconOnTheLeft/Guild_Action_CardFrame.png - frames/FactionFrames_IconOnTheLeft/Guild_Champion_CardFrame.png - frames/FactionFrames_IconOnTheLeft/Imperial_Action_CardFrame.png - frames/FactionFrames_IconOnTheLeft/Imperial_Champion_CardFrame.png - frames/FactionFrames_IconOnTheLeft/Necros_Action_CardFrame.png - frames/FactionFrames_IconOnTheLeft/Necros_Champion_CardFrame.png - frames/FactionFrames_IconOnTheLeft/Wild_Action_CardFrame.png - frames/FactionFrames_IconOnTheLeft/Wild_Champion_CardFrame.png + frames/Cleric_armor_frame + frames/Cleric_CardFrame + frames/Fighter_armor_frame + frames/Generic_CardFrame + frames/Generic_Top_CardFrame + frames/Guild_Action_CardFrame + frames/Guild_Champion_CardFrame + frames/HR_CardFrame_Action_Guild + frames/HR_CardFrame_Action_Imperial + frames/HR_CardFrame_Action_Necros + frames/HR_CardFrame_Action_Wild + frames/HR_CardFrame_Champion_Guild + frames/HR_CardFrame_Champion_Imperial + frames/HR_CardFrame_Champion_Necros + frames/HR_CardFrame_Champion_Wild + frames/HR_CardFrame_Item_Generic + frames/Imperial_Action_CardFrame + frames/Imperial_Champion_CardFrame + frames/Necros_Action_CardFrame + frames/Necros_Champion_CardFrame + frames/Ranger_armor_frame + frames/Ranger_CardFrame + frames/Thief_armor_frame + frames/Thief_CardFrame + frames/Treasure_CardFrame + frames/Warrior_CardFrame + frames/warrior_top + frames/Wild_Action_CardFrame + frames/Wild_Champion_CardFrame + frames/Wizard_armor_frame + frames/Wizard_CardFrame + frames/Cleric_Frames/Cleric_Treasure_CardFrame + frames/FactionFrames_IconOnTheLeft/Guild_Action_CardFrame + frames/FactionFrames_IconOnTheLeft/Guild_Champion_CardFrame + frames/FactionFrames_IconOnTheLeft/Imperial_Action_CardFrame + frames/FactionFrames_IconOnTheLeft/Imperial_Champion_CardFrame + frames/FactionFrames_IconOnTheLeft/Necros_Action_CardFrame + frames/FactionFrames_IconOnTheLeft/Necros_Champion_CardFrame + frames/FactionFrames_IconOnTheLeft/Wild_Action_CardFrame + frames/FactionFrames_IconOnTheLeft/Wild_Champion_CardFrame Icons Icons may be used for choice layout generation or to set icons for buffs, skills and abilities @@ -1851,47 +3043,189 @@ Possible values: icons/wizard_spellcaster_gloves -Avatars +## Avatars You need to specify full path when using for layout. But only avatar name “assassin”, when used as actual avatar. Possible values: + avatars/Alchemist_01 + avatars/Alchemist_02 + avatars/Barbarian_01 + avatars/Barbarian_02 avatars/ambushers + avatars/any_scenario_queue avatars/assassin avatars/assassin_flipped + avatars/bard_01 + avatars/bard_02 avatars/broelyn avatars/broelyn__loreweaver avatars/chanting_cultist avatars/chest avatars/cleric_01 avatars/cleric_02 - avatars/cristov_s_recruits + avatars/cleric_alt_01 + avatars/cleric_alt_02 avatars/cristov__the_just + avatars/cristov_s_recruits + avatars/druid_01 + avatars/druid_02 + avatars/dwarf_cleric_female_01 + avatars/dwarf_cleric_female_02 + avatars/dwarf_cleric_male_01 + avatars/dwarf_cleric_male_02 + avatars/dwarf_fighter_female_01 + avatars/dwarf_fighter_female_02 + avatars/dwarf_fighter_male_01 + avatars/dwarf_fighter_male_02 + avatars/dwarf_ranger_female_01 + avatars/dwarf_ranger_female_02 + avatars/dwarf_ranger_male_01 + avatars/dwarf_ranger_male_02 + avatars/dwarf_thief_female_01 + avatars/dwarf_thief_female_02 + avatars/dwarf_thief_male_01 + avatars/dwarf_thief_male_02 + avatars/dwarf_wizard_female_01 + avatars/dwarf_wizard_female_02 + avatars/dwarf_wizard_male_01 + avatars/dwarf_wizard_male_02 + avatars/elf_cleric_female_01 + avatars/elf_cleric_female_02 + avatars/elf_cleric_male_01 + avatars/elf_cleric_male_02 + avatars/elf_fighter_female_01 + avatars/elf_fighter_female_02 + avatars/elf_fighter_male_01 + avatars/elf_fighter_male_02 + avatars/elf_ranger_female_01 + avatars/elf_ranger_female_02 + avatars/elf_ranger_male_01 + avatars/elf_ranger_male_02 + avatars/elf_thief_female_01 + avatars/elf_thief_female_02 + avatars/elf_thief_male_01 + avatars/elf_thief_male_02 + avatars/elf_wizard_female_01 + avatars/elf_wizard_female_02 + avatars/elf_wizard_male_01 + avatars/elf_wizard_male_02 + avatars/elf_wizard_male_03 avatars/fighter_01 avatars/fighter_02 + avatars/fighter_alt_01 + avatars/fighter_alt_02 + avatars/halfdemon_cleric_female_01 + avatars/halfdemon_cleric_female_02 + avatars/halfdemon_cleric_male_01 + avatars/halfdemon_cleric_male_02 + avatars/halfdemon_fighter_female_01 + avatars/halfdemon_fighter_female_02 + avatars/halfdemon_fighter_male_01 + avatars/halfdemon_fighter_male_02 + avatars/halfdemon_ranger_female_01 + avatars/halfdemon_ranger_female_02 + avatars/halfdemon_ranger_male_01 + avatars/halfdemon_ranger_male_02 + avatars/halfdemon_thief_female_01 + avatars/halfdemon_thief_female_02 + avatars/halfdemon_thief_male_01 + avatars/halfdemon_thief_male_02 + avatars/halfdemon_wizard_female_01 + avatars/halfdemon_wizard_female_02 + avatars/halfdemon_wizard_male_01 + avatars/halfdemon_wizard_male_02 avatars/inquisition avatars/krythos avatars/lord_callum avatars/lys__the_unseen avatars/man_at_arms + avatars/monk_01 + avatars/monk_01_2 + avatars/monk_02 avatars/monsters_in_the_dark + avatars/necromancer_01 + avatars/necromancer_02 avatars/necromancers avatars/ogre - avatars/orcs + avatars/ogre_cleric_female_01 + avatars/ogre_cleric_female_02 + avatars/ogre_cleric_male_01 + avatars/ogre_cleric_male_02 + avatars/ogre_fighter_female_01 + avatars/ogre_fighter_female_02 + avatars/ogre_fighter_male_01 + avatars/ogre_fighter_male_02 + avatars/ogre_ranger_female_01 + avatars/ogre_ranger_female_02 + avatars/ogre_ranger_male_01 + avatars/ogre_ranger_male_02 + avatars/ogre_thief_female_01 + avatars/ogre_thief_female_02 + avatars/ogre_thief_male_01 + avatars/ogre_thief_male_02 + avatars/ogre_wizard_female_01 + avatars/ogre_wizard_female_02 + avatars/ogre_wizard_male_01 + avatars/ogre_wizard_male_02 + avatars/orc_cleric_female_01 + avatars/orc_cleric_female_02 + avatars/orc_cleric_male_01 + avatars/orc_cleric_male_02 + avatars/orc_fighter_female_01 + avatars/orc_fighter_female_02 + avatars/orc_fighter_male_01 + avatars/orc_fighter_male_02 avatars/orc_raiders + avatars/orc_ranger_female_01 + avatars/orc_ranger_female_02 + avatars/orc_ranger_male_01 + avatars/orc_ranger_male_02 + avatars/orc_thief_female_01 + avatars/orc_thief_female_02 + avatars/orc_thief_male_01 + avatars/orc_thief_male_02 + avatars/orc_wizard_female_01 + avatars/orc_wizard_female_02 + avatars/orc_wizard_male_01 + avatars/orc_wizard_male_02 + avatars/orcs avatars/origins_flawless_track avatars/origins_shoulder_bash avatars/pirate avatars/profit avatars/ranger_01 avatars/ranger_02 + avatars/ranger_alt_01 + avatars/ranger_alt_02 + avatars/ranger_alt_03 avatars/rayla__endweaver avatars/rayla__endweaver_flipped avatars/robbery avatars/ruinos_zealot avatars/skeleton + avatars/smallfolk_cleric_female_01 + avatars/smallfolk_cleric_female_02 + avatars/smallfolk_cleric_male_01 + avatars/smallfolk_cleric_male_02 + avatars/smallfolk_fighter_female_01 + avatars/smallfolk_fighter_female_02 + avatars/smallfolk_fighter_male_01 + avatars/smallfolk_fighter_male_02 + avatars/smallfolk_ranger_female_01 + avatars/smallfolk_ranger_female_02 + avatars/smallfolk_ranger_male_01 + avatars/smallfolk_ranger_male_02 + avatars/smallfolk_thief_female_01 + avatars/smallfolk_thief_female_02 + avatars/smallfolk_thief_male_01 + avatars/smallfolk_thief_male_02 + avatars/smallfolk_wizard_female_01 + avatars/smallfolk_wizard_female_02 + avatars/smallfolk_wizard_male_01 + avatars/smallfolk_wizard_male_02 avatars/smugglers avatars/spider avatars/summoner @@ -1899,14 +3233,67 @@ Possible values: avatars/the_wolf_tribe avatars/thief_01 avatars/thief_02 + avatars/thief_alt_01 + avatars/thief_alt_02 avatars/troll avatars/vampire_lord avatars/wizard_01 avatars/wizard_02 + avatars/wizard_alt_01 + avatars/wizard_alt_02 avatars/wolf_shaman - - -Zoomed buffs + avatars/WarInTheWild/Bloodfang + avatars/WarInTheWild/Gorg__Orc_Shaman + avatars/WarInTheWild/Gorg__Orc_Shaman_flip + avatars/WarInTheWild/bjorn__the_centurion + avatars/WarInTheWild/bjorn__the_centurion_flip + avatars/WarInTheWild/dark_energy + avatars/WarInTheWild/death_threat + avatars/WarInTheWild/domination + avatars/WarInTheWild/droga__guild_enforcer + avatars/WarInTheWild/galok__the_vile + avatars/WarInTheWild/galok__the_vile_flip + avatars/WarInTheWild/grak__storm_giant + avatars/WarInTheWild/honed_black_arrow + avatars/WarInTheWild/kasha__the_awakener + avatars/WarInTheWild/kasha__the_awakener_cropped + avatars/WarInTheWild/kasha__the_awakener_flip + avatars/WarInTheWild/krythos_master_vampire + avatars/WarInTheWild/mobia__elf_lord + avatars/WarInTheWild/orc_grunt + avatars/WarInTheWild/raiding_party + avatars/WarInTheWild/rampage + avatars/WarInTheWild/ren__bounty_hunter + avatars/WarInTheWild/slaughterclaw + avatars/WarInTheWild/the_call + avatars/WarInTheWild/the_summoning + avatars/WarInTheWild/torgen_rocksplitter + avatars/WarInTheWild/valius__fire_dragon + avatars/WarInTheWild/wolf_form + avatars/journeys/abomination + avatars/journeys/alpha_wolf + avatars/journeys/andor_the_valiant + avatars/journeys/broken_tables_and_chairs + avatars/journeys/command + avatars/journeys/dire_wolf + avatars/journeys/droga_guild_enforcer + avatars/journeys/dwarf_thief_female + avatars/journeys/fettered_imp + avatars/journeys/halfdemon_fighter_female + avatars/journeys/hardy_hedgehog + avatars/journeys/human_necromancer_male + avatars/journeys/lenka_the_hunter + avatars/journeys/olara_the_slayer + avatars/journeys/orc_wizard_female_02 + avatars/journeys/pelleas__the_seeker + avatars/journeys/ren_bounty_hunter + avatars/journeys/robbery + avatars/journeys/smallfolk_male + avatars/journeys/veteran_squire + avatars/journeys/waking_dragon + + +## Zoomed buffs These are larger variants of some icons @@ -1943,7 +3330,51 @@ These are larger variants of some icons zoomedbuffs/wizard_spell_components -Card list +## Coop backgrounds + scenariointrobackgrounds/pirate + scenariointrobackgrounds/the_wolf_tribe + scenariointrobackgrounds/deceptions + scenariointrobackgrounds/raiding_party + scenariointrobackgrounds/dark_sign + scenariointrobackgrounds/orcs + scenariointrobackgrounds/ruinos_zealot + scenariointrobackgrounds/inquisition + scenariointrobackgrounds/test_of_mettle + scenariointrobackgrounds/the_unseen + scenariointrobackgrounds/man_at_arms + scenariointrobackgrounds/ambush_at_the_docs + scenariointrobackgrounds/profit + scenariointrobackgrounds/necromancers + scenariointrobackgrounds/smuggling_operation + scenariointrobackgrounds/journeys/enraged_bear + scenariointrobackgrounds/journeys/confiscate + scenariointrobackgrounds/journeys/reclaim_the_forest + scenariointrobackgrounds/journeys/sway + scenariointrobackgrounds/journeys/bounty_collection + scenariointrobackgrounds/journeys/thrash + scenariointrobackgrounds/journeys/enthralled_regulars + scenariointrobackgrounds/journeys/corruption + scenariointrobackgrounds/journeys/abomination + scenariointrobackgrounds/journeys/command + scenariointrobackgrounds/journeys/loot + scenariointrobackgrounds/journeys/dragon_shard + scenariointrobackgrounds/warInthewild/torgen_rocksplitter + scenariointrobackgrounds/warInthewild/krythos_master_vampire + scenariointrobackgrounds/warInthewild/raiding_party + scenariointrobackgrounds/warInthewild/the_summoning + scenariointrobackgrounds/warInthewild/dark_energy + scenariointrobackgrounds/warInthewild/Kasha__the_Awakener + scenariointrobackgrounds/warInthewild/galok__the_vile_bonus + scenariointrobackgrounds/warInthewild/mobia__elf_lord + scenariointrobackgrounds/warInthewild/rampage + scenariointrobackgrounds/warInthewild/legionnaire + scenariointrobackgrounds/warInthewild/death_threat + scenariointrobackgrounds/warInthewild/grak__storm_giant + scenariointrobackgrounds/warInthewild/orc_grunt + scenariointrobackgrounds/warInthewild/honed_black_arrow + scenariointrobackgrounds/warInthewild/domination + scenariointrobackgrounds/lys_the_unseen +# Card list Card available: @@ -2157,7 +3588,7 @@ Card available: word_of_power_carddef() -Card Subtypes +# Card Subtypes Card subtypes are set to allow special processing, like knives or arrows. You may check for it using isCardType(subType) @@ -2246,5 +3677,237 @@ You may check for it using isCardType(subType) attachmentType noKillType -- mark champions with this sybtype to avoid AI killing it (Redeemed Ruinos) -© 2022 Wise Wizard Games, LLC. - + +# Good Practices +## Revealing cards + +When you reveal cards, you move them to reveal scroller. +There are two types of reveal scrollers: + +- common reveal - both players can view card faces in it. +- personal reveal - only one player can view card face in it. Other player sees card back. + +When you move a card to common reveal, its a good practice to let players to examine cards before moving them to next location. +To do that - use following effect. + + waitForClickEffect("Text you see", "Text your opponent sees"), + +It’s also good for personal reveal, so both you and your opponent know what’s going on at the moment. + + waitForClickEffect("Top card of your deck", "Top card of opponent's deck"), + + +# Card examples +## Flipping skill + +Barbarian Eternal Rage skill as an example. +When activated, it transforms itself into berserk variant and stays expended until next turn as transform doesn’t affect expend status of a card. + + function barbarian_eternal_rage_carddef() + + return createSkillDef({ + id = "barbarian_eternal_rage", + name = "Eternal Rage", + description = "Available at level 1", + types = { barbarianType }, + abilities = { + createAbility({ + id="barbarian_eternal_rage", + trigger = uiTrigger, + effect = drawCardsEffect(1).seq(addSlotToPlayerEffect(currentPlayer(), createPlayerSlot({ + key = berserkSlotKey, + expiry = { neverExpiry } + }))). + seq(transformTarget("barbarian_eternal_rage_berserk").apply(selectSource())). + seq(fireAbilityTriggerEffect(barbarianGoneBerserkTrigger)), + cost = combineCosts({ + expendCost, + goldCost(ifInt(isSkillNotFree(), toIntExpression(2), toIntExpression(0))) + }), + activations = multipleActivations, + promptType = showPrompt, + layout = loadLayoutData("layouts/skills_abilities/barbarian/barbarian_eternal_ragev45"), + aiPriority = toIntExpression(-1) + }) + }, + layoutPath = "icons/barbarian_eternal_rage", + layout = loadLayoutData("layouts/skills_abilities/barbarian/barbarian_eternal_ragev45") + }) + end + + function barbarian_eternal_rage_berserk_carddef() + + return createSkillDef({ + id = "barbarian_eternal_rage_berserk", + name = "Eternal Rage", + description = "Available at level 1", + types = { barbarianType}, + abilities = { + createAbility({ + id = "barbarian_eternal_rage_berserk_auto_ability", + effect = ifElseEffect(countPlayerSlots(currentPid, berserkImmuneSlotKey).lte(0), damagePlayerEffect(currentPid, toIntExpression(1)), nullEffect()), + trigger = endOfTurnTrigger, + activations = multipleActivations, + }), + createAbility({ + id = "barbarian_eternal_rage_berserk_ability", + trigger = uiTrigger, + effect = removeSlotFromPlayerEffect(currentPlayer(), berserkSlotKey). + seq(transformTarget("barbarian_eternal_rage").apply(selectSource())), + cost = combineCosts({ + expendCost, + goldCost(ifInt(isSkillNotFree(), toIntExpression(2), toIntExpression(0))) + }), + activations = multipleActivations, + promptType = showPrompt, + layout = loadLayoutData("layouts/skills_abilities/barbarian/barbarian_eternal_rage_berserk"), + aiPriority = toIntExpression(-1) + }) + }, + layout = loadLayoutData("layouts/skills_abilities/barbarian/barbarian_eternal_rage_berserk"), + layoutPath = "icons/barbarian_eternal_rage_berserk" + }) + end +## Class ability + + + function bard_bold_saga_carddef() + local card_name = "bard_bold_saga" + local selector = selectLoc(centerRowLoc).Where(isChampion().And(isCardAcquirable()).And(isCardAffordable())) + local checkSelector = selectLoc(centerRowLoc).Where(isChampion().And(isCardAcquirable()) + .And(getCardCost().lte(getPlayerGold(ownerPid).add(toIntExpression(1))))) + local buffSelector = selectLoc(centerRowLoc).Where(isChampion()) + local buff = getCostDiscountBuff(card_name, 1, buffSelector) + + return createHeroAbilityDef({ + id = card_name, + name = "Bold Saga", + types = { bardType }, + tags = { bardGalleryCardTag }, + abilities = { + createAbility({ + id = card_name .. "_ability_sacrifice", + effect = createCardEffect(buff, currentBuffsLoc) + .seq(e.AcquireToTargets( + toStringExpression("Acquire a champion directly into play"), + selector, + CardLocEnum.InPlay, + { acquireIntoPLayTag, expensiveTag } + )), + cost = sacrificeSelfCost, + trigger = uiTrigger, + promptType = showPrompt, + filter = s.Where(isChampion()), + warning = "You are trying to acquire normally while Bold Saga can be used instead", + check = checkSelector.count().gte(1), + tags = { discountTag, acquireIntoPLayTag }, + aiPriority = toIntExpression(100), + layout = loadLayoutData(skillsDefaultPath .. card_name) + }) + }, + layoutPath = iconDefaultPath .. card_name, + layout = loadLayoutData(skillsDefaultPath .. card_name) + }) + end + + +## Black Spike + local function black_spike_carddef() + local cardLayout = createLayout({ + name = "Black Spike", + art = "art/t_krythos_master_vampire", + frame = "frames/coop_campaign_cardframe", + xmlText = [[ + + + + ]], + health = 0, + isGuard = false + }) + + return createChampionDef({ + id="black_spike", + name="Black Spike", + types={ noStealType }, + acquireCost=0, + health = 0, + isGuard = false, + abilities = { + createAbility({ + id="black_spike_ability", + trigger = autoTrigger, + effect = gainCombatEffect(selectSource().sum(getCardHealth())), + check = selectLoc(currentHandLoc).count().eq(0) + }), + sacrificeSelfOnLeavePlayAbility("black_spike_sacrifice") + }, + layoutPath = "icons/slaughterclaw", + layout = cardLayout + }) + end + + +## Sprout Spike Skill + local function sprout_spike_skill() + local cardLayout = createLayout({ + name = "Sprout a Spike", + art = "art/campaign/warinthewild/slaughterclaw_fixed", + frame = "frames/coop_campaign_cardframe", + text = "Instead of acquiring a card, sacrifice it and sprout a spike equal to its cost." + }) + + return createSkillDef({ + id = "sprout_spike", + name = "Sprout a Spike", + abilities = { + createAbility({ + id = "sprout_spike", + effect = incrementCounterEffect("black_spike_guard", selectLoc(loc(currentPid, discardPloc)).reverse().take(1).sum(getCardCost())) + .seq(sacrificeTarget().apply(selectLoc(loc(currentPid, discardPloc)).reverse().take(1))) + .seq(createCardEffect(black_spike_carddef(), revealLoc)) + .seq(grantHealthTarget(getCounter("black_spike_guard")).apply(selectLoc(revealLoc))) + .seq(moveTarget(currentInPlayLoc).apply(selectLoc(revealLoc))) + .seq(resetCounterEffect("black_spike_guard")), + trigger = onAcquireGlobalTrigger, + activations = multipleActivations, + promptType = showPrompt, + tags = { gainHealthTag } + }) + }, + layoutPath = "features/warinthewild/slaughterclaw_spike", + layout = cardLayout + }) + end + + +## Barbarian Roaring Cowl + function barbarian_roaring_cowl_carddef() + + return createMagicArmorDef({ + id = "barbarian_roaring_cowl", + name = "Roaring Cowl", + description = "Available at level 21", + types = { barbarianType, magicArmorType, backType }, + tags = { barbarianGalleryCardTag, devCardTag }, + level = 21, + acquireCost = 0, + abilities = { + createAbility({ + id = "barbarian_roaring_cowl_ability", + effect = prepareTarget().apply(selectLoc(currentSkillsLoc)) + .seq(addSlotToPlayerEffect(controllerPid, createPlayerIntExpressionSlot(skillCostModKey, toIntExpression(-1), { endOfTurnExpiry }))), + cost = expendCost, + check = getPlayerHealth(currentPid).gte(55).And(getAbilityActivationsThisTurn().gte(1)), + trigger = autoTrigger, + activations = singleActivation, + tags = { drawTag } + }), + }, + layoutPath = "icons/barbarian_roaring_cowl_02", + layout = loadLayoutData("layouts/barbarian/barbarian_roaring_cowl") + }) + end + +© 2024 Wise Wizard Games, LLC. + From 7d9a51debb5c49ecb0c8253cd7fc0437399d23f1 Mon Sep 17 00:00:00 2001 From: Jseph Date: Mon, 29 Dec 2025 22:10:24 -0800 Subject: [PATCH 03/10] Fix string for purchase ability. --- Jseph/purchase_reserve_cards.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jseph/purchase_reserve_cards.lua b/Jseph/purchase_reserve_cards.lua index c776513..9dff5c3 100644 --- a/Jseph/purchase_reserve_cards.lua +++ b/Jseph/purchase_reserve_cards.lua @@ -89,7 +89,7 @@ function purchase_first_reserve_skill_def() local mainLayout = createLayout({ name = "Purchase Reserve", art = "art/T_Taxation", - text = "Acquire reserve card.\nCost: 7 gold + 2 gold per index - 1 gold per turn.", + text = "Acquire reserve card.\nCost: 8 gold + 2 gold per index - 1 gold per turn.", }) return createSkillDef({ From ea25e3c19db49822442247c275b121af341427a6 Mon Sep 17 00:00:00 2001 From: Jseph Date: Tue, 30 Dec 2025 15:13:19 -0800 Subject: [PATCH 04/10] Update SetUpGame to add for both players. Also improve ui experience and consider the purchase an acquisition. --- Jseph/purchase_reserve_cards.lua | 85 ++++++++++++++++++-------------- Jseph/trickster.lua | 7 +-- 2 files changed, 51 insertions(+), 41 deletions(-) diff --git a/Jseph/purchase_reserve_cards.lua b/Jseph/purchase_reserve_cards.lua index 9dff5c3..8f56383 100644 --- a/Jseph/purchase_reserve_cards.lua +++ b/Jseph/purchase_reserve_cards.lua @@ -7,11 +7,11 @@ require 'mediumai' require 'easyai' -- Buff that counts how many times the player's deck was shuffled (due to draw or otherwise) -function draw_shuffle_counter_def() +function keep_cards_in_reserve_buf_def() local layout = createLayout({ - name = "Draw Shuffle Counter", + name = "Keep Cards in Reserve", art = "art/t_cunning_of_the_wolf", - text = format("Shuffles: {0}", { getCounter("draw_shuffles") }) + text = "Reserve cards must be purchased." }) local currentReserve = loc(currentPid, reservePloc) local reserveSlot = createSlot({ @@ -32,18 +32,10 @@ function draw_shuffle_counter_def() }) return createBuffDef({ - id = "draw_shuffle_counter", - name = "Draw Shuffle Counter", + id = "keep_cards_in_reserve", + name = "Keep Cards in Reserve", layout = layout, abilities = { - createAbility({ - id = "draw_shuffle_counter_inc", - trigger = deckShuffledTrigger, - activations = multipleActivations, - effect = incrementCounterEffect("draw_shuffles", const(1)).seq(showTextExpressionEffect( - format("Your deck has been shuffled {0} times.", { getCounter("draw_shuffles") }) - )) - }), createAbility({ id = "add_slot_to_reserve_cards", trigger = autoTrigger, @@ -69,10 +61,12 @@ local function create_purchase_ability(baseCost, index, slot) layout = createLayout({ name = "Purchase Reserve", art = "art/T_Taxation", - text = format("{0}, : Move {1} to your hand.\n(-1 per turn)", { dynamicCost, getCardNameStringExpression(target) }), + text = format("{0}, : Acquire {1} to top of deck.\n(-1 per turn)", { dynamicCost, getCardNameStringExpression(target) }), }), effect = addSlotToTarget(slot).apply(target) - .seq(moveTarget(currentHandLoc).apply(target)), + .seq(acquireForFreeTarget(CardLocEnum.ExtraReveal).apply(target)) + .seq(showTextTarget("Acquired reserve card").apply(selectSource())) + .seq(moveTarget(CardLocEnum.Deck).apply(selectLoc(loc(currentPid, extraRevealPloc)).take(1))), cost = combineCosts({ expendCost, goldCost(dynamicCost) }), check = selectLoc(loc(currentPid, reservePloc)).count().gte(index) }) @@ -104,16 +98,18 @@ function purchase_first_reserve_skill_def() create_purchase_ability(15, 4, slot), }, layout = mainLayout, - layoutPath = "avatars/profit" + -- Note: Capitolization matters here. If this is not all lowercase the game + -- crashes at startup for some reason. + layoutPath = "art/t_taxation" }) end function setupGame(g) registerCards(g, { - purchase_first_reserve_skill_def() - }) + }) + standardSetup(g, { - description = "Custom no heroes game", + description = "Knights of Balance: A Community Game Balancing Effort.", playerOrder = { plid1, plid2 }, ai = ai.CreateKillSwitchAi(createAggressiveAI(), createHardAi2()), timeoutAi = createTimeoutAi(), @@ -121,49 +117,62 @@ function setupGame(g) players = { { id = plid1, - startDraw = 5, - name = "Player 1", - avatar="assassin", - health = 50, + startDraw = 3, + init = { + fromEnv = plid1 + }, cards = { + reserve = { + --{ qty = 1, card = wizard_treasure_map_carddef() } + --{ qty = 1, card = ranger_parrot_carddef() } + }, deck = { - { qty=10, card=fire_gem_carddef() }, }, - reserve = { - { qty = 1, card = wizard_treasure_map_carddef() }, - { qty = 1, card = ranger_parrot_carddef() }, - { qty = 1, card = fire_gem_carddef() }, - { qty = 1, card = gold_carddef() }, + hand = { + --{ qty = 5, card = fire_gem_carddef() }, + }, + discard = { }, skills = { - purchase_first_reserve_skill_def() + purchase_first_reserve_skill_def(), }, buffs = { drawCardsCountAtTurnEndDef(5), discardCardsAtTurnStartDef(), fatigueCount(40, 1, "FatigueP1"), - draw_shuffle_counter_def(), + keep_cards_in_reserve_buf_def(), } } }, { id = plid2, startDraw = 5, - name = "Player 2", - avatar="assassin", - health = 50, + init = { + fromEnv = plid2 + }, cards = { + reserve = { + --{ qty = 1, card = wizard_treasure_map_carddef() } + --{ qty = 1, card = ranger_parrot_carddef() } + }, deck = { - { qty=2, card=dagger_carddef() }, - { qty=8, card=gold_carddef() }, + }, + hand = { + --{ qty = 5, card = fire_gem_carddef() }, + }, + discard = { + }, + skills = { + purchase_first_reserve_skill_def(), }, buffs = { drawCardsCountAtTurnEndDef(5), discardCardsAtTurnStartDef(), - fatigueCount(40, 1, "FatigueP2") + fatigueCount(40, 1, "FatigueP2"), + keep_cards_in_reserve_buf_def(), } } - } + }, } }) end diff --git a/Jseph/trickster.lua b/Jseph/trickster.lua index 64ac0f1..899cad8 100644 --- a/Jseph/trickster.lua +++ b/Jseph/trickster.lua @@ -515,14 +515,15 @@ end + function setupMeta(meta) meta.name = "trickster" - meta.minLevel = 3 - meta.maxLevel = 3 + meta.minLevel = 0 + meta.maxLevel = 0 meta.introbackground = "" meta.introheader = "" meta.introdescription = "" - meta.path = "C:/Program Files (x86)/Steam/steamapps/common/Hero Realms/Custom Scripts/trickster.lua" + meta.path = "C:/Users/jseph/hero-realms-lua-scripts/hero-realms-lua-scripts/Jseph/trickster.lua" meta.features = { } From 79a5fc35997d22c421f99a33bb874db7c15388ca Mon Sep 17 00:00:00 2001 From: Jseph Date: Wed, 31 Dec 2025 23:44:16 -0800 Subject: [PATCH 05/10] Update to use static prices for cards. --- Jseph/purchase_reserve_cards.lua | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Jseph/purchase_reserve_cards.lua b/Jseph/purchase_reserve_cards.lua index 8f56383..689a39a 100644 --- a/Jseph/purchase_reserve_cards.lua +++ b/Jseph/purchase_reserve_cards.lua @@ -105,8 +105,21 @@ function purchase_first_reserve_skill_def() end function setupGame(g) - registerCards(g, { - }) + --testing flags + local add_all_reserve_cards_to_deck = true + local all_card_defs = {} + local class_list = { "fighter", "wizard", "cleric", "ranger", "thief", + "barbarian", "alchemist", "druid", "necromancer", "bard", "monk" } + if add_all_reserve_cards_to_deck then + for n, x in pairs(_G) do + if type(x) == "function" and string.find(n, "carddef$") then + local firstWord = string.split(n, "_")[1] + table.insert(all_card_defs, x()) + end + end + end + table.insert(all_card_defs, purchase_first_reserve_skill_def()) + registerCards(g, all_card_defs) standardSetup(g, { description = "Knights of Balance: A Community Game Balancing Effort.", From 929a7c4c258a054361f63b6fce182b1e0e2f6b98 Mon Sep 17 00:00:00 2001 From: Jseph Date: Wed, 31 Dec 2025 23:44:55 -0800 Subject: [PATCH 06/10] Push more changes for purchase_reserve_cards --- Jseph/purchase_reserve_cards.lua | 155 +++++++++++++++++++++++-------- 1 file changed, 114 insertions(+), 41 deletions(-) diff --git a/Jseph/purchase_reserve_cards.lua b/Jseph/purchase_reserve_cards.lua index 689a39a..3bdd881 100644 --- a/Jseph/purchase_reserve_cards.lua +++ b/Jseph/purchase_reserve_cards.lua @@ -6,7 +6,17 @@ require 'hardai' require 'mediumai' require 'easyai' --- Buff that counts how many times the player's deck was shuffled (due to draw or otherwise) +--testing flags +local addAllClassCardsToDeck = false +local decreaseCostPerTurn = false +local debugStart = false + +-- constants +local cardCostOverrides = { + {"wizard_treasure_map", 2}, + {"ranger_parrot", 3} +} + function keep_cards_in_reserve_buf_def() local layout = createLayout({ name = "Keep Cards in Reserve", @@ -30,7 +40,7 @@ function keep_cards_in_reserve_buf_def() activations = multipleActivations, tags = { } }) - + return createBuffDef({ id = "keep_cards_in_reserve", name = "Keep Cards in Reserve", @@ -61,17 +71,57 @@ local function create_purchase_ability(baseCost, index, slot) layout = createLayout({ name = "Purchase Reserve", art = "art/T_Taxation", - text = format("{0}, : Acquire {1} to top of deck.\n(-1 per turn)", { dynamicCost, getCardNameStringExpression(target) }), + text = format("{0}, : Acquire {1} to hand.\n(-1 per turn)", { dynamicCost, getCardNameStringExpression(target) }), }), effect = addSlotToTarget(slot).apply(target) .seq(acquireForFreeTarget(CardLocEnum.ExtraReveal).apply(target)) .seq(showTextTarget("Acquired reserve card").apply(selectSource())) - .seq(moveTarget(CardLocEnum.Deck).apply(selectLoc(loc(currentPid, extraRevealPloc)).take(1))), + .seq(moveTarget(CardLocEnum.Hand).apply(selectLoc(loc(currentPid, extraRevealPloc)).take(1))), cost = combineCosts({ expendCost, goldCost(dynamicCost) }), check = selectLoc(loc(currentPid, reservePloc)).count().gte(index) }) end +local function basic_acquire_ability_layout() + local target = selectLoc(loc(currentPid, reservePloc)).take(1) + local cardCost = const(4) + for _, override in ipairs(cardCostOverrides) do + cardCost = ifInt( + target.where(isCardName(override[1])).count().eq(const(1)), + const(override[2]), + cardCost) + end + return createLayout({ + name = "Purchase Reserve", + art = "art/T_Taxation", + text = format(", : Acquire {1} to hand.", + { cardCost, getCardNameStringExpression(target) }), + }) +end + +local function create_basic_acquire_ability(slot) + local target = selectLoc(loc(currentPid, reservePloc)).take(1) + local cardCost = const(4) + for _, override in ipairs(cardCostOverrides) do + cardCost = ifInt( + target.where(isCardName(override[1])).count().eq(const(1)), + const(override[2]), + cardCost) + end + return createAbility({ + id = "purchase_first_reserve_card", + trigger = uiTrigger, + promptType = showPrompt, + layout = basic_acquire_ability_layout(), + effect = addSlotToTarget(slot).apply(target) + .seq(acquireForFreeTarget(CardLocEnum.ExtraReveal).apply(target)) + .seq(showTextTarget("Acquired reserve card").apply(selectSource())) + .seq(moveTarget(CardLocEnum.Hand).apply(selectLoc(loc(currentPid, extraRevealPloc)).take(1))), + cost = combineCosts({ expendCost, goldCost(cardCost) }), + check = selectLoc(loc(currentPid, reservePloc)).count().gte(1) + }) +end + -- Skill: pay dynamic gold to purchase one of the first 4 cards in your reserve function purchase_first_reserve_skill_def() local slot = createSlot({ id = "purchased_reserve_card_slot", expiresArray = { neverExpiry } }) @@ -85,18 +135,23 @@ function purchase_first_reserve_skill_def() art = "art/T_Taxation", text = "Acquire reserve card.\nCost: 8 gold + 2 gold per index - 1 gold per turn.", }) + local purchaseAbilities = {} + if decreaseCostPerTurn then + table.insert(purchaseAbilities, create_purchase_ability(9, 1, slot)) + table.insert(purchaseAbilities, create_purchase_ability(11, 2, slot)) + table.insert(purchaseAbilities, create_purchase_ability(13, 3, slot)) + table.insert(purchaseAbilities, create_purchase_ability(15, 4, slot)) + else + mainLayout = basic_acquire_ability_layout() + table.insert(purchaseAbilities, create_basic_acquire_ability(slot)) + end return createSkillDef({ id = "purchase_first_reserve_skill", name = "Purchase Reserve", cardTypeLabel = "Skill", types = { skillType }, - abilities = { - create_purchase_ability(9, 1, slot), - create_purchase_ability(11, 2, slot), - create_purchase_ability(13, 3, slot), - create_purchase_ability(15, 4, slot), - }, + abilities = purchaseAbilities, layout = mainLayout, -- Note: Capitolization matters here. If this is not all lowercase the game -- crashes at startup for some reason. @@ -105,22 +160,47 @@ function purchase_first_reserve_skill_def() end function setupGame(g) - --testing flags - local add_all_reserve_cards_to_deck = true - local all_card_defs = {} - local class_list = { "fighter", "wizard", "cleric", "ranger", "thief", + local allCardDefs = {} + local classList = { "fighter", "wizard", "cleric", "ranger", "thief", "barbarian", "alchemist", "druid", "necromancer", "bard", "monk" } - if add_all_reserve_cards_to_deck then + + local function contains(t, val) + for _, v in pairs(t) do + if v == val then return true end + end + return false + end + + if addAllClassCardsToDeck then for n, x in pairs(_G) do if type(x) == "function" and string.find(n, "carddef$") then - local firstWord = string.split(n, "_")[1] - table.insert(all_card_defs, x()) + local firstWord = string.match(n, "^([^_]+)_") + if contains(classList, firstWord) then + table.insert(allCardDefs, x()) + end end end end - table.insert(all_card_defs, purchase_first_reserve_skill_def()) - registerCards(g, all_card_defs) - + local allToRegister = { purchase_first_reserve_skill_def() } + for _, cardDef in ipairs(allCardDefs) do + table.insert(allToRegister, cardDef) + end + registerCards(g, allToRegister) + local p1Deck = {} + if addAllClassCardsToDeck then + for _, cardDef in ipairs(allCardDefs) do + table.insert(p1Deck, { qty = 1, card = cardDef }) + end + end + local p1Reserve = {} + local p1Hand = {} + if debugStart then + p1Reserve = { + { qty = 1, card = wizard_treasure_map_carddef() }, + { qty = 1, card = ranger_parrot_carddef() }, + } + p1Hand = { { qty = 5, card = fire_gem_carddef() }} + end standardSetup(g, { description = "Knights of Balance: A Community Game Balancing Effort.", playerOrder = { plid1, plid2 }, @@ -135,15 +215,9 @@ function setupGame(g) fromEnv = plid1 }, cards = { - reserve = { - --{ qty = 1, card = wizard_treasure_map_carddef() } - --{ qty = 1, card = ranger_parrot_carddef() } - }, - deck = { - }, - hand = { - --{ qty = 5, card = fire_gem_carddef() }, - }, + reserve = p1Reserve, + deck = p1Deck, + hand = p1Hand, discard = { }, skills = { @@ -193,15 +267,14 @@ end function endGame(g) end - function setupMeta(meta) - meta.name = "purchase_reserve_cards" - meta.minLevel = 0 - meta.maxLevel = 0 - meta.introbackground = "" - meta.introheader = "" - meta.introdescription = "" - meta.path = "C:/Users/jseph/hero-realms-lua-scripts/hero-realms-lua-scripts/Jseph/purchase_reserve_cards.lua" - meta.features = { -} - - end \ No newline at end of file +function setupMeta(meta) + meta.name = "purchase_reserve_cards" + meta.minLevel = 0 + meta.maxLevel = 0 + meta.introbackground = "" + meta.introheader = "" + meta.introdescription = "" + meta.path = "C:/Users/jseph/hero-realms-lua-scripts/hero-realms-lua-scripts/Jseph/purchase_reserve_cards.lua" + meta.features = { + } +end From 61794f7e957683699ddfc518fa26fbd64ebbd851 Mon Sep 17 00:00:00 2001 From: Jseph Date: Thu, 8 Jan 2026 21:45:10 -0800 Subject: [PATCH 07/10] Finish single card purchase from reserve. --- Jseph/purchase_reserve_cards.lua | 156 ++++++++++++++++++++++++++----- 1 file changed, 132 insertions(+), 24 deletions(-) diff --git a/Jseph/purchase_reserve_cards.lua b/Jseph/purchase_reserve_cards.lua index 3bdd881..b7bf37d 100644 --- a/Jseph/purchase_reserve_cards.lua +++ b/Jseph/purchase_reserve_cards.lua @@ -13,8 +13,95 @@ local debugStart = false -- constants local cardCostOverrides = { - {"wizard_treasure_map", 2}, - {"ranger_parrot", 3} + {"cleric_ship_s_bell", 4}, + {"cleric_imperial_sailor", 3}, -- This is apparently the old name of the card + {"cleric_sacred_censer", 4}, + {"cleric_holy_water", 4}, + {"cleric_morningstar", 7}, + {"cleric_enduring_follower", 6}, + {"cleric_motes_of_light", 5}, + {"cleric_benediction_beads", 7}, + {"fighter_cutlass", 3}, + {"fighter_bottle_of_rum", 3}, + {"fighter_chain", 6}, + {"fighter_javelin", 3}, + {"fighter_taunting_talisman", 4}, + {"fighter_ruinic_throwing_axe", 4}, -- The card name just says "Throwing Axe." I'm guessing that's a mistake in the card name. + {"fighter_iron_shield", 5}, + {"fighter_dazzling_ruby", 5}, + {"fighter_ruby_glaive", 5}, + {"ranger_spyglass", 3}, + {"ranger_parrot", 4}, + {"ranger_veiled_trap", 3}, + {"ranger_death_arrow", 4}, + {"ranger_hawk_pet", 3}, + {"ranger_gold_tipped_arrow", 6}, + {"ranger_trick_arrow", 3}, + {"ranger_stalking_bow", 6}, + {"thief_trick_dice", 4}, + {"thief_hook", 4}, + {"thief_kunai", 4}, + {"thief_blinding_powder", 6}, + {"thief_lockpick", 6}, + {"thief_ruby_encrusted_blade", 4}, + {"thief_parrying_dagger", 7}, + {"thief_lucky_knife", 6}, + {"wizard_ship_in_a_bottle", 4}, + {"wizard_treasure_map", 5}, + {"wizard_prestidigitation_charm", 5}, + {"wizard_clock_of_ages", 5}, + {"wizard_wand_of_wanting", 6}, + {"wizard_combust", 7}, + {"wizard_magic_scroll", 5}, + {"wizard_adept_s_components", 7}, + {"barbarian_ring_of_rage", 3}, + {"barbarian_earthshaker", 3}, + {"barbarian_caltrops", 3}, + {"barbarian_seething_spear", 4}, + {"barbarian_whip", 3}, + {"barbarian_double_bladed_hand_axe", 5}, + {"barbarian_net_of_thorns", 3}, + {"barbarian_pillage", 7}, + {"alchemist_fireworks", 4}, + {"alchemist_recalibration_crystal", 4}, + {"alchemist_kaleidoscope", 4}, + {"alchemist_vial_of_acid", 3}, + {"alchemist_recipe_book", 7}, + {"alchemist_rainbow_potion", 5}, + {"alchemist_silver_scales", 4}, + {"alchemist_diamond", 8}, + {"druid_entangling_roots", 2}, + {"druid_panther_eye_ring", 3}, + {"druid_wisdom_of_the_woods", 4}, + {"druid_sunbird", 4}, + {"druid_reclaim_the_forest", 4}, + {"druid_hardy_hedgehog", 6}, + {"druid_great_stag", 5}, + {"druid_flying_squirrel", 6}, + {"necromancer_severing_scythe", 4}, + {"necromancer_stitcher_s_kit", 5}, + {"necromancer_preserved_heart", 4}, + {"necromancer_dread_cauldron", 3}, + {"necromancer_bag_of_bones", 5}, + {"necromancer_soul_cage", 5}, + {"necromancer_withered_wand", 4}, -- Named this way instead of withering_wand for some reason + {"necromancer_company_of_corpses", 5}, + {"bard_musical_darts", 3}, + {"bard_songbook", 3}, + {"bard_muse_s_paper", 5}, + {"bard_music_box", 4}, + {"bard_whistling_rapier", 7}, + {"bard_enchanted_flute", 5}, + {"bard_tuning_fork", 5}, + {"bard_captivating_herald", 6}, + {"monk_amulet_of_resolve", 4}, + {"monk_horn_of_ascendance", 3}, + {"monk_dragon_staff", 4}, + {"monk_tranquil_wind", 4}, + {"monk_arm_bands_of_solemnity", 5}, + {"monk_void_s_eye", 4}, + {"monk_staff_of_the_phoenix", 5}, + {"monk_radiant_blossom", 4}, } function keep_cards_in_reserve_buf_def() @@ -28,18 +115,28 @@ function keep_cards_in_reserve_buf_def() id = "reserve_card_slot", expiresArray = { neverExpiry }, }) - local moveBackToReserve = createCardEffectAbility({ - id = "move_back_to_reserve", - effect = moveTarget(currentReserve).apply(selectTargets().where( + local unpurchasedReserveCardsNotInReserve = selectTargets().where( isCardWithSlot("reserve_card_slot").And( isCardWithSlot("purchased_reserve_card_slot").invert() - ))), + )).exclude(selectLoc(loc(currentPid, myRevealPloc))) + .exclude(selectLoc(loc(currentPid, reservePloc))) + local moveBackToReserve = createCardEffectAbility({ + id = "move_back_to_reserve", + effect = moveTarget(currentReserve).apply(unpurchasedReserveCardsNotInReserve), cost = noCost, trigger = locationChangedCardTrigger, check = const(1).eq(1), activations = multipleActivations, tags = { } }) + + local applyCostChangeEffect = nullEffect() + for _, override in ipairs(cardCostOverrides) do + applyCostChangeEffect = applyCostChangeEffect.seq( + addSlotToTarget(createCostChangeSlot(-override[2], { endOfTurnExpiry })) + .apply(selectLoc(currentReserve).where(isCardName(override[1]))) + ) + end return createBuffDef({ id = "keep_cards_in_reserve", @@ -48,9 +145,10 @@ function keep_cards_in_reserve_buf_def() abilities = { createAbility({ id = "add_slot_to_reserve_cards", - trigger = autoTrigger, - activations = singleActivation, + trigger = startOfTurnTrigger, + activations = multipleActivations, effect = addSlotToTarget(reserveSlot).apply(selectLoc(currentReserve)) + .seq(applyCostChangeEffect) }), }, cardEffectAbilities = { @@ -83,7 +181,7 @@ local function create_purchase_ability(baseCost, index, slot) end local function basic_acquire_ability_layout() - local target = selectLoc(loc(currentPid, reservePloc)).take(1) + local target = selectLoc(loc(ownerPid, reservePloc)).take(1) local cardCost = const(4) for _, override in ipairs(cardCostOverrides) do cardCost = ifInt( @@ -94,30 +192,40 @@ local function basic_acquire_ability_layout() return createLayout({ name = "Purchase Reserve", art = "art/T_Taxation", - text = format(", : Acquire {1} to hand.", - { cardCost, getCardNameStringExpression(target) }), + text = format(": Acquire reserve cards to hand.{0}", + { ifElseString( + target.count().eq(const(0)), + "", + format("\nNext card: {0}\n({1} gold)", {getCardNameStringExpression(target), cardCost}))}), }) end local function create_basic_acquire_ability(slot) - local target = selectLoc(loc(currentPid, reservePloc)).take(1) - local cardCost = const(4) - for _, override in ipairs(cardCostOverrides) do - cardCost = ifInt( - target.where(isCardName(override[1])).count().eq(const(1)), - const(override[2]), - cardCost) - end + local target = selectLoc(loc(currentPid, reservePloc)).take(5) return createAbility({ id = "purchase_first_reserve_card", trigger = uiTrigger, promptType = showPrompt, + activations = multipleActivations, layout = basic_acquire_ability_layout(), - effect = addSlotToTarget(slot).apply(target) - .seq(acquireForFreeTarget(CardLocEnum.ExtraReveal).apply(target)) - .seq(showTextTarget("Acquired reserve card").apply(selectSource())) - .seq(moveTarget(CardLocEnum.Hand).apply(selectLoc(loc(currentPid, extraRevealPloc)).take(1))), - cost = combineCosts({ expendCost, goldCost(cardCost) }), + effect = moveTarget(loc(currentPid, myRevealPloc)).apply(target.reverse()) + .seq(ifElseEffect( + selectLoc(loc(currentPid, myRevealPloc)).take(1).where(getCardCost().lte(getPlayerGold(ownerPid))).count().gte(const(1)), + pushTargetedEffect({ + desc = "Choose a card to purchase", + validTargets = selectLoc(loc(currentPid, myRevealPloc)).take(1).where(getCardCost().lte(getPlayerGold(ownerPid))), + min = 0, + max = 1, + targetEffect = addSlotToTarget(slot).seq(acquireTarget(0, CardLocEnum.Hand)) + .seq(showTextTarget("Acquired reserve card").apply(selectSource())) + .seq(moveTarget(CardLocEnum.Reserve).apply(selectLoc(loc(currentPid, myRevealPloc)).reverse())), + tags = { } + }), + waitForClickEffect("No affordable cards.", "") + .seq(moveTarget(CardLocEnum.Reserve).apply(selectLoc(loc(currentPid, myRevealPloc)).reverse())) + .seq(prepareTarget().apply(selectSource())) + )), + cost = expendCost, check = selectLoc(loc(currentPid, reservePloc)).count().gte(1) }) end From 076b8c054415aceedc52ae35f9d4d3bdbd8b8997 Mon Sep 17 00:00:00 2001 From: Jseph Date: Thu, 8 Jan 2026 21:50:06 -0800 Subject: [PATCH 08/10] Update doc string. --- Jseph/purchase_reserve_cards.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jseph/purchase_reserve_cards.lua b/Jseph/purchase_reserve_cards.lua index b7bf37d..9449b53 100644 --- a/Jseph/purchase_reserve_cards.lua +++ b/Jseph/purchase_reserve_cards.lua @@ -310,7 +310,7 @@ function setupGame(g) p1Hand = { { qty = 5, card = fire_gem_carddef() }} end standardSetup(g, { - description = "Knights of Balance: A Community Game Balancing Effort.", + description = "Purchase Reserve Cards: A balancing effort by Jseph, Userkaffe and Azgalor.", playerOrder = { plid1, plid2 }, ai = ai.CreateKillSwitchAi(createAggressiveAI(), createHardAi2()), timeoutAi = createTimeoutAi(), From 184167700bc79956fa6d4896662933d7aa6b3b87 Mon Sep 17 00:00:00 2001 From: Jseph Date: Thu, 12 Feb 2026 21:30:43 -0800 Subject: [PATCH 09/10] Improve trickster healing and monkey --- Jseph/purchase_reserve_cards.lua | 121 +++++++++++-------------------- Jseph/trickster.lua | 73 +++++++++++++++++-- 2 files changed, 111 insertions(+), 83 deletions(-) diff --git a/Jseph/purchase_reserve_cards.lua b/Jseph/purchase_reserve_cards.lua index 9449b53..7e1e378 100644 --- a/Jseph/purchase_reserve_cards.lua +++ b/Jseph/purchase_reserve_cards.lua @@ -111,6 +111,7 @@ function keep_cards_in_reserve_buf_def() text = "Reserve cards must be purchased." }) local currentReserve = loc(currentPid, reservePloc) + local opponentReserve = loc(oppPid, reservePloc) local reserveSlot = createSlot({ id = "reserve_card_slot", expiresArray = { neverExpiry }, @@ -134,7 +135,8 @@ function keep_cards_in_reserve_buf_def() for _, override in ipairs(cardCostOverrides) do applyCostChangeEffect = applyCostChangeEffect.seq( addSlotToTarget(createCostChangeSlot(-override[2], { endOfTurnExpiry })) - .apply(selectLoc(currentReserve).where(isCardName(override[1]))) + .apply(selectLoc(currentReserve).union(selectLoc(opponentReserve)) + .where(isCardName(override[1]))) ) end @@ -146,7 +148,7 @@ function keep_cards_in_reserve_buf_def() createAbility({ id = "add_slot_to_reserve_cards", trigger = startOfTurnTrigger, - activations = multipleActivations, + activations = singleActivation, effect = addSlotToTarget(reserveSlot).apply(selectLoc(currentReserve)) .seq(applyCostChangeEffect) }), @@ -157,29 +159,6 @@ function keep_cards_in_reserve_buf_def() }) end --- Helper function to create purchase abilities for reserve cards -local function create_purchase_ability(baseCost, index, slot) - local target = selectLoc(loc(currentPid, reservePloc)).take(index).reverse().take(1) - local rawCost = const(baseCost).add(getTurnsPlayed(currentPid).negate()) - local dynamicCost = ifInt(rawCost.gte(const(0)), rawCost, const(0)) - return createAbility({ - id = "purchase_reserve_activate_" .. index, - trigger = uiTrigger, - promptType = showPrompt, - layout = createLayout({ - name = "Purchase Reserve", - art = "art/T_Taxation", - text = format("{0}, : Acquire {1} to hand.\n(-1 per turn)", { dynamicCost, getCardNameStringExpression(target) }), - }), - effect = addSlotToTarget(slot).apply(target) - .seq(acquireForFreeTarget(CardLocEnum.ExtraReveal).apply(target)) - .seq(showTextTarget("Acquired reserve card").apply(selectSource())) - .seq(moveTarget(CardLocEnum.Hand).apply(selectLoc(loc(currentPid, extraRevealPloc)).take(1))), - cost = combineCosts({ expendCost, goldCost(dynamicCost) }), - check = selectLoc(loc(currentPid, reservePloc)).count().gte(index) - }) -end - local function basic_acquire_ability_layout() local target = selectLoc(loc(ownerPid, reservePloc)).take(1) local cardCost = const(4) @@ -192,11 +171,7 @@ local function basic_acquire_ability_layout() return createLayout({ name = "Purchase Reserve", art = "art/T_Taxation", - text = format(": Acquire reserve cards to hand.{0}", - { ifElseString( - target.count().eq(const(0)), - "", - format("\nNext card: {0}\n({1} gold)", {getCardNameStringExpression(target), cardCost}))}), + text = ": Acquire the next reserve card to hand." }) end @@ -257,7 +232,6 @@ function purchase_first_reserve_skill_def() return createSkillDef({ id = "purchase_first_reserve_skill", name = "Purchase Reserve", - cardTypeLabel = "Skill", types = { skillType }, abilities = purchaseAbilities, layout = mainLayout, @@ -268,47 +242,10 @@ function purchase_first_reserve_skill_def() end function setupGame(g) - local allCardDefs = {} - local classList = { "fighter", "wizard", "cleric", "ranger", "thief", - "barbarian", "alchemist", "druid", "necromancer", "bard", "monk" } - - local function contains(t, val) - for _, v in pairs(t) do - if v == val then return true end - end - return false - end + registerCards(g, { + purchase_first_reserve_skill_def(), + }) - if addAllClassCardsToDeck then - for n, x in pairs(_G) do - if type(x) == "function" and string.find(n, "carddef$") then - local firstWord = string.match(n, "^([^_]+)_") - if contains(classList, firstWord) then - table.insert(allCardDefs, x()) - end - end - end - end - local allToRegister = { purchase_first_reserve_skill_def() } - for _, cardDef in ipairs(allCardDefs) do - table.insert(allToRegister, cardDef) - end - registerCards(g, allToRegister) - local p1Deck = {} - if addAllClassCardsToDeck then - for _, cardDef in ipairs(allCardDefs) do - table.insert(p1Deck, { qty = 1, card = cardDef }) - end - end - local p1Reserve = {} - local p1Hand = {} - if debugStart then - p1Reserve = { - { qty = 1, card = wizard_treasure_map_carddef() }, - { qty = 1, card = ranger_parrot_carddef() }, - } - p1Hand = { { qty = 5, card = fire_gem_carddef() }} - end standardSetup(g, { description = "Purchase Reserve Cards: A balancing effort by Jseph, Userkaffe and Azgalor.", playerOrder = { plid1, plid2 }, @@ -318,53 +255,81 @@ function setupGame(g) players = { { id = plid1, + --isAi = true, startDraw = 3, init = { fromEnv = plid1 }, cards = { - reserve = p1Reserve, - deck = p1Deck, - hand = p1Hand, + reserve = { + --{ qty = 1, card = wizard_treasure_map_carddef() }, + --{ qty = 1, card = ranger_parrot_carddef() } + }, + deck = { + }, + hand = { + --{ qty = 1, card = thief_enchanted_garrote_carddef() }, + --{ qty = 1, card = ranger_honed_black_arrow_carddef() }, + --{ qty = 2, card = cleric_follower_b_carddef() }, + --{ qty = 2, card = cleric_imperial_sailor_carddef() }, + --{ qty = 1, card = cleric_brightstar_shield_carddef() }, + --{ qty = 1, card = fighter_rallying_flag_carddef() }, + --{ qty = 1, card = barbarian_disorienting_headbutt_carddef() }, + }, discard = { + -- { qty = 2, card = torgen_rocksplitter_carddef() }, + -- { qty = 2, card = cleric_follower_b_carddef() }, + -- { qty = 1, card = cleric_follower_a_carddef() }, + -- { qty = 1, card = cleric_veteran_follower_carddef() }, + -- { qty = 1, card = cleric_redeemed_ruinos_carddef() }, }, skills = { purchase_first_reserve_skill_def(), + --{ qty = 1, card = fighter_helm_of_fury_carddef() }, + --{ qty = 1, card = alchemist_spectrum_spectacles_carddef() } }, buffs = { + keep_cards_in_reserve_buf_def(), drawCardsCountAtTurnEndDef(5), discardCardsAtTurnStartDef(), fatigueCount(40, 1, "FatigueP1"), - keep_cards_in_reserve_buf_def(), } } }, { id = plid2, + --isAi = true, startDraw = 5, init = { fromEnv = plid2 }, cards = { reserve = { - --{ qty = 1, card = wizard_treasure_map_carddef() } + --{ qty = 1, card = wizard_treasure_map_carddef() }, --{ qty = 1, card = ranger_parrot_carddef() } }, deck = { }, hand = { - --{ qty = 5, card = fire_gem_carddef() }, + --{ qty = 1, card = ranger_light_crossbow_carddef() }, + --{ qty = 1, card = ranger_honed_black_arrow_carddef() }, + --{ qty = 2, card = cleric_follower_b_carddef() }, + --{ qty = 2, card = cleric_imperial_sailor_carddef() }, + --{ qty = 1, card = cleric_brightstar_shield_carddef() }, + --{ qty = 1, card = sway_carddef() }, }, discard = { + --{ qty = 2, card = cleric_follower_b_carddef() }, }, skills = { purchase_first_reserve_skill_def(), + --{ qty = 1, card = cleric_shining_breastplate_carddef() }, }, buffs = { + keep_cards_in_reserve_buf_def(), drawCardsCountAtTurnEndDef(5), discardCardsAtTurnStartDef(), fatigueCount(40, 1, "FatigueP2"), - keep_cards_in_reserve_buf_def(), } } }, diff --git a/Jseph/trickster.lua b/Jseph/trickster.lua index 899cad8..7ba4d76 100644 --- a/Jseph/trickster.lua +++ b/Jseph/trickster.lua @@ -24,6 +24,23 @@ Decrease the cost of two random cards in the market by {gold_2} until your next ]] +pet_monkey_layout = [[ + + + + + + + + + + + + + + +]] + hidden_power_layout = [[ : Shuffle your deck then draw {1} card(s)", + text = format("{0}: Gain {0} and shuffle your deck then draw {1} card(s)", {s.Minus(s.getCounter("reshuffle_card_count"), s.Const(1)), getCounter("reshuffle_card_count")}) }), @@ -90,7 +108,7 @@ end local function reshuffle_effect() return pushTargetedEffect({ desc = - "Pay X to put X+1 cards back in your deck, shuffle, and then draw X+1 cards.", + "Pay X to gain X, put X+1 cards back in your deck, shuffle, and then draw X+1 cards.", min=1, max=ifInt( selectLoc(loc(currentPid, handPloc)).count() @@ -110,7 +128,7 @@ local function reshuffle_hand_skill() name = "Mulligan", art = "art/t_prism_rainerpetter", frame = "frames/alchemist_frames/alchemist_item_cardframe", - text = ": Pay X to put X+1 cards back in your deck, shuffle, and then draw X+1 cards." + text = ": Pay X to gain X and put X+1 cards back in your deck, shuffle, and then draw X+1 cards." }) --[[local effect_chain = ignoreTarget(showTextEffect("Listing functions!")) for name, value in pairs(_G) do @@ -262,7 +280,7 @@ local function double_or_nothing_card_def() }) end -local function pet_monkey_effect() +local function pet_monkey_effect_1() local kMoveText = "Moving to top of market deck." return pushTargetedEffect({ desc = "Select a card to swap with the top card of the market deck.", @@ -276,12 +294,56 @@ local function pet_monkey_effect() }) end +local function pet_monkey_effect_2() + return moveTarget(currentRevealLoc).apply(selectLoc(tradeDeckLoc).take(3).reverse()) + .seq(noUndoEffect()) + .seq(promptSplit({ + selector = selectLoc(currentRevealLoc), + take = const(0), -- number of cards to take for split + sort = const(0), -- number of cards to be sorted for ef2 + minTake = const(3), -- Number of cards for ef1 + ef1 = moveTarget(tradeDeckLoc), -- effect to be applied to cards left + ef2 = ignoreTarget(nullEffect()), -- effect to be applied to sorted cards + header = "Pet Monkey", -- prompt header + description = "Rearrange the top 3 cards of the market deck.", + rightPileDesc = "Rightmost on top.", + pile1Name = "Top 3 cards.", + pile2Name = "Unused.", + eff1Tags = { }, + eff2Tags = { }})) +end + +local function pet_monkey_effect() + return pushChoiceEffect({ + choices = { + { + effect = pet_monkey_effect_1(), + layout = createLayout({ + name = "Pet Monkey", + art = "art/epicart/kong", + text = "Swap any card on the market row with the top card of the market deck." + }), + }, + { + effect = pet_monkey_effect_2(), + layout = createLayout({ + name = "Pet Monkey", + art = "art/epicart/kong", + text = "Rearrange the top 3 cards of the market deck.", + check = selectLoc(tradeDeckLoc).count() + .gte(const(3)) + }), + }, + } + }) +end + local function pet_monkey_card_def() local cardLayout = createLayout({ name = "Pet Monkey", art = "art/epicart/kong", frame = "frames/alchemist_frames/alchemist_item_cardframe", - text = ": Swap any card on the market row with the top card of the market deck.", + xmlText = pet_monkey_layout, }) return createChampionDef({ id = "pet_monkey_jseph", @@ -516,6 +578,7 @@ end + function setupMeta(meta) meta.name = "trickster" meta.minLevel = 0 From 5acc42d03a7c145d7e81b9fb7fcd5af94db26feb Mon Sep 17 00:00:00 2001 From: Jseph Date: Sat, 7 Mar 2026 13:36:34 -0800 Subject: [PATCH 10/10] Cosmetic updates for trickster. --- Aarkenell/12 classes in one | 4438 +++++++++++++++++++++++------- Jseph/random_testing_sandbox.lua | 289 ++ Jseph/trickster.lua | 113 +- 3 files changed, 3748 insertions(+), 1092 deletions(-) create mode 100644 Jseph/random_testing_sandbox.lua diff --git a/Aarkenell/12 classes in one b/Aarkenell/12 classes in one index 0583c00..f0e4b5e 100644 --- a/Aarkenell/12 classes in one +++ b/Aarkenell/12 classes in one @@ -6,7 +6,14 @@ require "hardai" require "mediumai" require "easyai" ---[[ Updated 12.04.2024 by Aarkenell +--[[ Updated 28/2/2025 by Aarkenell +Terramancer & Pyromancer now have unique cards generated for eacFh player, for cards that have custom counters. This enables mirror matches with these classes. +Dynamic text added to Pyro & Terra cards with counters. +]] + +--[[ +Updates +19.04.2025 - art and cardframe decapitalisation post WWG patch ]] -- Game start -- @@ -18,51 +25,126 @@ local function chooseStart() upperTitle = "Welcome! How would you like to proceed?", lowerTitle = "", --- 1.1 choice - effectFirst= pushChoiceEffectWithTitle( - { - - choices = { - -- 1.1a choice - choose custom - { - effect = moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)).union(selectLoc(loc(currentPid, deckPloc)).union(selectLoc(loc(currentPid, skillsPloc)).union(selectLoc(loc(currentPid, reservePloc)) +-- 1.1 choose class + effectFirst = pushChoiceEffect( +{ +choices = { +-- Choice 1: Classes by Aarkenell + { + effect = moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)).union(selectLoc(loc(currentPid, deckPloc)).union(selectLoc(loc(currentPid, skillsPloc)).union(selectLoc(loc(currentPid, reservePloc)) + .union(selectLoc(loc(currentPid, buffsPloc)).where(isCardType(ogreType).Or(isCardType(orcType)).Or(isCardType(elfType).Or(isCardType(dwarfType).Or(isCardType(smallfolkType)).Or(isCardType(halfDemonType)).Or(isCardType(magicArmorType)).Or(isCardName("smallfolk_hide_buff")))))))))) + .seq(gainCombatEffect(-1)) + .seq(setPlayerNameEffect("Unknown", currentPid)) + .seq(setPlayerAvatarEffect("assassin", currentPid)) + .seq(gainMaxHealthEffect(currentPid, const(50).add(getPlayerMaxHealth(currentPid).negate()))) + .seq(gainHealthEffect(getPlayerMaxHealth(currentPid).add(getPlayerHealth(currentPid).negate()))) + --.seq(createCardEffect(choose_lich_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(choose_brewmaster_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(choose_thandarlorian_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(choose_terramancer_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(choose_pyromancer_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(choose_cryomancer_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(choose_apothecary_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(choose_demonologist_carddef(), loc(currentPid, asidePloc))) + + .seq(moveTarget(handPloc).apply(selectLoc(loc(currentPid, asidePloc)))) + .seq(waitForClickEffect("Please play the card corresponding to the class you wish to play.", "")), + + layout = createLayout({ + name = "Classes by Aarkenell", + art = "art/epicart/the_people_s_champion", + xmlText=[[ + + + + + ]] + }), + + tags = {} + }, + +-- Choice 2: Class by other Designers +{ + effect = moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)).union(selectLoc(loc(currentPid, deckPloc)).union(selectLoc(loc(currentPid, skillsPloc)).union(selectLoc(loc(currentPid, reservePloc)) .union(selectLoc(loc(currentPid, buffsPloc)).where(isCardType(ogreType).Or(isCardType(orcType)).Or(isCardType(elfType).Or(isCardType(dwarfType).Or(isCardType(smallfolkType)).Or(isCardType(halfDemonType)).Or(isCardType(magicArmorType)).Or(isCardName("smallfolk_hide_buff")))))))))) .seq(gainCombatEffect(-1)) .seq(setPlayerNameEffect("Unknown", currentPid)) .seq(setPlayerAvatarEffect("assassin", currentPid)) .seq(gainMaxHealthEffect(currentPid, const(50).add(getPlayerMaxHealth(currentPid).negate()))) - .seq(gainHealthEffect(50)) - .seq(createCardEffect(choose_S_and_R_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(choose_paladin_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(choose_witch_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(choose_shaman_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(choose_king_midas_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(choose_brewmaster_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(choose_thandarlorian_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(choose_terramancer_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(choose_pyromancer_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(choose_cryomancer_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(choose_apothecary_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(choose_demonologist_carddef(), loc(currentPid, asidePloc))) - .seq(moveTarget(handPloc).apply(selectLoc(loc(currentPid, asidePloc)))) - .seq(waitForClickEffect("Please play the card corresponding to the class you wish to play.", "")) - .seq(waitForClickEffect("Note: Game does not currently work properly if both players choose Pyromancer or both choose Terramancer.")), + .seq(gainHealthEffect(getPlayerMaxHealth(currentPid).add(getPlayerHealth(currentPid).negate()))) + .seq(createCardEffect(choose_trickster_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(choose_S_and_R_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(choose_paladin_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(choose_witch_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(choose_shaman_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(choose_king_midas_carddef(), loc(currentPid, asidePloc))) + + .seq(moveTarget(handPloc).apply(selectLoc(loc(currentPid, asidePloc)))) + .seq(waitForClickEffect("Please play the card corresponding to the class you wish to play.", "")), + + layout = createLayout({ + name = "Classes by Other Designers", + art = "art/epicart/insurgency", + xmlText=[[ + + + + + ]] + }) , + + tags = {} + }, + +-- Choice 3: Random +{ + effect = moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)).union(selectLoc(loc(currentPid, deckPloc)).union(selectLoc(loc(currentPid, skillsPloc)).union(selectLoc(loc(currentPid, reservePloc)) + .union(selectLoc(loc(currentPid, buffsPloc)).where(isCardType(ogreType).Or(isCardType(orcType)).Or(isCardType(elfType).Or(isCardType(dwarfType).Or(isCardType(smallfolkType)).Or(isCardType(halfDemonType)).Or(isCardType(magicArmorType)).Or(isCardName("smallfolk_hide_buff")))))))))) + .seq(sacrificeTarget().apply(selectLoc(loc(currentPid, skillsPloc)))) + .seq(moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)))) + .seq(randomEffect({ + valueItem(1, createCardEffect(choose_demonologist_carddef(), currentHandLoc)), + valueItem(1, createCardEffect(choose_apothecary_carddef(), currentHandLoc)), + valueItem(1, createCardEffect(choose_cryomancer_carddef(), currentHandLoc)), + valueItem(1, createCardEffect(choose_pyromancer_carddef(), currentHandLoc)), + valueItem(1, createCardEffect(choose_terramancer_carddef(), currentHandLoc)), + valueItem(1, createCardEffect(choose_thandarlorian_carddef(), currentHandLoc)), + valueItem(1, createCardEffect(choose_brewmaster_carddef(), currentHandLoc)), + valueItem(1, createCardEffect(choose_paladin_carddef(), currentHandLoc)), + valueItem(1, createCardEffect(choose_shaman_carddef(), currentHandLoc)), + valueItem(1, createCardEffect(choose_king_midas_carddef(), currentHandLoc)), + valueItem(1, createCardEffect(choose_witch_carddef(), currentHandLoc)), + valueItem(1, createCardEffect(choose_S_and_R_carddef(), currentHandLoc)), + valueItem(1, createCardEffect(choose_trickster_carddef(), currentHandLoc)), + --valueItem(1, createCardEffect(choose_lich_carddef(), currentHandLoc)) + })) + .seq(sacrificeTarget().apply(selectSource())) + .seq(noUndoEffect()), layout = createLayout({ - name = "Pick a custom class", - art = "art/T_Storm_Siregar", - xmlText=[[ - - - + name = "Random Class", + art = "art/t_confused_apparition", + xmlText=[[ + + + + + + + + - ]] }), tags = {} - }, - -- 1.1b choice - choose standard + }, + +-- Choice 4- choose standard { effect = pushChoiceEffectWithTitle( { @@ -75,7 +157,7 @@ level 3 Custom Characters to play." fontsize="26"/> layout = createLayout( { name = "Yes", - art = "art/treasures/T_cleric_elixir_green", + art = "art/treasures/t_cleric_elixir_green", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -96,7 +178,7 @@ Start playing." fontsize="30" flexiblewidth="1" /> layout = createLayout( { name = "No", - art = "art/treasures/T_cleric_elixir_blue_purple", + art = "art/treasures/t_cleric_elixir_blue_purple", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -115,9 +197,10 @@ Start playing." fontsize="30" flexiblewidth="1" /> upperTitle = "Confirm your choice. Do you want to use your standard Hero?", lowerTitle = "" }), + condition = getHeroLevel(currentPid).lte(4), layout = createLayout({ name = "Selected class", - art = "art/T_All_Heroes", + art = "art/t_storm_siregar", xmlText=[[ @@ -126,14 +209,11 @@ Start playing." fontsize="30" flexiblewidth="1" /> ]] }), tags = {} - } - }, - - upperTitle = "Make your choice.", - lowerTitle = "Do you want to play as a custom class, or use the character you picked when starting the game?", - - }), --- 1.2 choice + } +} +} +), +-- 1.2 choice - learn about the mod effectSecond = pushChoiceEffectWithTitle( { @@ -144,18 +224,28 @@ Start playing." fontsize="30" flexiblewidth="1" /> layout = createLayout({ name = "Updates to this mod", - art = "art/T_fighter_sharpening_stone", - xmlText=[[ + art = "art/t_fighter_sharpening_stone", + xmlText=[[ + - + - ]] + ]] + }), tags = {} }, @@ -164,79 +254,47 @@ Demon Hunter custom card added to market deck effect = nullEffect(), layout = createLayout({ name = "Other mods to try", - art = "art/treasures/T_spyglass", + art = "art/treasures/t_spyglass", xmlText=[[ - + +<size=90%>Look out for these games in the custom lobby, or join the RealmsRising Discord to request custom challenges." fontsize="14"/> ]] }), tags = {} }, - - -- 1.2c choice - Resolving game crashes - { - effect = storyTellEffectWithPortrait("origins_flawless_track", "Hi Folks – Aarkenell here – thanks for playing this mod. I hope you’re enjoying it.") - .seq(storyTellEffectWithPortrait("origins_flawless_track", "If you play HR on a mobile device and you’ve been playing custom games for a while")) - .seq(storyTellEffectWithPortrait("origins_flawless_track", "you may notice that your games take longer to load or that the app crashes if you try to play more than one custom game in your queue.")) - .seq(storyTellEffectWithPortrait("origins_flawless_track", "A few community members and I have flagged this issue with the WWG team.")) - .seq(storyTellEffectWithPortrait("origins_flawless_track", "We know many people play exclusively on mobile, and this issue really detracts from your play experience.")) - .seq(storyTellEffectWithPortrait("origins_flawless_track", "We don’t know if/when they will be able to resolve this. So in the meantime, here’s a temporary fix:")) - .seq(storyTellEffectWithPortrait("origins_flawless_track", "If you encounter game crashes whilst playing custom games, uninstalling and reinstalling the HR app will fix this issue. ")) - .seq(storyTellEffectWithPortrait("origins_flawless_track", "At least until you play a bunch more custom games.")) - .seq(storyTellEffectWithPortrait("origins_flawless_track", "(I play a lot of custom games and have only had to reinstall the game 2-3 times in about 18 months.)")) - .seq(storyTellEffectWithPortrait("origins_flawless_track", "This will, of course, mean the app needs to download all of the file packages again, ")) - .seq(storyTellEffectWithPortrait("origins_flawless_track", "so you may want to do this whilst you’ve got access to a decent wifi connection. ")) - .seq(storyTellEffectWithPortrait("origins_flawless_track", "But at least you’ll be able to get back to your regular playing experience.")) - .seq(storyTellEffectWithPortrait("origins_flawless_track", "If/when WWG inform us of a more permanent fix, I’ll let you know.")) - .seq(storyTellEffectWithPortrait("origins_flawless_track", "Until then, happy gaming!")) - .seq(leftStoryTellEffectWithPortrait("origins_flawless_track", "Now, please hit 'Undo' to return to the menu.")) - , - - layout = createLayout({ - name = "Resolving game crashes", - art = "art/T_word_of_power", - xmlText=[[ - - - - - - ]] - }), - tags = {} - }, - -- 1.2d choice - Community news + + -- 1.2c choice - Community news { - effect = storyTellEffectWithPortrait("ogre", "Hi! I’m Rob d’Ogrety, CEO of ‘Why’s Wizard Games?’") - .seq(leftStoryTellEffectWithPortrait("inquisition", "And I’m Dwarfin Kastle – creator of the award-winning deckbuilding game about growing trees in space – Star Elms.")) - .seq(storyTellEffectWithPortrait("ogre", "We just wanted to take a moment to say how much we love the community that has sprung up around Hero Realms…")) - .seq(leftStoryTellEffectWithPortrait("inquisition", "We love you!")) - .seq(storyTellEffectWithPortrait("ogre", " …and how much we love the experiments and new ideas being offered through custom lua scripting.")) - .seq(leftStoryTellEffectWithPortrait("inquisition", "Yeah! We love lua!")) - .seq(leftStoryTellEffectWithPortrait("inquisition", "Lua, Lua! Oh baby! Me gotta go! Aye-yi-yi-yi!")) - .seq(storyTellEffectWithPortrait("ogre", " Thanks DK.")) - .seq(storyTellEffectWithPortrait("ogre", "Be sure to join the Realms Rising server on Discord to share your feedback on this mod and to discover new custom games to play.")) - .seq(storyTellEffectWithPortrait("ogre", "And listen to the Sparks and Recreation podcast for information on Community events, meta-analysis, and everything Hero Realms.")) - .seq(storyTellEffectWithPortrait("ogre", "Or jump over to RealmsRising.com for in-depth articles on strategy and gameplay.")) + effect = storyTellEffectWithPortrait("ogre_fighter_male_02", "Hi! I’m Rob d’Ogrety, CEO of ‘Why’s Wizard Games?’") + .seq(leftStoryTellEffectWithPortrait("dwarf_fighter_male_02", "And I’m Dwarfin Kastle – creator of the award-winning deckbuilding game about growing trees in space – Star Elms.")) + .seq(storyTellEffectWithPortrait("ogre_fighter_male_02", "We just wanted to take a moment to say how much we love the community that has sprung up around Hero Realms…")) + .seq(leftStoryTellEffectWithPortrait("dwarf_fighter_male_02", "We love you!")) + .seq(storyTellEffectWithPortrait("ogre_fighter_male_02", " …and how much we love the experiments and new ideas being offered through custom lua scripting.")) + .seq(leftStoryTellEffectWithPortrait("dwarf_fighter_male_02", "Yeah! We love lua!")) + .seq(leftStoryTellEffectWithPortrait("dwarf_fighter_male_02", "Lua, Lua! Oh baby! We gotta code! Aye-yi-yi-yi!")) + .seq(storyTellEffectWithPortrait("ogre_fighter_male_02", " Thanks DK.")) + .seq(storyTellEffectWithPortrait("ogre_fighter_male_02", "Be sure to join the Realms Rising server on Discord to share your feedback on this mod and to discover new custom games to play.")) + .seq(storyTellEffectWithPortrait("ogre_fighter_male_0", "And listen to the Sparks and Recreation podcast for information on Community events, meta-analysis, and everything Hero Realms.")) + .seq(storyTellEffectWithPortrait("ogre_fighter_male_02", "Or jump over to RealmsRising.com for in-depth articles on strategy and gameplay.")) .seq(storyTellEffectWithPortrait("origins_flawless_track", "And if you have ideas for new custom classes or scenarios you’d like to see, drop me, Aarkenell, a message in the Realms Rising Discord.")) - .seq(storyTellEffectWithPortrait("ogre", "Whether you join the Realms Rising community or not, we hope you keep enjoying the game and all the custom content from our great community. ")) - .seq(storyTellEffectWithPortrait("ogre", "Bye for now.")) - .seq(leftStoryTellEffectWithPortrait("inquisition", "Lua, Lua! Oh baby! We gotta go. Bye-yi-yi-yi!")) + .seq(storyTellEffectWithPortrait("ogre_fighter_male_02", "Whether you join the Realms Rising community or not, we hope you keep enjoying the game and all the custom content from our great community.")) + .seq(storyTellEffectWithPortrait("ogre_fighter_male_02", "Bye for now.")) + .seq(leftStoryTellEffectWithPortrait("dwarf_fighter_male_02", "Lua, Lua! Oh baby! We gotta go. Bye-yi-yi-yi!")) .seq(storyTellEffectWithPortrait("origins_flawless_track", "Please hit 'Undo' to return to the menu.")) , layout = createLayout({ name = "Community News", - art = "art/T_borg_ogre_mercenary", + art = "art/t_borg_ogre_mercenary", xmlText=[[ @@ -254,10 +312,10 @@ Here's how to fix it." fontsize="26"/> }), --- 1.1 layout +-- 1.1 layout - choose class layoutFirst = createLayout({ name = "To Battle!", - art = "art/T_Unify_Apsara", + art = "art/t_unify_apsara", xmlText=[[ @@ -267,17 +325,17 @@ Here's how to fix it." fontsize="26"/> ]] }), --- 1.2 layout +-- 1.2 layout - learn about the mod layoutSecond = createLayout({ name = "Updates & Info", - art = "art/treasures/T_Magic_Scroll_Souveraine", + art = "art/treasures/t_magic_scroll_souveraine", xmlText=[[ @@ -290,38 +348,140 @@ end -- Custom Class Selection cards -function choose_demonologist_carddef() +function choose_lich_carddef() + + local ef_Corruption = createCardEffect(lich_corruption_carddef(), loc(currentPid, skillsPloc)) + .seq(sacrificeSelf()) + + local corruption20buff = createGlobalBuff({ + id="corruption20buff", + name = "Corruption", + abilities = { + createAbility({ + id= "corruption20buff", + trigger = deckShuffledTrigger, + effect = ifElseEffect(selectLoc(loc(currentPid, handPloc)).union(selectLoc(loc(currentPid, inPlayPloc))).union(selectLoc(loc(currentPid, deckPloc))).where(isCardType(minionType).invert()).count().gte(20), ef_Corruption, nullEffect()), + }), + }, + buffDetails = createBuffDetails({ + name = "Corruption", + art = "art/t_charing_guardian", + text = "When you shuffle your deck, if you have 20 or more cards in your deck, hand and in play (not including minions), gain the Corruption skill." + }) + }) + + + local Grimoirebuff = createGlobalBuff({ + id="Grimoirebuff", + name = "Grimoire", + abilities = { + createAbility({ + id= "Grimoirebuff", + trigger = startOfTurnTrigger, + effect = nullEffect(), + }), + }, + buffDetails = createBuffDetails({ + name = "Grimoire", + art = "art/t_rayla_endweaver", + text = "You start with 10 cards (8 unique minions) in your Grimoire. Whenever you perform a Summoning, randomly take one card from your Grimoire and put it into play. When a minion is stunned it returns to your Grimoire." + }) + }) + + local souljarbuff = createGlobalBuff({ + id="souljarbuff", + name = "Soul Jar", + abilities = { + createAbility({ + id= "souljarbuff", + trigger = startOfTurnTrigger, + effect = nullEffect(), + }), + }, + buffDetails = createBuffDetails({ + name = "Soul Jar", + art = "art/treasures/t_wizard_elixir_blue", + text = "Each game 1 of 6 Soul Jars are randomly chosen to determine the Lich's Health and Skill." + }) + }) + + + --[[local ef_Control = transformTarget("control_skill").apply(selectLoc(loc(currentPid, skillsPloc)).where(isCardName("corruption_skill")).take(1)) + .seq(sacrificeSelf()) + + local control30buff = createGlobalBuff({ + id="control30buff", + name = "Control", + abilities = { + createAbility({ + id= "control30buff", + trigger = deckShuffledTrigger, + effect = ifElseEffect(selectLoc(loc(currentPid, handPloc)).union(selectLoc(loc(currentPid, inPlayPloc))).union(selectLoc(loc(currentPid, deckPloc))).where(isCardType(minionType).invert()).count().gte(30), ef_Control, nullEffect()), + }), + }, + buffDetails = createBuffDetails({ + name = "Control", + art = "art/t_charing_guardian", + text = "When you shuffle your deck, if you have 30 or more cards in your deck, hand and in play (not including minions), replace Corruption skill with the Control skill." + }) + }) +]] return createDef({ - id="choose_demonologist", - name="Choose the Demonologist", + id="choose_lich", + name="Choose the Lich", types={noStealType, itemType}, cardTypeLabel = "Item", playLocation = castPloc, acquireCost=0, abilities = { createAbility({ - id="choose_demonologist", + id="choose_lich", trigger= onPlayTrigger, playAllType = noPlayPlayType, effect = sacrificeTarget().apply(selectLoc(loc(currentPid, skillsPloc))) - .seq(moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)))) --here-- - .seq(setPlayerNameEffect("Demonologist", currentPid)) - .seq(setPlayerAvatarEffect("ruinos_zealot", currentPid)) - .seq(gainMaxHealthEffect(currentPid, const(50).add(getPlayerMaxHealth(currentPid).negate()))) - .seq(gainHealthEffect(50)) - .seq(createCardEffect(demonologist_summon_demon_carddef(), currentSkillsLoc)) - .seq(createCardEffect(demonologist_summon_demon_master_carddef(), currentSkillsLoc)) - .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) --here-- - .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)))) + .seq(setPlayerNameEffect("Lich", currentPid)) + .seq(setPlayerAvatarEffect("WarInTheWild/krythos_master_vampire", currentPid)) + --buffs + .seq(createCardEffect(corruption20buff, loc(currentPid, buffsPloc))) + .seq(createCardEffect(Grimoirebuff, loc(currentPid, buffsPloc))) + .seq(createCardEffect(souljarbuff, loc(currentPid, buffsPloc))) + --.seq(createCardEffect(control30buff, loc(currentPid, buffsPloc))) + + --soul jar skill + .seq(randomEffect({ + valueItem(1, createCardEffect(lich_SJ_DI_carddef(), loc(currentPid, skillsPloc)).seq(gainMaxHealthEffect(currentPid, const(35).add(getPlayerMaxHealth(currentPid).negate()))).seq(gainHealthEffect(35))), + valueItem(1, createCardEffect(lich_SJ_HoE_carddef(), loc(currentPid, skillsPloc)).seq(gainMaxHealthEffect(currentPid, const(30).add(getPlayerMaxHealth(currentPid).negate()))).seq(gainHealthEffect(30))), + valueItem(1, createCardEffect(lich_SJ_UR_carddef(), loc(currentPid, skillsPloc)).seq(gainMaxHealthEffect(currentPid, const(55).add(getPlayerMaxHealth(currentPid).negate()))).seq(gainHealthEffect(55))), + valueItem(1, createCardEffect(lich_SJ_M_carddef(), loc(currentPid, skillsPloc)).seq(gainMaxHealthEffect(currentPid, const(45).add(getPlayerMaxHealth(currentPid).negate()))).seq(gainHealthEffect(45))), + valueItem(1, createCardEffect(lich_SJ_V_carddef(), loc(currentPid, skillsPloc)).seq(gainMaxHealthEffect(currentPid, const(50).add(getPlayerMaxHealth(currentPid).negate()))).seq(gainHealthEffect(50))), + valueItem(1, createCardEffect(lich_SJ_IW_carddef(), loc(currentPid, skillsPloc)).seq(gainMaxHealthEffect(currentPid, const(40).add(getPlayerMaxHealth(currentPid).negate()))).seq(gainHealthEffect(40))), + })) + + --deck + .seq(createCardEffect(lich_soul_diamond_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(lich_soul_diamond_carddef(), loc(currentPid, asidePloc))) .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(demonologist_shadow_feeder_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(demonologist_void_guard_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(demonologist_lesser_devourer_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(demonologist_shadow_gem_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(ruby_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(ruby_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(lich_frozen_touch_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(lich_soul_crush_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(lich_minor_summoning_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(lich_major_summoning_carddef(), loc(currentPid, asidePloc))) .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) + --grimoire minions + .seq(createCardEffect(lich_abomination_minion_carddef(), loc(nil, nullPloc))) + .seq(createCardEffect(lich_banshee_minion_carddef(), loc(nil, nullPloc))) + .seq(createCardEffect(lich_ghoul_minion_carddef(), loc(nil, nullPloc))) + .seq(createCardEffect(lich_revenant_minion_carddef(), loc(nil, nullPloc))) + .seq(createCardEffect(lich_skeleton_horde_minion_carddef(), loc(nil, nullPloc))) + .seq(createCardEffect(lich_skeleton_horde_minion_carddef(), loc(nil, nullPloc))) + .seq(createCardEffect(lich_skeleton_horde_minion_carddef(), loc(nil, nullPloc))) + .seq(createCardEffect(lich_wall_of_bones_minion_carddef(), loc(nil, nullPloc))) + .seq(createCardEffect(lich_wall_of_fire_minion_carddef(), loc(nil, nullPloc))) + .seq(createCardEffect(lich_zombie_minion_carddef(), loc(nil, nullPloc))) + --setup .seq(shuffleEffect(currentDeckLoc)) .seq(sacrificeTarget().apply(selectSource())) .seq(pushChoiceEffectWithTitle( @@ -335,7 +495,7 @@ function choose_demonologist_carddef() layout = createLayout( { name = "Yes", - art = "art/treasures/T_cleric_elixir_green", + art = "art/treasures/t_cleric_elixir_green", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -356,7 +516,7 @@ Start playing." fontsize="30" flexiblewidth="1" /> layout = createLayout( { name = "No", - art = "art/treasures/T_cleric_elixir_blue_purple", + art = "art/treasures/t_cleric_elixir_blue_purple", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -372,70 +532,70 @@ Start playing." fontsize="30" flexiblewidth="1" /> tags = {} } }, - upperTitle = "Confirm your choice. Do you want to use the Demonologist?", + upperTitle = "Confirm your choice. Do you want to use the lich?", lowerTitle = "" })) }) }, layout = createLayout({ - name = "Demonologist", - art = "art/T_Tyrannor_The_Devourer", - frame = "frames/Coop_Campaign_CardFrame", + name = "Lich", + art = "art/t_charing_guardian", + frame = "frames/coop_campaign_cardframe", xmlText=[[ - - - - - - + + + + + + - - - + + + ]] }), }) - end +end -function choose_apothecary_carddef() +function choose_trickster_carddef() return createDef({ - id="choose_apothecary", - name="Choose the Apothecary", + id="choose_trickster", + name="Choose Trickster", types={noStealType, itemType}, cardTypeLabel = "Item", playLocation = castPloc, acquireCost=0, abilities = { createAbility({ - id="choose_apothecary", + id="choose_trickster", trigger= onPlayTrigger, playAllType = noPlayPlayType, effect = sacrificeTarget().apply(selectLoc(loc(currentPid, skillsPloc))) .seq(moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)))) - .seq(setPlayerNameEffect("Apothecary", currentPid)) - .seq(setPlayerAvatarEffect("alchemist_01", currentPid)) - .seq(gainMaxHealthEffect(currentPid, const(52).add(getPlayerMaxHealth(currentPid).negate()))) - .seq(gainHealthEffect(52)) - .seq(createCardEffect(apothecary_restorative_draught_carddef(), currentSkillsLoc)) - .seq(createCardEffect(apothecary_custom_brew_carddef(), currentSkillsLoc)) - .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(apothecary_mezzaluna_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(apothecary_apprentice_potion_maker_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(apothecary_yellow_potion_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(apothecary_red_potion_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(apothecary_green_potion_carddef(), loc(currentPid, asidePloc))) + .seq(setPlayerNameEffect("Trickster", currentPid)) + .seq(setPlayerAvatarEffect("smugglers", currentPid)) + .seq(gainMaxHealthEffect(currentPid, const(56).add(getPlayerMaxHealth(currentPid).negate()))) + .seq(gainHealthEffect(getPlayerMaxHealth(currentPid).add(getPlayerHealth(currentPid).negate()))) + .seq(createCardEffect(trickster_reshuffle_hand_skill(), currentSkillsLoc)) + .seq(createCardEffect(trickster_stack_the_deck_ability_def(), currentSkillsLoc)) + .seq(createCardEffect(trickster_hidden_power_card_def(), loc(currentPid, asidePloc))) + .seq(createCardEffect(trickster_double_or_nothing_card_def(), loc(currentPid, asidePloc))) + .seq(createCardEffect(trickster_pet_monkey_card_def(), loc(currentPid, asidePloc))) + .seq(createCardEffect(trickster_blackmarket_the_docks_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(trickster_blackmarket_the_shambles_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(trickster_blackmarket_the_sewers_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(trickster_blackmarket_the_back_room_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(trickster_blackmarket_the_catacombs_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(ruby_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(fighter_longsword_carddef(), loc(currentPid, asidePloc))) .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) .seq(shuffleEffect(currentDeckLoc)) - .seq(sacrificeTarget().apply(selectSource())) + .seq(sacrificeTarget().apply(selectSource())) .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) .seq(shuffleEffect(currentDeckLoc)) .seq(sacrificeTarget().apply(selectSource())) @@ -487,63 +647,66 @@ Start playing." fontsize="30" flexiblewidth="1" /> tags = {} } }, - upperTitle = "Confirm your choice. Do you want to use the Apothecary?", + upperTitle = "Confirm your choice. Do you want to use the Trickster?", lowerTitle = "" })) }) }, layout = createLayout({ - name = "Apothecary", - art = "art/treasures/T_Green_Potions_Medium", + name = "Trickster", + art = "avatars/smugglers", frame = "frames/Coop_Campaign_CardFrame", xmlText=[[ - + - + - + + + + + ]] }), }) - end +end -function choose_cryomancer_carddef() +function choose_demonologist_carddef() return createDef({ - id="choose_cryomancer", - name="Choose the Cryomancer", + id="choose_demonologist", + name="Choose the Demonologist", types={noStealType, itemType}, cardTypeLabel = "Item", playLocation = castPloc, acquireCost=0, abilities = { createAbility({ - id="choose_cryomancer", + id="choose_demonologist", trigger= onPlayTrigger, playAllType = noPlayPlayType, effect = sacrificeTarget().apply(selectLoc(loc(currentPid, skillsPloc))) - .seq(moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)))) - .seq(setPlayerNameEffect("Cryomancer", currentPid)) - .seq(setPlayerAvatarEffect("rayla__endweaver_flipped", currentPid)) - .seq(gainMaxHealthEffect(currentPid, const(52).add(getPlayerMaxHealth(currentPid).negate()))) - .seq(gainHealthEffect(52)) - .seq(createCardEffect(cryomancer_frostbiteskill_carddef(), currentSkillsLoc)) - .seq(createCardEffect(cryomancer_blizzard_carddef(), currentSkillsLoc)) - .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)))) --here-- + .seq(setPlayerNameEffect("Demonologist", currentPid)) + .seq(setPlayerAvatarEffect("ruinos_zealot", currentPid)) + .seq(gainMaxHealthEffect(currentPid, const(50).add(getPlayerMaxHealth(currentPid).negate()))) + .seq(gainHealthEffect(getPlayerMaxHealth(currentPid).add(getPlayerHealth(currentPid).negate()))) + .seq(createCardEffect(demonologist_summon_demon_carddef(), currentSkillsLoc)) + .seq(createCardEffect(demonologist_summon_demon_master_carddef(), currentSkillsLoc)) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) --here-- .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(cryomancer_ice_burst_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(cryomancer_ice_shield_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(cryomancer_ice_gem_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(cryomancer_freeze_carddef(), loc(currentPid, asidePloc))) - .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) - .seq(shuffleEffect(currentDeckLoc)) - .seq(sacrificeTarget().apply(selectSource())) + .seq(createCardEffect(demonologist_shadow_feeder_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(demonologist_void_guard_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(demonologist_lesser_devourer_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(demonologist_shadow_gem_carddef(), loc(currentPid, asidePloc))) .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) .seq(shuffleEffect(currentDeckLoc)) .seq(sacrificeTarget().apply(selectSource())) @@ -558,7 +721,7 @@ function choose_cryomancer_carddef() layout = createLayout( { name = "Yes", - art = "art/treasures/T_cleric_elixir_green", + art = "art/treasures/t_cleric_elixir_green", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -579,7 +742,7 @@ Start playing." fontsize="30" flexiblewidth="1" /> layout = createLayout( { name = "No", - art = "art/treasures/T_cleric_elixir_blue_purple", + art = "art/treasures/t_cleric_elixir_blue_purple", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -595,59 +758,291 @@ Start playing." fontsize="30" flexiblewidth="1" /> tags = {} } }, - upperTitle = "Confirm your choice. Do you want to use the Cryomancer?", + upperTitle = "Confirm your choice. Do you want to use the Demonologist?", lowerTitle = "" })) }) }, layout = createLayout({ - name = "Cryomancer", - art = "art/T_Heavy_Gust", - frame = "frames/Coop_Campaign_CardFrame", + name = "Demonologist", + art = "art/epicart/dirge_of_scara", + frame = "frames/coop_campaign_cardframe", xmlText=[[ - + - + - + + + + + + ]] }), }) end -function choose_pyromancer_carddef() +function choose_apothecary_carddef() return createDef({ - id="choose_pyromancer", - name="Choose the Pryomancer", + id="choose_apothecary", + name="Choose the Apothecary", types={noStealType, itemType}, cardTypeLabel = "Item", playLocation = castPloc, acquireCost=0, abilities = { createAbility({ - id="choose_pyromancer", + id="choose_apothecary", trigger= onPlayTrigger, playAllType = noPlayPlayType, effect = sacrificeTarget().apply(selectLoc(loc(currentPid, skillsPloc))) .seq(moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)))) - .seq(setPlayerNameEffect("Pyromancer", currentPid)) - .seq(setPlayerAvatarEffect("summoner", currentPid)) - .seq(gainMaxHealthEffect(currentPid, const(48).add(getPlayerMaxHealth(currentPid).negate()))) - .seq(gainHealthEffect(48)) - .seq(createCardEffect(pyromancer_fuel_1carddef(), currentSkillsLoc)) - .seq(createCardEffect(pyromancer_conflagration_1p_carddef(), currentSkillsLoc)) + .seq(setPlayerNameEffect("Apothecary", currentPid)) + .seq(setPlayerAvatarEffect("alchemist_01", currentPid)) + .seq(gainMaxHealthEffect(currentPid, const(52).add(getPlayerMaxHealth(currentPid).negate()))) + .seq(gainHealthEffect(getPlayerMaxHealth(currentPid).add(getPlayerHealth(currentPid).negate()))) + .seq(createCardEffect(apothecary_restorative_draught_carddef(), currentSkillsLoc)) + .seq(createCardEffect(apothecary_custom_brew_carddef(), currentSkillsLoc)) .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(ruby_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(pyromancer_sear_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(pyromancer_scorch_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(pyromancer_fire_shard_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(apothecary_mezzaluna_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(apothecary_apprentice_potion_maker_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(apothecary_yellow_potion_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(apothecary_red_potion_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(apothecary_green_potion_carddef(), loc(currentPid, asidePloc))) + .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) + .seq(shuffleEffect(currentDeckLoc)) + .seq(sacrificeTarget().apply(selectSource())) + .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) + .seq(shuffleEffect(currentDeckLoc)) + .seq(sacrificeTarget().apply(selectSource())) + .seq(pushChoiceEffectWithTitle( + { + choices = { + { + effect = moveTarget(tradeDeckLoc).apply(selectLoc(centerRowLoc)) + .seq(shuffleTradeDeckEffect()) + .seq(refillMarketEffect(const(0)).seq(refillMarketEffect(const(1))).seq(refillMarketEffect(const(2))).seq(refillMarketEffect(const(3))).seq(refillMarketEffect(const(4)))) + .seq(addEffect(endTurnEffect())), + layout = createLayout( + { + name = "Yes", + art = "art/treasures/t_cleric_elixir_green", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText=[[ + + + + + + ]], + + } + ), + tags = {} + }, + + { + effect = nullEffect(), + layout = createLayout( + { + name = "No", + art = "art/treasures/t_cleric_elixir_blue_purple", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText=[[ + + + + + + ]], + + } + ), + tags = {} + } + }, + upperTitle = "Confirm your choice. Do you want to use the Apothecary?", + lowerTitle = "" + })) + }) + }, + layout = createLayout({ + name = "Apothecary", + art = "art/treasures/t_green_potions_medium", + frame = "frames/coop_campaign_cardframe", + xmlText=[[ + + + + + + + + + ]] + }), + }) + end + +function choose_cryomancer_carddef() + return createDef({ + id="choose_cryomancer", + name="Choose the Cryomancer", + types={noStealType, itemType}, + cardTypeLabel = "Item", + playLocation = castPloc, + acquireCost=0, + abilities = { + createAbility({ + id="choose_cryomancer", + trigger= onPlayTrigger, + playAllType = noPlayPlayType, + effect = sacrificeTarget().apply(selectLoc(loc(currentPid, skillsPloc))) + .seq(moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)))) + .seq(setPlayerNameEffect("Cryomancer", currentPid)) + .seq(setPlayerAvatarEffect("rayla__endweaver_flipped", currentPid)) + .seq(gainMaxHealthEffect(currentPid, const(52).add(getPlayerMaxHealth(currentPid).negate()))) + .seq(gainHealthEffect(getPlayerMaxHealth(currentPid).add(getPlayerHealth(currentPid).negate()))) + .seq(createCardEffect(cryomancer_frostbiteskill_carddef(), currentSkillsLoc)) + .seq(createCardEffect(cryomancer_freezing_fog_carddef(), currentSkillsLoc)) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(cryomancer_ice_sickle_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(cryomancer_frostwulf_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(cryomancer_ice_gem_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(cryomancer_freeze_carddef(), loc(currentPid, asidePloc))) + .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) + .seq(shuffleEffect(currentDeckLoc)) + .seq(sacrificeTarget().apply(selectSource())) + .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) + .seq(shuffleEffect(currentDeckLoc)) + .seq(sacrificeTarget().apply(selectSource())) + .seq(pushChoiceEffectWithTitle( + { + choices = { + { + effect = moveTarget(tradeDeckLoc).apply(selectLoc(centerRowLoc)) + .seq(shuffleTradeDeckEffect()) + .seq(refillMarketEffect(const(0)).seq(refillMarketEffect(const(1))).seq(refillMarketEffect(const(2))).seq(refillMarketEffect(const(3))).seq(refillMarketEffect(const(4)))) + .seq(addEffect(endTurnEffect())), + layout = createLayout( + { + name = "Yes", + art = "art/treasures/t_cleric_elixir_green", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText=[[ + + + + + + ]], + + } + ), + tags = {} + }, + + { + effect = nullEffect(), + layout = createLayout( + { + name = "No", + art = "art/treasures/t_cleric_elixir_blue_purple", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText=[[ + + + + + + ]], + + } + ), + tags = {} + } + }, + upperTitle = "Confirm your choice. Do you want to use the Cryomancer?", + lowerTitle = "" + })) + }) + }, + layout = createLayout({ + name = "Cryomancer", + art = "art/t_heavy_gust", + frame = "frames/coop_campaign_cardframe", + xmlText=[[ + + + + + + + + + + + + + ]] + }), + }) + end + +function choose_pyromancer_carddef() + return createDef({ + id="choose_pyromancer", + name="Choose the Pryomancer", + types={noStealType, itemType}, + cardTypeLabel = "Item", + playLocation = castPloc, + acquireCost=0, + abilities = { + createAbility({ + id="choose_pyromancer", + trigger= onPlayTrigger, + playAllType = noPlayPlayType, + effect = sacrificeTarget().apply(selectLoc(loc(currentPid, skillsPloc))) + .seq(moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)))) + .seq(setPlayerNameEffect("Pyromancer", currentPid)) + .seq(setPlayerAvatarEffect("summoner", currentPid)) + .seq(gainMaxHealthEffect(currentPid, const(48).add(getPlayerMaxHealth(currentPid).negate()))) + .seq(gainHealthEffect(getPlayerMaxHealth(currentPid).add(getPlayerHealth(currentPid).negate()))) + .seq(ifElseEffect(getTurnsPlayed(oppPid).eq(1), createCardEffect(pyromancer_1p_fuel_1carddef(), currentSkillsLoc), createCardEffect(pyromancer_2p_fuel_1carddef(), currentSkillsLoc))) + .seq(ifElseEffect(getTurnsPlayed(oppPid).eq(1), createCardEffect(pyromancer_conflagration_1p_carddef(), currentSkillsLoc), createCardEffect(pyromancer_conflagration_2p_carddef(), currentSkillsLoc))) + .seq(ifElseEffect(getTurnsPlayed(oppPid).eq(1), incrementCounterEffect("fuel_1p", 1), incrementCounterEffect("fuel_2p", 1))) + .seq(ifElseEffect(getTurnsPlayed(oppPid).eq(1), incrementCounterEffect("conflagration_1p", 5), incrementCounterEffect("conflagration_2p", 5))) + --.seq(addSlotToPlayerEffect(currentPlayer(), createPlayerSlot({ key = "pyroSkill", expiry = { neverExpiry } }))) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(ruby_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(pyromancer_sear_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(pyromancer_scorch_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(pyromancer_fire_shard_carddef(), loc(currentPid, asidePloc))) .seq(createCardEffect(pyromancer_combust_carddef(), loc(currentPid, asidePloc))) .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) .seq(shuffleEffect(currentDeckLoc)) @@ -666,7 +1061,7 @@ function choose_pyromancer_carddef() layout = createLayout( { name = "Yes", - art = "art/treasures/T_cleric_elixir_green", + art = "art/treasures/t_cleric_elixir_green", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -687,7 +1082,7 @@ Start playing." fontsize="30" flexiblewidth="1" /> layout = createLayout( { name = "No", - art = "art/treasures/T_cleric_elixir_blue_purple", + art = "art/treasures/t_cleric_elixir_blue_purple", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -704,25 +1099,26 @@ Start playing." fontsize="30" flexiblewidth="1" /> } }, upperTitle = "Confirm your choice. Do you want to use the Pyromancer?", - lowerTitle = "Note: Skills and Abilities will not work properly if both players have chosen Pyromancer." })) }) }, layout = createLayout({ name = "Pyromancer", - art = "art/T_Blistering_Blaze", - frame = "frames/Coop_Campaign_CardFrame", + art = "art/t_blistering_blaze", + frame = "frames/coop_campaign_cardframe", xmlText=[[ - + - + - + ]] @@ -748,8 +1144,8 @@ function choose_terramancer_carddef() .seq(setPlayerNameEffect("Terramancer", currentPid)) .seq(setPlayerAvatarEffect("broelyn", currentPid)) .seq(gainMaxHealthEffect(currentPid, const(53).add(getPlayerMaxHealth(currentPid).negate()))) - .seq(gainHealthEffect(53)) - .seq(createCardEffect(terramancer_move_earth_carddef(), currentSkillsLoc)) + .seq(gainHealthEffect(getPlayerMaxHealth(currentPid).add(getPlayerHealth(currentPid).negate()))) + .seq(ifElseEffect(getTurnsPlayed(oppPid).eq(1), createCardEffect(terramancer_move_earth1_carddef(), currentSkillsLoc), createCardEffect(terramancer_move_earth2_carddef(), currentSkillsLoc))) .seq(createCardEffect(terramancer_swallowed_by_the_earth_def(), currentSkillsLoc)) .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) @@ -760,7 +1156,7 @@ function choose_terramancer_carddef() .seq(createCardEffect(terramancer_earth_gem_carddef(), loc(currentPid, asidePloc))) .seq(createCardEffect(terramancer_hurl_boulder_carddef(), loc(currentPid, asidePloc))) .seq(createCardEffect(terramancer_tremor_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(terramancer_clay_golem_carddef(), loc(currentPid, asidePloc))) + .seq(ifElseEffect(getTurnsPlayed(oppPid).eq(1), createCardEffect(terramancer_clay_golem1_carddef(), loc(currentPid, asidePloc)), createCardEffect(terramancer_clay_golem2_carddef(), loc(currentPid, asidePloc)))) .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) .seq(shuffleEffect(currentDeckLoc)) .seq(sacrificeTarget().apply(selectSource())) @@ -778,7 +1174,7 @@ function choose_terramancer_carddef() layout = createLayout( { name = "Yes", - art = "art/treasures/T_cleric_elixir_green", + art = "art/treasures/t_cleric_elixir_green", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -799,7 +1195,7 @@ Start playing." fontsize="30" flexiblewidth="1" /> layout = createLayout( { name = "No", - art = "art/treasures/T_cleric_elixir_blue_purple", + art = "art/treasures/t_cleric_elixir_blue_purple", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -816,14 +1212,14 @@ Start playing." fontsize="30" flexiblewidth="1" /> } }, upperTitle = "Confirm your choice. Do you want to use the Terramancer?", - lowerTitle = "Note: Skills and Abilities will not work properly if both players have chosen Terramancer." + })) }) }, layout = createLayout({ name = "Terramancer", - art = "art/T_Broelyn_Loreweaver_Old", - frame = "frames/Coop_Campaign_CardFrame", + art = "art/t_broelyn_loreweaver_old", + frame = "frames/coop_campaign_cardframe", xmlText=[[ @@ -834,10 +1230,9 @@ Terramancer" fontsize="26"/> + - + @@ -864,7 +1259,7 @@ function choose_thandarlorian_carddef() .seq(setPlayerNameEffect("Thandarlorian", currentPid)) .seq(setPlayerAvatarEffect("cristov__the_just", currentPid)) .seq(gainMaxHealthEffect(currentPid, const(36).add(getPlayerMaxHealth(currentPid).negate()))) - .seq(gainHealthEffect(const(36).add(getPlayerMaxHealth(currentPid).negate()))) + .seq(gainHealthEffect(getPlayerMaxHealth(currentPid).add(getPlayerHealth(currentPid).negate()))) .seq(createCardEffect(thandarlorian_dragon_shard_armour_carddef(), currentSkillsLoc)) .seq(createCardEffect(thandarlorian_bracer_blades_carddef(), currentSkillsLoc)) .seq(createCardEffect(thandarlorian_whipcord_carddef(), currentSkillsLoc)) @@ -895,7 +1290,7 @@ function choose_thandarlorian_carddef() layout = createLayout( { name = "Yes", - art = "art/treasures/T_cleric_elixir_green", + art = "art/treasures/t_cleric_elixir_green", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -916,7 +1311,7 @@ Start playing." fontsize="30" flexiblewidth="1" /> layout = createLayout( { name = "No", - art = "art/treasures/T_cleric_elixir_blue_purple", + art = "art/treasures/t_cleric_elixir_blue_purple", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -939,15 +1334,21 @@ Start playing." fontsize="30" flexiblewidth="1" /> }, layout = createLayout({ name = "The Thandarlorian", - art = "art/T_Domination", - frame = "frames/Coop_Campaign_CardFrame", + art = "art/t_domination", + frame = "frames/coop_campaign_cardframe", xmlText=[[ - + - + + + + + ]] @@ -973,7 +1374,7 @@ function choose_paladin_carddef() .seq(setPlayerNameEffect("Paladin", currentPid)) .seq(setPlayerAvatarEffect("cristov__the_just", currentPid)) .seq(gainMaxHealthEffect(currentPid, const(58).add(getPlayerMaxHealth(currentPid).negate()))) - .seq(gainHealthEffect(58)) + .seq(gainHealthEffect(getPlayerMaxHealth(currentPid).add(getPlayerHealth(currentPid).negate()))) .seq(createCardEffect(paladin_prayer_carddef(), currentSkillsLoc)) .seq(createCardEffect(paladin_sacred_oath_carddef(), currentSkillsLoc)) .seq(createCardEffect(gold_carddef(),loc(currentPid, asidePloc))) @@ -1003,7 +1404,7 @@ function choose_paladin_carddef() layout = createLayout( { name = "Yes", - art = "art/treasures/T_cleric_elixir_green", + art = "art/treasures/t_cleric_elixir_green", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -1024,7 +1425,7 @@ Start playing." fontsize="30" flexiblewidth="1" /> layout = createLayout( { name = "No", - art = "art/treasures/T_cleric_elixir_blue_purple", + art = "art/treasures/t_cleric_elixir_blue_purple", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -1047,8 +1448,8 @@ Start playing." fontsize="30" flexiblewidth="1" /> }, layout = createLayout({ name = "Paladin", - art = "art/T_Cristov_The_Just", - frame = "frames/Coop_Campaign_CardFrame", + art = "art/t_cristov_the_just", + frame = "frames/coop_campaign_cardframe", xmlText=[[ @@ -1081,7 +1482,7 @@ function choose_witch_carddef() .seq(setPlayerNameEffect("Witch", currentPid)) .seq(setPlayerAvatarEffect("chanting_cultist", currentPid)) .seq(gainMaxHealthEffect(currentPid, const(51).add(getPlayerMaxHealth(currentPid).negate()))) - .seq(gainHealthEffect(51)) + .seq(gainHealthEffect(getPlayerMaxHealth(currentPid).add(getPlayerHealth(currentPid).negate()))) .seq(createCardEffect(siphon_life_def(), currentSkillsLoc)) .seq(createCardEffect(piercing_screech_def(), currentSkillsLoc)) .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) @@ -1111,7 +1512,7 @@ function choose_witch_carddef() layout = createLayout( { name = "Yes", - art = "art/treasures/T_cleric_elixir_green", + art = "art/treasures/t_cleric_elixir_green", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -1132,7 +1533,7 @@ Start playing." fontsize="30" flexiblewidth="1" /> layout = createLayout( { name = "No", - art = "art/treasures/T_cleric_elixir_blue_purple", + art = "art/treasures/t_cleric_elixir_blue_purple", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -1156,7 +1557,7 @@ Start playing." fontsize="30" flexiblewidth="1" /> layout = createLayout({ name = "Witch", art = "avatars/chanting_cultist", - frame = "frames/Coop_Campaign_CardFrame", + frame = "frames/coop_campaign_cardframe", xmlText=[[ @@ -1189,7 +1590,7 @@ function choose_shaman_carddef() .seq(setPlayerNameEffect("Shaman", currentPid)) .seq(setPlayerAvatarEffect("wolf_shaman", currentPid)) .seq(gainMaxHealthEffect(currentPid, const(50).add(getPlayerMaxHealth(currentPid).negate()))) - .seq(gainHealthEffect(const(50).add(getPlayerMaxHealth(currentPid).negate()))) + .seq(gainHealthEffect(getPlayerMaxHealth(currentPid).add(getPlayerHealth(currentPid).negate()))) .seq(createCardEffect(gift_of_the_elements_carddef(), currentSkillsLoc)) .seq(createCardEffect(elemental_surge_def(), currentSkillsLoc)) .seq(createCardEffect(gold_carddef(), loc(currentPid, asidePloc))) @@ -1219,7 +1620,7 @@ function choose_shaman_carddef() layout = createLayout( { name = "Yes", - art = "art/treasures/T_cleric_elixir_green", + art = "art/treasures/t_cleric_elixir_green", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -1240,7 +1641,7 @@ Start playing." fontsize="30" flexiblewidth="1" /> layout = createLayout( { name = "No", - art = "art/treasures/T_cleric_elixir_blue_purple", + art = "art/treasures/t_cleric_elixir_blue_purple", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -1264,7 +1665,7 @@ Start playing." fontsize="30" flexiblewidth="1" /> layout = createLayout({ name = "Shaman", art = "avatars/wolf_shaman", - frame = "frames/Coop_Campaign_CardFrame", + frame = "frames/coop_campaign_cardframe", xmlText=[[ @@ -1298,7 +1699,7 @@ function choose_king_midas_carddef() .seq(setPlayerNameEffect("King Midas", currentPid)) .seq(setPlayerAvatarEffect("profit", currentPid)) .seq(gainMaxHealthEffect(currentPid, const(42).add(getPlayerMaxHealth(currentPid).negate()))) - .seq(gainHealthEffect(42)) + .seq(gainHealthEffect(getPlayerMaxHealth(currentPid).add(getPlayerHealth(currentPid).negate()))) .seq(createCardEffect(greed_is_good_skilldef(), currentSkillsLoc)) .seq(createCardEffect(golden_touch_abilitydef(), currentSkillsLoc)) .seq(createCardEffect(midas_kings_adviser_carddef(), loc(currentPid, asidePloc))) @@ -1328,7 +1729,7 @@ function choose_king_midas_carddef() layout = createLayout( { name = "Yes", - art = "art/treasures/T_cleric_elixir_green", + art = "art/treasures/t_cleric_elixir_green", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -1349,7 +1750,7 @@ Start playing." fontsize="30" flexiblewidth="1" /> layout = createLayout( { name = "No", - art = "art/treasures/T_cleric_elixir_blue_purple", + art = "art/treasures/t_cleric_elixir_blue_purple", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -1372,8 +1773,8 @@ Start playing." fontsize="30" flexiblewidth="1" /> }, layout = createLayout({ name = "King Midas", - art = "art/T_Bribe", - frame = "frames/Coop_Campaign_CardFrame", + art = "art/t_bribe", + frame = "frames/coop_campaign_cardframe", xmlText=[[ @@ -1405,9 +1806,9 @@ function choose_brewmaster_carddef() effect = sacrificeTarget().apply(selectLoc(loc(currentPid, skillsPloc))) .seq(moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)))) .seq(setPlayerNameEffect("Brewmaster", currentPid)) - .seq(setPlayerAvatarEffect("lord_callum", currentPid)) + .seq(setPlayerAvatarEffect("journeys/broken_tables_and_chairs", currentPid)) .seq(gainMaxHealthEffect(currentPid, const(55).add(getPlayerMaxHealth(currentPid).negate()))) - .seq(gainHealthEffect(55)) + .seq(gainHealthEffect(getPlayerMaxHealth(currentPid).add(getPlayerHealth(currentPid).negate()))) .seq(createCardEffect(brewmaster_a_round_on_the_house_def(), currentSkillsLoc)) .seq(createCardEffect(brewmaster_get_out_of_my_bar_carddef(), currentSkillsLoc)) .seq(createCardEffect(brewmaster_mead_carddef(), loc(currentPid, asidePloc))) @@ -1437,7 +1838,7 @@ function choose_brewmaster_carddef() layout = createLayout( { name = "Yes", - art = "art/treasures/T_cleric_elixir_green", + art = "art/treasures/t_cleric_elixir_green", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -1458,7 +1859,7 @@ Start playing." fontsize="30" flexiblewidth="1" /> layout = createLayout( { name = "No", - art = "art/treasures/T_cleric_elixir_blue_purple", + art = "art/treasures/t_cleric_elixir_blue_purple", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -1469,154 +1870,1821 @@ Start playing." fontsize="30" flexiblewidth="1" /> ]], - } - ), - tags = {} - } - }, - upperTitle = "Confirm your choice. Do you want to use the Brewmaster?", - lowerTitle = "" - })) + } + ), + tags = {} + } + }, + upperTitle = "Confirm your choice. Do you want to use the Brewmaster?", + lowerTitle = "" + })) + }) + }, + layout = createLayout({ + name = "Brewmaster", + art = "avatars/journeys/broken_tables_and_chairs", + frame = "frames/coop_campaign_cardframe", + xmlText=[[ + + + + + + + + + + + + + ]] + }), + }) + end + +function choose_S_and_R_carddef() + return createDef({ + id="choose_S&R", + name="Choose Sparks & Rec Enjoyer", + types={noStealType, itemType}, + cardTypeLabel = "Item", + playLocation = castPloc, + acquireCost=0, + abilities = { + createAbility({ + id="choose_S&R", + trigger= onPlayTrigger, + playAllType = noPlayPlayType, + effect = sacrificeTarget().apply(selectLoc(loc(currentPid, skillsPloc))) + .seq(moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)))) + .seq(setPlayerNameEffect("Sparks & Rec Enjoyer", currentPid)) + .seq(setPlayerAvatarEffect("ambushers", currentPid)) + .seq(gainMaxHealthEffect(currentPid, const(54).add(getPlayerMaxHealth(currentPid).negate()))) + .seq(gainHealthEffect(getPlayerMaxHealth(currentPid).add(getPlayerHealth(currentPid).negate()))) + .seq(createCardEffect(bird_dog_def(), currentSkillsLoc)) + .seq(createCardEffect(patron_shoutout_def(), currentSkillsLoc)) + .seq(createCardEffect(situational_card_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(situational_card_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(situational_card_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(situational_card_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(situational_card_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(hero_dash_helper_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(wwyd_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(nostra_dbl_damus_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(blank_to_my_blank_carddef(), loc(currentPid, asidePloc))) + .seq(createCardEffect(congrats_youre_a_nerd_carddef(), loc(currentPid, asidePloc))) + .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) + .seq(shuffleEffect(currentDeckLoc)) + .seq(sacrificeTarget().apply(selectSource())) + .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) + .seq(shuffleEffect(currentDeckLoc)) + .seq(sacrificeTarget().apply(selectSource())) + .seq(pushChoiceEffectWithTitle( + { + choices = { + { + effect = moveTarget(tradeDeckLoc).apply(selectLoc(centerRowLoc)) + .seq(shuffleTradeDeckEffect()) + .seq(refillMarketEffect(const(0)).seq(refillMarketEffect(const(1))).seq(refillMarketEffect(const(2))).seq(refillMarketEffect(const(3))).seq(refillMarketEffect(const(4)))) + .seq(addEffect(endTurnEffect())), + layout = createLayout( + { + name = "Yes", + art = "art/treasures/t_cleric_elixir_green", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText=[[ + + + + + + ]], + + } + ), + tags = {} + }, + + { + effect = nullEffect(), + layout = createLayout( + { + name = "No", + art = "art/treasures/t_cleric_elixir_blue_purple", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText=[[ + + + + + + ]], + + } + ), + tags = {} + } + }, + upperTitle = "Confirm your choice. Do you want to use the Sparks & Recreations Enjoyer?", + lowerTitle = "" + })) + }) + }, + layout = createLayout({ + name = "S&R Enjoyer", + art = "avatars/ambushers", + frame = "frames/coop_campaign_cardframe", + xmlText=[[ + + + + + + + + + + + + + ]] + }), + }) + end + + +--Lich cards +function lich_corruption_carddef() + local cardLayout = createLayout({ + name = "Corruption", + art = "art/epicart/necromancer_apprentice", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText = + [[ + + + + + + + + + + ]] + }) + + return createSkillDef({ + id = "corruption_skill", + name = "Corruption", + types = { skillType }, + layout = cardLayout, + layoutPath = "art/epicart/necromancer_apprentice", + abilities = { + createAbility({ + id="corruption20buff_combat", + trigger = uiTrigger, + activations = singleActivation, + layout = cardLayout, + promptType = showPrompt, + effect = gainCombatEffect(1) + .seq(randomTarget(const(1), moveTarget(inPlayPloc)).apply(selectLoc(loc(nil, nullPloc)))) + .seq(noUndoEffect()), + check = selectLoc(loc(nil, nullPloc)).count().gte(1), + cost = expendCost, + }), + + } + + }) +end + +function lich_control_carddef() + local cardLayout = createLayout({ + name = "Control", + art = "art/epicart/necromancer_lord", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText = + [[ + + + + + + + + + + ]] + }) + + return createSkillDef({ + id = "control_skill", + name = "Coontrol", + types = { skillType }, + layout = cardLayout, + layoutPath = "art/epicart/necromancer_lord", + abilities = { + createAbility({ + id="control30buff_combat", + trigger = uiTrigger, + activations = singleActivation, + layout = cardLayout, + promptType = showPrompt, + effect = gainCombatEffect(2) + .seq(randomTarget(const(2), moveTarget(inPlayPloc)).apply(selectLoc(loc(nil, nullPloc)))) + .seq(noUndoEffect()), + check = selectLoc(loc(nil, nullPloc)).count().gte(2), + cost = expendCost, + }), + + } + + }) +end + + +function lich_soul_diamond_carddef() + return createItemDef({ + id = "lich_soul_diamond", + name = "Lich Soul Diamond", + playLocation = castPloc, + acquireCost = 0, + isGuard = false, + types = { itemType, currencyType, gemType, nostealType }, + layout = createLayout({ + name = "Soul Diamond", + art = "art/classes/necromancer/soul_cage", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + cost = 0, + xmlText= + [[ + + + + + + + + + + + + + + + + + + + + ]] + }), + abilities = { + createAbility({ + id = "lich_soul_diamond_item_main", + trigger = autoTrigger, + cost = noCost, + effect = ifElseEffect(isPlayerAi(oppPid),gainGoldEffect(3),gainGoldEffect(1)), + }), + + createAbility({ + id = "lich_soul_diamond_item_stun", + trigger = uiTrigger, + + activations = singleActivation, + effect = pushChoiceEffectWithTitle( + { + + choices = + { + { + effect = stunTarget().apply(selectLoc(loc(oppPid, inPlayPloc))), + check = selectLoc(loc(oppPid, inPlayPloc)).where(isCardStunnable()), + layout = createLayout({ + name = "Soul Diamond", + art = "art/treasures/necromancer/soul_cage", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + cost = 0, + xmlText= + [[ + + + + + + + + + + + ]], + }), + }, + + }, + + } + ), + cost = sacrificeSelfCost, + + }), + }, + + layout = createLayout( + { + name = "Soul Diamond", + art = "art/treasures/necromancer/soul_cage", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText = + [[ + + + + + + + + + + + + + + + + + + ]] + } + ), + layoutPath = "art/t_gold", + }) +end + +function lich_frozen_touch_carddef() + return createActionDef({ + id = "lich_frozen_touch", + name = "Frozen Touch", + types = {actionType, nostealType}, + cardTypeLabel = "Action", + playLocation = castPloc, + acquireCost = 0, + abilities = { + createAbility( + { + id = "frozen_touch_main", + trigger = autoTrigger, + cost = noCost, + activations = singleActivations, + effect = gainCombatEffect(1), + } + ) + }, + layout = createLayout( + { + name = "Frozen Touch", + art = "art/epicart/zannos__corpse_lord", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + cost = 0, + xmlText=[[ + + + + + + + + ]], + + } + ) + } + ) +end + + +function lich_soul_crush_carddef() + return createActionDef({ + id = "lich_soul_crush_curse", + name = "Lich Soul Crush Curse", + acquireCost = 0, + + types = { actionType, curseType, nostealType }, + layout = createLayout({ + name = "Soul Crush", + art = "art/epicart/consume", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + cost = 0, + xmlText= + [[ + + + + + + + + + ]] + }), + abilities = { + createAbility({ + id = "lich_soul_crush_curse_main", + trigger = autoTrigger, + cost = noCost, + effect = pushChoiceEffect + ( + { + choices = + { + + -- Gain 3 combat + { + effect = gainCombatEffect(3), + layout = layoutCard( + { + title = "Soul Crush", + art = "art/epicart/scara_s_will", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText = + [[ + + + + + + + + ]] + } + ), + }, + -- Stun an opposing champion + { + effect = pushTargetedEffect({ + desc = "Stun target champion.", + validTargets = oppStunnableSelector(), + min = 0, + max = 1, + targetEffect = stunTarget(), + }), + condition = selectLoc(loc(oppPid, inPlayPloc)).where(isCardChampion()).count().gte(1), + layout = layoutCard( + { + title = "Soul Crush", + art = "art/epicart/scara_s_will", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText = + [[ + + + + + + + + + ]] + } + ), + }, + + } + } + ) + }) + + }, + }) +end + +function lich_minor_summoning_carddef() + return createActionDef({ + id = "lich_minor_summoning_curse", + name = "Minor Summoning", + playLocation = castPloc, + acquireCost = 0, + types = { actionType, curseType, nostealType }, + layout = createLayout({ + name = "Minor Summoning", + art = "art/epicart_necrovirus", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + cost = 0, + xmlText= + [[ + + + + + + + + + ]] + }), + abilities = { + createAbility({ + id = "lich_minor_summoning", + trigger = autoTrigger, + cost = expendCost, + effect = createCardEffect(necromancer_skeleton_warrior_carddef(), currentInPlayLoc), + }), + }, + }) +end + +function lich_major_summoning_carddef() + return createActionDef({ + id = "lich_major_summoning", + name = "Major Summoning", + playLocation = castPloc, + acquireCost = 0, + types = { actionType, curseType, nostealType }, + layout = createLayout({ + name = "Major Summoning", + art = "art/epicart_abyss_summoner", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + cost = 0, + xmlText= + [[ + + + + + + + + + ]] + }), + abilities = { + createAbility({ + id = "lich_major_summoning", + trigger = autoTrigger, + cost = expendCost, + effect = randomTarget(const(1), moveTarget(inPlayPloc)).apply(selectLoc(loc(nil, nullPloc))).seq(noUndoEffect()), + check = selectLoc(loc(nil, nullPloc)).count().gte(1), + }), + }, + }) +end + +--Lich minions +function lich_abomination_minion_carddef() + return createChampionDef({ + id = "lich_abomination_minion", + name = "Lich Abomination Minion", + acquireCost = 0, + health = 6, + isGuard = true, + types = { minionType, undeadType, constructType }, + layout = createLayout({ + name = "Abomination", + art = "art/epicart/scrap_golem_token", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + cost = 0, + health = 6, + isGuard = true, + xmlText= + [[ + + + + + + + + + + + + + + + + ]] + }), + abilities = { + createAbility({ + id = "lich_abomination_minion_main", + trigger = autoTrigger, + cost = expendCost, + activations = multipleActivations, + effect = gainCombatEffect(4), + }), + + createAbility({ + id = "lich_abomination_minion_sac", + trigger = onLeavePlayTrigger, + cost = noCost, + activations = singleActivations, + effect = moveTarget(nullPloc).apply(selectSource()) + }) + }, + }) +end + +function lich_banshee_minion_carddef() + return createChampionDef({ + id = "lich_banshee_minion", + name = "Lich Banshee Minion", + acquireCost = 0, + health = 3, + isGuard = false, + types = { minionType, undeadType }, + layout = createLayout({ + name = "Banshee", + art = "art/t_banshee", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + cost = 0, + health = 3, + isGuard = false, + xmlText= + [[ + + + + + + + + + + + + + + + + + + ]] + }), + abilities = { + createAbility({ + id = "lich_banshee_minion_main", + trigger = autoTrigger, + cost = expendCost, + activations = multipleActivations, + effect = hitOpponentEffect(2).seq(damageTarget(2).apply(selectLoc(loc(oppPid, inPlayPloc)))) + }), + + createAbility({ + id = "lich_banshee_minion_sac", + trigger = onLeavePlayTrigger, + cost = noCost, + activations = singleActivations, + effect = moveTarget(nullPloc).apply(selectSource()) + }) + }, + }) +end + +function lich_ghoul_minion_carddef() + return createChampionDef({ + id = "lich_ghoul_minion", + name = "Lich Ghoul Minion", + acquireCost = 0, + health = 5, + isGuard = true, + types = { minionType, undeadType }, + layout = createLayout({ + name = "Ghoul", + art = "art/epicart/carrion_demon", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + cost = 0, + health = 5, + isGuard = true, + xmlText= + [[ + + + + + + + + ]] + }), + abilities = { + createAbility({ + id = "lich_ghoul_minion_main", + trigger = uiTrigger, + promptType = showPrompt, + layout = layoutCard( + { + name = "Ghoul", + art = "art/epicart/carrion_demon", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText=[[ + + + + + + + + ]] + } + ), + cost = expendCost, + activations = multipleActivations, + effect = gainCombatEffect(3).seq(gainCombatEffect(selectLoc(currentDiscardLoc).where(isCardChampion()).count())), + --ifElseEffect(selectLoc(currentInPlayLoc).where(isCardType(demonType)).count().gte(1),gainCombatEffect(3),gainCombatEffect(1)) + }), + + createAbility({ + id = "lich_ghoul_minion_sac", + trigger = onLeavePlayTrigger, + cost = noCost, + activations = singleActivations, + effect = moveTarget(nullPloc).apply(selectSource()) + }) + }, + }) +end + +function lich_revenant_minion_carddef() + return createChampionDef({ + id = "lich_revenant_minion", + name = "Lich Revenant Minion", + acquireCost = 0, + health = 5, + isGuard = false, + types = { minionType, undeadType }, + layout = createLayout({ + name = "Revenant", + art = "art/epicart/scara_s_will", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + cost = 0, + health = 5, + isGuard = false, + xmlText= + [[ + + + + + + + + + + ]] + }), + abilities = { + createAbility({ + id = "lich_revenant_minion_main", + trigger = uiTrigger, + cost = expendCost, + activations = multipleActivations, + effect = pushChoiceEffect + ( + { + choices = + { + + -- Gain 5 health + { + effect = gainHealthEffect(5), + layout = layoutCard( + { + title = "Revenant", + art = "art/epicart/scara_s_will", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText = + [[ + + + + + + + + ]] + } + ), + }, + -- Stun an opposing champion + { + effect = pushTargetedEffect({ + desc = "Stun an opposing champion.", + validTargets = oppStunnableSelector(), + min = 0, + max = 1, + targetEffect = stunTarget(), + }), + condition = selectLoc(loc(oppPid, inPlayPloc)).where(isCardChampion()).count().gte(1), + layout = layoutCard( + { + title = "Revenant", + art = "art/epicart/scara_s_will", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText = + [[ + + + + + + + + ]] + } + ), + }, + + } + } + ) + }), + + createAbility({ + id = "lich_revenant_minion_sac", + trigger = onLeavePlayTrigger, + cost = noCost, + activations = singleActivations, + effect = moveTarget(nullPloc).apply(selectSource()) + }) + }, + }) +end + +function lich_skeleton_horde_minion_carddef() + return createChampionDef({ + id = "lich_skeleton_horde_minion", + name = "Lich Skeleton Horde Minion", + acquireCost = 0, + health = 4, + isGuard = false, + types = { minionType, undeadType }, + layout = createLayout({ + name = "Skeleton Horde", + art = "art/t_angry_skeleton", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + cost = 0, + health = 4, + isGuard = false, + xmlText= + [[ + + + + + + + + + + ]] + }), + abilities = { + createAbility({ + id = "lich_skeleton_horde_minion_main", + trigger = autoTrigger, + cost = expendCost, + activations = multipleActivations, + effect = gainCombatEffect(3), + }), + + createAbility({ + id = "lich_skeleton_horde_minion_sac", + trigger = onLeavePlayTrigger, + cost = noCost, + activations = singleActivations, + effect = moveTarget(nullPloc).apply(selectSource()) + }) + }, + }) +end + +function lich_wall_of_bones_minion_carddef() + return createChampionDef({ + id = "lich_wall_of_bones_minion", + name = "Lich Wall of Bones Minion", + acquireCost = 0, + health = 8, + isGuard = true, + types = { minionType, undeadType, constructType }, + layout = createLayout({ + name = "Wall of Bones", + art = "art/t_skeleton", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + cost = 0, + health = 8, + isGuard = true, + xmlText= + [[ + + + + + + + + ]] + }), + abilities = { + createAbility({ + id = "lich_wall_of_bones_minion_main", + trigger = autoTrigger, + --cost = expendCost, + effect = nullEffect() + }), + + createAbility({ + id = "lich_wall_of_bones_minion_sac", + trigger = onLeavePlayTrigger, + cost = noCost, + activations = singleActivations, + effect = moveTarget(nullPloc).apply(selectSource()) + }) + }, + }) +end + +function lich_wall_of_fire_minion_carddef() + return createChampionDef({ + id = "lich_wall_of_fire_minion", + name = "Lich Wall of Fire Minion", + acquireCost = 0, + health = 7, + isGuard = false, + types = { minionType, magicType, constructType }, + layout = createLayout({ + name = "Wall of Fire", + art = "art/t_charing_guardian", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + cost = 0, + health = 7, + isGuard = false, + xmlText= + [[ + + + + + + + ]] + }), + abilities = { + createAbility({ + id = "lich_wall_of_fire_minion_main", + trigger = autoTrigger, + cost = expendCost, + activations = multipleActivations, + effect = hitOpponentEffect(2), + }), + + createAbility({ + id = "lich_wall_of_bones_minion_sac", + trigger = onLeavePlayTrigger, + cost = noCost, + activations = singleActivations, + effect = moveTarget(nullPloc).apply(selectSource()) + }) + }, + }) +end + +function lich_zombie_minion_carddef() + return createChampionDef({ + id = "lich_zombie_minion", + name = "Lich Zombie Minion", + acquireCost = 0, + health = 1, + isGuard = false, + cardTypeLabel = "undeadType", + types = { minionType, undeadType }, + layout = createLayout({ + name = "Zombie", + art = "art/sets/promos1art/zombie", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + cost = 0, + health = 1, + isGuard = false, + xmlText= + [[ + + + + + + + + + + + + + + ]] + }), + abilities = { + createAbility({ + id = "lich_zombie_minion_main", + trigger = autoTrigger, + cost = expendCost, + activations = multipleActivations, + effect = gainCombatEffect(2) + }), + + createAbility({ + id = "lich_zombie_minion_sac", + trigger = onLeavePlayTrigger, + cost = noCost, + activations = singleActivations, + effect = moveTarget(nullPloc).apply(selectSource()) + }) + + }, + }) +end + +--Lich Soul jars (Skills) + +function lich_SJ_HoE_carddef() + local cardLayout = createLayout({ + name = "Heart of Evil", + art = "art/t_elixir_of_concentration", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText = + [[ + + + > + + + + ]] + }) + + return createSkillDef({ + id = "cryptskill_skill", + name = "Heart of Evil", + types = { skillType }, + layout = cardLayout, + layoutPath = "art/t_elixir_of_concentration", + abilities = { + createAbility({ + id = "lich_SJ_HoE_ab", + trigger = uiTrigger, + activations = singleActivation, + layout = cardLayout, + promptType = showPrompt, + effect = randomTarget(const(1), moveTarget(inPlayPloc)).apply(selectLoc(loc(nil, nullPloc))).seq(noUndoEffect()), + check = selectLoc(loc(nil, nullPloc)).count().gte(1), + cost = expendCost, + }), + + } + + }) +end + +function lich_SJ_DI_carddef() + local cardLayout = createLayout({ + name = "Deep Insight", + art = "art/t_elixir_of_wisdom", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText = + [[ + + +> + + + + ]] + }) + + return createSkillDef({ + id = "cryptskill_skill", + name = "Deep Insight", + types = { skillType }, + layout = cardLayout, + layoutPath = "art/t_elixir_of_wisdom", + abilities = { + createAbility({ + id = "lich_SJ_DI_ab", + trigger = uiTrigger, + activations = singleActivation, + layout = cardLayout, + promptType = showPrompt, + effect = drawCardsEffect(1).seq(forceDiscard(1)), + cost = expendCost, + }), + + } + + }) +end + +function lich_SJ_IW_carddef() + local cardLayout = createLayout({ + name = "Infernal Wealth", + art = "art/t_elixir_of_endurance", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText = + [[ + + + + + + + + + + ]] + }) + + return createSkillDef({ + id = "cryptskill_skill", + name = "Infernal Wealth", + types = { skillType }, + layout = cardLayout, + layoutPath = "art/t_elixir_of_endurance", + abilities = { + createAbility({ + id = "lich_SJ_IW_ab", + trigger = uiTrigger, + activations = singleActivation, + layout = cardLayout, + promptType = showPrompt, + effect = pushChoiceEffect + ( + { + choices = + { + + { + effect = gainGoldEffect(2), + layout = layoutCard( + { + title = "Infernal Wealth", + art = "art/t_elixir_of_endurance", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText = + [[ + + + + + + + + ]] + } + ), + }, + + { + effect = pushTargetedEffect({ + desc = "Stun target champion.", + validTargets = oppStunnableSelector(), + min = 0, + max = 1, + targetEffect = stunTarget(), + }), + condition = selectLoc(loc(oppPid, inPlayPloc)).where(isCardChampion()).count().gte(1), + layout = layoutCard( + { + title = "Infernal Wealth", + art = "art/t_elixir_of_endurance", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText = + [[ + + + + + + + + + ]] + } + ), + }, + + } + } + ), + cost = expendCost, + }), + + } + + }) +end + +function lich_SJ_UR_carddef() + local cardLayout = createLayout({ + name = "Unending Rage", + art = "art/treasures/t_wizard_elixir_orange", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText = + [[ + + +> + + + + ]] + }) + + return createSkillDef({ + id = "cryptskill_skill", + name = "Unending Rage", + types = { skillType }, + layout = cardLayout, + layoutPath = "art/treasures/t_wizard_elixir_orange", + abilities = { + createAbility({ + id = "lich_SJ_UR_ab", + trigger = uiTrigger, + activations = singleActivation, + layout = cardLayout, + promptType = showPrompt, + effect = gainCombatEffect(7), + cost = expendCost, + }), + + } + + }) +end + +function lich_SJ_V_carddef() + local cardLayout = createLayout({ + name = "Void", + art = "art/treasures/t_wizard_elixir_silver", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText = + [[ + + +> + + + + ]] + }) + + return createSkillDef({ + id = "cryptskill_skill", + name = "Void", + types = { skillType }, + layout = cardLayout, + layoutPath = "art/treasures/t_wizard_elixir_silver", + abilities = { + createAbility({ + id = "lich_SJ_V_ab", + trigger = uiTrigger, + activations = singleActivation, + layout = cardLayout, + promptType = showPrompt, + effect = hitOpponentEffect(3), + cost = expendCost, + }), + + } + + }) +end + +function lich_SJ_M_carddef() + local cardLayout = createLayout({ + name = "Manipulation", + art = "art/treasures/t_wizard_elixir_orange", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText = + [[ + + +> + + + + ]] + }) + + return createSkillDef({ + id = "cryptskill_skill", + name = "Manipulation", + types = { skillType }, + layout = cardLayout, + layoutPath = "art/treasures/t_wizard_elixir_orange", + abilities = { + createAbility({ + id = "lich_SJ_M_ab", + trigger = uiTrigger, + activations = singleActivation, + layout = cardLayout, + promptType = showPrompt, + effect = pushTargetedEffect({ + desc = "Acquire a card of cost four or less for free.", + validTargets = selectLoc(centerRowLoc).union(selectLoc(fireGemsLoc)).where(isCardAcquirable().And(getCardCost().lte(4))), + min = 0, + max = 1, + targetEffect = moveTarget(loc(currentPid, discardPloc)), + }), + cost = expendCost, + }), + + } + + }) +end + +-- Trickster cards +trickster_double_or_nothing_layout = [[ + + + + + +]] + +trickster_pet_monkey_layout = [[ + + + + +]] + +trickster_blackmarket_layout_template = [[ + + + + + + +]] + +trickster_hidden_power_layout = [[ + + + + + + + + + + + + + + + + + + + + + + + + + +]] + +trickster_replace_ability_layout = [[ + + + + + + + + + + +]] + +function trickster_show_choices_effect() + return pushChoiceEffect({ + choices = { + { + effect = gainGoldEffect(s.Minus(s.Const(1), s.getCounter("trickster_reshuffle_card_count"))) + .seq(gainHealthEffect(s.Minus(s.getCounter("trickster_reshuffle_card_count"), s.Const(1)))) + .seq(animateShuffleDeckEffect(currentPid)) + -- Shuffle a few times to diminish the impact of the WWG + -- shuffle implementation being bugged + .seq(shuffleEffect(loc(currentPid, deckPloc))) + .seq(shuffleEffect(loc(currentPid, deckPloc))) + .seq(shuffleEffect(loc(currentPid, deckPloc))) + .seq(moveTarget(loc(currentPid, asidePloc)).apply(selectLoc(currentDeckLoc))) + .seq(randomEffect({ + valueItem(1, moveTarget(currentDeckLoc).apply(selectLoc(loc(currentPid, asidePloc)).reverse())), + valueItem(1, moveTarget(currentDeckLoc).apply(selectLoc(loc(currentPid, asidePloc)))), + })) + .seq(drawCardsEffect(s.getCounter("trickster_reshuffle_card_count"))), + layout = createLayout({ + name = "Continue", + art = "art/epicart/erratic_research", + text = format("{0}: Gain {0} and shuffle your deck then draw {1} card(s)", + {s.Minus(s.getCounter("trickster_reshuffle_card_count"), s.Const(1)), + getCounter("trickster_reshuffle_card_count")}) + }), + }, + } + }) +end + +function trickster_reshuffle_effect() + return pushTargetedEffect({ + desc = + "Pay X to gain X, put X+1 cards back in your deck, shuffle, and then draw X+1 cards.", + min=1, + max=ifInt( + selectLoc(loc(currentPid, handPloc)).count() + .gte(getPlayerGold(currentPid).add(const(1))), + getPlayerGold(currentPid).add(const(1)), + selectLoc(loc(currentPid, handPloc)).count()), + validTargets = selectLoc(loc(currentPid, handPloc)), + targetEffect = moveTarget(loc(currentPid, deckPloc)) + .seq(resetCounterEffect("trickster_reshuffle_card_count")) + .seq(incrementCounterEffect("trickster_reshuffle_card_count", selectTargets().count())) + .seq(trickster_show_choices_effect()) + }) +end + +function trickster_reshuffle_hand_skill() + local cardLayout = createLayout({ + name = "Mulligan", + art = "art/epicart/erratic_research", + frame = "frames/alchemist_frames/alchemist_item_cardframe", + text = ": Put X cards back in your deck, shuffle, then draw that many cards. Pay X-1 to do this, Gain X-1." + }) + card = createSkillDef({ + id = "trickster_reshuffle_skill_jseph", + name = "Reshuffle", + abilities = { + createAbility({ + id = "trickster_reshuffle_ability_id_jseph", + trigger = uiTrigger, + promptType = showPrompt, + layout = cardLayout, + effect = trickster_reshuffle_effect(), + cost = expendCost, + check = selectLoc(loc(currentPid, handPloc)).count() + .gte(const(1)), + activations = multipleActivations, + }) + }, + layout = cardLayout, + layoutPath = "art/epicart/erratic_research", + }) + return card +end + +function sacrifice_from_discard_effect() + return pushTargetedEffect({ + desc = "Sacrifice from discard.", + min=0, + max=1, + validTargets = sacrificeSelector(selectLoc(currentDiscardLoc)), + targetEffect = sacrificeTarget() + }) +end + +function trickster_switch_impl(val, cases) + local len = #cases + local effect = nullEffect() + for i = len, 1, -1 do + local c = cases[i][1] + local e = cases[i][2] + effect = ifElseEffect(val.where(c).count().gte(1),e,effect) + end + return effect +end + +function trickster_hidden_power_card_def() + local cardLayout = createLayout({ + name = "Hidden Power", + art = "art/epicart/reusable_knowledge", + frame = "frames/alchemist_frames/alchemist_item_cardframe", + xmlText = trickster_hidden_power_layout, + }) + local kTopCardText = "Top card of the market deck." + local add_faction = function(faction) + return addSlotToTarget(createFactionsSlot({faction},{leavesPlayExpiry, endOfTurnExpiry})) + .apply(selectSource()) + end + local kBonusEffects = { + {isCardFaction(wildFaction), gainCombatEffect(3).seq(add_faction(wildFaction))}, + {isCardFaction(guildFaction), gainGoldEffect(1).seq(add_faction(guildFaction))}, + {isCardFaction(necrosFaction), sacrifice_from_discard_effect().seq(add_faction(necrosFaction))}, + {isCardFaction(imperialFaction), gainHealthEffect(5).seq(add_faction(imperialFaction))}, + } + return createActionDef({ + id = "trickster_hidden_power_jseph", + name = "Hidden Power", + tags = {}, + types = {}, + factions = {}, + acquireCost = 0, + abilities = { + createAbility({ + id = "trickster_hidden_power_ability_jseph", + effect = noUndoEffect() + .seq(moveTarget(revealLoc).apply(selectLoc(tradeDeckLoc).take(1))) + .seq(waitForClickEffect(kTopCardText, kTopCardText)) + .seq(trickster_switch_impl(selectLoc(revealLoc), kBonusEffects)) + .seq(moveTarget(tradeDeckLoc).apply(selectLoc(revealLoc))), + cost = noCost, + trigger = onPlayTrigger, + tags = {gainCombatTag}, + aiPriority = toIntExpression(5), }) }, - layout = createLayout({ - name = "Brewmaster", - art = "art/treasures/T_Fighter_Elixir_Red", - frame = "frames/Coop_Campaign_CardFrame", - xmlText=[[ - - - - - - - + layout=cardLayout, + }) +end - - - - - ]] - }), +function trickster_double_or_nothing_effect() + local slot = createSlot({ + id = "trickster_double_or_nothing_discount", + expiresArray = { endOfTurnExpiry } }) + return pushChoiceEffect({ + choices = { + { + effect = gainGoldEffect(1), + layout = createLayout({ + name = "Gold", + art = "art/treasures/t_trick_dice", + text = format("{{{0} gold}}",{1}) + }), + }, + { + effect = randomTarget(const(2), addSlotToTarget(slot) + .seq(noUndoEffect())) + .apply(selectLoc(centerRowLoc)) + .seq(createCardEffect(getCostDiscountBuff("Double or Nothing", 2, selectLoc(centerRowLoc).where(isCardWithSlotString("trickster_double_or_nothing_discount"))), currentBuffsLoc)), + + layout = createLayout({ + name = "Discount", + art = "art/treasures/t_trick_dice", + text = "Discount two random cards in the market by until you acquire your next card." + }), + } + } + }) +end + +function trickster_double_or_nothing_card_def() + local cardLayout = createLayout({ + name = "Double or Nothing", + art = "art/treasures/t_trick_dice", + frame = "frames/alchemist_frames/alchemist_item_cardframe", + xmlText = trickster_double_or_nothing_layout }) - end + return createActionDef({ + id = "trickster_double_or_nothing_jseph", + name = "Double or Nothing", + tags = {}, + types = {}, + factions = {}, + acquireCost = 0, + abilities = { + createAbility({ + id = "trickster_double_or_nothing_ability_jseph", + effect = trickster_double_or_nothing_effect(), + cost = noCost, + trigger = onPlayTrigger, + tags = {gainCombatTag}, + aiPriority = toIntExpression(5), + }) + }, + layout=cardLayout, + }) +end -function choose_S_and_R_carddef() - return createDef({ - id="choose_S&R", - name="Choose Sparks & Rec Enjoyer", - types={noStealType, itemType}, - cardTypeLabel = "Item", - playLocation = castPloc, - acquireCost=0, +function trickster_pet_monkey_effect() + return moveTarget(currentRevealLoc).apply(selectLoc(tradeDeckLoc).take(3)) + .seq(noUndoEffect()) + .seq(promptSplit({ + selector = selectLoc(currentRevealLoc), + take = const(0), -- number of cards to take for split + sort = const(0), -- number of cards to be sorted for ef2 + minTake = const(3), -- Number of cards for ef1 + ef1 = moveTarget(tradeDeckLoc), -- effect to be applied to cards left + ef2 = ignoreTarget(nullEffect()), -- effect to be applied to sorted cards + header = "Pet Monkey", -- prompt header + description = "Rearrange the top 3 cards of the market deck.", + rightPileDesc = "Rightmost on top.", + pile1Name = "Top 3 cards.", + pile2Name = "Unused.", + eff1Tags = { }, + eff2Tags = { }})) +end + +function trickster_pet_monkey_card_def() + local cardLayout = createLayout({ + name = "Pet Monkey", + art = "art/epicart/kong", + frame = "frames/alchemist_frames/alchemist_item_cardframe", + xmlText = trickster_pet_monkey_layout, + health = 2, + isGuard = false + }) + return createChampionDef({ + id = "trickster_pet_monkey_jseph", + name = "Pet Monkey", + types = {minionType, championType}, + acquireCost = 0, + health = 2, + isGuard = false, abilities = { createAbility({ - id="choose_S&R", - trigger= onPlayTrigger, - playAllType = noPlayPlayType, - effect = sacrificeTarget().apply(selectLoc(loc(currentPid, skillsPloc))) - .seq(moveTarget(sacrificePloc).apply(selectLoc(loc(currentPid, handPloc)))) - .seq(setPlayerNameEffect("Sparks & Rec Enjoyer", currentPid)) - .seq(setPlayerAvatarEffect("ambushers", currentPid)) - .seq(gainMaxHealthEffect(currentPid, const(54).add(getPlayerMaxHealth(currentPid).negate()))) - .seq(gainHealthEffect(54)) - .seq(createCardEffect(bird_dog_def(), currentSkillsLoc)) - .seq(createCardEffect(patron_shoutout_def(), currentSkillsLoc)) - .seq(createCardEffect(situational_card_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(situational_card_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(situational_card_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(situational_card_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(situational_card_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(hero_dash_helper_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(wwyd_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(nostra_dbl_damus_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(blank_to_my_blank_carddef(), loc(currentPid, asidePloc))) - .seq(createCardEffect(congrats_youre_a_nerd_carddef(), loc(currentPid, asidePloc))) - .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) - .seq(shuffleEffect(currentDeckLoc)) - .seq(sacrificeTarget().apply(selectSource())) - .seq(moveTarget(deckPloc).apply(selectLoc(loc(currentPid, asidePloc)))) - .seq(shuffleEffect(currentDeckLoc)) - .seq(sacrificeTarget().apply(selectSource())) - .seq(pushChoiceEffectWithTitle( - { - choices = { - { - effect = moveTarget(tradeDeckLoc).apply(selectLoc(centerRowLoc)) - .seq(shuffleTradeDeckEffect()) - .seq(refillMarketEffect(const(0)).seq(refillMarketEffect(const(1))).seq(refillMarketEffect(const(2))).seq(refillMarketEffect(const(3))).seq(refillMarketEffect(const(4)))) - .seq(addEffect(endTurnEffect())), - layout = createLayout( - { - name = "Yes", - art = "art/treasures/T_cleric_elixir_green", - frame = "frames/necromancer_frames/necromancer_item_cardframe", - xmlText=[[ - - - - - - ]], + id = "trickster_pet_monkey_ability_jseph", + trigger = uiTrigger, + cost = expendCost, + activations = multipleActivations, + effect = trickster_pet_monkey_effect()}) + }, + layout = cardLayout, + }) +end - } - ), - tags = {} - }, - - { - effect = nullEffect(), - layout = createLayout( - { - name = "No", - art = "art/treasures/T_cleric_elixir_blue_purple", - frame = "frames/necromancer_frames/necromancer_item_cardframe", - xmlText=[[ - - - - - - ]], +function trickster_blackmarket_effect(index) + local kMoveText = "Moving to top of market deck." + return pushTargetedEffect({ + desc = "Select a card to swap with the top card of the market deck.", + min=0, + max=1, + validTargets = selectLoc(centerRowLoc).take(index).reverse().take(1), + targetEffect = moveTarget(revealLoc).seq( + -- Only show text and prevent undo if a card was selected + ifEffect(selectLoc(revealLoc).count().gte(1), + waitForClickEffect(kMoveText, kMoveText) + .seq(moveTarget(tradeDeckLoc).apply(selectLoc(revealLoc))) + .seq(noUndoEffect()))) + }) +end - } - ), - tags = {} - } - }, - upperTitle = "Confirm your choice. Do you want to use the Sparks & Recreations Enjoyer?", - lowerTitle = "" - })) +function trickster_generate_blackmarket_card(index, title, flavor_text, art) + local cardLayout = createLayout({ + name = title, + art = art, + frame = "frames/alchemist_frames/alchemist_item_cardframe", + xmlText = trickster_blackmarket_layout_template:format(index, flavor_text), + }) + return createItemDef({ + id = "trickster_blackmarket_jseph" .. index, + name = title, + types = {itemType}, + acquireCost = 0, + abilities = { + createAbility({ + id = "trickster_blackmarket_ability_jseph" .. index, + trigger = uiTrigger, + cost = noCost, + activations = singleActivation, + effect = trickster_blackmarket_effect(index)}), + createAbility({ + id = "trickster_blackmarket_gain_gold_jseph" .. index, + trigger = onPlayTrigger, + cost = noCost, + activations = singleActivation, + effect = gainGoldEffect(const(1)) }) }, - layout = createLayout({ - name = "S&R Enjoyer", - art = "avatars/ambushers", - frame = "frames/Coop_Campaign_CardFrame", - xmlText=[[ - - - - - - - + layout = cardLayout, + }) +end - - - - - ]] - }), - }) - end +-- also good art: art/epicart/the_gudgeon +--[[ +Blackmarket ideas: +the docks: art/t_maurader +the catacombs: art/t_broelyn_loreweaver_old +the back room: art/t_bribe +the sewers: art/treasures/thief_hideaway_cloak +the shambles: art/t_violent_gale +]] + +function trickster_blackmarket_the_back_room_carddef() + return trickster_generate_blackmarket_card(1, "The Back Room", + "There is no honor among thieves, but there is a great deal of profit", + "art/t_bribe") +end + +function trickster_blackmarket_the_catacombs_carddef() + return trickster_generate_blackmarket_card(2, "The Catacombs", + "Gold speaks a language that even the dead can understand", + "art/t_broelyn_loreweaver_old") +end + +function trickster_blackmarket_the_docks_carddef() + return trickster_generate_blackmarket_card(3, "The Docks", + "It was a city of a thousand surprises, most of them unpleasant", + "art/t_maurader") +end + +function trickster_blackmarket_the_sewers_carddef() + return trickster_generate_blackmarket_card(4, "The Sewers", + "Doors are for people who don't have enough imagination", + "art/treasures/thief_hideaway_cloak") +end + +function trickster_blackmarket_the_shambles_carddef() + return trickster_generate_blackmarket_card(5, "The Shambles", + "A short knife is better than a long argument", + "art/t_violent_gale") +end + +function trickster_card_in_current_discard() + return selectTargets().exclude(selectLoc(currentDiscardLoc)).count().eq(0) +end +function trickster_sacrifice_target_and_replace(location) + local equalValue = getCardPrintedCost().eq(selectTargets().sum(getCardPrintedCost())) + return sacrificeTarget().apply(selectTargets()) + .seq(moveTarget(location).apply( + selectLoc(tradeDeckLoc).take(1).where(equalValue).union(selectLoc(tradeDeckLoc).where(equalValue)).take(1))) + .seq(noUndoEffect()) +end + +function trickster_stack_the_deck_effect_2(location) + return pushTargetedEffect({ + desc = "Select the second card to sacrifice and replace.", + min=0, + max=1, + validTargets = sacrificeSelector(selectLoc(location).where(getCardPrintedCost().gte(1))), + targetEffect = ignoreTarget(trickster_sacrifice_target_and_replace(location)) + }) +end +function trickster_stack_the_deck_effect() + return pushTargetedEffect({ + desc = "Select the first card to sacrifice and replace.", + min=0, + max=1, + validTargets = sacrificeSelector( + selectLoc(loc(oppPid, discardPloc)).union(selectLoc(currentDiscardLoc))).where(getCardPrintedCost().gte(1)), + targetEffect = ignoreTarget( + ifElseEffect( + trickster_card_in_current_discard(), + trickster_sacrifice_target_and_replace(currentDiscardLoc) + .seq(trickster_stack_the_deck_effect_2(loc(oppPid, discardPloc))), + trickster_sacrifice_target_and_replace(loc(oppPid, discardPloc)) + .seq(trickster_stack_the_deck_effect_2(currentDiscardLoc)))) + }) +end + +function trickster_stack_the_deck_ability_def() + local cardLayout = createLayout({ + name = "Stack The Deck", + art = "art/epicart/street_swindler", + frame = "frames/necromancer_frames/necromancer_item_cardframe", + xmlText=trickster_replace_ability_layout + }) + return createSkillDef({ + id = "trickster_replace_ability_jseph", + name = "Stack The Deck", + types = { abilityType }, + layout = cardLayout, + layoutPath = "art/epicart/street_swindler", + abilities = { + createAbility({ + id = "trickster_replace_ability_ability_jseph", + trigger = uiTrigger, + activations = singleActivation, + layout = cardLayout, + promptType = showPrompt, + effect = trickster_stack_the_deck_effect(), + cost = sacrificeSelfCost, + }), + }, + }) +end -- Demonologist cards function demonologist_shadow_gem_carddef() @@ -1644,7 +3712,7 @@ function demonologist_shadow_gem_carddef() layout = layoutCard( { title = "Shadow Gem", - art = "art/treasures/T_Brillant_Ruby", + art = "art/treasures/thief_brillant_ruby", xmlText=[[ @@ -1670,7 +3738,7 @@ function demonologist_shadow_gem_carddef() layout = layoutCard( { title = "Shadow Gem", - art = "art/treasures/T_Brillant_Ruby", + art = "art/treasures/thief_brillant_ruby", xmlText=[[ @@ -1694,7 +3762,7 @@ function demonologist_shadow_gem_carddef() layout = createLayout( { name = "Shadow Gem", - art = "art/treasures/T_Brillant_Ruby", + art = "art/treasures/thief_brillant_ruby", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -1725,18 +3793,18 @@ function demonologist_shadow_feeder_carddef() abilities = { createAbility( { - id = "Shadow_feeder_main", + id = "shadow_feeder_main", trigger = uiTrigger, cost = expendCost, activations = multipleActivations, - effect = ifElseEffect(selectLoc(currentInPlayLoc).where(isCardType(demonType)).count().gte(1),gainCombatEffect(3),gainCombatEffect(1)) + effect = ifElseEffect(selectLoc(currentInPlayLoc).where(isCardType(demonType)).count().gte(2),gainCombatEffect(3),gainCombatEffect(1)) } ) }, layout = createLayout( { name = "Shadow Feeder", - art = "art/T_Devil", + art = "art/epicart/deathbringer", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -1781,7 +3849,7 @@ function demonologist_void_guard_carddef() layout = createLayout( { name = "Void guard", - art = "art/T_Midnight_Knight", + art = "art/t_midnight_knight", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -1826,7 +3894,7 @@ function demonologist_lesser_devourer_carddef() layout = createLayout( { name = "Lesser Devourer", - art = "art/T_Demon", + art = "art/t_demon", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText = [[ @@ -1850,7 +3918,7 @@ end function demonologist_summon_demon_carddef() local cardLayout = createLayout({ name = "Summon", - art = "icons/The_Summoning", + art = "icons/the_summoning", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -1876,11 +3944,11 @@ function demonologist_summon_demon_carddef() }) return createSkillDef({ - id = "demonologist_summon_skill", + id = "demonologist_summon_demon_skill", name = "Summon Demon", types = { skillType }, layout = cardLayout, - layoutPath = "icons/The_Summoning", + layoutPath = "icons/the_summoning", abilities = { createAbility({ id = "demonologist_summon_demon_ab", @@ -1901,7 +3969,7 @@ function demonologist_summon_demon_carddef() layout = createLayout( { name = "Demonic leech", - art = "art/T_wurm", + art = "art/t_wurm", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText = [[ @@ -1940,7 +4008,7 @@ Demonic Leech gains 1{shield} until it leaves play. " fontsize="18"/> layout = createLayout( { name = "Fel hound", - art = "art/T_Strength_of_the_wolf", + art = "art/epicart/demon_token", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -1970,7 +4038,7 @@ Demonic Leech gains 1{shield} until it leaves play. " fontsize="18"/> layout = createLayout( { name = "Succubus", - art = "art/T_Banshee", + art = "art/epicart/succubus", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -2004,7 +4072,7 @@ Demonic Leech gains 1{shield} until it leaves play. " fontsize="18"/> layout = createLayout( { name = "Summon Demon", - art = "icons/The_Summoning", + art = "icons/the_summoning", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -2078,7 +4146,7 @@ function demonologist_demonic_leech_carddef() layout = createLayout( { name = "Demonic leech", - art = "art/T_wurm", + art = "art/t_wurm", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText = [[ @@ -2146,7 +4214,7 @@ function demonologist_fel_hound_carddef() layout = createLayout( { name = "Fel hound", - art = "art/T_Strength_of_the_wolf", + art = "art/epicart/demon_token", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -2209,7 +4277,7 @@ function demonologist_succubus_carddef() layout = createLayout( { name = "Succubus", - art = "art/T_Banshee", + art = "art/epicart/succubus", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -2239,7 +4307,7 @@ end function demonologist_summon_demon_master_carddef() local cardLayout = createLayout({ name = "Summon Demon Master", - art = "art/T_Angry_Skeleton", + art = "art/t_angry_skeleton", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -2263,7 +4331,7 @@ Master into play. name = "Summon Demon Master", types = { skillType }, layout = cardLayout, - layoutPath = "art/T_Angry_Skeleton", + layoutPath = "art/t_angry_skeleton", abilities = { createAbility({ id = "demonologist_summon_demon_master_ab", @@ -2278,7 +4346,7 @@ Master into play. layout = createLayout( { name = "Summon Demon Master", - art = "art/T_Angry_Skeleton", + art = "art/t_angry_skeleton", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText=[[ @@ -2308,7 +4376,7 @@ function demonologist_incubus_carddef() name = "Incubus", types = {championType, demonType, minionType, nostealType}, acquireCost = 0, - health = 3, + health = 4, isGuard = false, abilities = { createAbility( @@ -2317,7 +4385,7 @@ function demonologist_incubus_carddef() trigger = uiTrigger, cost = expendCost, activations = multipleActivations, - effect = gainCombatEffect(2).seq(gainCombatEffect(getCounter("incubus"))) + effect = gainCombatEffect(3).seq(gainCombatEffect(getCounter("incubus"))) } ), @@ -2344,13 +4412,13 @@ function demonologist_incubus_carddef() }, layout = createLayout({ name = "Incubus", - art = "art/T_Lesser_Vampire", + art = "art/epicart/guilt_demon", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText = [[ - + @@ -2358,7 +4426,7 @@ function demonologist_incubus_carddef() ]], - health = 3, + health = 4, isGuard = false }) } @@ -2372,7 +4440,7 @@ function demonologist_keeper_of_the_void_carddef() name = "Keeper of the Void", types = {championType, demonType, minionType, nostealType}, acquireCost = 0, - health = 3, + health = 4, isGuard = true, abilities = { createAbility( @@ -2382,7 +4450,7 @@ function demonologist_keeper_of_the_void_carddef() cost = expendCost, activations = multipleActivations, effect = gainCombatEffect(2) - .seq(grantHealthTarget(selectLoc(loc(currentPid, inPlayPloc)).where(isCardType("minion").And(isCardName("demonologist_keeper_of_the_void").invert())).count(), { SlotExpireEnum.startOfOwnerTurn }, nullEffect(), "Void Keeper").apply(selectSource())), + .seq(grantHealthTarget(divide(getCounter(selectLoc(loc(currentPid, inPlayPloc)).where(isCardType("minion").And(isCardName("demonologist_keeper_of_the_void").invert())).count()), 2), { SlotExpireEnum.startOfOwnerTurn }, nullEffect(), "Void Keeper").apply(selectSource())), } ), @@ -2399,21 +4467,20 @@ function demonologist_keeper_of_the_void_carddef() }, layout = createLayout({ name = "Keeper of the Void", - art = "art/T_Giant_Knight", + art = "art/epicart/spawning_demon", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText = [[ - - + + - - - + + ]], - health = 3, + health = 4, isGuard = true }) } @@ -2459,7 +4526,7 @@ function demonologist_demon_master_carddef() }, layout = createLayout({ name = "Demon Master", - art = "art/T_Angry_Skeleton", + art = "art/t_angry_skeleton", frame = "frames/necromancer_frames/necromancer_item_cardframe", xmlText = [[ @@ -2502,8 +4569,8 @@ function apothecary_mezzaluna_carddef() layout = createLayout( { name = "Mezzaluna", - art = "art/T_Spell_Components", - frame = "frames/Wizard_CardFrame", + art = "art/t_spell_components", + frame = "frames/wizard_cardframe", xmlText=[[ @@ -2541,7 +4608,7 @@ function apothecary_apprentice_potion_maker_carddef() layout = layoutCard( { title = "Apprentice Potion Maker", - art = "art/T_Thief_Shadow_Mask", + art = "art/t_thief_shadow_mask", xmlText=[[ @@ -2566,7 +4633,7 @@ function apothecary_apprentice_potion_maker_carddef() layout = layoutCard( { title = "Apprentice Potion Maker", - art = "art/T_Thief_Shadow_Mask", + art = "art/t_thief_shadow_mask", xmlText=[[ @@ -2586,8 +4653,8 @@ function apothecary_apprentice_potion_maker_carddef() layout = createLayout( { name = "Apprentice Potion Maker", - art = "art/T_Thief_Shadow_Mask", - frame = "frames/Wizard_CardFrame", + art = "art/t_thief_shadow_mask", + frame = "frames/wizard_cardframe", xmlText=[[ @@ -2633,8 +4700,8 @@ function apothecary_red_potion_carddef() }, layout = createLayout({ name = "Red potion", - art = "art/T_Elixir_of_strength", - frame = "frames/Wizard_CardFrame", + art = "art/t_elixir_of_strength", + frame = "frames/wizard_cardframe", xmlText=[[ @@ -2670,8 +4737,8 @@ function apothecary_green_potion_carddef() promptType = showPrompt, layout = createLayout({ name = "Green Potion", - art = "art/treasures/T_Green_Potions_Medium", - frame = "frames/Wizard_CardFrame", + art = "art/treasures/t_green_potions_medium", + frame = "frames/wizard_cardframe", xmlText=[[ @@ -2690,8 +4757,8 @@ function apothecary_green_potion_carddef() }, layout = createLayout({ name = "Green Potion", - art = "art/treasures/T_Green_Potions_Medium", - frame = "frames/Wizard_CardFrame", + art = "art/treasures/t_green_potions_medium", + frame = "frames/wizard_cardframe", xmlText=[[ @@ -2726,8 +4793,8 @@ function apothecary_yellow_potion_carddef() prompt = showPrompt, layout = layoutCard({ name = "yellow Potion", - art = "art/T_Elixir_of_fortune", - frame = "frames/Wizard_CardFrame", + art = "art/t_elixir_of_fortune", + frame = "frames/wizard_cardframe", xmlText=[[ @@ -2748,8 +4815,8 @@ function apothecary_yellow_potion_carddef() }, layout = createLayout({ name = "yellow Potion", - art = "art/T_Elixir_of_fortune", - frame = "frames/Wizard_CardFrame", + art = "art/t_elixir_of_fortune", + frame = "frames/wizard_cardframe", xmlText=[[ @@ -2776,8 +4843,8 @@ function apothecary_custom_brew_carddef() promptType = showPrompt, layout = createLayout({ name = "Custom Brew", - art = "art/T_Elixir_of_concentration", - frame = "frames/Wizard_CardFrame", + art = "art/t_elixir_of_concentration", + frame = "frames/wizard_cardframe", xmlText = [[ @@ -2802,8 +4869,8 @@ Draw 1 and opponent discards 1." fontsize="40" /> layout = layoutCard( { title = "Custom Brew", - art = "art/T_Elixir_of_concentration", - frame = "frames/Wizard_CardFrame", + art = "art/t_elixir_of_concentration", + frame = "frames/wizard_cardframe", xmlText = [[ @@ -2822,8 +4889,8 @@ Draw 1 and opponent discards 1." fontsize="40" /> layout = layoutCard( { title = "Custom Brew", - art = "art/T_Elixir_of_concentration", - frame = "frames/Wizard_CardFrame", + art = "art/t_elixir_of_concentration", + frame = "frames/wizard_cardframe", xmlText = [[ @@ -2842,8 +4909,8 @@ Draw 1 and opponent discards 1." fontsize="40" /> layout = layoutCard( { title = "Custom Brew", - art = "art/T_Elixir_of_concentration", - frame = "frames/Wizard_CardFrame", + art = "art/t_elixir_of_concentration", + frame = "frames/wizard_cardframe", xmlText = [[ @@ -2865,19 +4932,19 @@ Opponent discards 1." fontsize="28" /> }, layout = createLayout({ name = "Custom Brew", - art = "art/T_Elixir_of_concentration", - frame = "frames/Wizard_CardFrame", + art = "art/t_elixir_of_concentration", + frame = "frames/wizard_cardframe", text = " or or Draw 1 and opponent discards 1." }), - layoutPath = "art/T_Elixir_of_concentration", + layoutPath = "art/t_elixir_of_concentration", }) end function apothecary_restorative_draught_carddef() local cardLayout = createLayout({ name = "Restorative Draught", - art = "art/treasures/T_ranger_elixir_yellow", - frame = "frames/Wizard_CardFrame", + art = "art/treasures/t_ranger_elixir_yellow", + frame = "frames/wizard_cardframe", xmlText = [[ @@ -2897,7 +4964,7 @@ function apothecary_restorative_draught_carddef() name = "Restorative Draught", types = { skillType }, layout = cardLayout, - layoutPath = "art/treasures/T_ranger_elixir_yellow", + layoutPath = "art/treasures/t_ranger_elixir_yellow", abilities = { createAbility({ id = "apothecary_restorative_draught_ab", @@ -2922,18 +4989,18 @@ end -- Cryomancer cards -function cryomancer_ice_burst_carddef() +function cryomancer_ice_sickle_carddef() return createDef( { - id = "cryomancer_ice_burst", - name = "Ice Burst", + id = "cryomancer_ice_sickle", + name = "Ice Sickle", types = {noStealType, actionType}, acquireCost = 0, cardTypeLabel = "Action", playLocation = castPloc, abilities = { createAbility({ - id = "cryomancer_ice_burst", + id = "cryomancer_ice_sickle", layout = cardLayout, effect = gainCombatEffect(2), trigger = autoTrigger, @@ -2943,9 +5010,9 @@ function cryomancer_ice_burst_carddef() }, layout = createLayout( { - name = "Ice Burst", - art = "art/T_Flame_Burst", - frame = "frames/Wizard_CardFrame", + name = "Ice Sickle", + art = "art/t_longsword", + frame = "frames/wizard_cardframe", xmlText=[[ @@ -2953,7 +5020,7 @@ function cryomancer_ice_burst_carddef() - + ]], @@ -2963,46 +5030,54 @@ function cryomancer_ice_burst_carddef() ) end -function cryomancer_ice_shield_carddef() - return createDef( +function cryomancer_frostwulf_carddef() +--This is a token champion, that self-sacrifices when it leaves play + return createChampionDef( { - id = "cryomancer_ice_shield", - name = "Ice Shield", - types = {noStealType, actionType}, + id = "cryomancer_frostwulf", + name = "Frostwulf", + types = {championType, nostealType }, acquireCost = 0, - cardTypeLabel = "Action", - playLocation = castPloc, + health = 1, + isGuard = false, abilities = { - createAbility({ - id = "cryomancer_ice_shield", - layout = cardLayout, - effect = ifElseEffect(selectLoc(loc(currentPid, castPloc)).where(isCardAction()).count().gte(3), gainToughnessEffect(2).seq(gainCombatEffect(2)), gainToughnessEffect(2)), + --base ability + createAbility( + { + id = "frostwulf_main", trigger = autoTrigger, - tags = {} + cost = expendCost, + activations = multipleActivations, + effect = gainCombatEffect(2) + } - ), - - }, + )}, layout = createLayout( { - name = "Ice Shield", - art = "art/T_Glittering_Spray", - frame = "frames/Wizard_CardFrame", - xmlText = [[ - - - - - - + name = "Frostwulf", + art = "art/epicart/den_mother", + frame = "frames/wizard_cardframe", + xmlText=[[ + + + + + + + + -]], + + ]], + health = 1, + isGuard = false } ) } ) end + function cryomancer_ice_gem_carddef() return createDef( { @@ -3026,7 +5101,7 @@ function cryomancer_ice_gem_carddef() layout = layoutCard( { title = "Ice Gem", - art = "art/T_Wizard_Alchemist_S_Stone", + art = "art/t_wizard_alchemist_s_stone", xmlText=[[ @@ -3045,7 +5120,7 @@ function cryomancer_ice_gem_carddef() layout = layoutCard( { title = "Ice Gem", - art = "art/T_Wizard_Alchemist_S_Stone", + art = "art/t_wizard_alchemist_s_stone", xmlText=[[ @@ -3069,8 +5144,8 @@ function cryomancer_ice_gem_carddef() layout = createLayout( { name = "Ice Gem", - art = "art/T_Wizard_Alchemist_S_Stone", - frame = "frames/Wizard_CardFrame", + art = "art/t_wizard_alchemist_s_stone", + frame = "frames/wizard_cardframe", xmlText=[[ @@ -3116,8 +5191,8 @@ function cryomancer_freeze_carddef() layout = createLayout( { name = "Freeze", - art = "art/T_Wind_Tunnel", - frame = "frames/Wizard_CardFrame", + art = "art/epicart/polar_shock", + frame = "frames/wizard_cardframe", xmlText=[[ @@ -3137,8 +5212,8 @@ end function cryomancer_frostbiteskill_carddef() local cardLayout = createLayout({ name = "Frostbite", - art = "icons/wind_storm", - frame = "frames/Wizard_CardFrame", + art = "art/epicart/frost_giant", + frame = "frames/wizard_cardframe", xmlText = [[ @@ -3159,7 +5234,7 @@ function cryomancer_frostbiteskill_carddef() name = "Frostbite", types = { skillType }, layout = cardLayout, - layoutPath = "icons/wind_storm", + layoutPath = "art/epicart/frost_giant", abilities = { createAbility({ id = "cryomancer_frostbiteskill_ab", @@ -3200,8 +5275,8 @@ function cryomancer_frostbite_carddef() layout = createLayout( { name = "Frostbite", - art = "icons/wind_storm", - frame = "frames/Wizard_CardFrame", + art = "art/epicart/frost_giant", + frame = "frames/wizard_cardframe", xmlText = [[ @@ -3228,8 +5303,8 @@ function cryomancer_blizzard_carddef() promptType = showPrompt, layout = createLayout({ name = "Blizzard", - art = "art/T_heavy_gust", - frame = "frames/Wizard_CardFrame", + art = "art/t_heavy_gust", + frame = "frames/wizard_cardframe", xmlText = [[ @@ -3245,13 +5320,13 @@ Deal 3 damage to ALL champions, including your own. ]] }), - effect = drawCardsEffect(2).seq(damageTarget(3).apply(selectLoc(loc(currentPid, inPlayPloc)).union(selectLoc(loc(oppPid, inPlayPloc))).where(isCardChampion()))), + effect = drawCardsEffect(2).seq(damageTarget(3).apply(selectLoc(loc(currentPid, inPlayPloc)).union(selectLoc(loc(oppPid, inPlayPloc))).where(isCardType(tokenType).And(isCardType(demonType))))), cost = sacrificeSelfCost }) }, layout = createLayout({ name = "Blizzard", - art = "art/T_heavy_gust", + art = "art/t_heavy_gust", xmlText = [[ @@ -3271,6 +5346,188 @@ Deal 3 damage to ALL champions, including your own. }) end +function cryomancer_permafrost_carddef() + return createDef( + { + id = "permafrost", + name = "Permafrost", + types = {itemType}, + cardTypeLabel = "item", + playLocation = castPloc, + acquireCost = 0, + abilities = { + createAbility( + { + id = "permafrost_main", + trigger = autoTrigger, + cost = noCost, + activations = singleActivations, + effect = nullEffect() + + } + ) + }, + layout = createLayout( + { + name = "Permafrost", + art = "art/t_heavy_gust", + frame = "frames/wizard_cardframe", + cost = 0, + xmlText=[[ + + + + + + + + ]], + + } + ) + } + ) +end + + local CryoFrFogBuff = createGlobalBuff({ + id="cryomancer_freezing_fog_ability", + name = "Freezing Fog", + abilities = { + createAbility({ + id="cryomancer_freezing_fog_ability", + trigger = deckShuffledTrigger, + effect = moveToBottomDeckTarget(false, 0).apply(selectSavedTargets("Freezing Fog")).seq(sacrificeSelf()) + }) + }, + buffDetails = createBuffDetails({ + name = "Freezing Fog", + art = "art/t_chaotic_gust", + text = "One of your cards has been set aside. It will be placed on the bottom of your deck when you next shuffle." + }) + }) + +function cryomancer_freezing_fog_carddef() + return createHeroAbilityDef({ + id = "Freezing_fog_buff", + name = "Freezing Fog", + types = { heroAbilityType }, + abilities = { + createAbility({ + id = "Freezing_fog_Activate", + trigger = uiTrigger, + promptType = showPrompt, + layout = createLayout({ + name = "Freezing Fog", + art = "art/t_chaotic_gust", + frame = "frames/generic_cardframe", + xmlText=[[ + + + + + + + + + + + ]] + }), + effect = pushTargetedEffect({ + desc = "Set aside a champion of cost 5 or less from opponent's discard pile.", + min=0, + max=1, + validTargets = selectLoc(loc(oppPid, discardPloc)).where(getCardCost().lte(5)), + targetEffect = saveTarget("Freezing Fog").seq(moveTarget(sacrificePloc)) + .seq(createCardEffect(CryoFrFogBuff, loc(oppPid, buffsPloc))) + }), + cost = sacrificeSelfCost + }) + }, + layout = createLayout({ + name = "Freezing Fog", + art = "art/t_chaotic_gust", + frame = "frames/wizard_cardframe", + xmlText=[[ + + + + + + + + + + + ]] + }), + layoutPath = "art/t_chaotic_gust", + }) +end + + +function Cryomancer_Permafrost_Ab_carddef() +local cardLayout = createLayout({ + name = "Permafrost", + art = "art/t_heavy_gust", + frame = "frames/wizard_cardframe", + xmlText = [[ + + + + + + + + + + ]] +--Add later: That opponent reveals their hand and discards all champions and actions of cost 2{gold} or less. + }) + +local permafrostBuff = createGlobalBuff({ + id="permafrost_buff", + name = "Permafrost", + abilities = { + createAbility({ + id="permafrost_effect", + trigger = startOfTurnTrigger, + effect = moveToTopDeckTarget(true).apply(selectLoc(loc(currentPid, deckPloc)).where(isCardName("permafrost"))).seq(sacrificeSelf()) + + }) + }, + buffDetails = createBuffDetails({ + name = "Permafrost", + art = "art/t_heavy_gust", + text = "Winter is... here. (Puts Permafrost on top of your deck.)" + }) + }) + + return createHeroAbilityDef({ + + id = "Permafrost_Ab", + name = "Permafrost", + types = {skillType}, + layout = cardLayout, + layoutPath = "art/t_heavy_gust", + abilities = { + createAbility( + { + id = "Permafrost_Ab_main", + trigger = uiTrigger, + promptType = showPrompt, + layout = cardLayout, + effect = createCardEffect(cryomancer_permafrost_carddef(), loc(oppPid, deckPloc)).seq(createCardEffect(permafrostBuff, loc(oppPid, buffsPloc))) + --[[.seq(moveTarget(loc(oppPid, discardPloc)).apply(selectLoc(loc(oppPid, handPloc)).where(isCardChampion().And(getCardCost().lte(2)))))]], + cost = sacrificeSelfCost + } + ) + }, + + } + ) +end -- Pyromancer cards function pyromancer_combust_carddef() @@ -3300,8 +5557,8 @@ function pyromancer_combust_carddef() layout = createLayout( { name = "Combust", - art = "art/T_Spark", - frame = "frames/Wizard_CardFrame", + art = "art/epicart/zaltessa_s_fire", + frame = "frames/wizard_cardframe", xmlText=[[ @@ -3340,8 +5597,8 @@ function pyromancer_sear_carddef() layout = createLayout( { name = "Sear", - art = "art/T_Fire_Blast", - frame = "frames/Wizard_CardFrame", + art = "art/epicart/flame_spike", + frame = "frames/wizard_cardframe", xmlText=[[ @@ -3377,8 +5634,8 @@ function pyromancer_scorch_carddef() layout = createLayout( { name = "Scorch", - art = "art/T_Pillar_Of_Fire", - frame = "frames/Wizard_CardFrame", + art = "art/epicart/flame_strike", + frame = "frames/wizard_cardframe", xmlText=[[ @@ -3418,7 +5675,7 @@ function pyromancer_fire_shard_carddef() layout = layoutCard( { title = "Fire Shard", - art = "art/treasures/T_Sharpened_ruby", + art = "art/treasures/thief_sharpened_ruby", xmlText=[[ @@ -3434,8 +5691,8 @@ function pyromancer_fire_shard_carddef() effect = createCardEffect(fire_gem_carddef(), currentDiscardLoc), layout = layoutCard( { - title = "Shadow Gem", - art = "art/treasures/T_Sharpened_ruby", + title = "Fire Shard", + art = "art/treasures/thief_sharpened_ruby", xmlText=[[ @@ -3457,8 +5714,8 @@ function pyromancer_fire_shard_carddef() layout = createLayout( { name = "Fire Shard", - art = "art/treasures/T_Sharpened_ruby", - frame = "frames/Wizard_CardFrame", + art = "art/treasures/thief_sharpened_ruby", + frame = "frames/wizard_cardframe", xmlText=[[ @@ -3476,10 +5733,13 @@ Acquire a Fire Gem for free." fontsize="26" /> ) end + + + function pyromancer_conflagration_1p_carddef() return createHeroAbilityDef({ id = "conflagration_1p", - name = "Conflagration", + name = "Phoenix Fire", types = { heroAbilityType }, abilities = { createAbility({ @@ -3487,202 +5747,155 @@ function pyromancer_conflagration_1p_carddef() trigger = uiTrigger, promptType = showPrompt, layout = createLayout({ - name = "Conflagration", - art = "icons/growing_flame", - xmlText = [[ - - - - - - - - - ]] - }), - effect = gainCombatEffect(5).seq(gainCombatEffect(getCounter("conflagration_1p"))) - .seq(sacrificeTarget().apply(selectLoc(loc(currentPid, skillsPloc)).where(isCardName("pyromancer_fuel_1_skill").Or(isCardName("pyromancer_fuel_2_skill")).Or(isCardName("pyromancer_fuel_3_skill")).Or(isCardName("pyromancer_fuel_4_skill")).Or(isCardName("pyromancer_fuel_5_skill"))))) + name = "Phoenix Fire", + art = "art/epicart/fiery_demise", + xmlText = format([[ + + + + + + + + + + + ]], + { getCounter("conflagration_1p") }) + }), + effect = gainCombatEffect(getCounter("conflagration_1p")) + .seq(sacrificeTarget().apply(selectLoc(loc(currentPid, skillsPloc)).where(isCardName("pyromancer_1p_fuel_1_skill")))) .seq(createCardEffect(pyromancer_after_burn_carddef(), currentSkillsLoc)), cost = sacrificeSelfCost }), }, layout = createLayout({ - name = "Conflagration", - art = "icons/growing_flame", - xmlText = [[ - - - - - - - - -]] + name = "Phoenix Fire", + art = "art/epicart/fiery_demise", + xmlText = format([[ + + + + + + + + + + + ]], + { getCounter("conflagration_1p") }) }), - layoutPath = "icons/fire_bomb", + layoutPath = "art/epicart/fiery_demise", }) end - - -function pyromancer_fuel_1carddef() - local cardLayout = createLayout({ - name = "Fuel", - art = "icons/growing_flame", - frame = "frames/Wizard_CardFrame", - xmlText = [[ - - - - - - - - -]] - }) - return createSkillDef({ - id = "pyromancer_fuel_1_skill", - name = "Fuel", - types = { skillType }, - layout = cardLayout, - layoutPath = "icons/growing_flame", + +function pyromancer_conflagration_2p_carddef() + return createHeroAbilityDef({ + id = "conflagration_2p", + name = "Phoenix Fire", + types = { heroAbilityType }, abilities = { - createAbility({ - id = "pyromancer_fuel_1_ab", - trigger = uiTrigger, + createAbility({ + id = "conflagration_2pActivate", + trigger = uiTrigger, promptType = showPrompt, - activations = singleActivation, - layout = cardLayout, - effect = incrementCounterEffect("conflagration_1p", 1).seq(waitForClickEffect("Conflagration Total = 6")) - .seq(transformTarget("pyromancer_fuel_2_skill").apply(selectSource())), - cost = combineCosts({ - expendCost, - goldCost(2) - }), - }), - } - - }) -end - -function pyromancer_fuel_2carddef() - local cardLayout = createLayout({ - name = "Fuel", - art = "icons/growing_flame", - frame = "frames/Wizard_CardFrame", - xmlText = [[ - - - - - - - - -]] - }) + layout = createLayout({ + name = "Phoenix Fire", + art = "art/epicart/fiery_demise", + xmlText = format([[ + + + + + + + - return createSkillDef({ - id = "pyromancer_fuel_2_skill", - name = "Fuel", - types = { skillType }, - layout = cardLayout, - layoutPath = "icons/growing_flame", - abilities = { - createAbility({ - id = "pyromancer_fuel_2_ab", - trigger = uiTrigger, - promptType = showPrompt, - activations = singleActivation, - layout = cardLayout, - effect = incrementCounterEffect("conflagration_1p", 2).seq(waitForClickEffect("Conflagration Total = 8")) - .seq(transformTarget("pyromancer_fuel_3_skill").apply(selectSource())), - cost = combineCosts({ - expendCost, - goldCost(2) - }), - }), - } - - }) -end + + + ]], + { getCounter("conflagration_2p") }) + }), + effect = gainCombatEffect(getCounter("conflagration_2p")) + .seq(sacrificeTarget().apply(selectLoc(loc(currentPid, skillsPloc)).where(isCardName("pyromancer_2p_fuel_1_skill")))) + .seq(createCardEffect(pyromancer_after_burn_carddef(), currentSkillsLoc)), + cost = sacrificeSelfCost + }), + }, + layout = createLayout({ + name = "Phoenix Fire", + art = "art/epicart/fiery_demise", + xmlText = format([[ + + + + + + + -function pyromancer_fuel_3carddef() - local cardLayout = createLayout({ - name = "Fuel", - art = "icons/growing_flame", - frame = "frames/Wizard_CardFrame", - xmlText = [[ - - - - - - - - -]] - }) + + + ]], + { getCounter("conflagration_2p") }) + }), + layoutPath = "art/epicart/fiery_demise", + }) +end - return createSkillDef({ - id = "pyromancer_fuel_3_skill", - name = "Fuel", - types = { skillType }, - layout = cardLayout, - layoutPath = "icons/growing_flame", - abilities = { - createAbility({ - id = "pyromancer_fuel_3_ab", - trigger = uiTrigger, - promptType = showPrompt, - activations = singleActivation, - layout = cardLayout, - effect = incrementCounterEffect("conflagration_1p", 3).seq(waitForClickEffect("Conflagration Total = 11")) - .seq(transformTarget("pyromancer_fuel_4_skill").apply(selectSource())), - cost = combineCosts({ - expendCost, - goldCost(2) - }), - }), - } - - }) -end +pyroSkill = "pyroSkill" +pyroSkillValue = sumIntSlots(currentPlayer(), pyroSkill) -function pyromancer_fuel_4carddef() +function pyromancer_1p_fuel_1carddef() local cardLayout = createLayout({ - name = "Fuel", - art = "icons/growing_flame", - frame = "frames/Wizard_CardFrame", - xmlText = [[ - - - - - - - - -]] + name = "Research Phoenix Fire", + art = "art/epicart/arcane_research", + frame = "frames/wizard_cardframe", + xmlText = format([[ + + + + + + + + + + + ]], + { getCounter("fuel_1p") }) }) return createSkillDef({ - id = "pyromancer_fuel_4_skill", - name = "Fuel", + id = "pyromancer_1p_fuel_1_skill", + name = "Research Phoenix Fire", types = { skillType }, layout = cardLayout, - layoutPath = "icons/growing_flame", + layoutPath = "art/epicart/arcane_research", abilities = { createAbility({ - id = "pyromancer_fuel_4_ab", + id = "pyromancer_1p_fuel_1_ab", trigger = uiTrigger, promptType = showPrompt, activations = singleActivation, layout = cardLayout, - effect = incrementCounterEffect("conflagration_1p", 4).seq(waitForClickEffect("Conflagration Total = 15")) - .seq(transformTarget("pyromancer_fuel_5_skill").apply(selectSource())), + effect = incrementCounterEffect("conflagration_1p", (getCounter("fuel_1p"))) + .seq(ifElseEffect((getCounter("fuel_1p").lte(4)),incrementCounterEffect("fuel_1p", 1),nullEffect())), cost = combineCosts({ expendCost, goldCost(2) @@ -3693,37 +5906,43 @@ function pyromancer_fuel_4carddef() }) end -function pyromancer_fuel_5carddef() + +function pyromancer_2p_fuel_1carddef() local cardLayout = createLayout({ - name = "Fuel", - art = "icons/growing_flame", - frame = "frames/Wizard_CardFrame", - xmlText = [[ - - - - - - - - -]] + name = "Research Phoenix Fire", + art = "art/epicart/arcane_research", + frame = "frames/wizard_cardframe", + xmlText = format([[ + + + + + + + + + + + ]], + { getCounter("fuel_2p") }) }) return createSkillDef({ - id = "pyromancer_fuel_5_skill", - name = "Fuel", + id = "pyromancer_2p_fuel_1_skill", + name = "Research Phoenix Fire", types = { skillType }, layout = cardLayout, - layoutPath = "icons/growing_flame", + layoutPath = "art/epicart/arcane_research", abilities = { createAbility({ - id = "pyromancer_fuel_5_ab", + id = "pyromancer_2p_fuel_1_ab", trigger = uiTrigger, promptType = showPrompt, activations = singleActivation, layout = cardLayout, - effect = incrementCounterEffect("conflagration_1p", 5).seq(waitForClickEffect("+5 to Conflagration")), + effect = incrementCounterEffect("conflagration_2p", (getCounter("fuel_2p"))) + .seq(ifElseEffect((getCounter("fuel_2p").lte(4)),incrementCounterEffect("fuel_2p", 1),nullEffect())), cost = combineCosts({ expendCost, goldCost(2) @@ -3738,7 +5957,7 @@ function pyromancer_after_burn_carddef() local cardLayout = createLayout({ name = "After Burn", art = "icons/growing_flame", - frame = "frames/Wizard_CardFrame", + frame = "frames/wizard_cardframe", xmlText = [[ @@ -3815,7 +6034,7 @@ function terramancer_earth_gem_carddef() tags = {gainCombatTag} }, { - effect = transformTarget("terramancer_move_earth_skill_free").apply(selectLoc(currentSkillsLoc).where(isCardName("terramancer_move_earth_skill"))), + effect = addSlotToPlayerEffect(controllerPid, createPlayerIntExpressionSlot(skillCostModKey, toIntExpression(-2), { endOfTurnExpiry })), layout = layoutCard( { title = "Earth Gem", @@ -3823,7 +6042,8 @@ function terramancer_earth_gem_carddef() xmlText=[[ - + ]] @@ -3849,7 +6069,8 @@ function terramancer_earth_gem_carddef() +Your class skill costs +2{gold} less this turn." fontsize="24" /> ]] @@ -3872,7 +6093,7 @@ function terramancer_hurl_boulder_carddef() createAbility({ id = "terramancer_hurl_boulder", layout = cardLayout, - effect = ifElseEffect(selectLoc(currentSkillsLoc).where(isCardName("terramancer_move_earth_skill").Or(isCardName("terramancer_move_earth_skill_free")).And(isCardExpended())).count().gte(1),gainCombatEffect(4),gainCombatEffect(2)), + effect = ifElseEffect(selectLoc(currentSkillsLoc).where(isCardName("terramancer_move_earth1_skill").Or(isCardName("terramancer_move_earth2_skill")).And(isCardExpended())).count().gte(1),gainCombatEffect(4),gainCombatEffect(2)), trigger = autoTrigger, tags = {} } @@ -3882,7 +6103,7 @@ function terramancer_hurl_boulder_carddef() layout = createLayout( { name = "Hurl Boulder", - art = "icons/wizard_calm_channel", + art = "art/epicart/apocalypse", frame = "frames/druid_frames/druid_action_cardframe", xmlText=[[ @@ -3966,10 +6187,119 @@ function terramancer_tremor_carddef() ) end -function terramancer_clay_golem_carddef() +function terramancer_clay_golem1_carddef() + return createChampionDef( + { + id = "terramancer_clay_golem1", + name = "Clay Golem", + types = {championType, nostealType}, + acquireCost = 0, + health = 1, + isGuard = false, + abilities = { + createAbility( + { + id = "clay_golem1_main", + trigger = autoTrigger, + cost = expendCost, + activations = multipleActivations, + effect = gainCombatEffect(1) + + } + + ), + createAbility( + { + id = "clay_golem1_healthbuff", + trigger = onPlayTrigger, + cost = noCost, + activations = singleActivations, + effect = pushChoiceEffect( + { + choices = { + { + effect = nullEffect(), + layout = layoutCard( + { + title = "Clay Golem", + art = "art/t_stone_golem", + xmlText=[[ + + + + + + +]], + health = 1, + isGuard = false + } + ), + tags = {} + }, + { + effect = grantHealthTarget(2, { SlotExpireEnum.LeavesPlay }, nullEffect(), "Golem. Golem.").apply(selectSource()) + .seq(incrementCounterEffect("wallofEarth1Counter", -2)), + condition = getCounter("wallofEarth1Counter").gte(2), + layout = layoutCard( + { + title = "Clay Golem", + art = "art/t_stone_golem", + xmlText=[[ + + + + + + + + + + + ]], + health = 3, + isGuard = false + } + ), + } + } + } + ) + + } + + ) + }, + layout = createLayout( + { + name = "Clay Golem", + art = "art/t_stone_golem", + frame = "frames/druid_frames/druid_action_cardframe", + xmlText=[[ + + + + + + + + + + + ]], + health = 1, + isGuard = false + } + ) + } + ) +end + +function terramancer_clay_golem2_carddef() return createChampionDef( { - id = "terramancer_clay_golem", + id = "terramancer_clay_golem2", name = "Clay Golem", types = {championType, nostealType}, acquireCost = 0, @@ -3978,7 +6308,7 @@ function terramancer_clay_golem_carddef() abilities = { createAbility( { - id = "clay_golem_main", + id = "clay_golem2_main", trigger = autoTrigger, cost = expendCost, activations = multipleActivations, @@ -3989,7 +6319,7 @@ function terramancer_clay_golem_carddef() ), createAbility( { - id = "clay_golem_healthbuff", + id = "clay_golem2_healthbuff", trigger = onPlayTrigger, cost = noCost, activations = singleActivations, @@ -4001,7 +6331,7 @@ function terramancer_clay_golem_carddef() layout = layoutCard( { title = "Clay Golem", - art = "art/T_stone_golem", + art = "art/t_stone_golem", xmlText=[[ @@ -4018,13 +6348,12 @@ function terramancer_clay_golem_carddef() }, { effect = grantHealthTarget(2, { SlotExpireEnum.LeavesPlay }, nullEffect(), "Golem. Golem.").apply(selectSource()) - .seq(incrementCounterEffect("wallofEarthCounter", -2)) - .seq(simpleMessageExpressionEffect(format("Wall of Earth has {0} counters.", {getCounter("wallofEarthCounter")}))), - condition = getCounter("wallofEarthCounter").gte(2), + .seq(incrementCounterEffect("wallofEarth2Counter", -2)), + condition = getCounter("wallofEarth2Counter").gte(2), layout = layoutCard( { title = "Clay Golem", - art = "art/T_stone_golem", + art = "art/t_stone_golem", xmlText=[[ @@ -4054,7 +6383,7 @@ from your class skill." fontsize="20" flexiblewidth="10" /> layout = createLayout( { name = "Clay Golem", - art = "art/T_stone_golem", + art = "art/t_stone_golem", frame = "frames/druid_frames/druid_action_cardframe", xmlText=[[ @@ -4076,33 +6405,41 @@ from your class skill." fontsize="20" flexiblewidth="10" /> ) end -function terramancer_move_earth_carddef() +function terramancer_move_earth1_carddef() local cardLayout = createLayout({ name = "Move Earth", - art = "art/T_Fissure", + art = "art/t_fissure", frame = "frames/druid_frames/druid_item_cardframe", - xmlText = [[ - - - - - - - - -]] + xmlText = format([[ + + + + + + + + + + + + + + + ]], + { getCounter("wallofEarth1Counter"), getCounter("wallofEarth1Counter"), divide(getCounter("wallofEarth1Counter"), 2) } + ) }) return createSkillDef({ - id = "terramancer_move_earth_skill", + id = "terramancer_move_earth1_skill", name = "move_earth", types = { skillType }, layout = cardLayout, layoutPath = "icons/wizard_serene_channel", abilities = { createAbility({ - id = "move_earth_ab", + id = "move_earth1_ab", trigger = uiTrigger, promptType = showPrompt, activations = singleActivation, @@ -4111,12 +6448,11 @@ Remove all counters. Put a Wall token into play with {guard} equal to the number { choices = { { - effect = incrementCounterEffect("wallofEarthCounter", 2) - .seq(simpleMessageExpressionEffect(format("Wall of Earth has {0} counters.", {getCounter("wallofEarthCounter")}))), + effect = incrementCounterEffect("wallofEarth1Counter", 2), layout = layoutCard( { title = "Wall of Earth", - art = "art/T_Fissure", + art = "art/t_fissure", xmlText=[[ @@ -4134,14 +6470,14 @@ to this card." fontsize="30"/> { --here -- effect = createCardEffect(terramancer_wall_of_earth_carddef(), currentInPlayLoc) - .seq(grantHealthTarget((getCounter("wallofEarthCounter")), { SlotExpireEnum.LeavesPlay }, nullEffect(), "Wall of earth").apply(selectLoc(loc(currentPid, inPlayPloc)).where(isCardChampion().And(isCardName("terramancer_wall_of_earth"))))) - .seq(gainHealthEffect(divide(getCounter("wallofEarthCounter"), 2))) - .seq(resetCounterEffect("wallofEarthCounter")), - condition = getCounter("wallofEarthCounter").gte(2), + .seq(grantHealthTarget((getCounter("wallofEarth1Counter")), { SlotExpireEnum.LeavesPlay }, nullEffect(), "Wall of earth").apply(selectLoc(loc(currentPid, inPlayPloc)).where(isCardChampion().And(isCardName("terramancer_wall_of_earth"))))) + .seq(gainHealthEffect(divide(getCounter("wallofEarth1Counter"), 2))) + .seq(resetCounterEffect("wallofEarth1Counter")), + condition = getCounter("wallofEarth1Counter").gte(2), layout = layoutCard( { title = "Wall of Earth", - art = "art/T_Fissure", + art = "art/t_fissure", xmlText=[[ @@ -4169,33 +6505,41 @@ Gain {health} equal to half that number." fontsize="20" /> }) end -function terramancer_move_earth_free_carddef() +function terramancer_move_earth2_carddef() local cardLayout = createLayout({ name = "Move Earth", - art = "art/T_Fissure", + art = "art/t_fissure", frame = "frames/druid_frames/druid_item_cardframe", - xmlText = [[ - - - - - - - - -]] + xmlText = format([[ + + + + + + + + + + + + + + + ]], + { getCounter("wallofEarth2Counter"), getCounter("wallofEarth2Counter"), divide(getCounter("wallofEarth2Counter"), 2) } + ) }) return createSkillDef({ - id = "terramancer_move_earth_skill_free", + id = "terramancer_move_earth2_skill", name = "move_earth", types = { skillType }, layout = cardLayout, layoutPath = "icons/wizard_serene_channel", abilities = { createAbility({ - id = "move_earth_ab", + id = "move_earth2_ab", trigger = uiTrigger, promptType = showPrompt, activations = singleActivation, @@ -4204,12 +6548,11 @@ function terramancer_move_earth_free_carddef() { choices = { { - effect = incrementCounterEffect("wallofEarthCounter", 2) - .seq(simpleMessageExpressionEffect(format("Wall of Earth has {0} counters.", {getCounter("wallofEarthCounter")}))), + effect = incrementCounterEffect("wallofEarth2Counter", 2), layout = layoutCard( { title = "Wall of Earth", - art = "art/T_Fissure", + art = "art/t_fissure", xmlText=[[ @@ -4227,14 +6570,14 @@ to this card." fontsize="30"/> { --here -- effect = createCardEffect(terramancer_wall_of_earth_carddef(), currentInPlayLoc) - .seq(grantHealthTarget((getCounter("wallofEarthCounter")), { SlotExpireEnum.LeavesPlay }, nullEffect(), "Wall of earth").apply(selectLoc(loc(currentPid, inPlayPloc)).where(isCardChampion().And(isCardName("terramancer_wall_of_earth"))))) - .seq(gainHealthEffect(divide(getCounter("wallofEarthCounter"), 2))) - .seq(resetCounterEffect("wallofEarthCounter")), - condition = getCounter("wallofEarthCounter").gte(2), + .seq(grantHealthTarget((getCounter("wallofEarth2Counter")), { SlotExpireEnum.LeavesPlay }, nullEffect(), "Wall of earth").apply(selectLoc(loc(currentPid, inPlayPloc)).where(isCardChampion().And(isCardName("terramancer_wall_of_earth"))))) + .seq(gainHealthEffect(divide(getCounter("wallofEarth2Counter"), 2))) + .seq(resetCounterEffect("wallofEarth2Counter")), + condition = getCounter("wallofEarth2Counter").gte(2), layout = layoutCard( { title = "Wall of Earth", - art = "art/T_Fissure", + art = "art/t_fissure", xmlText=[[ @@ -4252,16 +6595,11 @@ Gain {health} equal to half that number." fontsize="20" /> } } ), - cost = expendCost + cost = combineCosts({ + expendCost, + goldCost(2) + }) }), - createAbility({ - id = "terramancer_extract_earth_free_sac", - trigger = endOfTurnTrigger, - activations = singleActivation, - layout = cardLayout, - effect =transformTarget("terramancer_move_earth_skill").apply(selectSource()), - cost = noCost, - }), } }) @@ -4281,7 +6619,7 @@ function terramancer_wall_of_earth_carddef() }, buffDetails = createBuffDetails({ name = "Wall of Earth", - art = "art/T_Fissure", + art = "art/t_fissure", text = "Champions cannot be targeted while Wall of Earth is in play." }) }) @@ -4320,7 +6658,7 @@ function terramancer_wall_of_earth_carddef() layout = createLayout( { name = "Wall of Earth", - art = "art/T_Fissure", + art = "art/t_fissure", frame = "frames/druid_frames/druid_action_cardframe", xmlText=[[ @@ -4346,7 +6684,7 @@ function terramancer_swallowed_by_the_earth_def() local abilityLayout = createLayout({ name = "Swallowed by the Earth", - art = "art/sets/Dungeons/dragged_below", + art = "art/sets/dungeons/dragged_below", frame = "frames/druid_frames/druid_item_cardframe", xmlText=[[ @@ -4391,7 +6729,7 @@ function terramancer_swallowed_by_the_earth_def() layout = layoutCard( { title = "Swallowed by the Earth", - art = "art/sets/Dungeons/dragged_below", + art = "art/sets/dungeons/dragged_below", xmlText=[[ @@ -4414,7 +6752,7 @@ function terramancer_swallowed_by_the_earth_def() layout = layoutCard( { title = "Swallowed by the Earth", - art = "art/sets/Dungeons/dragged_below", + art = "art/sets/dungeons/dragged_below", xmlText=[[ @@ -4439,7 +6777,7 @@ function terramancer_swallowed_by_the_earth_def() }) }, layout = abilityLayout, - layoutPath= "art/sets/Dungeons/dragged_below" + layoutPath= "art/sets/dungeons/dragged_below" }) end @@ -4465,8 +6803,8 @@ function thandarlorian_combat_shield_carddef() promptType = showPrompt, layout = createLayout({ name = "Combat Shield", - art = "art/T_Cleric_Brightstar_Shield", - frame = "frames/Coop_Campaign_CardFrame", + art = "art/epicart/angeline_s_favor", + frame = "frames/coop_campaign_cardframe", xmlText = [[ @@ -4484,8 +6822,8 @@ function thandarlorian_combat_shield_carddef() }, layout = createLayout({ name = "Combat Shield", - art = "art/T_Cleric_Brightstar_Shield", - frame = "frames/Coop_Campaign_CardFrame", + art = "art/epicart/angeline_s_favor", + frame = "frames/coop_campaign_cardframe", xmlText = [[ @@ -4520,8 +6858,8 @@ function thandarlorian_coordinated_attack_carddef() promptType = showPrompt, layout = createLayout({ name = "Coordinated Attack", - art = "art/T_Rally_The_Troops", - frame = "frames/Coop_Campaign_CardFrame", + art = "art/epicart/insurgency", + frame = "frames/coop_campaign_cardframe", xmlText = [[ @@ -4538,8 +6876,8 @@ function thandarlorian_coordinated_attack_carddef() }, layout = createLayout({ name = "Coordinated Attack", - art = "art/T_Rally_The_Troops", - frame = "frames/Coop_Campaign_CardFrame", + art = "art/epicart/insurgency", + frame = "frames/coop_campaign_cardframe", xmlText = [[ @@ -4571,8 +6909,8 @@ function thandarlorian_hand_ballista_carddef() }, layout = createLayout({ name = "Hand Ballista", - art = "art/T_Light_Crossbow", - frame = "frames/Coop_Campaign_CardFrame", + art = "art/t_light_crossbow", + frame = "frames/coop_campaign_cardframe", xmlText = [[ @@ -4612,8 +6950,8 @@ function thandarlorian_bounty_hunters_stone_of_seeking_carddef() effect = gainGoldEffect(1), layout = createLayout({ name = "Bounty Hunter's Stone of Seeking", - art = "art/T_Wizard_Alchemist_S_Stone", - frame = "frames/Coop_Campaign_CardFrame", + art = "art/t_wizard_alchemist_s_stone", + frame = "frames/coop_campaign_cardframe", Text = [[ @@ -4629,8 +6967,8 @@ function thandarlorian_bounty_hunters_stone_of_seeking_carddef() }, layout = createLayout({ name = "Bounty Hunter's Stone of Seeking", - art = "art/T_Wizard_Alchemist_S_Stone", - frame = "frames/Coop_Campaign_CardFrame", + art = "art/t_wizard_alchemist_s_stone", + frame = "frames/coop_campaign_cardframe", xmlText = [[ @@ -4657,7 +6995,7 @@ function thandarlorian_bracer_blades_carddef() promptType = showPrompt, layout = createLayout({ name = "Bracer Blades", - art = "art/treasures/T_Bracers_Of_Brawn", + art = "art/treasures/t_bracers_of_brawn", xmlText = [[ @@ -4686,7 +7024,7 @@ Make this choice 3 times." fontsize="24"/> layout = layoutCard( { title = "Bracer Blades", - art = "art/treasures/T_Bracers_Of_Brawn", + art = "art/treasures/t_bracers_of_brawn", text = "Deal 3 damage to target champion." } ), @@ -4697,7 +7035,7 @@ Make this choice 3 times." fontsize="24"/> layout = layoutCard( { title = "Bracer Blades", - art = "art/treasures/T_Bracers_Of_Brawn", + art = "art/treasures/t_bracers_of_brawn", xmlText=[[ @@ -4725,7 +7063,7 @@ Make this choice 3 times." fontsize="24"/> layout = layoutCard( { title = "Bracer Blades", - art = "art/treasures/T_Bracers_Of_Brawn", + art = "art/treasures/t_bracers_of_brawn", text = "Deal 3 damage to target champion." } ), @@ -4736,7 +7074,7 @@ Make this choice 3 times." fontsize="24"/> layout = layoutCard( { title = "Bracer Blades", - art = "art/treasures/T_Bracers_Of_Brawn", + art = "art/treasures/t_bracers_of_brawn", xmlText=[[ @@ -4764,7 +7102,7 @@ Make this choice 3 times." fontsize="24"/> layout = layoutCard( { title = "Bracer Blades", - art = "art/treasures/T_Bracers_Of_Brawn", + art = "art/treasures/t_bracers_of_brawn", text = "Deal 3 damage to target champion." } ), @@ -4775,7 +7113,7 @@ Make this choice 3 times." fontsize="24"/> layout = layoutCard( { title = "Bracer Blades", - art = "art/treasures/T_Bracers_Of_Brawn", + art = "art/treasures/t_bracers_of_brawn", xmlText=[[ @@ -4794,7 +7132,7 @@ Make this choice 3 times." fontsize="24"/> }, layout = createLayout({ name = "Bracer Blades", - art = "art/treasures/T_Bracers_Of_Brawn", + art = "art/treasures/t_bracers_of_brawn", xmlText = [[ @@ -4808,15 +7146,15 @@ Make this choice 3 times." fontsize="24"/> ]] }), - layoutPath = "art/treasures/T_Bracers_Of_Brawn", + layoutPath = "art/treasures/t_bracers_of_brawn", }) end function thandarlorian_whipcord_carddef() local cardLayout = createLayout({ name = "Whipcord", - art = "art/T_Thief_Enchanted_Garrote", - frame = "frames/Coop_Campaign_CardFrame", + art = "art/treasures/barbarian_whip", + frame = "frames/coop_campaign_cardframe", xmlText = [[ @@ -4836,7 +7174,7 @@ function thandarlorian_whipcord_carddef() name = "Whipcord", types = { skillType }, layout = cardLayout, - layoutPath = "art/T_Thief_Enchanted_Garrote", + layoutPath = "art/treasures/barbarian_whip", abilities = { createAbility({ id = "thandarlorian_whipcord_ab", @@ -4866,7 +7204,7 @@ function thandarlorian_whipcord_carddef() layout = layoutCard( { title = "Whipcord", - art = "art/T_Thief_Enchanted_Garrote", + art = "art/treasures/barbarian_whip", xmlText = [[ @@ -4897,7 +7235,7 @@ function thandarlorian_whipcord_carddef() layout = layoutCard( { title = "Whipcord", - art = "art/T_Thief_Enchanted_Garrote", + art = "art/treasures/barbarian_whip", xmlText = [[ @@ -4921,8 +7259,8 @@ end function thandarlorian_dragon_shard_armour_carddef() local cardLayout = createLayout({ name = "Dragon Shard Armour", - art = "art/T_Fighter_Helm_Of_Fury_2", - frame = "frames/Coop_Campaign_CardFrame", + art = "art/epicart/lesser_angel", + frame = "frames/coop_campaign_cardframe", xmlText = [[ @@ -4939,7 +7277,7 @@ function thandarlorian_dragon_shard_armour_carddef() name = "Dragon Shard Armour", types = { skillType }, layout = cardLayout, - layoutPath = "art/T_Fighter_Helm_Of_Fury_2", + layoutPath = "art/epicart/lesser_angel", abilities = { createAbility({ id = "thandarlorian_dragon_shard_armour_ab", @@ -4981,7 +7319,7 @@ function paladin_warhammer_carddef() layout = layoutCard( { title = "Warhammer", - art = "art/T_Flesh_Ripper", + art = "art/t_flesh_ripper", text = "Gain " } ), @@ -4992,7 +7330,7 @@ function paladin_warhammer_carddef() layout = layoutCard( { title = "Warhammer", - art = "art/T_Flesh_Ripper", + art = "art/t_flesh_ripper", text = "Gain " } ), @@ -5009,8 +7347,8 @@ function paladin_warhammer_carddef() layout = createLayout( { name = "Warhammer", - art = "art/T_Flesh_Ripper", - frame = "frames/Cleric_CardFrame", + art = "art/t_flesh_ripper", + frame = "frames/cleric_cardframe", text = "Gain or Gain \n If you have played a weapon this turn, gain both.", } ) @@ -5068,7 +7406,7 @@ function paladin_crusader_carddef() { name = "Crusader", art = "avatars/man_at_arms", - frame = "frames/Cleric_CardFrame", + frame = "frames/cleric_cardframe", text = " or ", health = 2, isGuard = true @@ -5082,7 +7420,7 @@ function paladin_prayer_carddef() local cardLayout = createLayout({ name = "Prayer", art = "icons/wind_storm", - frame = "frames/Cleric_CardFrame", + frame = "frames/cleric_cardframe", text = " \n Gain \n Gain " }) @@ -5120,8 +7458,8 @@ function paladin_sacred_oath_carddef() promptType = showPrompt, layout = createLayout ({ name = "Sacred Oath", - art = "art/T_Devotion", - frame = "frames/Cleric_CardFrame", + art = "art/t_devotion", + frame = "frames/cleric_cardframe", text = " Prepare up to\n3 champions\nin play." }), effect = pushTargetedEffect({ @@ -5136,10 +7474,10 @@ function paladin_sacred_oath_carddef() }, layout = createLayout({ name = "Sacred Oath", - art = "art/T_Devotion", + art = "art/t_devotion", text = " Prepare up to\n3 champions\nin play." }), - layoutPath = "art/T_Devotion", + layoutPath = "art/t_devotion", }) end @@ -5158,8 +7496,8 @@ function siphon_life_def() promptType = showPrompt, layout = createLayout({ name = "Siphon Life", - art = "art/T_Life_Force", - frame = "frames/Wizard_CardFrame", + art = "art/t_life_force", + frame = "frames/wizard_cardframe", xmlText = [[ @@ -5173,8 +7511,8 @@ function siphon_life_def() }, layout = createLayout({ name = "Siphon Life", - art = "art/T_Life_Force", - frame = "frames/Wizard_CardFrame", + art = "art/t_life_force", + frame = "frames/wizard_cardframe", xmlText = [[ @@ -5182,7 +7520,7 @@ function siphon_life_def() ]] }), - layoutPath = "art/T_Life_Force" + layoutPath = "art/t_life_force" }) end @@ -5201,7 +7539,7 @@ function piercing_screech_def() promptType = showPrompt, layout = createLayout({ name = "Piercing Screech", - art = "art/T_Banshee", + art = "art/t_banshee", xmlText = [[ @@ -5217,7 +7555,7 @@ function piercing_screech_def() }, layout = createLayout({ name = "Piercing Screech", - art = "art/T_Banshee", + art = "art/t_banshee", xmlText = [[ @@ -5227,7 +7565,7 @@ function piercing_screech_def() ]] }), - layoutPath= "art/T_Banshee" + layoutPath= "art/t_banshee" }) end @@ -5235,7 +7573,7 @@ function witch_flash_freeze_carddef() local cardLayout = createLayout({ name = "Flash Freeze", art = "icons/ranger_fast_track", - frame = "frames/Wizard_CardFrame", + frame = "frames/wizard_cardframe", text = "
Expend target champion." }) @@ -5262,8 +7600,8 @@ end function witch_cauldron_carddef() local cardLayout = createLayout({ name = "Witch's Cauldron", - art = "art/T_Confused_Apparition", - frame = "frames/Coop_Campaign_CardFrame", + art = "art/t_confused_apparition", + frame = "frames/coop_campaign_cardframe", cardTypeLabel = "Item", xmlText = [[ @@ -5321,8 +7659,8 @@ function flame_burst_carddef() }, layout = createLayout({ name = "Flame Burst", - art = "art/T_Spreading_Sparks", - frame = "frames/Generic_CardFrame", + art = "art/t_spreading_sparks", + frame = "frames/generic_cardframe", xmlText = [[ @@ -5355,7 +7693,7 @@ function call_lightning_carddef() layout = createLayout({ name = "Call Lightning", art = "icons/wizard_soul_channel", - frame = "frames/Generic_CardFrame", + frame = "frames/generic_cardframe", xmlText = [[ @@ -5387,8 +7725,8 @@ function natures_blessing_carddef() }, layout = createLayout({ name = "Natures Blessing", - art = "art/T_Nature_S_Bounty", - frame = "frames/Generic_CardFrame", + art = "art/t_nature_s_bounty", + frame = "frames/generic_cardframe", xmlText = [[ @@ -5406,7 +7744,7 @@ function gift_of_the_elements_carddef() local protectionLayout = layoutCard({ name = "Protection", - art = "art/T_Splashing_Wave", + art = "art/t_splashing_wave", xmlText = [[ @@ -5420,7 +7758,7 @@ function gift_of_the_elements_carddef() local destructionLayout = layoutCard({ name = "Destruction", - art = "art/T_Spreading_Blaze", + art = "art/t_spreading_blaze", xmlText = [[ @@ -5463,7 +7801,7 @@ function gift_of_the_elements_carddef() layout = createLayout({ name = "Gift of the Elements", art = "icons/wind_storm", - frame = "frames/Generic_CardFrame", + frame = "frames/generic_cardframe", xmlText = [[ @@ -5549,8 +7887,8 @@ function midas_kings_adviser_carddef() }, layout = createLayout({ name = "Kings adviser", - art = "art/T_Tithe_Priest", - frame = "frames/Treasure_CardFrame", + art = "art/t_tithe_priest", + frame = "frames/treasure_cardframe", text = "Lean into the power of “No” to fend off the distractions of shiny new objects.", xmlText = [[ @@ -5589,7 +7927,7 @@ function midas_gold_carddef() layout = createLayout({ name = "Gold", art = "art/gold_male_pale", - frame = "frames/Treasure_CardFrame", + frame = "frames/treasure_cardframe", xmlText = [[ @@ -5623,8 +7961,8 @@ function midas_liquid_gold_carddef() }, layout = createLayout({ name = "Liquid gold", - art = "art/treasures/T_Cleric_Elixir_Golden", - frame = "frames/Treasure_CardFrame", + art = "art/treasures/t_cleric_elixir_golden", + frame = "frames/treasure_cardframe", xmlText = [[ @@ -5655,8 +7993,8 @@ function greed_is_good_skilldef() promptType = showPrompt, layout = createLayout({ name = "Greed is good", - art = "art/T_Bribe", - frame = "frames/Treasure_CardFrame", + art = "art/t_bribe", + frame = "frames/treasure_cardframe", text = "", xmlText = [[ @@ -5677,8 +8015,8 @@ function greed_is_good_skilldef() }, layout = createLayout({ name = "Greed is good", - art = "art/T_Bribe", - frame = "frames/Treasure_CardFrame", + art = "art/t_bribe", + frame = "frames/treasure_cardframe", text = "", xmlText = [[ @@ -5693,7 +8031,7 @@ function greed_is_good_skilldef() ]] }), - layoutPath= "art/T_Bribe" + layoutPath= "art/t_bribe" }) end @@ -5712,8 +8050,8 @@ function golden_touch_abilitydef() promptType = showPrompt, layout = createLayout({ name = "Golden touch", - art = "art/T_Strength_In_Numbers", - frame = "frames/Treasure_CardFrame", + art = "art/t_strength_in_numbers", + frame = "frames/treasure_cardframe", text = "", xmlText = [[ @@ -5732,8 +8070,8 @@ function golden_touch_abilitydef() }, layout = createLayout({ name = "Golden touch", - art = "art/T_Strength_In_Numbers", - frame = "frames/Treasure_CardFrame", + art = "art/t_strength_in_numbers", + frame = "frames/treasure_cardframe", text = "", xmlText = [[ @@ -5744,7 +8082,7 @@ function golden_touch_abilitydef() ]] }), - layoutPath= "art/T_Strength_In_Numbers" + layoutPath= "art/t_strength_in_numbers" }) end @@ -5775,8 +8113,8 @@ function brewmaster_mead_carddef() layout = createLayout( { name = "Mead", - art = "art/treasures/T_Fighter_Elixir_Red", - frame = "frames/Generic_CardFrame", + art = "art/treasures/t_fighter_elixir_red", + frame = "frames/generic_cardframe", xmlText=[[ @@ -5815,8 +8153,8 @@ function brewmaster_spilled_drink_carddef() layout = layoutCard( { name = "Spilled Drink", - art = "art/treasures/T_Ranger_Elixir_Yellow", - frame = "frames/Generic_CardFrame", + art = "art/treasures/t_ranger_elixir_yellow", + frame = "frames/generic_cardframe", xmlText=[[ @@ -5831,11 +8169,11 @@ function brewmaster_spilled_drink_carddef() tags = {gainCombatTag} }, { - effect = forceDiscard(1).seq(drawCardsEffect(1)), + effect = forceDiscard(1).seq(drawCardsEffect(1)), layout = createLayout({ name = "Spilled Drink", - art = "art/treasures/T_Ranger_Elixir_Yellow", - frame = "frames/Generic_CardFrame", + art = "art/treasures/t_ranger_elixir_yellow", + frame = "frames/generic_cardframe", xmlText=[[ @@ -5859,8 +8197,8 @@ then draw a card." fontsize="24" /> layout = createLayout( { name = "Spilled Drink", - art = "art/treasures/T_Ranger_Elixir_Yellow", - frame = "frames/Generic_CardFrame", + art = "art/treasures/t_ranger_elixir_yellow", + frame = "frames/generic_cardframe", xmlText=[[ @@ -5919,8 +8257,8 @@ function brewmaster_tap_a_keg_carddef() layout = createLayout( { name = "Tap a Keg", - art = "art/treasures/T_Ranger_Elixir_Orange", - frame = "frames/Generic_CardFrame", + art = "art/treasures/t_ranger_elixir_orange", + frame = "frames/generic_cardframe", xmlText=[[ @@ -5991,7 +8329,7 @@ function brewmaster_serve_the_strong_stuff_carddef() }, createBuffDetails({ - art = "art/treasures/T_Bottle_Of_Rum", + art = "art/treasures/t_bottle_of_rum", name = "Serve the strong stuff", text = "Your first purchase this turn costs +1 gold." }), @@ -5999,8 +8337,8 @@ function brewmaster_serve_the_strong_stuff_carddef() layout = createLayout( { name = "Serve the strong stuff", - art = "art/treasures/T_Bottle_Of_Rum", - frame = "frames/Generic_CardFrame", + art = "art/treasures/t_bottle_of_rum", + frame = "frames/generic_cardframe", xmlText=[[ @@ -6041,7 +8379,7 @@ function brewmaster_regular_regulars_carddef() { name = "Regular Regulars", art = "avatars/cristov_s_recruits", - frame = "frames/Generic_CardFrame", + frame = "frames/generic_cardframe", xmlText=[[ @@ -6091,8 +8429,8 @@ function brewmaster_irregular_regulars_carddef() layout = createLayout( { name = "Iregular Regulars", - art = "art/T_Orc_Riot", - frame = "frames/Generic_CardFrame", + art = "art/t_orc_riot", + frame = "frames/generic_cardframe", xmlText=[[ @@ -6143,8 +8481,8 @@ function brewmaster_zaboozer_carddef() layout = createLayout( { name = "Zaboozer, Heroic Regular", - art = "art/T_Maurader", - frame = "frames/Generic_CardFrame", + art = "art/t_maurader", + frame = "frames/generic_cardframe", xmlText=[[ @@ -6179,8 +8517,8 @@ function brewmaster_a_round_on_the_house_def() local cardLayout = createLayout({ name = "A round on the House", - art = "art/T_Unify_Apsara", - frame = "frames/Generic_CardFrame", + art = "art/t_unify_apsara", + frame = "frames/generic_cardframe", xmlText=[[ @@ -6202,7 +8540,7 @@ Then, ALL champions (yours and opponent's) get -1{shield} until they leave play. name = "A round on the House", types = { skillType }, layout = cardLayout, - layoutPath = "art/T_Unify_Apsara", + layoutPath = "art/t_unify_apsara", abilities = { createAbility({ id = "a_round_on_the_house_ab", @@ -6244,8 +8582,8 @@ function brewmaster_get_out_of_my_bar_carddef() promptType = showPrompt, layout = createLayout({ name = "Get out of my bar", - art = "art/T_orc", - frame = "frames/Generic_CardFrame", + art = "art/t_orc", + frame = "frames/generic_cardframe", xmlText=[[ @@ -6269,8 +8607,8 @@ Or draw a card." fontsize="18" /> layout = layoutCard( { name = "Get out of my bar", - art = "art/T_orc", - frame = "frames/Generic_CardFrame", + art = "art/t_orc", + frame = "frames/generic_cardframe", xmlText=[[ @@ -6286,7 +8624,7 @@ Or draw a card." fontsize="18" /> }, { effect = pushTargetedEffect({ - desc = "Sacrifice a champion from opponent's discard pile. Only useable on champions stunned this turn.", + desc = "Set aside a champion you stunned this turn. The next time your opponent shuffles their deck, put that champion on the bottom of their deck.", min=0, max=1, validTargets = selectLoc(loc(oppPid, discardPloc)).where(isCardChampion()).where(isCardStunned()), @@ -6295,8 +8633,8 @@ Or draw a card." fontsize="18" /> }), layout = createLayout({ name = "Get out of my bar", - art = "art/T_orc", - frame = "frames/Generic_CardFrame", + art = "art/t_orc", + frame = "frames/generic_cardframe", xmlText=[[ @@ -6317,8 +8655,8 @@ Or draw a card." fontsize="18" /> }, layout = createLayout({ name = "Get out of my bar", - art = "art/T_orc", - frame = "frames/Generic_CardFrame", + art = "art/t_orc", + frame = "frames/generic_cardframe", xmlText=[[ @@ -6334,7 +8672,7 @@ Or draw a card." fontsize="18" /> ]] }), - layoutPath = "art/T_orc", + layoutPath = "art/t_orc", }) end @@ -6343,7 +8681,7 @@ end function bird_dog_def() local layout = createLayout({ name = "BirdWalking", - art = "art/T_Spark", + art = "art/t_spark", xmlText = [[ @@ -6371,19 +8709,19 @@ function bird_dog_def() }) }, layout = layout, - layoutPath= "art/T_Spark" + layoutPath= "art/t_spark" }) end function patron_shoutout_def() local layout = createLayout({ name = "Shoutout to the Patrons", - art = "art/T_Command", + art = "art/t_command", xmlText = [[{gold_3}]] }) local promptLayout = createLayout({ name = "Shoutout to the Patrons", - art = "art/T_Command", + art = "art/t_command", text = format("Gain {0} gold.", { 3 }), }) @@ -6405,15 +8743,15 @@ function patron_shoutout_def() }) }, layout = layout, - layoutPath= "art/T_Command" + layoutPath= "art/t_command" }) end function hero_dash_helper_carddef() local cardLayout = createLayout({ name = "Hero-Helper Plug", - art = "art/T_Stone_Guardian", - frame = "frames/Warrior_CardFrame", + art = "art/t_stone_guardian", + frame = "frames/warrior_cardframe", cardTypeLabel = "Champion", types = { championType, noStealType }, text = "
_________________
Visit: Hero-Helper.com" @@ -6441,8 +8779,8 @@ end function situational_card_carddef() local cardLayout = createLayout({ name = "Situational Card", - art = "art/T_Elixir_Of_Fortune", - frame = "frames/Cleric_Frames/Cleric_Treasure_CardFrame", + art = "art/t_elixir_of_fortune", + frame = "frames/cleric_frames/cleric_treasure_cardframe", cardTypeLabel = "Item", text = "
_______________
I would buy this over
Tithe Priest." }) @@ -6468,19 +8806,19 @@ end function wwyd_carddef() local cardLayout = createLayout({ name = "WWYD?", - art = "art/T_Edge_Of_The_Moat", - frame = "frames/Ranger_CardFrame", + art = "art/t_edge_of_the_moat", + frame = "frames/ranger_cardframe", cardTypeLabel = "Action", text = " or
If you have Street Thug in play, do both." }) local faceLayout = layoutCard({ title = "WWYD?", - art = "art/T_Blazing_Fire", + art = "art/t_blazing_fire", text = "" }) local removalLayout = layoutCard({ title = "WWYD?", - art = "art/T_Evangelize", + art = "art/t_evangelize", text = "" }) @@ -6519,8 +8857,8 @@ end function nostra_dbl_damus_carddef() local cardLayout = createLayout({ name = "NostraDblDamus", - art = "art/T_Channel", - frame = "frames/Wizard_CardFrame", + art = "art/t_channel", + frame = "frames/wizard_cardframe", cardTypeLabel = "Champion", types = { championType, noStealType }, text = "
Reveal the top card of your
deck. Discard it or put it back
on the top of your deck.
_________________
Visions of Thandar!" @@ -6562,8 +8900,8 @@ end function blank_to_my_blank_carddef() local cardLayout = createLayout({ name = "The _____ To My _____ ", - art = "art/T_Paladin_Sword", - frame = "frames/Warrior_CardFrame", + art = "art/t_paladin_sword", + frame = "frames/warrior_cardframe", cardTypeLabel = "Item", text = "" }) @@ -6589,8 +8927,8 @@ end function congrats_youre_a_nerd_carddef() local cardLayout = createLayout({ name = "Congrats, You're A Nerd", - art = "art/treasures/T_Wise_Cat_Familiar", - frame = "frames/Wizard_CardFrame", + art = "art/treasures/wizard_wizened_familiar", + frame = "frames/wizard_cardframe", cardTypeLabel = "Action", text = "Opponent discards 1.
_______________
When this card goes into your discard pile, put it on the bottom of your deck." }) @@ -6648,8 +8986,8 @@ function ee_demon_hunter_carddef() promptType = showPrompt, layout = createLayout({ name = "Demon Hunter", - art = "art/T_Paladin_Sword", - frame = "frames/Imperial_Champion_CardFrame", + art = "art/t_paladin_sword", + frame = "frames/imperial_champion_cardframe", xmlText = [[ @@ -6676,8 +9014,8 @@ Hit up to 3 champions for 2 damage each." fontsize="22" /> effect = gainCombatEffect(4), layout = createLayout({ name = "Demon Hunter", - art = "art/T_Paladin_Sword", - frame = "frames/Imperial_Champion_CardFrame", + art = "art/t_paladin_sword", + frame = "frames/imperial_champion_cardframe", xmlText = [[ @@ -6696,8 +9034,8 @@ Hit up to 3 champions for 2 damage each." fontsize="22" /> }), layout = createLayout({ name = "Demon Hunter", - art = "art/T_Paladin_Sword", - frame = "frames/Imperial_Champion_CardFrame", + art = "art/t_paladin_sword", + frame = "frames/imperial_champion_cardframe", xmlText = [[ @@ -6709,100 +9047,99 @@ Hit up to 3 champions for 2 damage each." fontsize="22" /> } }) }), - - createAbility({ - id = "demon_hunter_ally", + + createAbility({ + id = "demon_hunter_2ally", trigger = uiTrigger, - cost = noCost, - activations = singleActivation, - effect = drawCardsEffect(1), + effect = gainHealthEffect(selectLoc(loc(oppPid, discardPloc)).where(isCardStunned()).count().multiply(2)), promptType = showPrompt, layout = layoutCard( { name = "Demon Hunter", - art = "art/T_Paladin_Sword", - frame = "frames/Imperial_Champion_CardFrame", + art = "art/t_paladin_sword", + frame = "frames/imperial_champion_cardframe", xmlText=[[ - + ]] } - ), + ), + cost = noCost, + activations = singleActivation, + check = constBoolExpression(true), allyFactions = {imperialFaction} - }), + }), - createAbility({ - id = "demon_hunter_2ally", + createAbility({ + id = "demon_hunter_ally", trigger = uiTrigger, - effect = gainHealthEffect(selectLoc(loc(oppPid, discardPloc)).where(isCardStunned()).count().multiply(2)), + cost = noCost, + activations = singleActivation, + effect = drawCardsEffect(1), promptType = showPrompt, layout = layoutCard( { name = "Demon Hunter", - art = "art/T_Paladin_Sword", - frame = "frames/Imperial_Champion_CardFrame", + art = "art/t_paladin_sword", + frame = "frames/imperial_champion_cardframe", xmlText=[[ - + ]] } - ), - cost = noCost, - activations = singleActivation, - check = constBoolExpression(true), - allyFactions = {imperialFaction,imperialFaction} + ), + allyFactions = {imperialFaction, imperialFaction} }), + }, layout = createLayout( { name = "Demon Hunter", - art = "art/T_Paladin_Sword", - frame = "frames/Imperial_Champion_CardFrame", + art = "art/epicart/white_knight", + frame = "frames/imperial_champion_cardframe", cost = 5, xmlText=[[ - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + +
]], health = 5, @@ -6842,8 +9179,8 @@ function mythic_mercs_ee_scrapforks_carddef() layout = createLayout( { name = "Scrapforks", - art = "art/T_Fighter_Male", - frame = "frames/Treasure_CardFrame", + art = "art/epicart/lord_of_the_arena", + frame = "frames/treasure_cardframe", cost = 4, xmlText=[[ @@ -6888,7 +9225,8 @@ function mythic_mercs_ee_dblducks_carddef() selector = selectLoc(currentRevealLoc), take = const(1), -- number of cards to take for split sort = const(1), -- number of cards to be sorted for ef2 - ef1 = moveToTopDeckTarget(true), -- effect to be applied to cards left + minTake = const(1), -- number of mandatory cards moved to ef2 + ef1 = moveToTopDeckTarget(true, 1), -- effect to be applied to cards left ef2 = moveToBottomDeckTarget(true, 1), -- effect to be applied to sorted cards header = "Gaze into the future", -- prompt header description = "Look at the top two cards of your deck. Put one on the top and one on the bottom of your deck.", @@ -6905,8 +9243,8 @@ function mythic_mercs_ee_dblducks_carddef() layout = createLayout( { name = "DblDucks", - art = "art/T_Influence", - frame = "frames/Treasure_CardFrame", + art = "art/epicart/banishment", + frame = "frames/treasure_cardframe", cost = 4, xmlText=[[ @@ -6979,8 +9317,8 @@ function mythic_mercs_ee_parsons_carddef() effect = addSlotToTarget(createFactionsSlot({ imperialFaction }, { leavesPlayExpiry })).apply(selectSource()), layout = createLayout({ name = "Parons the Insider", - art = "art/T_Thief_Male_Alternate", - frame = "frames/Imperial_Champion_CardFrame", + art = "art/t_thief_male_alternate", + frame = "frames/imperial_champion_cardframe", xmlText = [[ @@ -6993,8 +9331,8 @@ function mythic_mercs_ee_parsons_carddef() effect = addSlotToTarget(createFactionsSlot({ wildFaction }, { leavesPlayExpiry })).apply(selectSource()), layout = createLayout({ name = "Parons the Insider", - art = "art/T_Thief_Male_Alternate", - frame = "frames/Wild_Champion_CardFrame", + art = "art/t_thief_male_alternate", + frame = "frames/wild_champion_cardframe", xmlText = [[ @@ -7007,8 +9345,8 @@ function mythic_mercs_ee_parsons_carddef() effect = addSlotToTarget(createFactionsSlot({ guildFaction }, { leavesPlayExpiry })).apply(selectSource()), layout = createLayout({ name = "Parons the Insider", - art = "art/T_Thief_Male_Alternate", - frame = "frames/Guild_Champion_CardFrame", + art = "art/t_thief_male_alternate", + frame = "frames/guild_champion_cardframe", xmlText = [[ @@ -7021,8 +9359,8 @@ function mythic_mercs_ee_parsons_carddef() effect = addSlotToTarget(createFactionsSlot({ necrosFaction }, { leavesPlayExpiry })).apply(selectSource()), layout = createLayout({ name = "Parons the Insider", - art = "art/T_Thief_Male_Alternate", - frame = "frames/Necros_Champion_CardFrame", + art = "art/t_thief_male_alternate", + frame = "frames/necros_champion_cardframe", xmlText = [[ @@ -7044,8 +9382,8 @@ function mythic_mercs_ee_parsons_carddef() layout = createLayout( { name = "Parsons the Insider", - art = "art/T_Thief_Male_Alternate", - frame = "frames/Treasure_CardFrame", + art = "art/t_thief_male_alternate", + frame = "frames/treasure_cardframe", cost = 5, xmlText=[[ @@ -7070,7 +9408,6 @@ You may put a card from your discard pile onto the bottom of your deck" fontsize ) end - function mythic_mercs_ee_stigmalingpa_carddef() return createChampionDef( { @@ -7103,8 +9440,8 @@ function mythic_mercs_ee_stigmalingpa_carddef() layout = createLayout( { name = "Stigmalingpa", - art = "art/T_Wizard_Runic_Robes", - frame = "frames/Treasure_CardFrame", + art = "art/epicart/reusable_knowledge", + frame = "frames/treasure_cardframe", cost = 4, xmlText=[[ @@ -7170,8 +9507,8 @@ function mythic_mercs_ee_agent_th_carddef() layout = createLayout( { name = "Agent Teeth Hurting", - art = "art/T_Elven_Curse", - frame = "frames/Treasure_CardFrame", + art = "art/epicart/psionic_assault", + frame = "frames/treasure_cardframe", cost = 4, xmlText=[[ @@ -7228,8 +9565,8 @@ function cat_familiar_token_carddef() effect = gainCombatEffect(1), layout = createLayout({ name = "Familiar Cat", - art = "art/T_Cat_Familiar", - frame = "frames/Treasure_CardFrame", + art = "art/t_cat_familiar", + frame = "frames/treasure_cardframe", xmlText = [[ @@ -7242,8 +9579,8 @@ function cat_familiar_token_carddef() effect = gainGoldEffect(1), layout = createLayout({ name = "Familiar Cat", - art = "art/T_Cat_Familiar", - frame = "frames/Treasure_CardFrame", + art = "art/t_cat_familiar", + frame = "frames/treasure_cardframe", xmlText = [[ @@ -7256,8 +9593,8 @@ function cat_familiar_token_carddef() effect = gainHealthEffect(1), layout = createLayout({ name = "Familiar Cat", - art = "art/T_Cat_Familiar", - frame = "frames/Treasure_CardFrame", + art = "art/t_cat_familiar", + frame = "frames/treasure_cardframe", xmlText = [[ @@ -7286,8 +9623,8 @@ function cat_familiar_token_carddef() layout = createLayout( { name = "Familiar Cat", - art = "art/T_Cat_Familiar", - frame = "frames/Treasure_CardFrame", + art = "art/t_cat_familiar", + frame = "frames/treasure_cardframe", xmlText=[[ @@ -7308,6 +9645,7 @@ function cat_familiar_token_carddef() ) end + function kit_cat_token_carddef() --This is a token champion, that self-sacrifices when it leaves play return createChampionDef( @@ -7327,7 +9665,6 @@ function kit_cat_token_carddef() cost = expendCost, activations = multipleActivations, effect = hitOpponentEffect(2) - } ) , @@ -7345,8 +9682,8 @@ function kit_cat_token_carddef() layout = createLayout( { name = "Kit Cat", - art = "art/treasures/T_Wise_Cat_Familiar", - frame = "frames/Treasure_CardFrame", + art = "art/treasures/wizard_wizened_familiar", + frame = "frames/treasure_cardframe", xmlText=[[ @@ -7404,8 +9741,8 @@ function chunky_cat_token_carddef() layout = createLayout( { name = "Kit Cat Chunky", - art = "art/treasures/T_Fat_Cat_Familiar", - frame = "frames/Treasure_CardFrame", + art = "art/treasures/wizard_content_familiar", + frame = "frames/treasure_cardframe", xmlText=[[ @@ -7426,7 +9763,6 @@ function chunky_cat_token_carddef() ) end - function big_bad_kitty_token_carddef() --This is a token champion, that self-sacrifices when it leaves play return createChampionDef( @@ -7464,8 +9800,8 @@ function big_bad_kitty_token_carddef() layout = createLayout( { name = "Big Bad Kitty", - art = "art/T_Strength_of_the_wolf", - frame = "frames/Treasure_CardFrame", + art = "art/t_strength_of_the_wolf", + frame = "frames/treasure_cardframe", xmlText=[[ @@ -7523,8 +9859,8 @@ function surprise_dragon_token_carddef() layout = createLayout( { name = "Surprise Dragon", - art = "art/T_Arkus_Imperial_Dragon", - frame = "frames/Treasure_CardFrame", + art = "art/epicart/dragonling", + frame = "frames/treasure_cardframe", xmlText=[[ @@ -7579,6 +9915,38 @@ function setupGame(g) registerCards( g, { + choose_lich_carddef(), + lich_corruption_carddef(), + lich_soul_diamond_carddef(), + lich_minor_summoning_carddef(), + lich_major_summoning_carddef(), + lich_frozen_touch_carddef(), + lich_soul_crush_carddef(), + lich_abomination_minion_carddef(), + lich_banshee_minion_carddef(), + lich_ghoul_minion_carddef(), + lich_revenant_minion_carddef(), + lich_skeleton_horde_minion_carddef(), + lich_wall_of_bones_minion_carddef(), + lich_wall_of_fire_minion_carddef(), + lich_zombie_minion_carddef(), + lich_SJ_DI_carddef(), + lich_SJ_HoE_carddef(), + lich_SJ_IW_carddef(), + lich_SJ_M_carddef(), + lich_SJ_UR_carddef(), + lich_SJ_V_carddef(), + choose_trickster_carddef(), + trickster_reshuffle_hand_skill(), + trickster_hidden_power_card_def(), + trickster_double_or_nothing_card_def(), + trickster_pet_monkey_card_def(), + trickster_blackmarket_the_docks_carddef(), + trickster_blackmarket_the_shambles_carddef(), + trickster_blackmarket_the_sewers_carddef(), + trickster_blackmarket_the_back_room_carddef(), + trickster_blackmarket_the_catacombs_carddef(), + trickster_stack_the_deck_ability_def(), choose_demonologist_carddef(), demonologist_shadow_gem_carddef(), demonologist_shadow_feeder_carddef(), @@ -7598,28 +9966,28 @@ function setupGame(g) apothecary_yellow_potion_carddef(), apothecary_mezzaluna_carddef(), choose_cryomancer_carddef(), - cryomancer_ice_burst_carddef(), - cryomancer_ice_shield_carddef(), + cryomancer_ice_sickle_carddef(), + cryomancer_frostwulf_carddef(), cryomancer_ice_gem_carddef(), cryomancer_freeze_carddef(), cryomancer_frostbite_carddef(), + cryomancer_permafrost_carddef(), + cryomancer_freezing_fog_carddef(), choose_pyromancer_carddef(), pyromancer_sear_carddef(), pyromancer_scorch_carddef(), pyromancer_fire_shard_carddef(), pyromancer_combust_carddef(), - pyromancer_fuel_1carddef(), - pyromancer_fuel_2carddef(), - pyromancer_fuel_3carddef(), - pyromancer_fuel_4carddef(), - pyromancer_fuel_5carddef(), + pyromancer_1p_fuel_1carddef(), + pyromancer_2p_fuel_1carddef(), choose_terramancer_carddef(), terramancer_earth_gem_carddef(), terramancer_hurl_boulder_carddef(), terramancer_tremor_carddef(), - terramancer_clay_golem_carddef(), - terramancer_move_earth_carddef(), - terramancer_move_earth_free_carddef(), + terramancer_clay_golem1_carddef(), + terramancer_clay_golem2_carddef(), + terramancer_move_earth1_carddef(), + terramancer_move_earth2_carddef(), terramancer_swallowed_by_the_earth_def(), choose_thandarlorian_carddef(), thandarlorian_coordinated_attack_carddef(), @@ -7665,6 +10033,7 @@ function setupGame(g) nostra_dbl_damus_carddef(), blank_to_my_blank_carddef(), congrats_youre_a_nerd_carddef(), + --Easter egg cards --[[mythic_mercs_ee_scrapforks_carddef(), mythic_mercs_ee_dblducks_carddef(), @@ -7683,11 +10052,12 @@ function setupGame(g) standardSetup( g, { - description = "Lilu Dallas Multi-Class. Multi-class script by Aarkenell and Userkaffe. Classes by Aarkenell, Userkaffe, AgentC13, Filtrophobe, Zabuza & WardenSlayer. Updated 12.04.2024.", + description = "Multi-class script by Aarkenell and Userkaffe. Classes by Aarkenell, Userkaffe, AgentC13, Filtrophobe, Zabuza & WardenSlayer. Updated 19.04.2025.", playerOrder = { plid1, plid2 }, ai = ai.CreateKillSwitchAi(createAggressiveAI(), createHardAi2()), timeoutAi = createTimeoutAi(), opponents = { { plid1, plid2 } }, + centerRow = { --[["demon_hunter"]] }, tradeDeckExceptions = { -- here we set which cards populate market deck --[[ { qty=1, cardId="scrapforks" }, @@ -7751,4 +10121,4 @@ function setupMeta(meta) meta.features = { } -end +end \ No newline at end of file diff --git a/Jseph/random_testing_sandbox.lua b/Jseph/random_testing_sandbox.lua new file mode 100644 index 0000000..8917020 --- /dev/null +++ b/Jseph/random_testing_sandbox.lua @@ -0,0 +1,289 @@ +require 'herorealms' +require 'decks' +require 'stdlib' +require 'stdcards' +require 'hardai' +require 'mediumai' +require 'easyai' + +function trickster_reshuffle_effect() + return pushTargetedEffect({ + desc = "Put X cards back in your deck, shuffle, and then draw X cards.", + min=1, + max=selectLoc(loc(currentPid, handPloc)).count(), + validTargets = selectLoc(loc(currentPid, handPloc)), + targetEffect = moveTarget(loc(currentPid, deckPloc)) + .seq(resetCounterEffect("trickster_reshuffle_card_count")) + .seq(incrementCounterEffect("trickster_reshuffle_card_count", selectTargets().count())) + .seq(animateShuffleDeckEffect(currentPid)) + .seq(shuffleEffect(loc(currentPid, deckPloc))) + .seq(drawCardsEffect(s.getCounter("trickster_reshuffle_card_count"))) + }) +end + +function trickster_reshuffle_hand_skill() + local cardLayout = createLayout({ + name = "Mulligan", + art = "art/epicart/erratic_research", + frame = "frames/alchemist_frames/alchemist_item_cardframe", + text = "Put X cards back in your deck, shuffle, and then draw X cards." + }) + local drawCardLayout = createLayout({ + name = "Draw Card", + art = "art/epicart/erratic_research", + frame = "frames/alchemist_frames/alchemist_item_cardframe", + text = "Draw a card" + }) + local discardRandomCardLayout = createLayout({ + name = "Discard Random Card", + art = "art/epicart/erratic_research", + frame = "frames/alchemist_frames/alchemist_item_cardframe", + text = "Discard a random card (chosen by randomTarget)" + }) + local acquireCardForFreeLayout = createLayout({ + name = "Acquire Card for Free", + art = "art/epicart/erratic_research", + frame = "frames/alchemist_frames/alchemist_item_cardframe", + text = "Acquire a random card (chosen by randomTarget) for free" + }) + card = createSkillDef({ + id = "trickster_reshuffle_skill_jseph", + name = "Reshuffle", + abilities = { + createAbility({ + id = "trickster_reshuffle_ability_id_jseph", + trigger = uiTrigger, + promptType = showPrompt, + layout = cardLayout, + effect = trickster_reshuffle_effect(), + cost = noCost, + check = selectLoc(loc(currentPid, handPloc)).count() + .gte(const(1)), + activations = multipleActivations, + }), + createAbility({ + id = "trickster_reshuffle_draw_a_card", + trigger = uiTrigger, + promptType = showPrompt, + layout = drawCardLayout, + effect = drawCardsEffect(const(1)), + cost = noCost, + activations = multipleActivations, + }), + createAbility({ + id = "trickster_reshuffle_discard_a_card", + trigger = uiTrigger, + promptType = showPrompt, + layout = discardRandomCardLayout, + effect = randomTarget( + const(1), moveTarget(loc(currentPid, discardPloc)) + ).apply(selectLoc(currentHandLoc)), + cost = noCost, + activations = multipleActivations, + }), + createAbility({ + id = "trickster_reshuffle_acquire_a_card_for_free", + trigger = uiTrigger, + promptType = showPrompt, + layout = acquireCardForFreeLayout, + effect = randomTarget( + const(1), moveTarget(loc(currentPid, discardPloc)) + ).apply(selectLoc(centerRowLoc)), + cost = noCost, + activations = multipleActivations, + }) + }, + layout = cardLayout, + layoutPath = "art/epicart/erratic_research", + }) + return card +end + +local n = 2 + +function n_cost_card_carddef(i) + local layout = createLayout({ + name = "Card " .. i, + art = "art/epicart/erratic_research", + frame = "frames/alchemist_frames/alchemist_item_cardframe", + text = "Cost: " .. i .. " card" + }) + return createItemDef({ + id = "n_cost_card_jseph_" .. i, + name = "Card " .. i, + abilities = {}, + acquireCost = i, + layout = layout, + layoutPath = "art/epicart/erratic_research", + }) +end + +function shuffle_test_skill() + local cardLayout = createLayout({ + name = "Shuffle Test", + art = "art/epicart/erratic_research", + frame = "frames/alchemist_frames/alchemist_item_cardframe", + text = "Shuffle a deck of " .. n .. " cards." + }) + -- Setup the effect to shuffle n cards into the deck + local setup_effect = sacrificeTarget().apply(selectLoc(loc(currentPid, deckPloc))) + for i = 1, n do + setup_effect = setup_effect.seq(createCardEffect(n_cost_card_carddef(i), loc(currentPid, deckPloc))) + end + --[[local shuffle_and_count_effect = moveTarget(loc(oppPid, asidePloc)).apply(selectLoc(loc(currentPid, asidePloc))) + .seq(moveTarget(loc(currentPid, asidePloc)).apply(selectLoc(loc(oppPid, asidePloc)).order(cardCost()))) + .seq(shuffleEffect(loc(currentPid, asidePloc)))]] + local shuffle_and_count_effect = moveToBottomDeckTarget(true, 0).apply(selectLoc(loc(currentPid, deckPloc)).order(cardCost())) + .seq(shuffleEffect(loc(currentPid, deckPloc))) + for i = 1, n do + for j = 1, n do + shuffle_and_count_effect = shuffle_and_count_effect.seq( + incrementCounterEffect("shuffle_test_counter (" .. i .. ", " .. j .. ")", + selectLoc(loc(currentPid, deckPloc)).take(const(i)).reverse().take(const(1)).where(cardCost().eq(const(j))).count() + ) + ) + end + end + -- Print results + local print_results_effect = nullEffect() + for i = 1, n do + for j = 1, n do + print_results_effect = print_results_effect.seq( + showTextExpressionEffect(format("Result ({0}, {1}) = {2}", {i, j, getCounter("shuffle_test_counter (" .. i .. ", " .. j .. ")")})) + ) + end + end + local full_effect = setup_effect + for i = 1, 100 do + full_effect = full_effect.seq(shuffle_and_count_effect) + end + full_effect = full_effect.seq(print_results_effect) + + card = createSkillDef({ + id = "shuffle_test_skill_jseph", + name = "Shuffle Test", + trigger = uiTrigger, + abilities = { + createAbility({ + id = "shuffle_test_ability_id_jseph", + trigger = uiTrigger, + promptType = showPrompt, + layout = cardLayout, + effect = full_effect, + cost = noCost, + check = selectLoc(loc(currentPid, handPloc)).count() + .gte(const(1)), + activations = multipleActivations, + }) + }, + activations = multipleActivations, + cost = noCost, + layout = cardLayout, + layoutPath = "art/epicart/erratic_research", + }) + return card +end + + +function setupGame(g) + local cards_to_register = {} + for i = 1, n do + table.insert(cards_to_register, n_cost_card_carddef(i)) + end + table.insert(cards_to_register, trickster_reshuffle_hand_skill()) + table.insert(cards_to_register, shuffle_test_skill()) + registerCards(g, cards_to_register) + + standardSetup(g, { + description = "Random Testing Sandbox", + --This defines the play order, only 2 players is supported at this time + playerOrder = { plid1, plid2 }, + --AI is optional but I always include it to have the option to test with an AI player + -- + ai = ai.CreateKillSwitchAi(createAggressiveAI(), createHardAi2()), + timeoutAi = createTimeoutAi(), + opponents = { { plid1, plid2 } }, + --This player object contains/creates all the information about each player. + --In this example the data is imported from the player/character (ie the class, health, and starting cards) + players = { + { + id = plid1, + init = { + --This is the part where the character data is imported + --fromEnv = plid1 + }, + --Uncomment the line below to make this player an AI. One note about AI, it doesnt work in online games so make sure to comment it back out before uploading + --isAi = true, + --This sets how many cards a player has on thier first turn (player 1 normally has only 3) + startDraw = 3, + health = 10, + name = "Trickster", + avatar = "smugglers", + cards = { + --Since the starting deck cards/skills are predetermined, only buffs need to be here + buffs = { + --This sets how many cards a player draws at the end of the turn, normally 5 + drawCardsCountAtTurnEndDef(5), + --This handles the case where a player needs to discard a card at the start of their turn (discard effects/skills) + discardCardsAtTurnStartDef(), + --This is used to track the turn counter so the "Enrage" syste, triggers to try and end the game after the set number of turns. + fatigueCount(40, 1, "FatigueP1"), + }, + skills = { + trickster_reshuffle_hand_skill(), + shuffle_test_skill(), + } + + } + }, + { + id = plid2, + --Uncomment the line below to make this player an AI. One note about AI, it doesnt work in online games so make sure to comment it back out before uploading + --isAi = true, + --This sets how many cards a player has on thier first turn (player 1 normally has only 3) + startDraw = 5, + health = 10, + init = { + -- This is the part where the character data is imported + -- fromEnv = plid2 + }, + cards = { + --Since the starting deck cards/skills are predetermined, only buffs need to be here + buffs = { + --This sets how many cards a player draws at the end of the turn, normally 5 + drawCardsCountAtTurnEndDef(5), + --This handles the case where a player needs to discard a card at the start of their turn (discard effects/skills) + discardCardsAtTurnStartDef(), + --This is used to track the turn counter so the "Enrage" syste, triggers to try and end the game after the set number of turns. + fatigueCount(40, 1, "FatigueP2"), + }, + skills = { + trickster_reshuffle_hand_skill(), + } + } + }, + } + }) +end + + +function endGame(g) -- more info on this later +end + + + + + + + function setupMeta(meta) + meta.name = "random_testing_sandbox" + meta.minLevel = 0 + meta.maxLevel = 0 + meta.introbackground = "" + meta.introheader = "" + meta.introdescription = "" + meta.path = "C:/Users/jseph/hero-realms-lua-scripts/hero-realms-lua-scripts/Jseph/random_testing_sandbox.lua" + meta.features = { +} + + end \ No newline at end of file diff --git a/Jseph/trickster.lua b/Jseph/trickster.lua index 7ba4d76..0bdf576 100644 --- a/Jseph/trickster.lua +++ b/Jseph/trickster.lua @@ -25,22 +25,19 @@ Decrease the cost of two random cards in the market by {gold_2} until your next ]] pet_monkey_layout = [[ - - - - - - - - - - - - - + + + ]] +black_market_layout = [[ + + + + +]] + hidden_power_layout = [[