From 4248d1930ef5a34f2b00e31116e92d5755b2af00 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Thu, 14 Nov 2024 11:54:34 +0800 Subject: [PATCH 01/13] Update snapshots-android.yml --- .github/workflows/snapshots-android.yml | 27 ++++--------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/.github/workflows/snapshots-android.yml b/.github/workflows/snapshots-android.yml index 04fdf794e11..e59b5744d21 100644 --- a/.github/workflows/snapshots-android.yml +++ b/.github/workflows/snapshots-android.yml @@ -89,31 +89,12 @@ jobs: - name: Build/Install/Publish to GitHub Packages Apache Maven run: | export _JAVA_OPTIONS="-Xmx2g" - d=$(date +%m.%d) - # Replace date in forge-gui-mobile/src/forge/Forge.java - # sed -i -e "s/-SNAPSHOT/-SNAPSHOT-${d}/g" forge-gui-mobile/src/forge/Forge.java mvn -U -B -P android-release-build install -e -Dcardforge-repo.username=${{ secrets.FTP_USERNAME }} -Dcardforge-repo.password=${{ secrets.FTP_PASSWORD }} -Dandroid.sdk.path=/usr/local/lib/android/sdk -Dandroid.buildToolsVersion=35.0.0 -Dmaven.test.skip=true - mkdir -p forge-gui-android/target/upload - mv forge-gui-android/target/*-signed-aligned.apk forge-gui-android/target/upload/ - mv forge-gui-android/target/assets.zip forge-gui-android/target/upload/ - cd forge-gui-android/target/upload/ - # Get the first APK file in the folder + mkdir upload + mv /home/runner/work/forge/forge/forge-gui-android/target/*-signed-aligned.apk upload/ + mv /home/runner/work/forge/forge/forge-gui-android/target/assets.zip upload/ + cd upload ls - apk_file=$(find . -maxdepth 1 -type f -name '*.apk' -print -quit) - - if [ -n "$apk_file" ]; then - version=$(echo "$apk_file" | grep -oP 'forge-android-\K\d+\.\d+\.\d+-SNAPSHOT' | sed 's/-signed-aligned.apk//') - echo "APK File: $apk_file" - echo "Version: $version" - # mv *.apk "forge-android-$version-$d-signed-aligned.apk" - - echo "$version-$d" > version.txt - else - echo "No .apk files found in the specified folder." - fi - - cd - - env: GITHUB_TOKEN: ${{ github.token }} From bf24813aa79d401ab4068f68712607f8e754d19b Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Thu, 14 Nov 2024 11:59:14 +0800 Subject: [PATCH 02/13] update local-dir --- .github/workflows/snapshots-android.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snapshots-android.yml b/.github/workflows/snapshots-android.yml index e59b5744d21..d1fdb04adb9 100644 --- a/.github/workflows/snapshots-android.yml +++ b/.github/workflows/snapshots-android.yml @@ -105,6 +105,6 @@ jobs: server: ftp.cardforge.org username: ${{ secrets.FTP_USERNAME }} password: ${{ secrets.FTP_PASSWORD }} - local-dir: forge-gui-android/target/upload/ + local-dir: upload/ server-dir: downloads/dailysnapshots/ state-name: .ftp-deploy-android-sync-state.json From 80215206922366225402307ae0be4fd515d438d6 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Thu, 14 Nov 2024 07:09:21 +0100 Subject: [PATCH 03/13] moved GameEventCardForetold and GameEventCardPlotted to GameLogFormatter --- .../src/main/java/forge/game/GameLogFormatter.java | 11 +++++++++++ .../main/java/forge/game/card/CardFactoryUtil.java | 4 ---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameLogFormatter.java b/forge-game/src/main/java/forge/game/GameLogFormatter.java index 4599f4c17b3..911cd2e7f37 100644 --- a/forge-game/src/main/java/forge/game/GameLogFormatter.java +++ b/forge-game/src/main/java/forge/game/GameLogFormatter.java @@ -306,6 +306,17 @@ public class GameLogFormatter extends IGameEventVisitor.Base { return new GameLogEntry(GameLogEntryType.MULLIGAN, message); } + @Override + public GameLogEntry visit(GameEventCardForetold ev) { + String sb = TextUtil.concatWithSpace(ev.activatingPlayer.toString(), "has foretold."); + return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, sb); + } + + @Override + public GameLogEntry visit(GameEventCardPlotted ev) { + return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, ev.toString()); + } + @Subscribe public void recieve(GameEvent ev) { GameLogEntry le = ev.visit(this); diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index be1af91202f..4f9cc1b0551 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -3101,8 +3101,6 @@ public class CardFactoryUtil { // because it doesn't work other wise c.setForetoldCostByEffect(true); } - String sb = TextUtil.concatWithSpace(getActivatingPlayer().toString(), "has foretold."); - game.getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb); game.fireEvent(new GameEventCardForetold(getActivatingPlayer())); } }; @@ -3425,8 +3423,6 @@ public class CardFactoryUtil { c.setPlotted(true); - String sb = TextUtil.concatWithSpace(getActivatingPlayer().toString(), "has plotted", c.toString()); - game.getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb); game.fireEvent(new GameEventCardPlotted(c, getActivatingPlayer())); } }; From 88ca1d509f9515914167948d2e63742de6f321f5 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Thu, 14 Nov 2024 15:28:43 +0800 Subject: [PATCH 04/13] Update VAssignGenericAmount.java support double tapping the mana symbols for max amount --- .../match/views/VAssignGenericAmount.java | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/forge-gui-mobile/src/forge/screens/match/views/VAssignGenericAmount.java b/forge-gui-mobile/src/forge/screens/match/views/VAssignGenericAmount.java index ca738800da4..9fc02058b6a 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VAssignGenericAmount.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VAssignGenericAmount.java @@ -70,7 +70,6 @@ public class VAssignGenericAmount extends FDialog { /** Constructor. * - * @param attacker0 {@link forge.game.card.Card} * @param targets Map, map of GameEntity and its maximum assignable amount * @param amount Total amount to be assigned * @param atLeastOne Must assign at least one amount to each target @@ -168,7 +167,7 @@ public class VAssignGenericAmount extends FDialog { obj = add(new EffectSourcePanel((CardView)entity)); } else if (entity instanceof PlayerView) { PlayerView player = (PlayerView)entity; - obj = add(new MiscTargetPanel(player.getName(), MatchController.getPlayerAvatar(player))); + obj = add(new MiscTargetPanel(player.getName(), MatchController.getPlayerAvatar(player), null)); } else if (entity instanceof Byte) { FSkinImageInterface manaSymbol; byte color = (Byte) entity; @@ -185,9 +184,9 @@ public class VAssignGenericAmount extends FDialog { } else { // Should never come here, but add this to avoid compile error manaSymbol = Forge.getAssets().images().get(FSkinProp.IMG_MANA_COLORLESS); } - obj = add(new MiscTargetPanel("", manaSymbol)); + obj = add(new MiscTargetPanel("", manaSymbol, entity)); } else { - obj = add(new MiscTargetPanel(entity.toString(), FSkinImage.UNKNOWN)); + obj = add(new MiscTargetPanel(entity.toString(), FSkinImage.UNKNOWN, null)); } label = add(new FLabel.Builder().text("0").font(FSkinFont.get(18)).align(Align.center).build()); btnSubtract = add(new FLabel.ButtonBuilder().icon(FSkinImage.MINUS).command(e -> assignAmountTo(entity, false)).build()); @@ -232,19 +231,21 @@ public class VAssignGenericAmount extends FDialog { } } - private static class MiscTargetPanel extends FDisplayObject { - private static final FSkinFont FONT = FSkinFont.get(18); - private static FSkinColor getForeColor() { + private class MiscTargetPanel extends FDisplayObject { + private final FSkinFont FONT = FSkinFont.get(18); + private FSkinColor getForeColor() { if (Forge.isMobileAdventureMode) return FSkinColor.get(Colors.ADV_CLR_TEXT); return FSkinColor.get(Colors.CLR_TEXT); } private final String name; private final FImage image; + private final Object entity; - private MiscTargetPanel(String name0, FImage image0) { + private MiscTargetPanel(String name0, FImage image0, Object entity0) { name = name0; image = image0; + entity = entity0; } @Override @@ -254,6 +255,24 @@ public class VAssignGenericAmount extends FDialog { g.drawImage(image, 0, 0, w, w); g.drawText(name, FONT, getForeColor(), 0, w, w, h - w, false, Align.center, true); } + + @Override + public boolean tap(float x, float y, int count) { + if (count > 1 && entity != null) { + AssignTarget at = targetsMap.get(entity); + int assigned = at.amount; + int leftToAssign = Math.max(0, at.max - assigned); + int amountToAdd = Math.min(getRemainingAmount(), leftToAssign); + + if (0 == amountToAdd || amountToAdd + assigned < 0) { + return false; + } + + addAssignedAmount(at, amountToAdd); + updateLabels(); + } + return super.tap(x, y, count); + } } private void assignAmountTo(Object source, boolean isAdding) { From 4ed7cb30d87653ebf11f3f8c499366b406ad3b49 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Thu, 14 Nov 2024 15:51:11 +0800 Subject: [PATCH 05/13] Update SColumnUtil.java add adventure default columns --- forge-gui/src/main/java/forge/itemmanager/SColumnUtil.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/forge-gui/src/main/java/forge/itemmanager/SColumnUtil.java b/forge-gui/src/main/java/forge/itemmanager/SColumnUtil.java index ff0328f4e28..b2309f45346 100644 --- a/forge-gui/src/main/java/forge/itemmanager/SColumnUtil.java +++ b/forge-gui/src/main/java/forge/itemmanager/SColumnUtil.java @@ -143,6 +143,13 @@ public final class SColumnUtil { return columns; } + public static Map getAdventureCollectionDefaultColumns() { + Map columns = getCardColumns(ColumnDef.QUANTITY, false, false, false, true, false); + columns.get(ColumnDef.NEW).setSortPriority(1); + columns.get(ColumnDef.NAME).setSortPriority(2); + return columns; + } + public static Map getAttractionPoolDefaultColumns() { //Similar to special card pool, but show the collector number and hide the type. List colDefs = new ArrayList<>(); From e0d8b24412e764468a137a80754e3f3511581cf6 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Thu, 14 Nov 2024 15:52:18 +0800 Subject: [PATCH 06/13] Update ItemManagerConfig.java use adventure default columns --- .../src/main/java/forge/itemmanager/ItemManagerConfig.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/forge-gui/src/main/java/forge/itemmanager/ItemManagerConfig.java b/forge-gui/src/main/java/forge/itemmanager/ItemManagerConfig.java index 685f1d6deda..7f4adf97539 100644 --- a/forge-gui/src/main/java/forge/itemmanager/ItemManagerConfig.java +++ b/forge-gui/src/main/java/forge/itemmanager/ItemManagerConfig.java @@ -119,9 +119,9 @@ public enum ItemManagerConfig { null, null, 3, 0), NET_ARCHIVE_BLOCK_DECKS(SColumnUtil.getDecksDefaultColumns(false, false), false, false, false, null, null, 3, 0), - ADVENTURE_EDITOR_POOL(SColumnUtil.getConquestCollectionDefaultColumns(), false, false, false, + ADVENTURE_EDITOR_POOL(SColumnUtil.getAdventureCollectionDefaultColumns(), false, false, false, null, null, 6, 0), - ADVENTURE_STORE_POOL(SColumnUtil.getConquestCollectionDefaultColumns(), false, false, true, + ADVENTURE_STORE_POOL(SColumnUtil.getAdventureCollectionDefaultColumns(), false, false, true, null, null, 6, 0), ADVENTURE_SIDEBOARD(SColumnUtil.getDeckEditorDefaultColumns(), false, false, true, null, null, 6, 0), @@ -376,4 +376,4 @@ public enum ItemManagerConfig { e.printStackTrace(); } } -} \ No newline at end of file +} From 919832cc06c4d2445f1cb4bf869b1d7f4c29a3ca Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Thu, 14 Nov 2024 16:11:14 +0800 Subject: [PATCH 07/13] Update pom.xml remove duplicate plugin --- forge-gui-desktop/pom.xml | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/forge-gui-desktop/pom.xml b/forge-gui-desktop/pom.xml index 1aeb7a74877..6bd211ca954 100644 --- a/forge-gui-desktop/pom.xml +++ b/forge-gui-desktop/pom.xml @@ -54,6 +54,19 @@ false + + released-version + validate + + released-version + + + + parse-version + + parse-version + + @@ -170,26 +183,6 @@ - - org.codehaus.mojo - build-helper-maven-plugin - 3.6.0 - - - released-version - validate - - released-version - - - - parse-version - - parse-version - - - - se.bjurr.gitchangelog git-changelog-maven-plugin From ac96890f44757b3b27f524ba98821700399eaa0c Mon Sep 17 00:00:00 2001 From: tool4ever Date: Thu, 14 Nov 2024 10:34:05 +0100 Subject: [PATCH 08/13] Some fixes (#6575) --- .../src/main/java/forge/game/GameAction.java | 2 +- .../java/forge/game/ability/AbilityUtils.java | 14 ++++++++++--- .../java/forge/game/cost/ICostVisitor.java | 5 ++++- .../game/replacement/ReplacementHandler.java | 1 + .../screens/match/controllers/CCombat.java | 3 +-- .../res/cardsfolder/i/infernal_vessel.txt | 2 +- .../k/king_of_the_oathbreakers.txt | 2 +- .../res/cardsfolder/n/needletooth_pack.txt | 2 +- .../res/cardsfolder/t/the_mindskinner.txt | 2 +- .../java/forge/gamemodes/match/GameLobby.java | 20 +++++++------------ 10 files changed, 29 insertions(+), 24 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index e1ccedf9df1..a8b6930717c 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -2456,7 +2456,7 @@ public class GameAction { game.getAction().reveal(milledPlayer, destination, p, false, message, addSuffix); } game.getGameLog().add(GameLogEntryType.ZONE_CHANGE, p + " milled " + - Lang.joinHomogenous(milled) + toZoneStr + "."); + Lang.joinHomogenous(milledPlayer) + toZoneStr + "."); } } 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 6f3475832fe..d0e727a2bed 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -1640,9 +1640,15 @@ public class AbilityUtils { return doXMath(calculateAmount(c, sq[v ? 1 : 2], ctb), expr, c, ctb); } + SpellAbility sa = null; if (ctb instanceof SpellAbility) { - final SpellAbility sa = (SpellAbility) ctb; + sa = (SpellAbility) ctb; + } else if (sq[0].contains("xPaid") && ctb instanceof TriggerReplacementBase) { + // try avoid fallback + sa = ((TriggerReplacementBase) ctb).getOverridingAbility(); + } + if (sa != null) { // special logic for xPaid in SpellAbility if (sq[0].contains("xPaid")) { SpellAbility root = sa.getRootAbility(); @@ -1672,7 +1678,8 @@ public class AbilityUtils { // and the spell that became that object as it resolved had a value of X chosen for any of its costs, // the value of X for that ability is the same as the value of X for that spell, although the value of X for that permanent is 0. if (TriggerType.ChangesZone.equals(t.getMode()) && ZoneType.Battlefield.name().equals(t.getParam("Destination"))) { - return doXMath(c.getXManaCostPaid(), expr, c, ctb); + int x = isUnlinkedFromCastSA(ctb, c) ? 0 : c.getXManaCostPaid(); + return doXMath(x, expr, c, ctb); } else if (TriggerType.SpellCast.equals(t.getMode())) { // Cast Trigger like Hydroid Krasis SpellAbilityStackInstance castSI = (SpellAbilityStackInstance) root.getTriggeringObject(AbilityKey.StackInstance); @@ -1696,7 +1703,8 @@ public class AbilityUtils { } if (root.isReplacementAbility() && sa.hasParam("ETB")) { - return doXMath(c.getXManaCostPaid(), expr, c, ctb); + int x = isUnlinkedFromCastSA(ctb, c) ? 0 : c.getXManaCostPaid(); + return doXMath(x, expr, c, ctb); } return doXMath(0, expr, c, ctb); diff --git a/forge-game/src/main/java/forge/game/cost/ICostVisitor.java b/forge-game/src/main/java/forge/game/cost/ICostVisitor.java index 1ee90baa331..190e1c806ce 100644 --- a/forge-game/src/main/java/forge/game/cost/ICostVisitor.java +++ b/forge-game/src/main/java/forge/game/cost/ICostVisitor.java @@ -105,6 +105,7 @@ public interface ICostVisitor { public T visit(CostFlipCoin cost) { return null; } + @Override public T visit(CostForage cost) { return null; @@ -146,7 +147,9 @@ public interface ICostVisitor { } @Override - public T visit(CostPromiseGift cost) { return null; } + public T visit(CostPromiseGift cost) { + return null; + } @Override public T visit(CostPutCardToLib cost) { diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java index cd28fa4316a..75262669cfe 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java @@ -665,6 +665,7 @@ public class ReplacementHandler { } List possibleReplacers = new ArrayList<>(replaceCandidateMap.keySet()); + // TODO should be able to choose different order for each entity ReplacementEffect chosenRE = decider.getController().chooseSingleReplacementEffect(possibleReplacers); List> runParamList = replaceCandidateMap.get(chosenRE); diff --git a/forge-gui-desktop/src/main/java/forge/screens/match/controllers/CCombat.java b/forge-gui-desktop/src/main/java/forge/screens/match/controllers/CCombat.java index 750d7403a42..9ee50bc9fc1 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/match/controllers/CCombat.java +++ b/forge-gui-desktop/src/main/java/forge/screens/match/controllers/CCombat.java @@ -78,9 +78,8 @@ public class CCombat implements ICDoc { } display.append("\n"); - PlayerView controller = null; if (defender instanceof CardView) { - controller = ((CardView) defender).getController(); + PlayerView controller = ((CardView) defender).getController(); if (controller == null) //shouldn't be null but display card's + controller ie Black Knight's controller display.append(Lang.getInstance().getPossesive(defender.getName())).append(" controller"); diff --git a/forge-gui/res/cardsfolder/i/infernal_vessel.txt b/forge-gui/res/cardsfolder/i/infernal_vessel.txt index 8aec8a859df..064ca619fab 100644 --- a/forge-gui/res/cardsfolder/i/infernal_vessel.txt +++ b/forge-gui/res/cardsfolder/i/infernal_vessel.txt @@ -2,7 +2,7 @@ Name:Infernal Vessel ManaCost:2 B Types:Creature Human Cleric PT:2/1 -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self+notDemon | Execute$ TrigReturn | TriggerDescription$ When this creature dies, if it wasn't a Demon, return it to the battlefield under its owner's control with two +1/+1 counters on it. It's a Demon in addition to its other types. +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self+nonDemon | Execute$ TrigReturn | TriggerDescription$ When this creature dies, if it wasn't a Demon, return it to the battlefield under its owner's control with two +1/+1 counters on it. It's a Demon in addition to its other types. SVar:TrigReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ TriggeredNewCardLKICopy | WithCountersType$ P1P1 | WithCountersAmount$ 2 | AnimateSubAbility$ DBAnimate SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Types$ Demon | Duration$ Permanent Oracle:When this creature dies, if it wasn't a Demon, return it to the battlefield under its owner's control with two +1/+1 counters on it. It's a Demon in addition to its other types. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/k/king_of_the_oathbreakers.txt b/forge-gui/res/cardsfolder/k/king_of_the_oathbreakers.txt index 9e400b406ba..4a93802da32 100644 --- a/forge-gui/res/cardsfolder/k/king_of_the_oathbreakers.txt +++ b/forge-gui/res/cardsfolder/k/king_of_the_oathbreakers.txt @@ -3,7 +3,7 @@ ManaCost:2 W B Types:Legendary Creature Spirit Noble PT:3/3 K:Flying -T:Mode$ BecomesTarget | ValidTarget$ Card.Self,Spirit.YouCtrl+Other | ValidSource$ Spell | TriggerZones$ Battlefield | Execute$ TrigPhaseOut | TriggerDescription$ Whenever CARDNAME or another Spirit you control becomes the target of a spell, it phases out. (Treat it and anything attached to it as though they don't exist until your next turn.) +T:Mode$ BecomesTarget | ValidTarget$ Card.Self,Spirit.YouCtrl+Other+inZoneBattlefield | ValidSource$ Spell | TriggerZones$ Battlefield | Execute$ TrigPhaseOut | TriggerDescription$ Whenever CARDNAME or another Spirit you control becomes the target of a spell, it phases out. (Treat it and anything attached to it as though they don't exist until your next turn.) SVar:TrigPhaseOut:DB$ Phases | Defined$ TriggeredTargetLKICopy T:Mode$ PhaseIn | ValidCard$ Card.Self,Spirit.YouCtrl+Other | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever CARDNAME or another Spirit you control phases in, create a tapped 1/1 white Spirit creature token with flying. SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_spirit_flying | TokenOwner$ You | TokenTapped$ True diff --git a/forge-gui/res/cardsfolder/n/needletooth_pack.txt b/forge-gui/res/cardsfolder/n/needletooth_pack.txt index 4c7b222a5c3..81e7abaf07b 100644 --- a/forge-gui/res/cardsfolder/n/needletooth_pack.txt +++ b/forge-gui/res/cardsfolder/n/needletooth_pack.txt @@ -2,7 +2,7 @@ Name:Needletooth Pack ManaCost:3 G G Types:Creature Dinosaur PT:4/5 -T:Mode$ Phase | Phase$ End of Turn | CheckSVar$ Morbid | SVarCompare$ GE1 | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Morbid — At the beginning of your end step, if a creature died this turn, put two +1/+1 counters on target creature you control. +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | CheckSVar$ Morbid | SVarCompare$ GE1 | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Morbid — At the beginning of your end step, if a creature died this turn, put two +1/+1 counters on target creature you control. SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | CounterNum$ 2 SVar:Morbid:Count$Morbid.1.0 Oracle:Morbid — At the beginning of your end step, if a creature died this turn, put two +1/+1 counters on target creature you control. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/t/the_mindskinner.txt b/forge-gui/res/cardsfolder/t/the_mindskinner.txt index fce38f7d439..74bcacb712a 100644 --- a/forge-gui/res/cardsfolder/t/the_mindskinner.txt +++ b/forge-gui/res/cardsfolder/t/the_mindskinner.txt @@ -3,7 +3,7 @@ ManaCost:U U U Types:Legendary Enchantment Creature Nightmare PT:10/1 S:Mode$ CantBlockBy | ValidAttacker$ Creature.Self | Description$ CARDNAME can't be blocked. -R:Event$ DamageDone | ActiveZones$ Battlefield | ValidSource$ Card.YouCtrl,Emblem.YouCtrl | ValidTarget$ Opponent | ReplaceWith$ Mill | PreventionEffect$ True | ExecuteMode$ PerTarget | Description$ If a source you control would deal damage to an opponent, prevent that damage and each opponent mills that many cards. +R:Event$ DamageDone | ActiveZones$ Battlefield | ValidSource$ Card.YouCtrl,Emblem.YouCtrl | ValidTarget$ Opponent | ReplaceWith$ Mill | PreventionEffect$ True | ExecuteMode$ PerSource | Description$ If a source you control would deal damage to an opponent, prevent that damage and each opponent mills that many cards. SVar:Mill:DB$ Mill | Defined$ Opponent | NumCards$ X SVar:X:ReplaceCount$DamageAmount Oracle:The Mindskinner can't be blocked.\nIf a source you control would deal damage to an opponent, prevent that damage and each opponent mills that many cards. diff --git a/forge-gui/src/main/java/forge/gamemodes/match/GameLobby.java b/forge-gui/src/main/java/forge/gamemodes/match/GameLobby.java index c92bc865263..340a9633b46 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/GameLobby.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/GameLobby.java @@ -443,17 +443,13 @@ public abstract class GameLobby implements IHasGameType { Deck deck = slot.getDeck(); RegisteredPlayer rp = new RegisteredPlayer(deck); - if (variantTypes.isEmpty()) { - rp.setTeamNumber(team); - players.add(rp.setPlayer(lobbyPlayer)); - } - else { + if (!variantTypes.isEmpty()) { if (isCommanderMatch) { final GameType commanderGameType = isOathbreakerMatch ? GameType.Oathbreaker : - isTinyLeadersMatch ? GameType.TinyLeaders : - isBrawlMatch ? GameType.Brawl : - GameType.Commander; + isTinyLeadersMatch ? GameType.TinyLeaders : + isBrawlMatch ? GameType.Brawl : + GameType.Commander; if (checkLegality) { final String errMsg = commanderGameType.getDeckFormat().getDeckConformanceProblem(deck); if (errMsg != null) { @@ -481,7 +477,6 @@ public abstract class GameLobby implements IHasGameType { Iterable schemes = null; Iterable planes = null; - //Archenemy if (variantTypes.contains(GameType.ArchenemyRumble) || (variantTypes.contains(GameType.Archenemy) && isArchenemy)) { final CardPool schemePool = deck.get(DeckSection.Schemes); @@ -495,7 +490,6 @@ public abstract class GameLobby implements IHasGameType { schemes = schemePool == null ? Collections.emptyList() : schemePool.toFlatList(); } - //Planechase if (variantTypes.contains(GameType.Planechase)) { final CardPool planePool = deck.get(DeckSection.Planes); if (checkLegality) { @@ -508,7 +502,6 @@ public abstract class GameLobby implements IHasGameType { planes = planePool == null ? Collections.emptyList() : planePool.toFlatList(); } - //Vanguard if (variantTypes.contains(GameType.Vanguard)) { if (avatarPool == null || avatarPool.countAll() == 0) { //ERROR! null if avatar deselected on list SOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblNoSelectedVanguardAvatarForPlayer", name)); @@ -517,10 +510,11 @@ public abstract class GameLobby implements IHasGameType { } rp = RegisteredPlayer.forVariants(activeSlots.size(), variantTypes, deck, schemes, isArchenemy, planes, avatarPool); - rp.setTeamNumber(team); - players.add(rp.setPlayer(lobbyPlayer)); } + rp.setTeamNumber(team); + players.add(rp.setPlayer(lobbyPlayer)); + if (!isAI) { guis.put(rp, gui); } From bd186003855223c1c5bb412de2f7891667ccfa2a Mon Sep 17 00:00:00 2001 From: tool4ever Date: Fri, 15 Nov 2024 07:01:08 +0100 Subject: [PATCH 09/13] Fix DontPayTapCostWithManaSources (#6578) * Fix DontPayTapCostWithManaSources * Foundations Update Bulletin (engine) --------- Co-authored-by: tool4EvEr --- .../src/main/java/forge/ai/AiController.java | 9 ++++ .../main/java/forge/ai/AiCostDecision.java | 21 +++------ .../src/main/java/forge/ai/ComputerUtil.java | 1 + .../main/java/forge/ai/ComputerUtilCost.java | 46 ++++--------------- .../main/java/forge/ai/ComputerUtilMana.java | 3 +- .../src/main/java/forge/game/card/Card.java | 3 -- .../forge/game/replacement/ReplaceDamage.java | 3 -- .../res/cardsfolder/p/pariahs_shield.txt | 2 +- .../res/cardsfolder/s/sephara_skys_blade.txt | 1 - .../res/cardsfolder/w/warlords_elite.txt | 1 - .../cardsfolder/z/zahid_djinn_of_the_lamp.txt | 2 - 11 files changed, 29 insertions(+), 63 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 14915df4a79..e20e9718b34 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -22,6 +22,8 @@ import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; + +import forge.ai.AiCardMemory.MemorySet; import forge.ai.ability.ChangeZoneAi; import forge.ai.ability.LearnAi; import forge.ai.simulation.SpellAbilityPicker; @@ -845,6 +847,13 @@ public class AiController { return AiPlayDecision.CantAfford; } + // check if enough left (pass memory indirectly because we don't want to include those) + Set tappedForMana = AiCardMemory.getMemorySet(player, MemorySet.PAYS_TAP_COST); + if (tappedForMana != null && tappedForMana.isEmpty() && + !ComputerUtilCost.checkTapTypeCost(player, sa.getPayCosts(), host, sa, new CardCollection(tappedForMana))) { + return AiPlayDecision.CantAfford; + } + // if we got here, looks like we can play the final cost and we could properly set up and target the API and // are willing to play the SA return AiPlayDecision.WillPlay; diff --git a/forge-ai/src/main/java/forge/ai/AiCostDecision.java b/forge-ai/src/main/java/forge/ai/AiCostDecision.java index 662a42b7318..9ebcff668a4 100644 --- a/forge-ai/src/main/java/forge/ai/AiCostDecision.java +++ b/forge-ai/src/main/java/forge/ai/AiCostDecision.java @@ -2,6 +2,8 @@ package forge.ai; import com.google.common.base.Predicates; import com.google.common.collect.Lists; + +import forge.ai.AiCardMemory.MemorySet; import forge.card.CardType; import forge.card.MagicColor; import forge.game.Game; @@ -32,6 +34,10 @@ public class AiCostDecision extends CostDecisionMakerBase { discarded = new CardCollection(); tapped = new CardCollection(); + Set tappedForMana = AiCardMemory.getMemorySet(ai0, MemorySet.PAYS_TAP_COST); + if (tappedForMana != null) { + tapped.addAll(tappedForMana); + } } @Override @@ -440,21 +446,6 @@ public class AiCostDecision extends CostDecisionMakerBase { return null; } - if ("DontPayTapCostWithManaSources".equals(source.getSVar("AIPaymentPreference"))) { - CardCollectionView toExclude = - CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), type.split(";"), - ability.getActivatingPlayer(), ability.getHostCard(), ability); - toExclude = CardLists.filter(toExclude, card -> { - for (final SpellAbility sa : card.getSpellAbilities()) { - if (sa.isManaAbility() && sa.getPayCosts().hasTapCost()) { - return true; - } - } - return false; - }); - exclude.addAll(toExclude); - } - String totalP = ""; CardCollectionView totap; if (isVehicle) { diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 0d83d91aefe..2d4b7308c8c 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -782,6 +782,7 @@ public class ComputerUtil { } CardLists.sortByPowerAsc(typeList); + // TODO prefer noncreatures without tap abilities final CardCollection tapList = new CardCollection(); diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java index bfee0f90eb7..dfd27b69b04 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java @@ -29,6 +29,7 @@ import forge.util.collect.FCollectionView; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; +import java.util.Collection; import java.util.List; import java.util.Set; @@ -450,7 +451,7 @@ public class ComputerUtilCost { * the source * @return true, if successful */ - public static boolean checkTapTypeCost(final Player ai, final Cost cost, final Card source, final SpellAbility sa, final CardCollection alreadyTapped) { + public static boolean checkTapTypeCost(final Player ai, final Cost cost, final Card source, final SpellAbility sa, final Collection alreadyTapped) { if (cost == null) { return true; } @@ -487,8 +488,8 @@ public class ComputerUtilCost { c = AbilityUtils.calculateAmount(source, part.getAmount(), sa); } CardCollection exclude = new CardCollection(); - if (AiCardMemory.getMemorySet(ai, MemorySet.PAYS_TAP_COST) != null) { - exclude.addAll(AiCardMemory.getMemorySet(ai, MemorySet.PAYS_TAP_COST)); + if (alreadyTapped != null) { + exclude.addAll(alreadyTapped); } // trying to produce mana that includes tapping source that will already be tapped if (exclude.contains(source) && cost.hasTapCost()) { @@ -500,12 +501,12 @@ public class ComputerUtilCost { } CardCollection tapChoices = ComputerUtil.chooseTapType(ai, type, source, cost.hasTapCost(), c, exclude, sa); if (tapChoices != null) { - for (Card choice : tapChoices) { - AiCardMemory.rememberCard(ai, choice, MemorySet.PAYS_TAP_COST); - } - // if manasource gets tapped to produce it also can't help paying another - if (cost.hasTapCost()) { - AiCardMemory.rememberCard(ai, source, MemorySet.PAYS_TAP_COST); + if (alreadyTapped != null) { + alreadyTapped.addAll(tapChoices); + // if manasource gets tapped to produce it also can't help paying another + if (cost.hasTapCost()) { + alreadyTapped.add(source); + } } return true; } @@ -600,33 +601,6 @@ public class ComputerUtilCost { } } - // TODO: Alternate costs which involve both paying mana and tapping a card, e.g. Zahid, Djinn of the Lamp - // Current AI decides on each part separately, thus making it possible for the AI to cheat by - // tapping a mana source for mana and for the tap cost at the same time. Until this is improved, AI - // will not consider mana sources valid for paying the tap cost to avoid this exact situation. - if ("DontPayTapCostWithManaSources".equals(sa.getHostCard().getSVar("AIPaymentPreference"))) { - for (final CostPart part : sa.getPayCosts().getCostParts()) { - if (part instanceof CostTapType) { - CardCollectionView nonManaSources = - CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), part.getType().split(";"), - sa.getActivatingPlayer(), sa.getHostCard(), sa); - nonManaSources = CardLists.filter(nonManaSources, card -> { - boolean hasManaSa = false; - for (final SpellAbility sa1 : card.getSpellAbilities()) { - if (sa1.isManaAbility() && sa1.getPayCosts().hasTapCost()) { - hasManaSa = true; - break; - } - } - return !hasManaSa; - }); - if (nonManaSources.size() < part.convertAmount()) { - return false; - } - } - } - } - // Bail early on Casualty in case there are no cards that would make sense to pay with if (sa.getHostCard().hasKeyword(Keyword.CASUALTY)) { for (final CostPart part : sa.getPayCosts().getCostParts()) { diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index b23d4d6b250..1dd1c1448e8 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -268,6 +268,7 @@ public class ComputerUtilMana { } for (final SpellAbility ma : saList) { + // this rarely seems like a good idea if (ma.getHostCard() == saHost) { continue; } @@ -276,7 +277,7 @@ public class ComputerUtilMana { continue; } - if (!ComputerUtilCost.checkTapTypeCost(ai, ma.getPayCosts(), ma.getHostCard(), sa, new CardCollection())) { + if (!ComputerUtilCost.checkTapTypeCost(ai, ma.getPayCosts(), ma.getHostCard(), sa, AiCardMemory.getMemorySet(ai, MemorySet.PAYS_TAP_COST))) { continue; } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index b8d7e07a2d7..383828f37bc 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -4096,9 +4096,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr } public final boolean isModified() { - if (!isCreature()) { - return false; - } if (this.isEquipped() || this.hasCounters()) { return true; } diff --git a/forge-game/src/main/java/forge/game/replacement/ReplaceDamage.java b/forge-game/src/main/java/forge/game/replacement/ReplaceDamage.java index cbd590a9d9f..32d3b393a80 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplaceDamage.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplaceDamage.java @@ -92,9 +92,6 @@ public class ReplaceDamage extends ReplacementEffect { return false; } } - if (hasParam("IsEquipping") && !getHostCard().isEquipping()) { - return false; - } if (hasParam("DamageTarget")) { //Lava Burst and Whippoorwill check diff --git a/forge-gui/res/cardsfolder/p/pariahs_shield.txt b/forge-gui/res/cardsfolder/p/pariahs_shield.txt index 1166af2a770..ce73061b4d3 100644 --- a/forge-gui/res/cardsfolder/p/pariahs_shield.txt +++ b/forge-gui/res/cardsfolder/p/pariahs_shield.txt @@ -2,6 +2,6 @@ Name:Pariah's Shield ManaCost:5 Types:Artifact Equipment K:Equip:3 -R:Event$ DamageDone | ActiveZones$ Battlefield | ValidTarget$ You | ReplaceWith$ DmgEquipped | IsEquipping$ True | DamageTarget$ Equipped | Description$ All damage that would be dealt to you is dealt to equipped creature instead. +R:Event$ DamageDone | ActiveZones$ Battlefield | ValidTarget$ You | ReplaceWith$ DmgEquipped | IsPresent$ Card.equipping | PresentDefined$ Self | DamageTarget$ Equipped | Description$ All damage that would be dealt to you is dealt to equipped creature instead. SVar:DmgEquipped:DB$ ReplaceEffect | VarName$ Affected | VarValue$ Equipped | VarType$ Card Oracle:All damage that would be dealt to you is dealt to equipped creature instead.\nEquip {3} diff --git a/forge-gui/res/cardsfolder/s/sephara_skys_blade.txt b/forge-gui/res/cardsfolder/s/sephara_skys_blade.txt index a1873fea7ce..4c2bc985e56 100644 --- a/forge-gui/res/cardsfolder/s/sephara_skys_blade.txt +++ b/forge-gui/res/cardsfolder/s/sephara_skys_blade.txt @@ -3,7 +3,6 @@ ManaCost:4 W W W Types:Legendary Creature Angel PT:7/7 S:Mode$ AlternativeCost | ValidSA$ Spell.Self | EffectZone$ All | Cost$ W tapXType<4/Creature.withFlying> | Description$ You may pay {W} and tap four untapped creatures you control with flying rather than pay this spell's mana cost. -SVar:AIPaymentPreference:DontPayTapCostWithManaSources K:Flying K:Lifelink S:Mode$ Continuous | Affected$ Creature.withFlying+Other+YouCtrl | AddKeyword$ Indestructible | Description$ Other creatures you control with flying have indestructible. diff --git a/forge-gui/res/cardsfolder/w/warlords_elite.txt b/forge-gui/res/cardsfolder/w/warlords_elite.txt index 615cf18e4a1..81de84233b6 100644 --- a/forge-gui/res/cardsfolder/w/warlords_elite.txt +++ b/forge-gui/res/cardsfolder/w/warlords_elite.txt @@ -3,6 +3,5 @@ ManaCost:2 W Types:Creature Human Soldier PT:4/4 A:SP$ PermanentCreature | Cost$ 2 W tapXType<2/Artifact;Creature;Land/artifacts, creatures, and/or lands> | SpellDescription$ As an additional cost to cast this spell, tap two untapped artifacts, creatures, and/or lands you control. -SVar:AIPaymentPreference:DontPayTapCostWithManaSources DeckHints:Type$Artifact Oracle:As an additional cost to cast this spell, tap two untapped artifacts, creatures, and/or lands you control. diff --git a/forge-gui/res/cardsfolder/z/zahid_djinn_of_the_lamp.txt b/forge-gui/res/cardsfolder/z/zahid_djinn_of_the_lamp.txt index 75c52775ad7..997b8c36bfd 100644 --- a/forge-gui/res/cardsfolder/z/zahid_djinn_of_the_lamp.txt +++ b/forge-gui/res/cardsfolder/z/zahid_djinn_of_the_lamp.txt @@ -4,6 +4,4 @@ Types:Legendary Creature Djinn PT:5/6 K:Flying S:Mode$ AlternativeCost | ValidSA$ Spell.Self | EffectZone$ All | Cost$ 3 U tapXType<1/Artifact> | Description$ You may pay {3}{U} and tap an untapped artifact you control rather than pay this spell's mana cost. -# TODO: Currently the AI may cheat without the following flag by tapping the same artifact for mana and for the tap cost, e.g. 2 Islands + Sol Ring. Remove this flag once the AI is smart enough not to do that. -SVar:AIPaymentPreference:DontPayTapCostWithManaSources Oracle:You may pay {3}{U} and tap an untapped artifact you control rather than pay this spell's mana cost.\nFlying From 4b13e55d83a561bbc86d1a254a3faa5a50637f36 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Fri, 15 Nov 2024 16:26:05 +0800 Subject: [PATCH 10/13] fix infinite notice for missing tokens --- forge-core/src/main/java/forge/ImageKeys.java | 4 ++-- forge-gui/src/main/java/forge/util/ImageFetcher.java | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/forge-core/src/main/java/forge/ImageKeys.java b/forge-core/src/main/java/forge/ImageKeys.java index aa0d4912565..803d437aa83 100644 --- a/forge-core/src/main/java/forge/ImageKeys.java +++ b/forge-core/src/main/java/forge/ImageKeys.java @@ -85,7 +85,7 @@ public final class ImageKeys { } private static final Map cachedCards = new HashMap<>(50000); - private static HashSet missingCards = new HashSet<>(); + public static HashSet missingCards = new HashSet<>(); public static void clearMissingCards() { missingCards.clear(); } @@ -310,7 +310,7 @@ public final class ImageKeys { } // System.out.println("File not found, no image created: " + key); - //add missing cards - disable for desktop version for compatibility reasons with autodownloader + // add missing cards - disable for desktop version for compatibility reasons with autodownloader if (isLibGDXPort && !hasSetLookup(filename)) //missing cards with setlookup is handled differently missingCards.add(filename); return null; diff --git a/forge-gui/src/main/java/forge/util/ImageFetcher.java b/forge-gui/src/main/java/forge/util/ImageFetcher.java index 516988793c2..45f87d9dfc8 100644 --- a/forge-gui/src/main/java/forge/util/ImageFetcher.java +++ b/forge-gui/src/main/java/forge/util/ImageFetcher.java @@ -228,6 +228,11 @@ public abstract class ImageFetcher { } if (filename.equalsIgnoreCase("null.jpg")) return; + + if (ImageKeys.missingCards.contains(filename)) + return; + + ImageKeys.missingCards.add(filename); System.err.println("No specified file for '" + filename + "'.. Attempting to download from default Url"); tokenUrl = String.format("%s%s", ForgeConstants.URL_TOKEN_DOWNLOAD, filename); } From a53dc4ef852e7fff3045db4da2893a9b30ce392a Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Sat, 16 Nov 2024 06:49:36 +0800 Subject: [PATCH 11/13] Update snapshots-android.yml add missing version.txt --- .github/workflows/snapshots-android.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/snapshots-android.yml b/.github/workflows/snapshots-android.yml index d1fdb04adb9..bfc32f45f87 100644 --- a/.github/workflows/snapshots-android.yml +++ b/.github/workflows/snapshots-android.yml @@ -93,6 +93,7 @@ jobs: mkdir upload mv /home/runner/work/forge/forge/forge-gui-android/target/*-signed-aligned.apk upload/ mv /home/runner/work/forge/forge/forge-gui-android/target/assets.zip upload/ + mv /home/runner/work/forge/forge/forge-gui-android/target/classes/assets/version.txt upload/ cd upload ls env: From cf84ae5719e6ee1755002d0a9fdd3e5222d915e2 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Sat, 16 Nov 2024 08:33:45 +0800 Subject: [PATCH 12/13] fix NoSuchElementException --- .../main/java/forge/gamemodes/match/input/InputQueue.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/forge-gui/src/main/java/forge/gamemodes/match/input/InputQueue.java b/forge-gui/src/main/java/forge/gamemodes/match/input/InputQueue.java index b669697d598..7319d31e2d2 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/input/InputQueue.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/input/InputQueue.java @@ -56,8 +56,9 @@ public class InputQueue extends Observable { if (topMostInput != inp) { System.out.println("Cannot remove input " + inp.getClass().getSimpleName() + " because it's not on top of stack. Stack = " + inputStack ); - } else { - inputStack.pop(); + } else if (topMostInput != null) { + // if topMostInput is null then it means the inputstack is already empty, why this is called twice? + inputStack.pop(); } updateObservers(); } From c11ca5d3fbdca457c25d66a98f9736f9d6475053 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Sat, 16 Nov 2024 09:39:28 +0800 Subject: [PATCH 13/13] Remove input check upload on android snapshot workflow Don't know why this input is needed but it seems it's not taken into account even with default value to true when the cron job triggers, we have already test build workflow for android apk along with debug signing for checking if build and signing succeeded. Check discord reports on missing/cannot update reports. --- .github/workflows/snapshots-android.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/snapshots-android.yml b/.github/workflows/snapshots-android.yml index bfc32f45f87..793f4cddd7f 100644 --- a/.github/workflows/snapshots-android.yml +++ b/.github/workflows/snapshots-android.yml @@ -8,11 +8,11 @@ on: description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' required: false default: false - upload_package: - type: boolean - description: 'Upload the completed Android package' - required: false - default: true + #upload_package: + # type: boolean + # description: 'Upload the completed Android package' + # required: false + # default: true schedule: # * is a special character in YAML so you have to quote this string - cron: '00 19 * * *' @@ -101,7 +101,7 @@ jobs: - name: 📂 Sync files uses: SamKirkland/FTP-Deploy-Action@v4.3.4 - if: ${{ inputs.upload_package }} + #if: ${{ inputs.upload_package }} with: server: ftp.cardforge.org username: ${{ secrets.FTP_USERNAME }}