Skip to content

Implement Rulebreaker (+Partial implementation of DeckRule) - #11740

Merged
tool4ever merged 18 commits into
Card-Forge:masterfrom
BigCrunch22:add-deck-rule-rulebreaker
Sep 3, 2026
Merged

Implement Rulebreaker (+Partial implementation of DeckRule)#11740
tool4ever merged 18 commits into
Card-Forge:masterfrom
BigCrunch22:add-deck-rule-rulebreaker

Conversation

@BigCrunch22

@BigCrunch22 BigCrunch22 commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Rulebreaker mechanic + partial DeckRule deckbuilding framework

Summary

This adds a new custom keyword, Rulebreaker, and the engine infrastructure needed to support it: a partial implementation of the DeckRule: line-type framework sketched in #8066 ("Rework deckbuilding rules into a set of objects"). Rulebreaker cards bend specific deckbuilding restrictions (color identity, deck size) when they're your commander. All 12 cards in the Rulebreaker cycle are implemented and scripted, and Adventure Mode's separate, pre-existing deck-legality enforcement has been checked against them and patched where it wasn't DeckRule-aware.

Background

Rulebreaker cards read like:

Rulebreaker — A deck with this commander can have artifact creature and Equipment cards of any color identity and any basic land cards.

This is a deckbuilding-legality effect, not a live-game one — it changes which cards are legal to put in the deck, not anything that happens once a game starts.

#8066 already proposes the right shape for this: a new top-level script line, DeckRule:, parsed independently of K: lines, with rule classes like ColorIdentity and Size. This PR implements that framework — scoped to what Rulebreaker actually needs (ColorIdentity and Size), not the issue's full vision (Copies, Commander, FormatPool, Variants, FormatRestrictions are not attempted here).

New: the DeckRule framework (forge-core)

Three new classes in forge.deck, plus a new shared restriction-matching helper in forge.card:

  • DeckRule — abstract base and dispatcher. Parses DeckRule:<RuleClass>:<Key$ Value | ...> lines and handles ActiveSection$ gating (e.g. a rule can be scoped to apply only while its card is the actual Commander). parseAll(PaperCard) also picks up that specific card's own marked colors (see AllowedAdditionalColor$ below), so a rule built from a deck's actual commander copy already knows the player's choice; parseAll(Iterable<String>) is the raw-line overload for callers like the live game Card that only have a card face's raw data, not a full PaperCard.
  • DeckRuleColorIdentityDeckRule:ColorIdentity:.... Supports:
    • Exempt$ <branches> — exempts matching cards from the color-identity check entirely. Branches are comma-separated (OR); within a branch, tokens are dot-then-plus, mirroring how ValidCard$/Card.isValid() restriction strings already work elsewhere in the engine. Recognized tokens: real core/super/subtype words (via CardType.parse), the bare word Permanent, or a stat filter (powerGE4, cmcGE7, ...). The actual parsing/matching for this now lives in CardRulesPredicates.restrictionList() (see below) rather than in this class.
    • Disable$ True — waives the color-identity check entirely (e.g. a Paradise Bird-style card).
    • AllowedAdditionalColor$ <n> + AllowedAdditionalColorType$ <branches> — grants a budget of n extra colors (beyond the commander's own color identity) to cards matching the AllowedAdditionalColorType$ branch grammar. Rather than auto-detecting which extra color(s) the player wants as cards get added, the player picks them upfront on the commander itself (Cryptic Spires-style) via a new deck-editor menu item — see Mobile UI below. This reuses the engine's existing PaperCard.getMarkedColors()/copyWithMarkedColors() mechanism, the same one Cryptic Spires-style lands already use, instead of inventing new state-tracking.
  • DeckRuleSizeDeckRule:Size:AdjustMax$ <Unlimited|±n>. Only AdjustMax$ is implemented; AdjustMin$/Cumulative$ are left alone since Advantageous Proclamation already has its own hardcoded check in DeckFormat, and wiring an overlapping rule risked double-applying the adjustment.
  • CardRulesPredicates.restrictionList() (new, in the existing forge.card package) — the generalized branch-list parser (dot-then-plus tokenizing, type/supertype/subtype matching via CardType.parse, the Permanent keyword, and a power/toughness/cmc stat-filter shorthand) that both Exempt$ and AllowedAdditionalColorType$ are built on. Pulled out of DeckRuleColorIdentity into a shared, general-purpose predicate per review feedback. It's also Changeling-aware, so a Changeling correctly satisfies any creature-type branch (e.g. Exempt$ Angel,Basic.Land) instead of being missed.

Engine plumbing changes

forge-core

  • ICardRawAbilites.java / CardFace.java — new deckRules bucket + getDeckRules(), mirroring the existing keywords bucket exactly (including the functional-variant copy-down logic).
  • CardRules.javaReader.parseLine() gets a DeckRule case (alongside the existing D: cases for DeckHints/DeckNeeds/etc.); new getDeckRules() accessor.
  • DeckFormat.java:
    • getDeckConformanceProblem() — gathers each commander's active DeckRuleColorIdentity/DeckRuleSize rules once, then consults them in both the main-deck and sideboard color-identity loops, and applies Size adjustments before the deck-size check.
    • isLegalCardForCommanderPredicate(commanders) — the predicate that actually controls which cards a live deck-editor UI shows in its catalog. Since a commander's AllowedAdditionalColor$ pick now lives on its own marked colors instead of being inferred from the rest of the deck, this only needs the commander list — no priming from the deck's current contents.

forge-game

  • Player.java — new getStartingLibrarySize(), computed directly as getRegisteredPlayer().getDeck().getMain().countAll() rather than tracked as a separate field with its own setter.
  • AbilityUtils.java — exposes it as Count$YourStartingLibrarySize, in both places YourStartingLife is already handled.
  • Card.javagetAbilityText(CardState state), the method that actually composes a card's live displayed text, now folds each active DeckRule's Description$ in right after the keyword-reminder text, matching where the Oracle template places it. Previously nothing carried a card's DeckRule: lines from forge-core over to the live game Card at all, so a Rulebreaker's description only ever showed up in its static, manually-duplicated Oracle: text — visible in out-of-game previews (deck editor, card database) but never on an actual permanent, in hand, or in the command zone. This runs through the same view-update path every card already relies on for its keyword/trigger/static text (updateStateForView() → updateKeywords() → updateAbilityText()), so it's populated reliably from the moment a card is created, with no extra per-zone plumbing needed. Because this lives in forge-game, it's shared by every GUI (desktop included) rather than being another mobile-only patch.

Mobile UI (forge-gui-mobile)

FDeckEditor.java needed several fixes to actually reflect DeckRule in the deck editor's live catalog (Android and iOS share this file via forge-gui-mobile, so one fix covers all three; forge-gui-desktop was checked and doesn't need equivalent changes — see Desktop below):

  • canOnlyBePartnerCommander() — was doing its own raw hasNoColorsExcept() check with zero DeckRule awareness, wrongly gating the "Add to deck" menu item for legitimately-exempt cards. Now consults each active DeckRuleColorIdentity rule directly (allowsOffColorIdentity, approvesAdditionalColor) before falling back to that check.
  • CatalogPage.refresh() — this is what actually controls catalog visibility (via cardPool.retainIf(...)), a different and more consequential gate than the menu-item check above. It calls DeckFormat.isLegalCardForCommanderPredicate(), which is now DeckRule-aware the same way.
  • New "Allowed Additional Colors" menu item, added to addPerCardItems() — shown on a commander card only while it has an active AllowedAdditionalColor$ budget (hasAllowedAdditionalColorBudget()). Mirrors the existing "Color Identity" menu item Cryptic Spires-style lands already use: it opens a color picker (capped at the rule's getAdditionalColorCount(), excluding colors already in the commander's identity), replaces the commander with a copy carrying the chosen colors via copyWithMarkedColors(), and refreshes the catalog page so it immediately reflects the new choice.

