From 7018d415c2ef3692ebb0decc87b587136b567ce6 Mon Sep 17 00:00:00 2001 From: Simisays <67333662+Simisays@users.noreply.github.com> Date: Thu, 9 Mar 2023 15:42:07 +0100 Subject: [PATCH 1/4] update --- .../Shandalar/maps/main.tiled-session | 20 +++++++++---------- .../Shandalar/maps/map/kiora_island.tmx | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/forge-gui/res/adventure/Shandalar/maps/main.tiled-session b/forge-gui/res/adventure/Shandalar/maps/main.tiled-session index b48752f88ad..75a908aaedd 100644 --- a/forge-gui/res/adventure/Shandalar/maps/main.tiled-session +++ b/forge-gui/res/adventure/Shandalar/maps/main.tiled-session @@ -40,7 +40,7 @@ "scale": 3, "selectedLayer": 4, "viewCenter": { - "x": 240, + "x": 239.83333333333331, "y": 136.16666666666663 } }, @@ -51,7 +51,7 @@ "scale": 2, "selectedLayer": 4, "viewCenter": { - "x": 240, + "x": 239.75, "y": 136.25 } }, @@ -62,7 +62,7 @@ "scale": 2, "selectedLayer": 4, "viewCenter": { - "x": 400, + "x": 399.75, "y": 319.75 } }, @@ -2438,7 +2438,7 @@ "scale": 1.5, "selectedLayer": 5, "viewCenter": { - "x": 384, + "x": 383.66666666666663, "y": 383.66666666666663 } }, @@ -2449,7 +2449,7 @@ "scale": 3, "selectedLayer": 4, "viewCenter": { - "x": 240, + "x": 239.83333333333331, "y": 239.83333333333331 } }, @@ -2613,7 +2613,7 @@ "scale": 3, "selectedLayer": 4, "viewCenter": { - "x": 106, + "x": 106.16666666666669, "y": 253.83333333333331 } }, @@ -2624,7 +2624,7 @@ "scale": 1, "selectedLayer": 4, "viewCenter": { - "x": 368, + "x": 368.5, "y": 200.5 } }, @@ -2632,8 +2632,8 @@ "scale": 1, "selectedLayer": 1, "viewCenter": { - "x": 509, - "y": 282.5 + "x": 400.5, + "y": 216.5 } }, "map/nest_white_1.tmx": { @@ -2894,7 +2894,7 @@ "scale": 3, "selectedLayer": 4, "viewCenter": { - "x": 263.3333333333333, + "x": 263.8333333333333, "y": 191.83333333333331 } }, diff --git a/forge-gui/res/adventure/Shandalar/maps/map/kiora_island.tmx b/forge-gui/res/adventure/Shandalar/maps/map/kiora_island.tmx index e41245ea372..c3877982c4f 100644 --- a/forge-gui/res/adventure/Shandalar/maps/map/kiora_island.tmx +++ b/forge-gui/res/adventure/Shandalar/maps/map/kiora_island.tmx @@ -7,12 +7,12 @@ - eJy9lb9OwzAQhw+J2kUQBiQegwHRgVeAp6mgLEwdgYmpa2f+SOQN8gDMwNhn4AUQd4p/snOxHUdFnPSpapK7z+ezkn1LdMBUzCFT/xNPzDPzwrwW5twYovs9ogfmjrk2472rCdFnhFxOLP7aG/4viW29OYa84bNL21KyZ+hPZjjkOVY1Ul59bnNe+RV36v6YfvW5TeUiX9dJcbLj895s33sxJbqcdr2xvUr5Uvfg1dfD+V6Zfr9yvoTG7ePCdPM3fP3H5Syq7nyxbtRcRvqNnStZ65o8X5O21ofyxuaa6zEk551bzznnn1beJV64hSMb3/d34/tduTWsKe/93u32DWqXK7Vmpn1+pt6NG1dnbr0LdWP9l8SZOg+Yp36fwofaj2oG8kxD5V4d4r2t/HwRtfJiz/X3bhtvGHCH/cAphN+7Mfs85A0DXsy5of4shF8B8frL + eJy9lTtOxDAQhgeJtRdBKJA4BgViC64Ap1nB0lBtCVRU227NQyI3yAGogXLPwAUQM4p/2ZnYjqNFjPQriu2Zz/NQsm+JDlgV65BV/5OeWM+sF9Zroc+NIbrfI3pg3bGuzXjuakL0GVHOJ2Z/zQ3fS2xbbk5D3PDs0rYqqRnykx4OcY5VjBRXz22OK09hp/bH5KvnNuULfx0npZMd7/dm+9yLKdHltMuN1SrFS+2Bq9fD/l6Zfr4yX6LG1XFhuv4bXv9xPouq21/cGzGXkXxjcyV3XZPX16SN9aG4sb7mcgyV486t1zn7n1aeJVyw5fyRjdf93fh8V+4Oa8pzv3e7eUO185VYM9Oen6lv48bFmVvPQtxY/iV2puYB/dTfU/AQ+9H2Z7Khcq424d5Wvr+wWnFRc/2/24YbGthhPmCKwv/dmDoPcUMDF31uqN8L0S8+a/oe - eJytlL9OwzAQxq8tidNMFMRaGACxABJi4BE6ULpXwFPACyAUmBgrAc8AiDdAwBqJAcTKDO+AuE+OZcdxwDE96VMT1+ffxfdnElGQXc0QLbWJFtvN/LYK3iSQe9HVamIvAdyjmOg4LjOVNlKizdT/LJO7mxA9WPcG1h6vX/O9Zl0tFxt68/yO7ViyIOQrM3xfI/n+lZSZ0+AuiPJ5PaH/mxVVns0e0HS4tp+Ljdjmhft7fev7Ly5sJyrrW8i843m/Q3TA6rNOW0QnrDtRPeM37lrqd0+oaewfJrLmoDHX3a3wY8JwX2Aj5sOOXMs45jPWeau6/z2S+5WQ30ERf8+TCUMfrVvnL3OOVlirjlzZXMQ7J6SaGLifls8j855YzzU10g9k2VzkRhnmri3T0O9jay3ERomu4/uafr0Rko+ZhV7Jix66/Adf1TNqc+SYS64ZlRu9CzZ6Cgrhmvog/aueTeXWzFD7fHuojmsybS7ec4cP+E24P6PWfGc= + eJytlMsuBEEUhs9ceqqnV4bYYoHYIBELjzALzF7wFLyASLOynATPgHgDwbYTC2JrzTuI86e60tWnq011ZU7yp29V9Z0+t3FEQXbTJVpqEy22m+3bynnjQO5Vv1ATewvgnvSITntlptFGQrSZ+J9lc3djoicRN7D2+f0txzXtF3KxoQ/P/9juaRaEfKXW3vdIP//EZeY0uPOqfN5AFd9mVJUn2UOaDlfuc7Hh25xy/69vfU/iwnaisn6VzjvuDztER6wF1nmL6Iz1oKpn/MddS/zihJrG+r1Y1xx0wHV3r/yYMMQLbPh83NHvUvb5gnXZqq7/jPR6I+R3mPs/8GTC0Efr4vxlztEKa9WRK8mFv7NKq4mB+y32PDPvhfVaUyPwM4QluciNMcxdKdvQ7zI+ITaKizp+rOnXO6X5mFnolSzvoevu5PPrzNQzanPkmEuuGZVZvQs2egoK4dr6ouJq7m1lYmaYdb49VMe1mZKL58yxB/wm3D9fB3xE @@ -20,7 +20,7 @@ - eJxjYGBgKOdloBuooJNdn1hoZ3YVHj9MYCXOjIXM5NtDSXzZI4VLCdScSizmVdMxTcAANn89YyDNLcSqhakrGQB/4gOv2MnXixx+rESkL3R7saUDUsFiPPZSK6yxhZEjEfmdXmUPDFASl5SYQy170QEbN4SmZ31BbH7GVyajA0rSIS6/w8SfUWAmstmkhDE56RoAwkMY7Q== + eJxjYGBgKOdloBuooJNdn1hoZ3YVHj9MYCXOjIXM5NtDSXzZI4VLCdScSizmVdMxTcAANn89YyDNLcSqhakrGQB/4gOv2MnXixx+rESkL3R7saUDUsFiPPZSK6yxhZEjEfmdXmUPDGBzpy4jdcwhRz05diMDNm4ITc/6gtj8jK9MRgeUpENcfoeJP6PATGSzSQljctI1AJToGUk= From d5a15fb14e2ea093c6cec285f04e12f44d910a7a Mon Sep 17 00:00:00 2001 From: Simisays <67333662+Simisays@users.noreply.github.com> Date: Thu, 9 Mar 2023 20:07:55 +0100 Subject: [PATCH 2/4] update --- .../res/adventure/Shandalar/custom_cards/nahiris_armory.txt | 3 ++- .../adventure/Shandalar/custom_cards/nahiris_boss_effect.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/forge-gui/res/adventure/Shandalar/custom_cards/nahiris_armory.txt b/forge-gui/res/adventure/Shandalar/custom_cards/nahiris_armory.txt index 4ae7057d5f0..427cf6e821b 100644 --- a/forge-gui/res/adventure/Shandalar/custom_cards/nahiris_armory.txt +++ b/forge-gui/res/adventure/Shandalar/custom_cards/nahiris_armory.txt @@ -3,5 +3,6 @@ ManaCost:no cost Types:Artifact S:Mode$ ReduceCost | ValidCard$ Card | ValidSpell$ Activated.Equip | Activator$ You | Amount$ 1 | EffectZone$ Command | Description$ Equip costs you pay cost {1} less. T:Mode$ AttackersDeclared | ValidAttackers$ Creature.modified+YouCtrl | TriggerZones$ Command | Execute$ TrigConjure | TriggerDescription$ Whenever a modified creature you control attacks, you may pay {M}{M}, if you do conjure a random card from Nahiri's Armory's Spellbook into your hand. -SVar:TrigConjure:AB$ MakeCard | Cost$ PayShards<2> | Conjure$ True | AtRandom$ True | Zone$ Hand | Spellbook$ Stoneforge Mystic,Danitha; Benalia's Hope,Ardenn; Intrepid Archaeologist,Open the Armory,Stone Haven Outfitter,Argentum Armor,Sword of the Animist,Masterwork of Ingenuity,Kaldra Compleat,Armored Skyhunter,Lion Sash,Relic Seeker,Esper Sentinel,Forgeborn Phoenix,Foundry Beetle,Inchblade Companion,Komainu Battle Armor,Luxior; Giada's Gift,Mace of Disruption,Nettlecyst,Shadowspear,Seraphic Greatsword,Soulstealer Axe,Sword of Body and Mind,Sword of Fire and Ice,Junkyard Scrapper,Soulstealer AxeSword of Feast and Famine,Expedition Supplier,Foundry Beetle,Armory Automaton,Bladegraft Aspirant,Dancing Sword,Jor Kadeen; First Goldwarden,Akiri; Fearless Voyager,Acclaimed Contender,Embercleave,Puresteel Paladin,Champion of the Flame,Tiana; Ship's Caretaker,Reyav; Master Smith,Sigarda's Aid,Armored Skyhunter,Bruenor Battlehammer,Halvar; God of Battle,Wyleth; Soul of Steel,Koll; the Forgemaster,Valduk; Keeper of the Flame,Fervent Champion,Cloudsteel Kirin,Leonin Shikari,Balan; Wandering Knight,Kor Duelist,Leonin Abunas,Zamriel; Seraph of Steel,Auriok Steelshaper +SVar:TrigConjure:AB$ MakeCard | Cost$ PayShards<2> | Conjure$ True | AtRandom$ True | Zone$ Hand | Spellbook$ +Stoneforge Mystic,Danitha; Benalia's Hope,Ardenn; Intrepid Archaeologist,Open the Armory,Stone Haven Outfitter,Argentum Armor,Sword of the Animist,Masterwork of Ingenuity,Kaldra Compleat,Armored Skyhunter,Lion Sash,Relic Seeker,Esper Sentinel,Forgeborn Phoenix,Foundry Beetle,Inchblade Companion,Komainu Battle Armor,Luxior; Giada's Gift,Mace of Disruption,Nettlecyst,Shadowspear,Seraphic Greatsword,Soulstealer Axe,Sword of Body and Mind,Sword of Fire and Ice,Junkyard Scrapper,Soulstealer Axe,Sword of Feast and Famine,Expedition Supplier,Foundry Beetle,Armory Automaton,Bladegraft Aspirant,Dancing Sword,Jor Kadeen; First Goldwarden,Akiri; Fearless Voyager,Acclaimed Contender,Embercleave,Puresteel Paladin,Champion of the Flame,Tiana; Ship's Caretaker,Reyav; Master Smith,Sigarda's Aid,Armored Skyhunter,Bruenor Battlehammer,Halvar; God of Battle,Wyleth; Soul of Steel,Koll; the Forgemaster,Valduk; Keeper of the Flame,Fervent Champion,Cloudsteel Kirin,Leonin Shikari,Balan; Wandering Knight,Kor Duelist,Leonin Abunas,Zamriel; Seraph of Steel,Auriok Steelshaper Oracle:Equip costs you pay cost {1} less.\nWhenever a modified creature you control attacks, you may pay {M}{M}, if you do conjure a random card from Nahiri's Armory's Spellbook into your hand. \ No newline at end of file diff --git a/forge-gui/res/adventure/Shandalar/custom_cards/nahiris_boss_effect.txt b/forge-gui/res/adventure/Shandalar/custom_cards/nahiris_boss_effect.txt index 41d868a7d31..215a2628956 100644 --- a/forge-gui/res/adventure/Shandalar/custom_cards/nahiris_boss_effect.txt +++ b/forge-gui/res/adventure/Shandalar/custom_cards/nahiris_boss_effect.txt @@ -4,7 +4,7 @@ Types:Enchantment S:Mode$ ReduceCost | ValidCard$ Card | ValidSpell$ Activated.Equip | Activator$ You | Amount$ 1 | EffectZone$ Command | Description$ Equip costs you pay cost {1} less. T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Command | Secondary$ True | Execute $ TrigConjure | TriggerDescription$ At the beginning of your upkeep or when an equipped creature you control attacks, conjure a card from Nahiri's Spellbook into your hand. T:Mode$ Attacks | ValidCard$ Creature.YouCtrl+equipped | TriggerZones$ Command | Execute$ TrigConjure | TriggerDescription$ At the beginning of your upkeep or when an equipped creature you control attacks, conjure a card from Nahiri's Spellbook into your hand. -SVar:TrigConjure:DB$ MakeCard | Zone$ Hand | Conjure$ True | AtRandom$ True | Spellbook$ Stoneforge Mystic,Danitha; Benalia's Hope,Ardenn, Intrepid Archaeologist,Open the Armory,Stone Haven Outfitter,Argentum Armor,Sword of the Animist,Masterwork of Ingenuity,Kaldra Compleat,Armored Skyhunter,Lion Sash,Relic Seeker,Esper Sentinel,Forgeborn Phoenix,Foundry Beetle,Inchblade Companion,Komainu Battle Armor,Luxior;Giada's Gift,Mace of Disruption,Nettlecyst,Shadowspear,Seraphic Greatsword,Soulstealer Axe,Sword of Body and Mind,Sword of Fire and Ice,Junkyard Scrapper,Soulstealer Axe,Sword of Feast and Famine,Expedition Supplier,Foundry Beetle,Armory Automaton,Bladegraft Aspirant,Dancing Sword,Jor Kadeen; First Goldwarden,Akiri; Fearless Voyager,Acclaimed Contender,Embercleave,Puresteel Paladin,Champion of the Flame,Tiana; Ship's Caretaker,Reyav; Master Smith,Sigarda's Aid,Armored Skyhunter,Bruenor Battlehammer,Halvar; God of Battle,Wyleth; Soul of Steel,Koll; the Forgemaster,Valduk; Keeper of the Flame,Fervent Champion,Cloudsteel Kirin,Leonin Shikari,Balan, Wandering Knight,Kor Duelist,Leonin Abunas,Zamriel, Seraph of Steel,Auriok Steelshaper +SVar:TrigConjure:DB$ MakeCard | Zone$ Hand | Conjure$ True | AtRandom$ True | Spellbook$ Stoneforge Mystic,Danitha; Benalia's Hope,Ardenn, Intrepid Archaeologist,Open the Armory,Stone Haven Outfitter,Argentum Armor,Sword of the Animist,Masterwork of Ingenuity,Kaldra Compleat,Armored Skyhunter,Lion Sash,Relic Seeker,Esper Sentinel,Forgeborn Phoenix,Foundry Beetle,Inchblade Companion,Komainu Battle Armor,Luxior;Giada's Gift,Mace of Disruption,Nettlecyst,Shadowspear,Seraphic Greatsword,Soulstealer Axe,Sword of Body and Mind,Sword of Fire and Ice,Junkyard Scrapper,Soulstealer Axe,Sword of Feast and Famine,Expedition Supplier,Foundry Beetle,Armory Automaton,Bladegraft Aspirant,Dancing Sword,Jor Kadeen; First Goldwarden,Akiri; Fearless Voyager,Acclaimed Contender,Embercleave,Puresteel Paladin,Champion of the Flame,Tiana; Ship's Caretaker,Reyav; Master Smith,Sigarda's Aid,Armored Skyhunter,Bruenor Battlehammer,Halvar; God of Battle,Wyleth; Soul of Steel,Koll; the Forgemaster,Valduk; Keeper of the Flame,Fervent Champion,Cloudsteel Kirin,Leonin Shikari,Balan; Wandering Knight,Kor Duelist,Leonin Abunas,Zamriel;Seraph of Steel,Auriok Steelshaper T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Command | Execute$ TrigToken | TriggerDescription$ At the beginning of your end step, create a 1/1 white Kor Soldier creature token SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_kor_soldier Oracle:Equip costs you pay cost {1} less.\nAt the beginning of your upkeep or whenever an equipped creature you control attacks, conjure a card from Nahiri's Spellbook into your hand\nAt the beginning of your end step, create a 1/1 white Kor Soldier creature token. \ No newline at end of file From b7105e1ee9f8ae1519d928cc2d593814c725c1c0 Mon Sep 17 00:00:00 2001 From: tool4EvEr Date: Thu, 9 Mar 2023 22:43:50 +0100 Subject: [PATCH 3/4] Some cleanup --- .../java/forge/ai/ability/ChangeZoneAi.java | 8 +-- .../java/forge/ai/ability/DamageDealAi.java | 2 +- .../forge/ai/ability/DamagePreventAi.java | 2 +- .../main/java/forge/ai/ability/DebuffAi.java | 13 ++--- .../main/java/forge/ai/ability/ProtectAi.java | 51 ++++++++----------- .../main/java/forge/ai/ability/PumpAi.java | 13 ++--- .../java/forge/ai/ability/SetStateAi.java | 4 +- .../java/forge/game/ability/AbilityUtils.java | 15 ++++-- .../game/ability/effects/PumpAllEffect.java | 2 +- .../game/ability/effects/PumpEffect.java | 5 +- .../ability/effects/RepeatEachEffect.java | 23 ++------- .../game/ability/effects/RepeatEffect.java | 16 ++---- forge-gui/res/cardsfolder/d/desolation.txt | 2 +- .../res/cardsfolder/w/wave_of_vitriol.txt | 2 +- 14 files changed, 59 insertions(+), 99 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java index 907d6438d88..fd02a6cb98e 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java @@ -1090,7 +1090,6 @@ public class ChangeZoneAi extends SpellAbilityAi { // Exile and bounce opponents stuff if (destination.equals(ZoneType.Exile) || origin.contains(ZoneType.Battlefield)) { - // don't rush bouncing stuff when not going to attack if (!immediately && game.getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2) && game.getPhaseHandler().isPlayerTurn(ai) @@ -1433,17 +1432,14 @@ public class ChangeZoneAi extends SpellAbilityAi { final ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination")); final TargetRestrictions tgt = sa.getTargetRestrictions(); - CardCollection list = CardLists.getValidCards(ai.getGame().getCardsIn(tgt.getZone()), tgt.getValidTgts(), ai, source, sa); - list = CardLists.getTargetableCards(list, sa); - - list.removeAll(sa.getTargets().getTargetCards()); + CardCollection list = new CardCollection(CardUtil.getValidCardsToTarget(tgt, sa)); if (list.isEmpty()) { return false; } // target loop - while (sa.getTargets().size() < tgt.getMinTargets(sa.getHostCard(), sa)) { + while (!sa.isMinTargetChosen()) { // AI Targeting Card choice = null; diff --git a/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java b/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java index d6a2d2cbe30..4fbf12b2ae9 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java @@ -757,7 +757,7 @@ public class DamageDealAi extends DamageAiBase { return false; } // TODO: Improve Damage, we shouldn't just target the player just because we can - if (sa.canTarget(enemy) && tcs.size() < tgt.getMaxTargets(source, sa)) { + if (sa.canTarget(enemy) && sa.canAddMoreTarget()) { if (((phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai)) || (SpellAbilityAi.isSorcerySpeed(sa, ai) && phase.is(PhaseType.MAIN2)) || ("PingAfterAttack".equals(logic) && phase.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && phase.isPlayerTurn(ai)) diff --git a/forge-ai/src/main/java/forge/ai/ability/DamagePreventAi.java b/forge-ai/src/main/java/forge/ai/ability/DamagePreventAi.java index 806c08fddfd..133fd5ce195 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DamagePreventAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DamagePreventAi.java @@ -130,7 +130,7 @@ public class DamagePreventAi extends SpellAbilityAi { ComputerUtilCard.sortByEvaluateCreature(combatants); for (final Card c : combatants) { - if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat) && tcs.size() < tgt.getMaxTargets(hostCard, sa)) { + if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat) && sa.canAddMoreTarget()) { tcs.add(c); chance = true; } diff --git a/forge-ai/src/main/java/forge/ai/ability/DebuffAi.java b/forge-ai/src/main/java/forge/ai/ability/DebuffAi.java index 119d4cb3e5c..448b7d0443e 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DebuffAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DebuffAi.java @@ -17,6 +17,7 @@ import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardLists; +import forge.game.card.CardUtil; import forge.game.combat.Combat; import forge.game.cost.Cost; import forge.game.phase.PhaseHandler; @@ -24,7 +25,6 @@ import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.spellability.TargetRestrictions; -import forge.game.zone.ZoneType; public class DebuffAi extends SpellAbilityAi { @@ -195,16 +195,13 @@ public class DebuffAi extends SpellAbilityAi { */ private boolean debuffMandatoryTarget(final Player ai, final SpellAbility sa, final boolean mandatory) { final TargetRestrictions tgt = sa.getTargetRestrictions(); - CardCollection list = CardLists.getTargetableCards(ai.getGame().getCardsIn(ZoneType.Battlefield), sa); + List list = CardUtil.getValidCardsToTarget(tgt, sa); if (list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) { sa.resetTargets(); return false; } - // Remove anything that's already been targeted - list.removeAll(sa.getTargets().getTargetCards()); - final CardCollection pref = CardLists.filterControlledBy(list, ai.getOpponents()); final CardCollection forced = CardLists.filterControlledBy(list, ai); final Card source = sa.getHostCard(); @@ -219,7 +216,7 @@ public class DebuffAi extends SpellAbilityAi { sa.getTargets().add(c); } - while (sa.getTargets().size() < tgt.getMinTargets(source, sa)) { + while (!sa.isMinTargetChosen()) { if (forced.isEmpty()) { break; } @@ -237,13 +234,13 @@ public class DebuffAi extends SpellAbilityAi { sa.getTargets().add(c); } - if (sa.getTargets().size() < tgt.getMinTargets(source, sa)) { + if (!sa.isMinTargetChosen()) { sa.resetTargets(); return false; } return true; - } // pumpMandatoryTarget() + } @Override protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { diff --git a/forge-ai/src/main/java/forge/ai/ability/ProtectAi.java b/forge-ai/src/main/java/forge/ai/ability/ProtectAi.java index f27a3326521..8d21940b918 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ProtectAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ProtectAi.java @@ -20,13 +20,13 @@ import forge.game.ability.effects.ProtectEffect; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardLists; +import forge.game.card.CardUtil; import forge.game.combat.Combat; import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.spellability.TargetRestrictions; -import forge.game.zone.ZoneType; import forge.util.MyRandom; public class ProtectAi extends SpellAbilityAi { @@ -168,23 +168,23 @@ public class ProtectAi extends SpellAbilityAi { @Override protected boolean checkApiLogic(final Player ai, final SpellAbility sa) { - if (!sa.usesTargeting()) { - final List cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa); - if (cards.size() == 0) { - return false; - } else if (cards.size() == 1) { - // Affecting single card - return getProtectCreatures(ai, sa).contains(cards.get(0)); - } - /* - * when this happens we need to expand AI to consider if its ok - * for everything? for (Card card : cards) { // TODO if AI doesn't - * control Card and Pump is a Curse, than maybe use? - * } - */ - } else { + if (sa.usesTargeting()) { return protectTgtAI(ai, sa, false); } + + final List cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa); + if (cards.size() == 0) { + return false; + } else if (cards.size() == 1) { + // Affecting single card + return getProtectCreatures(ai, sa).contains(cards.get(0)); + } + /* + * when this happens we need to expand AI to consider if its ok + * for everything? for (Card card : cards) { // TODO if AI doesn't + * control Card and Pump is a Curse, than maybe use? + * } + */ return false; } @@ -256,21 +256,15 @@ public class ProtectAi extends SpellAbilityAi { } // protectTgtAI() private static boolean protectMandatoryTarget(final Player ai, final SpellAbility sa) { - final Game game = ai.getGame(); - final TargetRestrictions tgt = sa.getTargetRestrictions(); - CardCollection list = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa); + final Card source = sa.getHostCard(); + final List list = CardUtil.getValidCardsToTarget(tgt, sa); - if (list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) { + if (list.size() < tgt.getMinTargets(source, sa)) { sa.resetTargets(); return false; } - // Remove anything that's already been targeted - for (final Card c : sa.getTargets().getTargetCards()) { - list.remove(c); - } - CardCollection pref = CardLists.filterControlledBy(list, ai); pref = CardLists.filter(pref, new Predicate() { @Override @@ -286,7 +280,6 @@ public class ProtectAi extends SpellAbilityAi { } }); final List forced = CardLists.filterControlledBy(list, ai); - final Card source = sa.getHostCard(); while (sa.canAddMoreTarget()) { if (pref.isEmpty()) { @@ -308,13 +301,13 @@ public class ProtectAi extends SpellAbilityAi { sa.getTargets().add(c); } - while (sa.getTargets().size() < tgt.getMinTargets(source, sa)) { + while (!sa.isMinTargetChosen()) { if (forced.isEmpty()) { break; } Card c; - if (CardLists.getNotType(forced, "Creature").size() == 0) { + if (CardLists.getNotType(forced, "Creature").isEmpty()) { c = ComputerUtilCard.getWorstCreatureAI(forced); } else { c = ComputerUtilCard.getCheapestPermanentAI(forced, sa, false); @@ -323,7 +316,7 @@ public class ProtectAi extends SpellAbilityAi { sa.getTargets().add(c); } - if (sa.getTargets().size() < tgt.getMinTargets(source, sa)) { + if (!sa.isMinTargetChosen()) { sa.resetTargets(); return false; } diff --git a/forge-ai/src/main/java/forge/ai/ability/PumpAi.java b/forge-ai/src/main/java/forge/ai/ability/PumpAi.java index 6d733072875..4f8e4f58f83 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PumpAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PumpAi.java @@ -616,23 +616,16 @@ public class PumpAi extends PumpAiBase { } private boolean pumpMandatoryTarget(final Player ai, final SpellAbility sa) { - final Game game = ai.getGame(); final TargetRestrictions tgt = sa.getTargetRestrictions(); - CardCollection list = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa); + List list = CardUtil.getValidCardsToTarget(tgt, sa); if (list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) { sa.resetTargets(); return false; } - // Remove anything that's already been targeted - for (final Card c : sa.getTargets().getTargetCards()) { - list.remove(c); - } - CardCollection pref; CardCollection forced; - final Card source = sa.getHostCard(); if (sa.isCurse()) { pref = CardLists.filterControlledBy(list, ai.getOpponents()); @@ -652,7 +645,7 @@ public class PumpAi extends PumpAiBase { sa.getTargets().add(c); } - while (sa.getTargets().size() < tgt.getMinTargets(source, sa)) { + while (!sa.isMinTargetChosen()) { if (forced.isEmpty()) { break; } @@ -668,7 +661,7 @@ public class PumpAi extends PumpAiBase { sa.getTargets().add(c); } - if (sa.getTargets().size() < tgt.getMinTargets(source, sa)) { + if (!sa.isMinTargetChosen()) { sa.resetTargets(); return false; } diff --git a/forge-ai/src/main/java/forge/ai/ability/SetStateAi.java b/forge-ai/src/main/java/forge/ai/ability/SetStateAi.java index 5a8ac20146a..921a6d361d1 100644 --- a/forge-ai/src/main/java/forge/ai/ability/SetStateAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/SetStateAi.java @@ -97,13 +97,13 @@ public class SetStateAi extends SpellAbilityAi { for (final Card c : list) { if (shouldTransformCard(c, ai, ph) || "Always".equals(logic)) { sa.getTargets().add(c); - if (sa.getTargets().size() == tgt.getMaxTargets(source, sa)) { + if (sa.isMaxTargetChosen()) { break; } } } - return sa.getTargets().size() >= tgt.getMinTargets(source, sa); + return sa.isMinTargetChosen(); } } else if ("TurnFace".equals(mode)) { if (!sa.usesTargeting()) { diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index 530d692c5b3..8c43e1ed456 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -96,6 +96,13 @@ public class AbilityUtils { final Game game = hostCard.getGame(); Card c = null; + Player player = null; + if (sa instanceof SpellAbility) { + player = ((SpellAbility)sa).getActivatingPlayer(); + } + if (player == null) { + player = hostCard.getController(); + } if (defined.equals("Self")) { c = hostCard; @@ -143,7 +150,7 @@ public class AbilityUtils { } } } else if (defined.equals("TopOfGraveyard")) { - final CardCollectionView grave = hostCard.getController().getCardsIn(ZoneType.Graveyard); + final CardCollectionView grave = player.getCardsIn(ZoneType.Graveyard); if (grave.size() > 0) { c = grave.getLast(); @@ -153,7 +160,7 @@ public class AbilityUtils { } } else if (defined.endsWith("OfLibrary")) { - final CardCollectionView lib = hostCard.getController().getCardsIn(ZoneType.Library); + final CardCollectionView lib = player.getCardsIn(ZoneType.Library); int libSize = lib.size(); if (libSize > 0) { // TopOfLibrary or BottomOfLibrary if (defined.startsWith("TopThird")) { @@ -354,7 +361,7 @@ public class AbilityUtils { candidates = game.getCardsIn(ZoneType.smartValueOf(zone)); validDefined = s[1]; } - cards.addAll(CardLists.getValidCards(candidates, validDefined, hostCard.getController(), hostCard, sa)); + cards.addAll(CardLists.getValidCards(candidates, validDefined, player, hostCard, sa)); return cards; } else if (defined.startsWith("ExiledWith")) { cards.addAll(hostCard.getExiledCards()); @@ -375,7 +382,7 @@ public class AbilityUtils { for (int i = 0; i < valids.length; i++) { valids[i] = "Card." + valids[i]; } - cards = CardLists.getValidCards(cards, valids, hostCard.getController(), hostCard, sa); + cards = CardLists.getValidCards(cards, valids, player, hostCard, sa); } return cards; diff --git a/forge-game/src/main/java/forge/game/ability/effects/PumpAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PumpAllEffect.java index 5af4dbc37aa..12e5b52e0a7 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PumpAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PumpAllEffect.java @@ -115,7 +115,7 @@ public class PumpAllEffect extends SpellAbilityEffect { sb.append(desc); return sb.toString(); - } // pumpAllStackDescription() + } @Override public void resolve(final SpellAbility sa) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java index 70d9e033171..6a04823abda 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java @@ -415,10 +415,7 @@ public class PumpEffect extends SpellAbilityEffect { final ZoneType pumpZone = sa.hasParam("PumpZone") ? ZoneType.smartValueOf(sa.getParam("PumpZone")) : ZoneType.Battlefield; - final int size = tgtCards.size(); - for (int j = 0; j < size; j++) { - final Card tgtC = tgtCards.get(j); - + for (Card tgtC : tgtCards) { // CR 702.26e if (tgtC.isPhasedOut()) { continue; diff --git a/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java index 52488f3005e..d8d19721c2a 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java @@ -32,14 +32,6 @@ public class RepeatEachEffect extends SpellAbilityEffect { final SpellAbility repeat = sa.getAdditionalAbility("RepeatSubAbility"); - if (repeat != null && !repeat.getHostCard().equalsWithTimestamp(source)) { - // TODO: for some reason, the host card of the original additional SA is set to the cloned card when - // the ability is copied (e.g. Clone Legion + Swarm Intelligence). Couldn't figure out why this happens, - // so this hack is necessary for now to work around this issue. - System.out.println("Warning: RepeatSubAbility had the wrong host set (potentially after cloning the root SA or changing zones), attempting to correct..."); - repeat.setHostCard(source); - } - final Player player = sa.getActivatingPlayer(); final Game game = player.getGame(); if (sa.hasParam("Optional") && sa.hasParam("OptionPrompt") && //for now, OptionPrompt is needed @@ -74,10 +66,6 @@ public class RepeatEachEffect extends SpellAbilityEffect { } else if (sa.hasParam("DefinedCards")) { repeatCards = AbilityUtils.getDefinedCards(source, sa.getParam("DefinedCards"), sa); - if (sa.hasParam("AdditionalRestriction")) { // lki cards might not be in game - repeatCards = CardLists.getValidCards(repeatCards, - sa.getParam("AdditionalRestriction"), source.getController(), source, sa); - } } boolean loopOverCards = repeatCards != null && !repeatCards.isEmpty(); @@ -125,13 +113,10 @@ public class RepeatEachEffect extends SpellAbilityEffect { // for a mixed list of target permanents and players, e.g. Soulfire Eruption if (sa.hasParam("RepeatTargeted")) { - final List tgts = getTargets(sa); - if (tgts != null) { - for (final Object o : tgts) { - source.addRemembered(o); - AbilityUtils.resolve(repeat); - source.removeRemembered(o); - } + for (final GameObject o : getTargets(sa)) { + source.addRemembered(o); + AbilityUtils.resolve(repeat); + source.removeRemembered(o); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/RepeatEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RepeatEffect.java index 3438ee5aa8e..4c8465101bf 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RepeatEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RepeatEffect.java @@ -26,17 +26,9 @@ public class RepeatEffect extends SpellAbilityEffect { // setup subability to repeat SpellAbility repeat = sa.getAdditionalAbility("RepeatSubAbility"); - if (repeat != null && !repeat.getHostCard().equals(source)) { - // TODO: for some reason, the host card of the original additional SA is set to the cloned card when - // the ability is copied (e.g. Clone Legion + Swarm Intelligence). Couldn't figure out why this happens, - // so this hack is necessary for now to work around this issue. - System.out.println("Warning: RepeatSubAbility had the wrong host set (potentially after cloning the root SA), attempting to correct..."); - repeat.setHostCard(source); - } - Integer maxRepeat = null; if (sa.hasParam("MaxRepeat")) { - maxRepeat = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("MaxRepeat"), sa); + maxRepeat = AbilityUtils.calculateAmount(source, sa.getParam("MaxRepeat"), sa); if (maxRepeat.intValue() == 0) return; // do nothing if maxRepeat is 0. the next loop will execute at least once } @@ -50,7 +42,7 @@ public class RepeatEffect extends SpellAbilityEffect { // Helm of Obedience vs Graveyard to Library replacement effect if (source.getName().equals("Helm of Obedience")) { - StringBuilder infLoop = new StringBuilder(sa.getHostCard().toString()); + StringBuilder infLoop = new StringBuilder(source.toString()); infLoop.append(" - To avoid an infinite loop, this repeat has been broken "); infLoop.append(" and the game will now continue in the current state, ending the loop early. "); infLoop.append("Once Draws are available this probably should change to a Draw."); @@ -84,7 +76,7 @@ public class RepeatEffect extends SpellAbilityEffect { } else { list = game.getCardsIn(ZoneType.Battlefield); } - list = CardLists.getValidCards(list, repeatPresent, sa.getActivatingPlayer(), sa.getHostCard(), sa); + list = CardLists.getValidCards(list, repeatPresent, activator, sa.getHostCard(), sa); final String rightString = repeatCompare.substring(2); int right = AbilityUtils.calculateAmount(sa.getHostCard(), rightString, sa); @@ -114,7 +106,7 @@ public class RepeatEffect extends SpellAbilityEffect { if (sa.hasParam("RepeatOptional")) { Player decider = sa.hasParam("RepeatOptionalDecider") ? AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("RepeatOptionalDecider"), sa).get(0) - : sa.getActivatingPlayer(); + : activator; return decider.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantRepeatProcessAgain"), null); } diff --git a/forge-gui/res/cardsfolder/d/desolation.txt b/forge-gui/res/cardsfolder/d/desolation.txt index 6cf3c555575..e95d45e7410 100644 --- a/forge-gui/res/cardsfolder/d/desolation.txt +++ b/forge-gui/res/cardsfolder/d/desolation.txt @@ -3,7 +3,7 @@ ManaCost:1 B B Types:Enchantment T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | Execute$ TrigSac | TriggerDescription$ At the beginning of each end step, each player who tapped a land for mana this turn sacrifices a land. Desolation deals 2 damage to each player who sacrificed a Plains this way. SVar:TrigSac:DB$ Sacrifice | SacValid$ Land | Defined$ Player.TappedLandForManaThisTurn | RememberSacrificed$ True | SubAbility$ DBRepeat -SVar:DBRepeat:DB$ RepeatEach | DefinedCards$ Remembered | AdditionalRestriction$ Plains | UseImprinted$ True | RepeatSubAbility$ DBDamage | SubAbility$ DBCleanup +SVar:DBRepeat:DB$ RepeatEach | DefinedCards$ Remembered.Plains | UseImprinted$ True | RepeatSubAbility$ DBDamage | SubAbility$ DBCleanup SVar:DBDamage:DB$ DealDamage | Defined$ ImprintedController | NumDmg$ 2 SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True AI:RemoveDeck:All diff --git a/forge-gui/res/cardsfolder/w/wave_of_vitriol.txt b/forge-gui/res/cardsfolder/w/wave_of_vitriol.txt index 37d37b40b24..093b3353e5c 100644 --- a/forge-gui/res/cardsfolder/w/wave_of_vitriol.txt +++ b/forge-gui/res/cardsfolder/w/wave_of_vitriol.txt @@ -2,7 +2,7 @@ Name:Wave of Vitriol ManaCost:5 G G Types:Sorcery A:SP$ SacrificeAll | Cost$ 5 G G | ValidCards$ Artifact,Enchantment,Land.nonBasic | RememberSacrificed$ True | SubAbility$ DBRepeat | SpellDescription$ Each player sacrifices all artifacts, enchantments, and nonbasic lands they control. For each land sacrificed this way, its controller may search their library for a basic land card and put it onto the battlefield tapped. Then each player who searched their library this way shuffles. -SVar:DBRepeat:DB$ RepeatEach | DefinedCards$ DirectRemembered | AdditionalRestriction$ Land | UseImprinted$ True | RepeatSubAbility$ DBSearch | ClearRemembered$ True | SubAbility$ DBShuffle +SVar:DBRepeat:DB$ RepeatEach | DefinedCards$ DirectRemembered.Land | UseImprinted$ True | RepeatSubAbility$ DBSearch | ClearRemembered$ True | SubAbility$ DBShuffle SVar:DBSearch:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Land.Basic | ChangeNum$ 1 | Tapped$ True | RememberChanged$ True | DefinedPlayer$ ImprintedController | Chooser$ ImprintedController | NoShuffle$ True | Optional$ True SVar:DBShuffle:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ ShuffleSearched | SubAbility$ DBCleanup SVar:ShuffleSearched:DB$ Shuffle | Defined$ Player.IsRemembered | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 From 5748d09b54a4183c4e54893b77753cf0006992cb Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Fri, 10 Mar 2023 09:18:43 +0800 Subject: [PATCH 4/4] fix particle effect initial position - move the offset calculation inside MapActor --- .../forge/adventure/character/MapActor.java | 20 ++++++++++++++++--- .../src/forge/adventure/stage/MapStage.java | 4 +--- .../src/forge/adventure/stage/WorldStage.java | 4 +--- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/forge-gui-mobile/src/forge/adventure/character/MapActor.java b/forge-gui-mobile/src/forge/adventure/character/MapActor.java index 304b8a2d52c..ffe50e1e676 100644 --- a/forge-gui-mobile/src/forge/adventure/character/MapActor.java +++ b/forge-gui-mobile/src/forge/adventure/character/MapActor.java @@ -63,6 +63,7 @@ public class MapActor extends Actor { { ParticleEffect effect = new ParticleEffect(); effect.load(Config.instance().getFile(path),Config.instance().getFile(path).parent()); + effect.setPosition(getCenterX(), getCenterY()); effects.add(new CurrentEffect(path, effect, offset, overlay)); if(duration!=0)//ParticleEffect.setDuration uses an integer for some reason { @@ -147,16 +148,29 @@ public class MapActor extends Actor { effect.effect.draw(batch); } } + float getCenterX() { + float scale = 1f; + if (this instanceof EnemySprite) { + scale = ((EnemySprite) this).getData().scale; + } + return getX()+(getWidth()*scale)/2; + } + float getCenterY() { + float scale = 1f; + if (this instanceof EnemySprite) { + scale = ((EnemySprite) this).getData().scale; + } + return getY()+(getHeight()*scale)/2; + } + @Override public void act(float delta) { super.act(delta); - - for(int i=0;i