Desktop (forge-gui-desktop)

No changes needed. Desktop's Commander editor doesn't do live catalog filtering at all — its own class doc comment says "least restrictive mode; all cards are available," and Main/Sideboard always show the fully unfiltered pool regardless of commander. It only enforces color identity at match-start time via GameLobby.java calling DeckFormat.getDeckConformanceProblem(), which is already DeckRule-aware through the forge-core changes above.

Adventure Mode (forge-gui-mobile)

Per review: Adventure Mode does have its own deck-legality enforcement, separate from everything above, and it wasn't DeckRule-aware. DuelScene.java's pre-battle deck fixup (applyAdventureDeckRules/applyAdventureCommandZoneRules) runs right before every duel — Adventure can't just block you from starting a fight with an "illegal" deck the way GameLobby can, so instead it silently rewrites the deck to fit. Two concrete breaks for Rulebreaker:

  • Color identity — its filter only understood raw commander color identity plus the pre-existing wildcard-color mechanic (getAddsWildCardColor()); with no Exempt$/Disable$/AllowedAdditionalColor$ awareness, it would silently strip a Rulebreaker's legitimately-exempt off-color cards (e.g. The Everforger's off-color artifact creatures/Equipment) out of the deck before every duel.
  • Deck size — its max-deck-size cap came straight from format.getMainRange().getMaximum(), with no DeckRuleSize awareness, so Whtz, the Bibliophile's AdjustMax$ Unlimited deck would get randomly trimmed back down to 99 cards before every fight.

Fixed by reusing the existing logic rather than re-deriving it: DeckFormat.allowsOffColorIdentity()/approvesAdditionalColor() were widened from private to public static so DuelScene can call them directly, and a small applyCommanderSizeRule() helper mirrors DeckFormat's own Unlimited/AdjustMax$ handling for the size cap.

AdventureDeckEditor.java (the actual deck-building screen) needed no changes — it extends FDeckEditor and doesn't override CatalogPage/DeckSectionPage's filtering, so it already inherited the Mobile UI fixes above, and its own "why is this deck invalid" display already calls the shared getDeckConformanceProblem().

Cards implemented

All 12 cards in the Rulebreaker cycle:

Card DeckRule clause Other abilities
The Everforger Exempt$ Artifact.Creature,Equipment,Basic.Land Once-per-turn copy trigger for artifact creature/Equipment spells
Hadran, Naya Sunseeder Exempt$ Creature.powerGE4 Any-color mana ability; draw on power-4+ ETB
Daxiver, Izzet Electromancer Exempt$ Instant,Sorcery Any-color mana restricted to instant/sorcery spells
Maular, the Next Evolution Exempt$ Creature.cmcGE7,Basic.Land Doubles power/toughness of attacking mv7+ creatures
Grizzlegom, Hurloon Hero Exempt$ Land 5-clause attack trigger, one per basic land type
Tolabow, Loch Rascal AllowedAdditionalColor$ 1 + AllowedAdditionalColorType$ Instant,Sorcery + Exempt$ Basic.Land Prowess Otter token on instant/sorcery cast
Whtz, the Bibliophile AdjustMax$ Unlimited Draw+gain ability, discounted with 200+ starting cards
Valko Indorian Exempt$ Phyrexian,Basic.Land Phyrexian creatures you control have menace and lifelink
Valko Indorian, Researcher Exempt$ Artifact,Enchantment Artifacts/enchantments you own become both types, all zones; +1/+1 counter on cast
Arvad of the Weatherlight Exempt$ Permanent.Legendary Combat damage puts a legendary permanent from hand onto the battlefield
The Unluckiest Planeswalker Exempt$ Aura,Basic.Land Can be your commander; two loyalty abilities (mana+token, discard/draw/reattach)
Seluma, Light of Aysen Exempt$ Angel,Basic.Land Flying; combat damage reanimates an Angel

Known limitations

  • DeckRule:Copies, DeckRule:Commander, DeckRule:FormatPool, etc. from #8066's fuller vision are not implemented — only ColorIdentity and Size, which is what this cycle needs.
  • Advantageous Proclamation still uses its own hardcoded deck-size check rather than DeckRule:Size; migrating it wasn't attempted, to avoid double-applying the adjustment.

Code changes made with heavy assistance from Claude

@BigCrunch22
BigCrunch22 marked this pull request as ready for review August 31, 2026 12:12
Comment thread forge-gui/res/cardsfolder/t/the_everforger.txt Outdated
Comment thread forge-gui/res/cardsfolder/t/the_everforger.txt Outdated
Comment thread forge-gui/res/cardsfolder/v/valko_indorian_researcher.txt Outdated
Comment thread forge-gui/res/cardsfolder/t/tolabow_loch_rascal.txt Outdated
- Fix double spacing issues
- Change ActivationLimit$ to ResolvedLimit$
@Jetz72

Jetz72 commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

How well do these hold up in Adventure Mode? It has its own enforcement logic for certain deck rules.

Comment thread forge-core/src/main/java/forge/deck/DeckRuleColorIdentity.java Outdated
Comment thread forge-core/src/main/java/forge/deck/DeckRuleColorIdentity.java Outdated
Comment thread forge-game/src/main/java/forge/game/player/Player.java Outdated
BigCrunch22 and others added 8 commits September 1, 2026 10:08
- Applied getRegisteredPlayer().getDeck to relevant parts of the code
- Removed "Type:" for parsing Exempt$
- AllowedAdditionalColor$ now includes AllowedAdditionalColorType$ to determine what type it affects
- Allow "Allowed Additional Colors" to work on forge-gui-desktop
- Update language files to add "Allowed Additional Colors" (not translated)

@tool4ever tool4ever left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice work

@tool4ever
tool4ever merged commit 68ff500 into Card-Forge:master Sep 3, 2026
3 checks passed
@BigCrunch22
BigCrunch22 deleted the add-deck-rule-rulebreaker branch September 4, 2026 03:51
delebedev added a commit to delebedev/forge that referenced this pull request Sep 5, 2026
* Add Assualt Drone (Card-Forge#11562)

* [maven-release-plugin] prepare release forge-2.0.14

* [maven-release-plugin] prepare for next development iteration

* Restore POM files for preparation of next release

* Restore pom files

* Migrate hobbit script files

* Realm of legends 1.067: Several small deckfixes; metalworker unban (Card-Forge#11570)

* Unbanned metalworker

* Deckfix

* Fixed Arzakon drop

* iOS: implement exit()/restart() as process termination

Review feedback on Card-Forge#11190 (Jetz72): the return-to-main-menu intercept
prevented restart-required settings (Adventure plane, skin, language,
card DB toggles) from ever taking effect — process-lifetime singletons
can only be rebuilt by a fresh launch. Forge iOS is sideload/TestFlight
distributed, so Apple's no-programmatic-exit HIG concern doesn't apply;
exit the process and let the user relaunch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* iOS: fix oversized in-game buttons on iPhone (HdpiMode.Pixels)

Follow-up to Card-Forge#11190 review: the prompt OK/Cancel buttons covered 87% of
an iPhone's width (13.4% of height, pinned at forge.util.Utils's max
clamp) vs ~56%/8.3% on a comparable Android phone. Root cause is a
points-vs-pixels unit mismatch in the default HdpiMode.Logical: sizes
come from getWidth/getHeight (logical points) but density from
getPpcX/getPpcY (physical px/cm), inflating the 1.1cm finger size by
the retina scale. Pixels mode makes both physical pixels — the exact
units Android reports. Phones only; iPad layout is device-verified and
stays on Logical.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Sound: split iOS Music-backed effects into MusicAudioClip

Review feedback on Card-Forge#11190 (Jetz72): replace the per-method isIOS
branches in the mobile AudioClip with a second IAudioClip
implementation. AudioClip is back to a pure OpenAL Sound impl; the
AVAudioPlayer/clock-domain logic moves verbatim to MusicAudioClip; the
createClip factories pick the implementation per call. Behavior-neutral
on every platform.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* iOS: ship an app icon via an Assets.xcassets catalog

Follow-up to Card-Forge#11190 review: the unsigned IPA contained zero icon data —
the robovm.xml/Info.plist entries pointed at Icon*.png files that were
never committed (silently dropped by a contributor-global '*.png'
gitignore rule), so sideloaded installs show the generic placeholder.
Commit an AppIcon asset catalog (1024px, opaque, from the Android
launcher artwork's framing) under forge-gui-ios/resources/; RoboVM
2.3.24 compiles *.xcassets resources with actool and merges the
generated CFBundleIcons/CFBundleIconName into Info.plist, so no build
changes are needed. Delete the dead legacy CFBundleIcons block and add
a .gitignore negation so global '*.png' excludes can't drop iOS icons
again.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* GUI: convert shared form-factor checks to GuiBase.isMobile()

Review feedback on Card-Forge#11190 (Jetz72); builds on the isMobile() helper
from Card-Forge#11382, converting the remaining sites where Android and iOS
provably want the same behavior: adventure data paths (Config, World),
fallback-skin title/transition textures (Assets), hardware-cursor skip
(Forge.setCursor), hover detection (FDisplayObject), battlefield-hover
gamepad gate (MatchScreen), PID logging (NetworkLogWriter).

Behavior-neutral by construction: isMobile() == isAndroid() on
Android and == false on desktop; only iOS changes, and each converted
site was individually audited. Sites where isAndroid()/isIOS() gate
genuine platform machinery (APK updates, audio backend selection,
orientation handling) intentionally keep the specific check; a
follow-up will tackle the behavior-changing sites (touchpad mode,
tooltips, on-screen keyboard) after device verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* iOS/mobile: remove RGB565 downgrade for card textures; use RGBA8888 everywhere

RGB565 was applied to both bundled cards (getCardTextureFilter) and downloaded
Scryfall images (downscaleCardPixmap) on iOS to reduce native memory. Measured
peak physical footprint is 1.9 GB in both cases (sim-verified), well within the
jetsam ceiling on 4 GB devices with the existing post-load GC in place.

Remove getCardTextureFilter() entirely — it was an iOS-only wrapper that now
just delegates to getTextureFilter(). Callers use getTextureFilter() directly.
downscaleCardPixmap still caps resolution at MAX_CARD_TEX_DIM but no longer
converts to RGB565.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* net: narrow ProcessHandle PID catch to Exception (Copilot Card-Forge#11190)

catch (Throwable t) here would also swallow fatal Errors (OutOfMemoryError).
ProcessHandle is desktop-only (guarded by !isMobile), so a plain Exception
catch is sufficient for the never-expected failure; the comment was also stale
(referenced the mobile jvmdg path this desktop branch never runs).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* iOS: show the real app version instead of hardcoded "0.0"

The mobile splash/home version came from the iOS IDeviceAdapter.getVersionString()
override, which was a hardcoded `return "0.0"`. RoboVM AOT-links into one binary with
no runtime JAR manifest, so BuildInfo.getVersionString() (manifest Implementation-Version)
resolves to "GIT" on iOS — the desktop/Android path doesn't work here. Read the app
bundle's own version via NSBundle instead (the iOS analog of Android's PackageManager
versionName), the same API already used for the CFBundleVersion cache-buster: shows
"<CFBundleShortVersionString> (<CFBundleVersion>)". Also un-stale the marketing version
(app.version was frozen at 1.0) to the pom versionCode so it reads 2.0.14. Sim-verified:
splash shows "v.2.0.14 (1)" on iPad and iPhone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* iOS: load downloaded card images through the AssetManager, not a bypass

Review feedback on Card-Forge#11190 (Jetz72): the iOS downloaded-image path bypassed the
AssetManager and reimplemented its responsibilities in a set of platform-specific
branches in ImageCache. Move it back in: a small custom TextureData (CardTextureData)
does the java.io byte read + downscale and is handed to the manager via
TextureParameter.textureData — the stock TextureLoader honours a preset textureData
and ignores the FileHandle, so the read stays a plain byte read (iOS can't reliably
load a FileHandle from Documents) while the AssetManager owns the whole lifecycle:
get/load/unload/dispose/context-loss reload/memory-tracking.

Deletes the three parallel structures (pixmapCache, the downloadedTextureCache LRU,
textureToPath), the isDownloadedImage bypass branch, and the toRelativePath rewriting.
loadAsset/getAsset now take the same manager path on every platform; the one residual
iOS check just selects the decode parameter. No custom loader / setLoader, so skins,
fonts, avatars and other textures are untouched. The old 48/120 downloaded ceiling
moves into the unified iOS card cap so the resident set still plateaus. Border keying
derives from the manager-owned TextureData (aligns store==lookup, fixing a Windows
desktop-wrapper backslash mismatch). Sim-verified on iPad + iPhone: cards render,
borders correct, ~1.9 GB peak (unchanged).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Clean up

* iOS: force-link the Xalan output serializer so XML saves work

Every XmlUtil.saveDocument() call failed on device with
"WrappedRuntimeException: org.apache.xml.serializer.ToXMLStream" — achievements
plus the card, deck and item-view preference writers, so none of them persisted.

Xalan picks its serializer by name: SerializerFactory reads the class from
org/apache/xml/serializer/output_{xml,html,text}.properties and Class.forName()s
it, which the tree-shaker can't see. The classes and those properties are both
present in the RoboVM runtime, so linking the package is all that's needed;
robovm.xml already force-links the transformer factory but not the serializer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Net lobby: stop per-keystroke CHANGE events and team-selection echo

Card-Forge#11190 made FTextField fire its CHANGE handler on every keystroke so search
fields can live-filter. Consumers that treat CHANGE as a committed value broke in
multiplayer: the online and in-match chats send one network message per typed
character (and the send handler clears the field, so every letter became its own
chat line), and the lobby name field commits per keystroke — a prefs.save() plus a
full slot-state broadcast per character.

CHANGE is now commit-only unless a field opts in via setLiveChangeEvents(true).
Every local-only filter opts in, so live filtering is unchanged where it was
intended: the item-manager card/deck search, the settings and card-image-browser
searches, the entity picker, the list chooser, the auto-yields filter and the file
chooser's filename field. The three consumers that treat CHANGE as committed —
both chat fields and the lobby name — are the ones that become commit-only.
FComboBox/FToggleSwitch/FSpinner raise CHANGE from their own code and are
unaffected.

Independently, FComboBox fires its changed handler for programmatic selection, so
a lobby broadcast that changes any panel's team re-enters teamChangedHandler on
every OTHER client for a panel that client does not own. The wire listener drops
the panel index and the server applies client updates to the sender's own slot, so
the echo rewrites the echoing client's own team: with three or more connected
players any team change cascades until the lobby converges onto one team. The
handler now ignores events for panels the client may not edit and events raised by
the network-apply path (setTeam/setArchenemyTeam), and setPlayerName no longer
clobbers a field that is mid-edit.

Verified with three simulators (host + two guests, iOS 18.4) joined over loopback:
typing a 5-character word then Enter produces exactly one chat message on all
three clients (was five), and moving one guest between teams leaves the host's and
the other guest's teams untouched instead of converging. Card search re-checked in
the deck editor: typing filters live (95084 -> 374 with no Enter) and backspace
re-widens it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Fix iOS text-field backspace deleting two characters at once

A hardware-keyboard backspace inside a text field arrived on two paths and each
deleted a character: the queued keyDown(Keys.BACKSPACE), and keyTyped('\b') from
gdx's invisible-UITextField delegate (the same delegate the software keyboard
uses). gdx's iOS backend suppresses its OWN queued KEY_TYPED while that field is
active, but not the keyDown, so the two survivors both ran — confirmed on the
simulator: one press logged down 67 + typed 8 in the same frame and removed two
chars. iOS-gate the keyDown branch so the keyTyped path performs the single
deletion whichever keyboard sent it; the software keyboard (keyTyped only, no
keyDown) is unchanged, and desktop/Android (keyDown only) keep their path.

Also fire CHANGE from the keyDown delete under liveChangeEvents, matching the
keyTyped branch, so on desktop/Android a live search filter refreshes while
deleting instead of only while typing.

Verified: iOS one press -> one delete + list re-filters (374->1153); Android one
press -> one delete + count refreshes (via keyDown, gated path unused there).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Update nighthowl_pursuer.txt

Closes Card-Forge#11569

* Star Trek Welcome Decks, batch 2 (Card-Forge#11549)

* Add Defense force aggressor (Card-Forge#11567)

* Add Organic Avulsion Unit (Card-Forge#11566)

* Create massimo_the_magician.txt (Card-Forge#11546)

* Prep for Isolated Hut map

* Prep for Isolated Hut map - everything but adding to map because I want to do that all at once with the other new areas

* An-Havva Inn mapfile

* Add Head of Security (Card-Forge#11575)

* Add Talarian Hook Spider (Card-Forge#11576)

* Yume, Chronicler of Valor (MBC) (Card-Forge#11538)

* Itazura, Lingering Wick (MBC) (Card-Forge#11545)

* Head of Security fix (Card-Forge#11579)

* Star Trek Welcome Decks, batch 3 (Card-Forge#11557)

* Sync missing keys from en-US.properties to all other language files.

* Fix typo in trigger limit from 'ResolveLimit' to 'ResolvedLimit'

* An-Havva Inn mapfile fix attempt

* Autumn willow deck

* Feroz and Joven and chandler decks, removed an-zerrin ruins from banlist

* Greensleeves deck

* Daughter of Autumn deck

* Ann-havva inn finishing touches save for adding it to the map

* Banned and Restricted Announcement for August 10, 2026

* Star Trek Welcome Decks, batch 4 (Card-Forge#11571)

* Deckfix

* Deckfix

* Update loyalty ability for Thomil the Destroyer

* iOS pipeline: self-recovering simulator builds (Card-Forge#11588)

RoboVM's ipad-sim mojo launches the app after the AOT link and blocks in
SimLauncherProcess.waitFor until the app quits — and on Apple Silicon it picks
its own simulator, so every scripted sim build hung at the launch step and
needed the wedged maven process killed by hand. All the pipeline needs from the
mojo there is target/robovm.tmp/config.xml, written once the link is done: run
the mojo in its own process group, stop it when config.xml lands (test -s plus
a settle sleep — the mojo opens the file before serializing into it), and let
assemble_arm64_sim_app rebundle with the standalone AppCompiler (the AOT cache
is content-hashed, so nothing recompiles).

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* Update cardnames translations

* Opposing directions now cancel each other out on keyboard
Added helper function to KeyBinding.java for getting pressed key from libGDX

* Edition updates: SLD, TRC, TRK

* Hatsune Miku Precon

* Update lake_town_mariners_gone_fishing.txt

* Nephilim epochal enemy

* Ancient Opal Cavern Map

* Custom hobbit precon

* Fixed typo in miku precon

* Added UB shops

* UB Shopfile sign updates

* Otherworldly market map

* Otherworldly market update

* Otherworldly market update again, high perfect morcant deck

* Zoom card when tapping on cardView on the stack (Card-Forge#11594)

* Zoom card when tapping on cardView on the stack

* update check

* Update dyfed_the_guiding_hand.txt

* Fix CardType.getSortedSubTypes init race + duplicate entries

getSortedSubTypes assigned the static field first and then sorted it in place,
so two threads racing the first-time lazy init could mutate the list mid-sort
and trip TimSort's "Comparison method violates its general contract"
IllegalArgumentException. Build a TreeSet locally and publish an ImmutableList:
the copy is complete before any caller can see it (and ImmutableList's final
fields make the unsynchronized publish safe), no locking needed. The TreeSet
also drops duplicate entries — some types appear in two sections (e.g.
Spacecraft), which previously showed up twice in the Advanced Search subtype
list.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Update dyfed_the_guiding_hand.txt (Card-Forge#11599)

* Fix valid card type from 'Land' to 'Mountain'

* More decklists for eclipsed court

* Fix unselectable tokens after game 1 of a match (Card-Forge#11592)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Another decklist; cut enchanted evening from enemies with archon to avoid infinite loop

* Revert "Feature/11167: Revamped input handling + better road detection"

* Last 2 eclipsed court decks, eclipsed court items

* Finishing eclipsed elven court dungeon

* Clean up (Card-Forge#11609)

* Update CardImageRenderer for Prepare Cards (Card-Forge#11607)

* Add Time Spiral Remastered(TSR) Rankings based on draftsim rankings.

* Fix CardImageRenderer
- closes Card-Forge#10038

* Ashling's Domain dungeon with all decks

* Update getaway_barrel.txt

* LibGDX Update 1.14.2 (Card-Forge#11606)

* LibGDX Update 1.14.2

* Update ios-pipeline.sh

* Fix DefaultAndroidInput delta reset

libgdx/libgdx#7838

---------

Co-authored-by: tool4ever <therealtoolkit@hotmail.com>

* Fix custom cards messing things up (Card-Forge#11615)

* Change card type to Legendary Artifact Equipment

* Update bolg_of_the_north.txt

* Mapfix

* Idyllic Beachfront, everything but enemies

* Finishing Idyllic Beachfront

* Peaceful clearing everything but decklists and enemies, adding new dungeons to map

* Move hardcoded Adventure Mode strings to language files

* Update de-DE.properties

* Update fr-FR.properties

* Update it-IT.properties

* Update ja-JP.properties

* Update pt-BR.properties

* Update zh-CN.properties

* Update ru-RU.properties

* Update ko-KR.properties

* Update valid card ownership in nether_traitor.txt

* Update manifest dread effect to trigger twice

* Edition updates: SLD

* Some Card Updates/Fixes (Card-Forge#11624)

* CardState: cache LandMana and copy them for LKI

* Peaceful clearing finished

* Fix Navigation Arrow position (Card-Forge#11626)

- adjust navigation arrow pointing from left most bottom to center of POI
- use nearest POI position from player center position

* Update slight_malfunction.txt

* Fix AI X sizing that left Curse of the Swine and Distorting Wake uncastable (Card-Forge#11338)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* Planeswalker dueling club map

* Alpha testing fixes

* Fix Dungeon Art render

* Fix QuestLogScene Track Button Listener

* Use Indicator for Quest Stages

* Fix variant colors (Card-Forge#11630)

* Added support for igd v2 gateways and multi-device upnp discovery (Card-Forge#11317)

* Update garruk_veiled_butcher.txt

* Fix for broken enemy deck (Card-Forge#11632)

* Implement Beam me up and Marooned's restriction on it (Card-Forge#11589) (Card-Forge#11633)

* Implement Beam me up and the Marooned restriction on it (Card-Forge#11589)

Beam me up <Cost> is Flashback with one extra mandatory cost: cast from your
graveyard for <Cost> if you also return a creature you control to its owner's
hand, then exile the spell. It reuses the existing graveyard-cast machinery -
getGraveyardSpellByKeyword, which Flashback, Harmonize and Mayhem already share,
plus the same "exile as it leaves the stack" replacement Flashback installs.

The return is appended to the cost in GameActionUtil rather than written into
each card script's keyword cost. Harmonize already adds its extra cost there, and
keeping it out of the script means the printed cost stays {2}{U} rather than
growing a Return clause, and no script has to repeat the wording.

Marooned's "can't be beamed up" has no keyword on the card, so it travels as a
granted keyword string, the way "doesn't untap during your untap step" does. The
restriction then rides on the appended cost's valid string:

    Return<1/Creature.YouCtrl+canBeBeamedUp/creature you control>

so a permanent that can't be beamed up simply isn't a legal way to pay, and both
the human and the AI paths honour it without extra code - CostReturn already
filters candidates by that string.

Wording for both cards is taken from Scryfall spoilers; TRK is dated 2026-11-13
and these are the only two cards in the set that touch the mechanic so far.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Fold the three graveyard-cast keywords into one branch

Beam me up's exile replacement was a third verbatim copy of Flashback's and
Harmonize's. The three bodies were identical apart from the name shown in the
description and the SpellAbility property the stack check matches on, so they are
now one branch that picks those two strings up front.

Harmonize had no test coverage, and the Flashback test that does exist checks
whether the graveyard cast is offered - which comes from GameActionUtil, not from
this code. Added a test that reads the ValidStackSa off each of the three
keywords' replacement, since a swapped name or property is exactly what folding
them together could get wrong and nothing else would have caught it.

This commit is separable: drop it and the mechanic in the previous commit stands
on its own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Update marooned.txt

* Carry "can't be beamed up" as a static rather than a granted keyword

Review preference: @tool4ever asked for a hidden keyword instead of a printed one
and @Hanmac for a static if possible. The static is the better of the two - it is
how every other "can't be X" restriction in the engine works, and it lets any card
grant this without inventing keyword text for the affected permanent to carry.

Adds StaticAbilityMode.CantBeBeamedUp and a StaticAbilityCantBeBeamedUp modelled
on StaticAbilityCantRegenerate. Marooned's line becomes

    S:Mode$ CantBeBeamedUp | ValidCard$ Permanent.EnchantedBy | Secondary$ True

matching the other aura-borne Cant statics, which mark the duplicate description
Secondary rather than printing the sentence twice. The cost's valid string is
unchanged, so CardProperty is the only caller that had to move.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Drop the description a Secondary static never prints

Card.java skips isSecondary() statics when it builds the ability text, so the
sentence was carried twice in the script and shown once. The R: line above it is
the one that prints.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: liamiak <liamiak1@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: tool4ever <therealtoolkit@hotmail.com>
Co-authored-by: Agetian <stavdev@mail.ru>

* Fix for broken map

* Improve clue effects

* Some cleanup (Card-Forge#11642)

* Implement charset fallback for ZipFile creation (Card-Forge#11640)

Added a fallback mechanism for charset selection in ZipFile creation to fix an unhandled exception on iOS.

* Use card art for prepared spell (Card-Forge#11641)

* Use card art for prepared spell

* prevent NPE

* Update adventure logo and backgrounds (Card-Forge#11645)

* Update adventure backgrounds

* Update adventure deckbox to pixelart

* fix inverted texture region image

* update adventure logo and repeating texture

* update items.png sprite for the adventure logos

* update remaining images to pixel art

* Enable ChangeZone in MayEffectFromOpeningDeck; update un-card list. (Card-Forge#10740)

* Storied: add Effect Image

* Oracle view mobile (Card-Forge#11399)

* Add Oracle text view on mobile

* Deprecated fix

* Add tooltip info (Card-Forge#11650)

* Add tooltip info

* update renders for tooltipinfo

* Fix/11167: Revamped input handling Android fix (Card-Forge#11647)

* Attempting to fix input
Everything works for the player animations are jittery on controller now.
haven't tested mobile either but mouse works.

(cherry picked from commit f28811a)

* Fixed controller jitter.
Still not tested on mobile but will do later. (Should be the same as mouse input)

(cherry picked from commit b260851)

* Cleanup of GameStage.java

(cherry picked from commit 467ab52)

* Mirroring changes from Card-Forge#11167

(cherry picked from commit 82714d1)

* Opposing directions now cancel each other out on keyboard
Added helper function to KeyBinding.java for getting pressed key from libGDX

(cherry picked from commit a631d4f)

* Added additional input method for Android devices for touchKnob

* Urgent Necropsy: fix missing planeswalker part

Closes Card-Forge#11649

* Fix tooltipInfo visibility onImageFetched

* Fix RewardActor newOverlayGestureListener (Card-Forge#11652)

* Fix RewardActor newOverlayGestureListener

* update check and remove unnecessary code

* cleanup

* Enhance holdtooltip View (Card-Forge#11653)

* Enhance holdtooltip View

* add backdrop for CardPack tooltip description

* Update adventure splash logo (Card-Forge#11656)

* Update fireminds_foresight.txt (Card-Forge#11658)

* Make Adventure action animations frame-aware (Card-Forge#11492)

* Adventure: Make action animation timing frame-aware

* Adventure: Harden frame-aware animation playback

* Adventure: Cap action animation waits

* Adventure: Limit death animations to three seconds

* fix rewardpack backdrop position

* update cBackDrop reference position

* Changes

* CardFactoryUtil: use AbilityStatic for Ascend

* Refactor AiCardMemory (Card-Forge#11667)

* Include previously missing shard when fetching lands (Card-Forge#11673)

* - Add puzzle PS_HOB1. (Card-Forge#11674)

* move isTurnFaceUp to getAllPossibleAbilities (Card-Forge#11654)

* move isTurnFaceUp to getAllPossibleAbilities

* Fix simulation gap

* Finish comment

* Remove obsolete check

---------

Co-authored-by: tool4EvEr <tool4EvEr@>

* Improve AI handling of storage lands (Card-Forge#11672)

* Improve AI handling of storage lands

* Harden storage land AI payoff planning

* Streamline storage land AI coverage

* Respect mana rules in storage land planning

* Simplify storage land AI handling

* Delete forge-gui-desktop/src/test/java/forge/ai/ability/StorageLandAiTest.java

---------

Co-authored-by: tool4ever <therealtoolkit@hotmail.com>
Co-authored-by: Agetian <stavdev@mail.ru>

* Fix NPE (Card-Forge#11683)

* Edition updates: PSPL, PZ2, SLD, SLZ, YMKM

* Don't create token copies that just die (Card-Forge#11690)

* Don't create token copies that just die

* Don't create token copies that just die

---------

Co-authored-by: tool4EvEr <tool4EvEr@>

* Add Japanese translations for Dominaria United (Card-Forge#11666)

* Replace a racy FCollection test with a deterministic one (Card-Forge#11677)

testCompletableFuture removes the same collection from four threads at
once and asserts the resulting size. That held when it was written
(0854fbb), where FCollection guarded every mutator with an explicit
lock. Card-Forge#6657 replaced that lock with Collections.synchronized wrappers,
which cannot make remove's two-collection update atomic, and b858b48
dropped the wrappers once threadSafeIterable was restored to handle
iteration. Concurrent removes have raced ever since.

Test what threadSafeIterable actually guarantees instead: the snapshot
lets a loop remove from the collection it is iterating without throwing
or skipping an element.

Co-authored-by: liamiak <liamiak1@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

* Add missing Lesson keyword to Yip Yip! (Card-Forge#11696)

* Restore longpress for Reward Actor (Card-Forge#11699)

* RewardActor - add longPress listener for holdtooltip

* update check

* Minor cleanup

* Update hulkling_young_avenger.txt

* Realm of legends 1.071: Handful of minor deck and mapfixes (Card-Forge#11703)

* Deckfix

* Mapfix, a few deckfixes

* Add Japanese translations for five sets (Card-Forge#11691)

* Fix AI cloning creatures that shrink under its own control (Card-Forge#11675)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

* Edition updates: SLD, SLZ

* Fix Nori, Teller of Tales (Card-Forge#11713)

* Add Japanese translations for OTJ, BLB, and ONE (Card-Forge#11711)

* Fix CON -> CFX

* Keep the game running when delta collection hits a concurrent change (Card-Forge#11723)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Clean up (Card-Forge#11726)

* Edition updates: PMEI, SLD, SLZ

* StaticAbilityManaRestriction: better ability for Myr Superion (Card-Forge#11700)

* StaticAbilityManaRestriction: better ability for Myr Superion

* remove special None case

* Update StaticAbilityManaRestriction.java

Restrict to just getHostCard

* Update StaticAbilityManaRestriction.java

fix import

* remove ValidSA

* Update hogaak_arisen_necropolis.txt

---------

Co-authored-by: tool4ever <therealtoolkit@hotmail.com>

* Allow selected cards in authored AI decks (Card-Forge#11684)

Co-authored-by: Agetian <stavdev@mail.ru>

* Teach AI the Embargo and Donate setup (Card-Forge#11688)

Co-authored-by: Agetian <stavdev@mail.ru>

* Improve AI handling of reveal-count effects (Card-Forge#11712)

Co-authored-by: tool4EvEr <tool4EvEr@>

* Fix self-sacrificing mana ritual source count (Card-Forge#11725)

* Add Japanese translations for MKM and DSK (Card-Forge#11722)

* Refactor render, fix dispose call, add OverlayText (Card-Forge#11730)

* Refactor render, fix dispose call, add OverlayText

* move screenshot, adventure and classic render on their own class

* update OverlayText, FrameRate, limit Spritebatch capacity

* null check

* unused import

* quick fix rendering of OverlayText on non GameScene

* Improve PumpAi timing logic (Card-Forge#11739)

Co-authored-by: tool4EvEr <tool4EvEr@>

* Fix Greensleeves ability definition (Card-Forge#11741)

* Fix PumpAi creature tap cost timing (Card-Forge#11742)

* Count all lands for sacrifice preference (Card-Forge#11743)

* Perfected Theory (FRA) (Card-Forge#11745)

* Refactor RewardActor, remove holdtooltip and use CardZoom via ViewRewardScene (Card-Forge#11737)

* Refactor RewardActor, remove holdtooltip and use CardZoom via ViewRewardScene

* remove unnecesary changes

* rename batch, add comment

* return generated image for cardpacks if cached image is not found

* use RewardActor object reference for CardView image

* clear CardView Object, update Zoom renders

* refactor Graphics, cached CardView with objects to clear, limit graphics spritebatch capacity

* New Game menu bugfix: Correct label alignment and fix text/style setters to properly invalidate the layout on MarqueeButton. (Card-Forge#11706)

* quick fix on dispose

* Added MSH Jumpstart (Card-Forge#11668)

* Update printsheets.txt

Added MSH Jumpstart packs.

* Update blocks.txt

Added MSH Jumpstart

* Update config.json

Added MSH Jumpstart

* Mark Sifa Grent +1 as a planeswalker ability (Card-Forge#11748)

* Edition updates: CMB1, FRA, SLD

* Move prepare card text details to CardDetailUtil (Card-Forge#11635)

* Rename batch and update Framerate Sprite count (Card-Forge#11749)

* Rename batch and update framerate sprites count for GameStage and UIScene

* update FrameRate

* Remove alara pack shop which suddenly broke, preventing this plot-critical map from loading

* Additional updates to reflect edition change from CON to CFX

* Solitary Cell (FRA) (Card-Forge#11757)

* Update Console-and-cheats.md (Card-Forge#11744)

Remove inaccurate part of `clearnosell`'s description.

* Realm of legends 1.073: Fallen Castles Rebalance (Card-Forge#11762)

* Rebalance to white and red fallen castles

* Full fallen castle rebalance

* Another revision to fallen castle rebalance

* Typo fix

* update disposables

* Make sim mode run without a display (Card-Forge#11761)

`sim` is documented as the way to run AI matches on headless servers, but it has
been unable to start without a display since 48f3ae1 (2022-04-16), and it
failed with no output at all.

Two defects combined.

GuiDesktop resolved the screen scale from a static initializer, so merely loading
the class called GraphicsEnvironment.getDefaultScreenDevice() and threw
HeadlessException. Main installs the GUI interface before it parses argv, so sim
died before it could read its own arguments. The screen scale is now resolved on
first use, via a holder so the read stays lock-free on the paint path, and falls
back to 1.0 when there is no display. Main sets java.awt.headless for the console
modes before any AWT class loads -- unless the user set it explicitly -- so a
desktop run of sim exercises the same path a server does.

The crash was also invisible. Sentry's uncaught handler chains to whatever
default handler is already installed, and nothing installed one this early, so
the exception was reported and discarded without reaching stderr. Main now
installs a printing handler before Sentry.init. It cannot simply be
registerErrorHandling() moved up: that reads ForgeConstants.LOG_FILE, whose class
initializer resolves ASSETS_DIR through GuiBase.getInterface(), so it needs the
GUI interface already set -- and the window needing coverage includes setting it.

Dialog entry points now degrade to a return value instead of throwing
HeadlessException. This matters most for BugReportDialog: a crash mid-run built a
dialog that threw, and that secondary exception replaced the crash being
reported, so a failed batch run lost the actual bug. showOptionDialog returns -1
("dialog closed without choosing", what FOptionPane already returns) rather than
the default option, because SOptionPane.showConfirmDialog reads index 0 as Yes --
returning the default would silently approve destructive prompts such as
overwriting a saved game state.

Verified on a host with no DISPLAY: a full game completes and exits 0, as do
2-player, 3-player, best-of-N, Commander, Planechase, and a 4-player bracket
tournament. Under Xvfb the GUI is unchanged, and HiDPI still reports 2.0 with
-Dsun.java2d.uiScale=2.

HeadlessStartupTest guards the regression. It forks a JVM with
-Djava.awt.headless=true, since GraphicsEnvironment caches headlessness on first
use and CI runs the suite under Xvfb with a real DISPLAY, so an in-process check
would exercise the headful path and prove nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Clean up (Card-Forge#11767)

* Cast Away Doubt (FRA) (Card-Forge#11768)

* Sentry: skip obvious custom script error (Card-Forge#11769)

* Reuse FrameBuffer

* Edition updates: FRA

* Update Premodern banlist: add Parallax Tide

Parallax Tide was banned in Premodern effective January 2026,
and this simple commit is long overdue!

* Fix wrong update logic (Card-Forge#11774)

* Fixed spelling mistake in printsheets.txt (Card-Forge#11775)

* Move GifAnimation object to Assets, fix resource NPE (Card-Forge#11777)

* Move GifAnimation object to Assets, fix resource NPE

* reorder to prevent extra initialization

* replace demo GIF preload

* Generous Revival (FRA) (Card-Forge#11779)

* Fixed typos in printsheets.txt

* Architecture to skip GUI updates when simulating (Card-Forge#11780)

* Implement Rulebreaker (+Partial implementation of DeckRule) (Card-Forge#11740)

* Set threads to daemon to gracefully exit the game while disposing assets (Card-Forge#11765)

* Set threads to daemon to gracefully exit the game while disposing assets

* rename AdventureScreen to AdventureLauncher, fix freeze on awaitnextinput thread when exiting

* resolve conflicts

* revert NewGameMenu

* revert this since it's not the scope of this PR

* update dispose, revert shaperenderer initialization

* Update Framebuffer generation (Card-Forge#11784)

* Update Framebuffer generation

* fix ondestroy bug

* Use cheap blur, Add asset graphics for rendering

* Crash Fix when restarting on Android (Card-Forge#11786)

* Crash Fix when restarting on Android

* dispose on destroy event

---------

Co-authored-by: coder-5 <98986223+coder-5@users.noreply.github.com>
Co-authored-by: Chris H <zenchristo@gmail.com>
Co-authored-by: GitHub Actions <actions@github.com>
Co-authored-by: Serafina <serafina2880@gmail.com>
Co-authored-by: shoeless <shoeless@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: tool4EvEr <tool4EvEr@>
Co-authored-by: tool4ever <therealtoolkit@hotmail.com>
Co-authored-by: Fulgur14 <54345051+Fulgur14@users.noreply.github.com>
Co-authored-by: Churrufli <churruflis@gmail.com>
Co-authored-by: Paul Hammerton <paulhammerton1@hotmail.co.uk>
Co-authored-by: Paul Hammerton <18243520+paulsnoops@users.noreply.github.com>
Co-authored-by: BramTeurlings <bram.teurlings@live.nl>
Co-authored-by: kevlahnota <anthonycalosa@gmail.com>
Co-authored-by: MostCromulent <201167372+MostCromulent@users.noreply.github.com>
Co-authored-by: Jetz72 <Jetz722@gmail.com>
Co-authored-by: vihaan <vihaan.agrawalcoder@gmail.com>
Co-authored-by: BigCrunch22 <59816726+BigCrunch22@users.noreply.github.com>
Co-authored-by: Hans Mackowiak <hanmac@gmx.de>
Co-authored-by: liamiak <liamiak@yahoo.com>
Co-authored-by: Giovanni Menzano <giovanni.menzano@gmail.com>
Co-authored-by: liamiak <liamiak1@gmail.com>
Co-authored-by: Agetian <stavdev@mail.ru>
Co-authored-by: voguelike <73729626+voguelike@users.noreply.github.com>
Co-authored-by: Eradev <Eradev@users.noreply.github.com>
Co-authored-by: Vanja <56617195+vanja-ivancevic@users.noreply.github.com>
Co-authored-by: Saku3san <106969562+Saku3san@users.noreply.github.com>
Co-authored-by: Zimmermann Gyula <graiondilach@hotmail.com>
Co-authored-by: squee1968 <105706641+squee1968@users.noreply.github.com>
Co-authored-by: Eradev <admin@eradev.com>
Co-authored-by: monkyman64 <168941314+monkyman64@users.noreply.github.com>
Co-authored-by: RogerSloan <1922907+RogerSloan@users.noreply.github.com>
Co-authored-by: Good-Girls-Go-Out-At-Night <goodgirlsgooutatnight@gmail.com>
Co-authored-by: Jamin Collins <jamin.collins@gmail.com>
Co-authored-by: Valerio Maggio <1908453+leriomaggio@users.noreply.github.com>
Co-authored-by: Valerio Maggio <leriomaggio@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants