* Added UI elements for commander mode in NewGame screen.
- still missing random pool of commander candidates sharing a color with each selected color identity.
* Added rudimentary commander choice with preview image.
For now, it is 5 suggestions per color identity with the requirement that the chosen color is part of the commander's identity.
* Commander card is now added to the player inventory and cannot be sold.
The save game adds a flag whether the game is in commander mode, which will be important for the future changes.
* Commander is now auto-added to the initial deck. Commander cannot be removed. Commander is properly saved and loaded.
* Duels now feature the command zone and all rules
* The chosen commander is now set to every deck if the game mode is commander.
* Only cards can be added to the deck that fulfill the color identity.
* heuristically add a pile of singleton cards to function as the starter for the commander's journey.
* Fix land color identity bug in commander pile
* Items are put on autosell if they do not match commander identity.
* Autosell is now properly set with the card flip timing rules. When visiting a shop, the deck conformaty message does not show anymore.
* Remove strict necessity for a fixed commander. This way, a player may choose to switch to a different legendary creature they find.
* 15 Streamlined commander choices without having to swap colors individually.
* Clean up to remove now redundant i18n label. More focus on creatures for starter commander decks.
* Refactor adventure commander deck generation into the DeckgenUtil class
* Refactorings as suggested by @Jetz72.
* Remove custom deck generator
* handle DeckFormat logic through AdventureDeckEditor.
Fix bugs, where partner commanders would be overwritten when using the stock DeckgenUtil.
* Remove random new line
* Code cleanup
* Fix unused import error
* Fix unused import error
* Revert "organise import" changes and random space linebreak changes.
* Removed another import package.* statement
* Implement fixed commander deck to start with. Removed UI edits and random commander choice.
* Add commander-specific cards and a two white overrun effects
* Revert to prior string formatting.
* Remove import *
* Formatting nitpicking to change as little original code as possible.
---------
Co-authored-by: Agetian <stavdev@mail.ru>
* Make getAllFaces return nonnull list
* Optimize Predicates
* CardDB and script syntax changes
* Apply syntax changes
* In-game support for flavor names
* Add display names to PaperCards
* Support searching by flavor names
* Remove some WIP stuff
* Update PaperCard translation key.
* Update capitalization
* Auto-map to variants when edition entry uses a flavor name
* Consolidate display name logic.
* Added syntax for generating flavor named variants in edition files.
* Some examples of new syntax.
* Ignore flavored oracle text when searching rules text
* add hasFlavorName
* Add image key
* Get correct variant from card requests with flavor names.
* turn CardChangedName into record
* Add CardLists.getDifferentNamesCount
* CostDiscard: rename to +WithDifferentNames
* CostSacrifice: use getDifferentNamesCount
* Refactor DifferentCardNames_
* add ChangeTypeDesc for basic land
* Search N is up to
* add some more ChangeTypeDesc
* add some more ChangeTypeDesc
* add some more ChangeTypeDesc
* Update panorama
* update landscape
* Update Clan Monuments
* Small Monument to Perfection change
* update capenna fetchers
* remove ChangeNum from basic Land searchers
* better desc for fetch lands
* remove uneeded ChangeTypeDesc
* Some cleanup.
* Expanded/fixed basic land set functions for quest and adventure.
* Get land sets from unlocked planes in conquest mode.
* Add importer for Adventure, Quest, and Conquest.
* Remove unused import
* Remove redundant override
* Deprecate hasBasicLands predicate.
* Delete getManaNameAndSymbol
---------
Co-authored-by: Jetz <Jetz722@gmail.com>
The project moved to GitHub almost a year ago.
Having duplicated, unmaintained templates will generate problems in the
future. If needed, they can always be retrieved from the commit history.
This bumps `izpack-maven-plugin`.
Tested with JDK 25.
Similar fix for another project:
https://github.com/lsc-project/lsc/pull/385
Original Error message:
`[ERROR] Failed to execute goal
org.codehaus.izpack:izpack-maven-plugin:5.2.3:izpack
(standard-installer) on project forge-installer: Execution
standard-installer
of goal org.codehaus.izpack:izpack-maven-plugin:5.2.3:izpack failed:
java.lang.ArrayIndexOutOfBoundsException: Index 70131 out of bounds for
length 22674 -> [Help 1]`
* Import from CubeCobra feature for Draft and Sealed
* adjust comment for cube Import
* CubeImporter refactoring and generalisation
* Allow hyphens in Cube ID validation
* remove unused imports
* make parseFromURL private
* add LAST_IMPORTED_CUBE_ID to ForgePreferences
---------
Co-authored-by: Antonio <mart@gmail.com>
- Generate CHANGES.txt in forge-gui-desktop/target/ instead of source tree
- Update installer to copy from target directory for all build profiles
- Add CHANGES.txt to .gitignore since it's generated
- Remove hardcoded fromRef to use latest tag automatically
- Remove maven-release-plugin exclusion for untracked file
* - Add achievement for EOE/EOC by Marek14.
* - Add AI logic for Krumar Initiate
* - Tweak AI logic for Krumar Initiate
* Update EndureAi.java
only when doing X
---------
Co-authored-by: Hans Mackowiak <hanmac@gmx.de>
* improve resource finding procedure and add android-dev-build
Added the `android-dev-build` profile to trigger building a developer
apk.
Developer apk is at application id forge.app.dev, and has name
'Forge (dev)'. It is installable in parallel with the official release
app allowing developers to test the android build on their phones
without interfering with their existing installation.
Updated how android resources are found to accomodate changes in
application id at runtime. This method doesn't rely on the 'R' package.
* Use all arguments of getIdentifier
Use all arguments of getIdentifier instead of using a fully qualified
resource name
* Gwen and Miles (Transformation to be added later)
* Create miles_morales_ultimate_spider_man.txt
* Update miles_morales_ultimate_spider_man.txt
* Update miles_morales_ultimate_spider_man.txt
* Add GamesInMatch combo box selection to booster draft page
Also updated combo box default to be seeded with the stored preferences
* Working comboboxes for desktop version
* Working linked buttons on Mobile
* Add binder classes for preferences and other Model components
* Move to pref binders for mobile GUI
* Allow search by type and cmc
* Allow colon and added aliases to cmc
* Support negate
* Extract parser function into its own file
* Support for "!"
* Colors test
* Color search
* Oracle text
* Numeric p/t search
* Set search + fixes
* Typo
* Rarity check
* Loyalty
* Cleanup
* Case ignore for kw
* Support for "or"
* Rename method
* Support parentheses
* Add cases for l
* Cleanup
* Use PaperCardPredicates.printedInSet
* Fix in set to remove conjured cards
* Use func
* Remove redundant check
When I put in the code to delete quest items on a new game+ I forgot to re-add the colorless rune in case you start without the main quest (you get it as part of the main quest). This fixes that.
* LF
* Support for Code2 and Aliases in sheets
* Support for Code2 and Aliases in sheets
* TryConvertingName
* Revert changes
* Update CardEdition.java
* Wrong check
* New sprites that fit established aesthetic
* Adjustments
* Remove downscale + fix wrong pixel on Sorin
---------
Co-authored-by: Agetian <stavdev@mail.ru>
* Cleaning up the file structure to move all Innistrad maps under the Innistrad folder, as well as some minor logic change to Shandalar Old Border's config.json to make maintenance easier. (By setting the desired sets in allowedEditions, we no longer have to restrict every new edition as it comes in.)
Finally, added block info for Alchemy: Innistrad, so it should start being available in events as well.
* Cleaning up the file structure to move all Innistrad maps under the Innistrad folder, as well as some minor logic change to Shandalar Old Border's config.json to make maintenance easier. (By setting all the desired sets to be viewed in allowedEditions, it will only shows those, and we no longer have to restrict every new edition as it comes in.) Also fixed a bug making the "Ghost Town" not work in Shandalar Old Border
Finally, added block info for Alchemy: Innistrad, so it should start being available in events as well.
* Update Adventure - Guardian Gladiolus.dck
---------
Co-authored-by: Hans Mackowiak <hanmac@gmx.de>
* Added: utility method for adding an integer selecting combo-box.
* Added: Adventure now supports dynamic amount of decks
* Tweaked: lowered max deck count to 20.
* Added: dynamic deck count can't ever be higher than the maximum in current version.
* Fixing an issue in which touching the space the map occupies outside of the world map does not allow the player to move (very relevant on maps with content in the top left corner)
* Fixing a bug in which the transition screen's non-blocking of the start match button can be clicked multiple times, which results in a crash when the match ends
When the "Visually Alert on Receipt of Priority" feature is enabled,
the `showTab()` call at the beginning of `forge.gui.framework.SDisplayUtil.remind()`
steals focus from the primary button whenever anything goes on the
stack.
This change, previously suggested by pfps as probably not necessary, is
indeed necessary after all to prevent that focus stealing by restoring
focus to the previous owner only if the owner was an `FButton`.
fixes https://github.com/Card-Forge/forge/issues/7660
* Fixing an issue in which touching the space the map occupies outside of the world map does not allow the player to move (very relevant on maps with content in the top left corner)
* Fixing several text based references that looked created in bulk with similar problems. Kiora and Teferi don't require going back to the town that issued the quest, and the others reference the wrong location when arriving there for where the reputation goes
* Fixing an issue in which touching the space the map occupies outside of the world map does not allow the player to move (very relevant on maps with content in the top left corner)
* Adventure Mode: Fix Quest_APortalToNowhere, in which the file path got moved but the point of interest json was not updated
* Manually altering points_of_interest.json instead of using the adventure editor tool
* Actual change was removed during synchronization with remote head, re-applying actual fix
* Add files via upload
* Update TypeLists.txt
* Create g_1_1_frog.txt
* Update quina_qu_gourmet.txt
I realized that I forgot to set the amount of tokens.
There's something strange with Phantom Train's ability where its P/T display doesn't update properly after getting the counter -- maybe because the counter is gained before animating?
Added monochrome icon pngs for each mipmap size and added them to the xml configs for the icons. The monochrome icons were made by editing the respective icon in paint.net.
* Fixing an issue in which touching the space the map occupies outside of the world map does not allow the player to move (very relevant on maps with content in the top left corner)
* Fix a misspelling in the name of a Merfolk Warrior when being previewed before a battle
Note from Scyfall:
Dominaria United series of 26 tokens
Dominaria United comes with two sets of tokens: a series of 26 bearing either DMU or DMC set codes, and a separate series of 12 tokens with exclusively the DMC set code. We don't know why they did things this way. We've opted to file the entire series of 26 here regardless of set code to avoid archival conflicts.
* Fixing an issue in which touching the space the map occupies outside of the world map does not allow the player to move (very relevant on maps with content in the top left corner)
* Attempt to band-aid two problems relating to showing a dialog to return to the main map, one in which some patrolling enemies ignore the menu restriction and another in which such enemies touching a player who is transitioning out of a dungeon causing a soft lock upon returning to the map
* Fixing an issue in which touching the space the map occupies outside of the world map does not allow the player to move (very relevant on maps with content in the top left corner)
* Fix an edge case where the On The Hunt quest can end up failing to ever spawn an enemy to hunt
---------
Co-authored-by: Agetian <stavdev@mail.ru>
* CardEdition: add collector number for other
* EditionEntry record
* Add getOtherImageKey
* Update StaticData.java
* use getOtherImageKey in getFacedownImageKey
* Update CardEdition.java
Remove findOther in favor of getOtherSet
* Update CardEdition.java
return findOther, but with Aggregates.random
* ~ move more helper images to ImageKeys
* HostPort now properly returns -1 when no port was specified
* refactored the URL parsing to now attempt a DNS lookup in cases where URI parsing fails
This now allows local computer names and localhost etc
Re wrote the URL parsing logic again, but due to increased complexity put it into a utility class
Fixed desktop version opening up a lobby even when it did not connect to a server
Wired up the new error message to mobile, and both error messages to Desktop
* First draft of the castle main quest bug workaround
Adds a possibility to start the game without the main quest and changes all castles to only let you into the gate if you either don't have a main quest active or are actively on the quest to defeat the castles.
* Rewrote main quest stuff
Wanted to decouple the main quest from the start of the game a little bit to allow the player to start without a main quest.
* Fully tested solution for main quest bug
Castles should now behave as expected.
Need to fix one minor bug still.
* Fixing the starting portal and wizard
This seems to have completely fixed the issues I have found with not starting with a main quest. Hopefully I have found all of the things that might break.
* Refactor - Unknown set code to constant
* Refactor - Support for multiple initial selections for getChoices
* Covert noSell and marked color identities into serializable flags
* Fix cards in deck not being converted to newer noSell format
* unused imports
* Fix NPE
* Cleanup card filter
* Remove 14-year-old check that shouldn't be possible anymore
* CRLF -> LF
---------
Co-authored-by: Jetz <Jetz722@gmail.com>
* Initial commit of network improvements
Seperated server properties into their own file, this will eventually help facilitate a headless server.
Fixed the localhost ip mapping to give the correct IP Address instead of failing and defaulting to "localhost"
Fixed UPnP as well as added some additional options and choices regarding UPnP
Added localization strings to all language files. (Translators will need to translate these, but the current English string is there for easy reference so they dont have to search the en-US file)
* Initial commit of network improvements
Seperated server properties into their own file, this will eventually help facilitate a headless server.
Fixed the localhost ip mapping to give the correct IP Address instead of failing and defaulting to "localhost"
Fixed UPnP as well as added some additional options and choices regarding UPnP
Added localization strings to all language files. (Translators will need to translate these, but the current English string is there for easy reference so they dont have to search the en-US file)
* Fixed properties file reference
* Refactored server address parsing logic to use the Java URI class for improved readability and robustness.
Extracted reusable code into separate functions to enhance modularity and maintainability.
General code cleanup to improve structure and readability.
* Fixed a potential issue if a protocol was already specified in the connection url.
* Removed logger implementation as changing loggers is out of scope for this PR
Reverted to JUPnP as its implementation is fixed in #7367
Made some of the new localization strings generic as they can be used elsewhere
Added a server.preferences.example file
removed the server port from the old location (forge.progile.properties.example)
Added a server port back into ForgeConstants as it doesnt make sense for the prefered hosting port of the user to override the default Forge connection port.
* resolve conflicts between this branch and master
* Implemented a parent class for all preference Enums so they can be passed into a function regardless of type using IPref, necessary since I separated server settings into its own FNetPref file
Added server preferences section to the preferences Desktop GUI
Added a port preference setting and a UPnP preference setting to the aforementioned server preferences section
Added a localizedComboBox and localizedComboBoxListener so that localized strings can be used in combobox dropdowns in the server preferences section.
TODO: (In scope)
The new server preferences section needs to be added to Android and IOS perhaps?
TODO: (out of scope)
GamePlayerUtil has a bunch on non localized english strings that should be converted to localized
* Fixed unused import
* Resolved merge conflicts
Added server settings to the reset to defaults function
This removes quest items from a NG+ run in adventure mode. It might not remove all when a file has multiple NG+'s already, but multiple resets will remove all of the items.
I decided to remove the quest item tag from the teleport runes, since they are not added by a quest but can be bought in the store instead. Keeping them seems a good move for NG+.
Now the script works with arrays, much cleaner. I have not been able to test the controller support, it should work but I don't have the means to test it.
* Add command zone effect displaying speed
* Remove enum counter type for speed.
* Make Start Your Engines an SBA.
* LifeLost -> LifeLostAll per speed rules.
* Use same game event for all speed changes.
* Fix keyword not appearing in detail text
* Cleanup extra createSpeedEffect.
* Add support for arbitrary overlay text. Remove fake counters.
* Text styling.
* Remove extra SBA check.
* Remove speed from PlayerView; localization support.
---------
Co-authored-by: Jetz <Jetz722@gmail.com>
* AnimateSubAbility rework
* Remove old fix code, it will no longer be needed
* Add zone ordering
* Add labels
* Clean up
* Fix test
* Tweak logic
* Fix Gilraen
* Refactor with counter
* Clean up
* Cleanup effect if card can't ETB
* Update semesters_end.txt
Format abilities
* Return as Aura trickery
* Apply effect before RE
* Update scripts
* Fix order after creating it earlier
* Fix test
---------
Co-authored-by: tool4EvEr <tool4EvEr@192.168.0.60>
Co-authored-by: Hans Mackowiak <hanmac@gmx.de>
* added PromisedGift as xCount
* SpellAbilityAi: move chooseOptionalCosts
* SpellAbilityAi: chooseOptionalCosts check for invalid targets
* Give API logic access to castSA
* ~ add BaseSpell for PromiseGift
* Unearth: remove unneeded Param
* PromiseGift: uses AITgts to stop AI from using Gift when not needed
---------
Co-authored-by: tool4EvEr <tool4EvEr@192.168.0.60>
Some quests would notify the player of Reputation Rewards that were not actually awarded. Likewise some dialogue choices or upon declining some quests would notify the player of a loss of reputation that wasn't actually deducted. And in one case, the listed reputation change was different than the actual change. This change should ensure all displayed reputation rewards and penalties are applied. (Only applies to Shandalar)
* Updates se.bjurr.gitchangelog to v2.2.0. POM changes to skip "GenerateGitChangelog" in android test and debug builds.
* Remove jitpack.io repo
* Update pom.xml
* Start of contraptions
* More contraption work
* More contraption stuff.
Steamflogger Boss implementation
* More Contraption things
* Use AI's preferred target for mandatory animate effects
* Possible fix for Boomflinger's empty target prompt
* Remove nullable low-definition mana pool icon.
* Contraption Predicates
* Desktop extra zone menu
* Remember cranked contraptions
* AnimateAi fallback to AITgts; Bee-Bee Gun
* Fix AI paying optional costs to assemble zero contraptions.
* Override predicted damage for DealDamage effects with random amounts
* Fix double prompt when reassembling other players' contraptions
* All remaining contraptions and support cards!
* Remove unused imports
* Some cleanup
* Implement non-assembled contraption rule.
Fix blank line prefix on card detail text.
---------
Co-authored-by: Jetz <Jetz722@gmail.com>
* chore: move files to be alphabetically sorted
* feat: make the alphabetically stored cube contents match the new file names
* fix: cube name typo in MTGO Legacy Cube 2017-01
---------
Co-authored-by: Agetian <stavdev@mail.ru>
Replace copyonwritearray to a custom implementation for concurrent modification exception. A little bit slower but supports iterator operations.
Added FCollectionTest
should display error for null icons on VPlayerPanel Infotab. If pointer is still null then the icon seems to be disposed (probably on android) or just deliberately missing.
The original purpose which is to create a copy of linkedlist to iterate to avoid concurrent modification, but we use copyonwritearray design and it is already thread safe and iteration while modification is handled internally.
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.
removed method to generate borderless card since we used shader as default method
Texture, TextureRegion and other LibGDX objects that are disposable should be nonstatic
show Morph image on FChoiceList
- Update install.xml to set 775 permissions for .command files in Script pack
- Add .command files to target directory copy tasks in pom.xml
- Allow macOS users to run Forge apps without manual chmod
This change ensures .command files receive proper executable permissions
during installation, matching the behavior of .sh files on Unix/Linux.
* - Improve AI for Canopy lands.
- Improve AI for Ugin's Labyrinth.
* - Pyrite Spellbomb: also don't sac immediately.
* - Improve AI for Eladamri, Korvecdal
* - Improve AI decision for Zuran Orb.
* - Cleaner implementation for Eladamri AI
- More generic implementation for SacToDraw AI that doesn't need a logic parameter
* - Fix imports
* - Cleaner implementation for Eladamri AI
* - Suggested code tweak
The main issues is that getAllCards returns a card list that is unique
by name. That menas each card only has a single rarity. In this case,
cards like "Forbidden Orchard" and "Choke" de-deuplicate to a version of
the card with 'S' rarity. This means that when filtering by rarity, the
'S' rarity will be filtered out.
Another similar issue is that when a card exists in multiple sets with
different rarities, you might filter to a set in which the card is 'R',
but the de-duped version in allCards has rarity 'U', so using the cardDB
to check the rarity to filter on doesn't work.
When filtering by a specific set, the solution is to use the rarity of
the card in that set.
When not filtering by a set but filtering by rarity, we have to address
the root cause of the de-duping.
WHen de-duping, I changed the logic to ignore 'S' rarities. This fixes
the rarity filter when not filtering by an edition (although if there
are multiple rarities you still have to kind of guess).
This has the side-effect of making the card image used something more
legible. Before 'Choke' de-duped to the MPS_AHK set which has illegible
text. By ignoring 'S' rarities, it not de-dups to 8ED.
More work could probably be done to improve de-duping, but perfect
correctness would probably involve de-duping only after filtering, which
is likely too slow.
Remove open confirmation and disable leaving inventory scene when opening boosters.
Change Booster Pack Inventory Name
Old:
Booster Pack: WAR
New:
War of the Sparks Booster
Final
Tweak
Corrected "[enviroment]" to "[environment]" in [README.md].
This pull request addresses a minor typo found in repository. The typo has been corrected to improve clarity and maintain the quality of the documentation.
This change is purely cosmetic and does not affect functionality.
* Added Your Plans Mean Nothing
Tested with teammates to ensure that if a teammate is targeted they do not get to draw cards but they do discard their hand.
* Update your_plans_mean_nothing.txt
* Support for icons in dialogs.
Mana abilities show chosen mana via icons.
Thread safety for showOptionDialog
* Ensure EventDispatchThread for showInputDialog
---------
Co-authored-by: Jetz <Jetz722@gmail.com>
* Make Myriad optional for each player again
* Fix scripts
* Fix NPE due to not removing empty shards
---------
Co-authored-by: TRT <>
Co-authored-by: tool4EvEr <tool4EvEr@192.168.0.59>
Fixed Trial of Agony which wasn't dealing the damage, and Disturbing Mirth which was only allowing you to sacrifice another enchantment (not a creature).
* 19 DSK cards
Ghost Vacuum has a weird bug: if AI uses the second ability, I get a window where I can order how the cards should enter the battlefield.
* Add files via upload
* Update grievous_wound.txt
* Update meathook_massacre_ii.txt
* Update say_its_name.txt
* Update say_its_name.txt
* Update drag_to_the_roots.txt
* Update patchwork_beastie.txt
---------
Co-authored-by: tool4ever <therealtoolkit@hotmail.com>
* Fix self-replacement scripts
* Fix scripts to use current value on resolve
* Clean up
* Try to get right controller when ordering
* Fix corner case
* Fix script
* Revert for now
---------
Co-authored-by: tool4EvEr <tool4EvEr@192.168.0.60>
* Unknown Philadelphia 2023 and Minenapolis 2023
* Barcelona 2023 what I could
* A few of the las vegas ones
* Questing role give to creature instead of being on aura
* Changed prizes to promo, should fix phila
And also put the you make the card on a special sheet, should fix a future issue if I got it right
* Fix the etb tapped from bloomburrow patch
Kept old wording for now cause those cards don't have oracle texts, might change if asked
* Las Vegas 2023 (what I could)
* Shortened set codes as suggested
* Fix edition change on las vegas ymtc
* GameActionUtil: use Multikicker as KeywordExtraCost
* ~ remove getMultiKickerManaCost
* ~ remove Announce$ Multikicker
* some AI tweaks
* Fix MultiKicker for AI
* Update ComputerUtilMana.java
* Update ComputerUtil.java
* GamePieceType and isCollectible.
Can no longer ante conjured cards.
Helper set of ZoneTypes that are part of the command zone.
* Faster GamePieceType evaluation
---------
Co-authored-by: Jetz <Jetz722@gmail.com>
I think it produces one mana of EACH color (I had AI use it to pay {2} UnlessCost). But I don't know how to fix that, so I have at least corrected the text.
Refactored functional variants to be merged in the card face rather than the card factory
Some variant characteristics now show in deck builder columns
Fixed loading progress amount
"[card] has neither ManaCost nor Color" warning now actually displays when these are missing.
Co-authored-by: Jetz <Jetz722@gmail.com>
Co-authored-by: tool4ever <therealtoolkit@hotmail.com>
* Add Forage Cost
* ~ fix PaymentDecision for now
* TriggerForage: add Forage trigger
* CostForage: Add First Part of Human Sacrifice Food Logic
* Human ready, first Forage card
* AiCostDecision: Basic AI for Forage
Attempting to add UNF's Nearby Planet using the Omo effect from M3C (and to a lesser extent Planar Nexus from MH3) as a reference.
Tested card, seems to work as intended. The typeline is messier than with the Mistform Ultimus/changeling effect for creatures (Mistform displays "(All)" whereas the Omo version spells out the types), but it is consistent with the referenced Modern Horizons cards.
Both goldLoss and lifeLoss where not being set on creation + update difficulty. So regardless of difficulty chosen, on defeat the default value on the difficultyData class for the properties (0.2f) was being used
I tried to assign the right image numbers, as the first image for most of cards is the extended version.
Since Overclocked Electromancer is still unimplemented, Creative Energy won't work yet.
MH3: Add card scripts for three MH3 cards, including token scripts:
- Arna Kennerüd, Skycaptain
- Genku, Future Shaper
- Thraben Charm
- 1/1 black Rat creature token with lifelink
- 1/2 blue Moonfolk creature token with flying
- 2/2 white Fox creature token with vigilance
* Next few Draft cards
* Code review fixes
* Add Spire Phantasm
* Add Agent of Acquisitions, Cogwork Librarian, Leovold's Operative
* Some small fixes
* Getting closer
* Now the UI is updating
* Switch DraftPack to a delegate class
* Fix agent of acquisitions when AI drafts it
* Quest for the Necropolis
Official preview showed the mana cost, so I can implement it now.
* Emrakul's Messenger
Seem to have slipped between the cracks.
* Hydroelectric Specimen // Hydroelectric Laboratory
Possible now since the official preview revealed the back face.
* Update hydroelectric_specimen_hydroelectric_laboratory.txt
* Update quest_for_the_necropolis.txt
---------
Co-authored-by: Fulgur14 <54345051+Fulgur14@users.noreply.github.com>
* - PreDH: Avoid warnings, exclude some sets that aren't yet in Forge
* - Enable PreDH format as always available to the player.
* - Fix capitalization for Caracal file name
I believe I included all legal sets, though I did omit a fair few promo sets since I couldn't tell if they would contain illegal modern cards. The legal sets are New Phyrexia and everything that came before it. Also, I couldn't figure out what the "Order" part was, so I left it blank.
* MH3: Emrakul, the World Anew + 2 cards
MH3: Add card scripts for three MH3 cards:
- Emrakul, the World Anew
- Grist, Viracious Larva // Grist, the Plague Swarm
- Phlage, Titan of Fire's Fury
* Update grist_voracious_larva_grist_the_plague_swarm.txt
I think this should be correct, based on my reading of other scripts that use both Remember and Imprint, like Break Out, but I'm not super well versed on the differences and relations between those two things. I have tested it both with and without Rest in Peace in play and it does seem to work as it should, though.
* add ManaCostShard Map and cleanup Desktop
* refactor mobile and remove SourceFile enum
* refactor mobile using ManaCostShard
* Fix Phyrexian Symbols and better String=>SkinProp mapping
Dev instructions here: [Getting Started](https://github.com/Card-Forge/forge/wiki) (Somewhat outdated)
## Requirements / Tools
- your favourite Java IDE (IntelliJ, Eclipse, VSCodium, Emacs, Vi...)
- Java JDK 17 or later
- Git
- Git client (optional)
- Maven
- GitHub account
- Libgdx (optional: familiarity with this library is helpful for mobile platform development)
- Android SDK (optional: for Android releases)
- RoboVM (optional: for iOS releases) (TBD: Current status of support by libgdx)
## Project Quick Setup
- Login into GitHub with your user account and fork the project.
- Clone your forked project to your local machine
- Go to the project location on your machine. Run Maven to download all dependencies and build a snapshot. Example for Windows & Linux: `mvn -U -B clean -P windows-linux install`
## IntelliJ
IntelliJ is the recommended IDE for Forge development. Quick start guide for [setting up the Forge project within IntelliJ](https://github.com/Card-Forge/forge/wiki/IntelliJ-setup).
## Eclipse
Eclipse includes Maven integration so a separate install is not necessary. For other IDEs, your mileage may vary.
At this time, Eclipse is not the recommended IDE for Forge development.
### Project Setup
- Follow the instructions for cloning from GitHub. You'll need to setup an account and your SSH key.
If you are on a Windows machine you can use Putty with TortoiseGit for SSH keys. Run puttygen.exe to generate the key -- save the private key and export
the OpenSSH public key. If you just leave the dialog open, you can copy and paste the key from it to your GitHub profile under
"SSH keys". Run pageant.exe and add the private key generated earlier. TortoiseGit will use this for accessing GitHub.
- Fork the Forge git repo to your GitHub account.
- Clone your forked repo to your local machine.
- Make sure the Java SDK is installed -- not just the JRE. Java 17 or newer required. If you execute `java -version` at the shell or command prompt, it should report version 17 or later.
- Install Eclipse 2021-12 or later for Java. Launch it.
- Create a workspace. Go to the workbench. Right-click inside of Package Explorer > Import... > Maven > Existing Maven Projects > Navigate to root path of the local forge repo and
ensure everything is checked > Finish.
- Let Eclipse run through building the project. You may be prompted for resolving any missing Maven plugins -- accept the ones offered. You may see errors appear in the "Problems" tab. These should
be automatically resolved as plug-ins are installed and Eclipse continues the build process. If this is the first time for some plug-in installs, Eclipse may prompt you to restart. Do so. Be patient
for this first time through.
- Once everything builds, all errors should disappear. You can now advance to Project launch.
### Project Launch
#### Desktop
This is the standard configuration used for releasing to Windows / Linux / MacOS.
- Right-click on forge-gui-desktop > Run As... > Java Application > "Main - forge.view" > Ok
- The familiar Forge splash screen, etc. should appear. Enjoy!
#### Mobile (Desktop dev)
This is the configuration used for doing mobile development using the Windows / Linux / MacOS front-end. Knowledge of libgdx is helpful here.
- Right-click on forge-gui-mobile-dev > Run As... > Java Application > "Main - forge.app" > Ok.
- A view similar to a mobile phone should appear. Enjoy!
### Eclipse / Android SDK Integration
Google no longer supports Android SDK releases for Eclipse. use IntelliJ.
#### Android SDK
TBD
##### Windows
TBD
##### Linux / Mac OSX
TBD
#### Android Plugin for Eclipse
TBD
#### Android Platform
In Intellij, if the SDK Manager is not already running, go to Tools > Android > Android SDK Manager. Install the following options / versions:
- Android SDK Build-tools 35.0.0
- Android 15 (API 35) SDK Platform
#### Proguard update
Standalone Proguard 7.6.0 is included with the project (proguard.jar) under forge-gui-android > tools and supports up to Java 23 (latest android uses Java 17).
#### Android Build
TBD
#### Android Deploy
TBD
#### Android Debugging
TBD
### Windows / Linux SNAPSHOT build
SNAPSHOT builds can be built via the Maven integration in Eclipse.
1. Create a Maven build for the forge top-level project. Right-click on the forge project. Run as.. > Maven build...
- On the Main tab, set Goals: clean install, set Profiles: windows-linux
2. Run forge Maven build. If everything built, you should see "BUILD SUCCESS" in the Console View.
The resulting snapshot will be found at: forge-gui-desktop/target/forge-gui-desktop-[version]-SNAPSHOT
## Card Scripting
Visit [this page](https://github.com/Card-Forge/forge/wiki/Card-scripting-API) for information on scripting.
Card scripting resources are found in the forge-gui/res/ path.
## General Notes
### Project Hierarchy
Forge is divided into 4 primary projects with additional projects that target specific platform releases. The primary projects are:
- forge-ai
- forge-core
- forge-game
- forge-gui
The platform-specific projects are:
- forge-gui-android
- forge-gui-desktop
- forge-gui-ios
- forge-gui-mobile
- forge-gui-mobile-dev
#### forge-ai
The forge-ai project contains the computer opponent logic for gameplay. It includes decision-making algorithms for specific abilities, cards and turn phases.
#### forge-core
The forge-core project contains the core game engine, card mechanics, rules engine, and fundamental game logic. It includes the implementation of Magic: The Gathering rules, card interactions, and the game state management system.
#### forge-game
The forge-game project handles the game session management, player interactions, and game flow control. It includes implementations for multiplayer support, game modes, matchmaking, and game state persistence. This module bridges the core game engine with the user interface and networking components.
#### forge-gui
The forge-gui project contains the user interface components and rendering logic for the game. It includes the main game window, card displays, player interactions, and the scripting resource definitions in the res/ path.
#### forge-gui-android
Libgdx-based backend targeting Android. Requires Android SDK and relies on forge-gui-mobile for GUI logic.
#### forge-gui-desktop
Java Swing based GUI targeting desktop machines.
Screen layout and game logic revolving around the GUI is found here. For example, the overlay arrows (when enabled) that indicate attackers and blockers, or the targets of the stack are defined and drawn by this.
#### forge-gui-ios
Libgdx-based backend targeting iOS. Relies on forge-gui-mobile for GUI logic.
#### forge-gui-mobile
Mobile GUI game logic utilizing [libgdx](https://libgdx.badlogicgames.com/) library. Screen layout and game logic revolving around the GUI for the mobile platforms is found here.
#### forge-gui-mobile-dev
Libgdx backend for desktop development for mobile backends. Utilizes LWJGL. Relies on forge-gui-mobile for GUI logic.
- you favourite Java IDE (IntelliJ, Eclipse, VSCodium, Emacs, Vi...)
- Java JDK 8 or later (some IDEs such as Eclipse require JDK11+, whereas the Android build currently only works with JDK8)
- Git
- Git client (optional)
- Maven
- GitHub account
- Libgdx (optional: familiarity with this library is helpful for mobile platform development)
- Android SDK (optional: for Android releases)
- RoboVM (optional: for iOS releases) (TBD: Current status of support by libgdx)
## ✨ Introduction
## Project Quick Setup
**Forge** is a dynamic and open-source **Rules Engine** tailored for **Magic: The Gathering** enthusiasts. Developed by a community of passionate programmers, Forge allows players to explore the rich universe of MTG through a flexible, engaging platform.
- Login into GitHub with your user account and fork the project.
**Note:** Forge operates independently and is not affiliated with Wizards of the Coast.
- Clone your forked project to your local machine
---
- Go to the project location on your machine. Run Maven to download all dependencies and build a snapshot. Example for Windows & Linux: `mvn -U -B clean -P windows-linux install`
## 🌟 Key Features
## Eclipse
- **🌐 Cross-Platform Support:** Play on **Windows, Mac, Linux,** and **Android**.
- **🔧 Extensible Architecture:** Built in **Java**, Forge encourages developers to contribute by adding features and cards.
- **🎮 Versatile Gameplay:** Dive into single-player modes or challenge opponents online!
Eclipse includes Maven integration so a separate install is not necessary. For other IDEs, your mileage may vary.
---
### Project Setup
## 🛠️ Installation Guide
- Follow the instructions for cloning from GitHub. You'll need to setup an account and your SSH key.
### 📥 Desktop Installation
1.**Latest Releases:** Download the latest version [here](https://github.com/Card-Forge/forge/releases/latest).
2.**Snapshot Build:** For the latest development version, grab the `forge-gui-desktop` tarball from our [Snapshot Build](https://github.com/Card-Forge/forge/releases/tag/daily-snapshots).
- **Tip:** Extract to a new folder to prevent version conflicts.
3.**User Data Management:** Previous players’ data is preserved during upgrades.
4.**Java Requirement:** Ensure you have **Java 17 or later** installed.
If you are on a Windows machine you can use Putty with TortoiseGit for SSH keys. Run puttygen.exe to generate the key -- save the private key and export
the OpenSSH public key. If you just leave the dialog open, you can copy and paste the key from it to your GitHub profile under
"SSH keys". Run pageant.exe and add the private key generated earlier. TortoiseGit will use this for accessing GitHub.
### 📱 Android Installation
- _(Note: **Android 11** is the minimum requirements with at least **6GB RAM** to run smoothly. You need to enable **"Install unknown apps"** for Forge to initialize and update itself)_
- Download the **APK** from the [Snapshot Build](https://github.com/Card-Forge/forge/releases/tag/daily-snapshots). On the first launch, Forge will automatically download all necessary assets.
- Fork the Forge git repo to your GitHub account.
---
- Clone your forked repo to your local machine.
## 🎮 Modes of Play
- Make sure the Java SDK is installed -- not just the JRE. Java 8 or newer required. If you execute `java -version` at the shell or command prompt, it should report version 1.8 or later.
Forge offers various exciting gameplay options:
- Install Eclipse 2018-12 or later for Java. Launch it.
### 🌍 Adventure Mode
Embark on a thrilling single-player journey where you can:
- Explore an overworld map.
- Challenge diverse AI opponents.
- Collect cards and items to boost your abilities.
- Create a workspace. Go to the workbench. Right-click inside of Package Explorer > Import... > Maven > Existing Maven Projects > Navigate to root path of the local forge repo and
- Let Eclipse run through building the project. You may be prompted for resolving any missing Maven plugins -- accept the ones offered. You may see errors appear in the "Problems" tab. These should
be automatically resolved as plug-ins are installed and Eclipse continues the build process. If this is the first time for some plug-in installs, Eclipse may prompt you to restart. Do so. Be patient
for this first time through.
### 🔍 Quest Modes
Engage in focused gameplay without the overworld exploration—perfect for quick sessions!
- Once everything builds, all errors should disappear. You can now advance to Project launch.
Download the following archived version of the Android SDK: http://dl-ssl.google.com/android/repository/tools_r25.2.3-windows.zip. Install it somewhere on your machine. This is referenced
in the following instructions as your 'Android SDK Install' path.
---
##### Linux / Mac OSX
TBD
#### Android Plugin for Eclipse
Google's last plugin release does not work completely with target's running Android 7.0 or later. Download the ADT-24.2.0-20160729.zip plugin
from: https://github.com/khaledev/ADT/releases
In Eclipse go to: Help > Install New Software... > Add > Name: ADT Update, Click on the "Archive:" button and navigate to the downloaded ADT-24.2.0-20160729.zip file > Add. Install all "Developer Tools". Eclipse
should restart and prompt you to run the SDK Manager. Launch it and continue to the next steps below.
#### Android Platform
In Eclipse, if the SDK Manager is not already running, go to Window > Android SDK Manager. Install the following options / versions:
- Android SDK Build-tools 26.0.1
- Android 8.0.0 (API 26) SDK Platform
- Google USB Driver (in case your phone is not detected by ADB)
Note that this will populate additional tools in the Android SDK install path extracted above.
#### Proguard update
The Proguard included with the Android SDK Build-tools is outdated and does not work with Java 1.8. Download Proguard 6.0.3 or later (last tested with 7.0.1) from https://github.com/Guardsquare/proguard
- Go to the Android SDK install path. Rename the tools/proguard/ path to tools/proguard-4.7/.
- Extract your Proguard version to the Android SDK install path under tools/. You will need to either rename the dir proguard-<your-version> to proguard/ or, if your filesystem supports it, use a symbolic link (the later is highly recommended), such as `ln -s proguard proguard-<your-version>`.
#### Android Build
The Eclipse plug-ins do NOT support building things for Android. They do however allow you to use the debugger so you can still set breakpoints and trace
things out. The steps below show how to generate a debug Android build.
1) Create a Maven build for the forge top-level project. Right-click on the forge project. Run as.. > Maven build...
- On the Main tab, set Goals: clean install
2) Run forge Maven build. If everything built, you should see "BUILD SUCCESS" in the Console View.
3) Right-click on the forge-gui-android project. Run as.. > Maven build...
- On the Main tab, set Goals: install, Profiles: android-debug
- On the Environment tab, you may need to define the variable ANDROID_HOME with the value containing the path to your Android SDK installation. For example, Variable: ANDROID_HOME, Value: Your Android SDK install path here.
4) Run the forge-gui-android Maven build. This may take a few minutes. If everything worked, you should see "BUILD SUCCESS" in the Console View.
Assuming you got this far, you should have an Android forge-android-[version].apk in the forge-gui-android/target path.
#### Android Deploy
You'll need to have the Android SDK install path platform-tools/ path in your command search path to easily deploy builds.
- Open a command prompt. Navigate to the forge-gui-android/target/ path.
- Connect your Android device to your dev machine.
- Ensure the device is visible using `adb devices`
- Remove the old Forge install if present: `adb uninstall forge.app`
- Install the new apk: `adb install forge-android-[version].apk`
#### Android Debugging
Assuming the apk is installed, launch it from the device.
In Eclipse, launch the DDMS. Window > Perspective > Open Perspective > Other... > DDMS. You should see the forge app in the list. Highlight the app, click on the green debug button and a
green debug button should appear next to the app's name. You can now set breakpoints and step through the source code.
### Windows / Linux SNAPSHOT build
SNAPSHOT builds can be built via the Maven integration in Eclipse.
1) Create a Maven build for the forge top-level project. Right-click on the forge project. Run as.. > Maven build...
- On the Main tab, set Goals: clean install, set Profiles: windows-linux
2) Run forge Maven build. If everything built, you should see "BUILD SUCCESS" in the Console View.
The resulting snapshot will be found at: forge-gui-desktop/target/forge-gui-desktop-[version]-SNAPSHOT
## IntelliJ
Quick start guide for [setting up the Forge project within IntelliJ](https://github.com/Card-Forge/forge/wiki/IntelliJ-setup).
## Card Scripting
Visit [this page](https://github.com/Card-Forge/forge/wiki/Card-scripting-API) for information on scripting.
Card scripting resources are found in the forge-gui/res/ path.
## General Notes
### Project Hierarchy
Forge is divided into 4 primary projects with additional projects that target specific platform releases. The primary projects are:
- forge-ai
- forge-core
- forge-game
- forge-gui
The platform-specific projects are:
- forge-gui-android
- forge-gui-desktop
- forge-gui-ios
- forge-gui-mobile
- forge-gui-mobile-dev
#### forge-ai
#### forge-core
#### forge-game
#### forge-gui
The forge-gui project includes the scripting resource definitions in the res/ path.
#### forge-gui-android
Libgdx-based backend targeting Android. Requires Android SDK and relies on forge-gui-mobile for GUI logic.
#### forge-gui-desktop
Java Swing based GUI targeting desktop machines.
Screen layout and game logic revolving around the GUI is found here. For example, the overlay arrows (when enabled) that indicate attackers and blockers, or the targets of the stack are defined and drawn by this.
#### forge-gui-ios
Libgdx-based backend targeting iOS. Relies on forge-gui-mobile for GUI logic.
#### forge-gui-mobile
Mobile GUI game logic utilizing [libgdx](https://libgdx.badlogicgames.com/) library. Screen layout and game logic revolving around the GUI for the mobile platforms is found here.
#### forge-gui-mobile-dev
Libgdx backend for desktop development for mobile backends. Utilizes LWJGL. Relies on forge-gui-mobile for GUI logic.
The AI is *not* "trained". It uses basic rules and can be easy to overcome knowing it's weaknesses.
The AI is:
* Best with Aggro and midrange decks
* Poor to Ok in control decks
* Pretty bad for most combo decks
If you want to train a model for the AI, please do. We would love to see something like that implemented in Forge.
# AI Matches from Command Line
The AI can battle itself in the command line, allowing the tests to be performed on headless servers or on computers that have poor graphic performance, and when you just don't need to see the match. This can be useful if you want to script testing of decks, test a large tournament, or just bash 100's of games out to see how well a deck performs.
Please understand, the AI is still the AI, and it's limitations exist even against itself. Games can lag and become almost unbearably long when the AI has a lot to think about, and you can't see what's on the table for it to play against. It's best if you set up the tournament and walk away, you can analyze logs later, results are printed at the end.
In linux and mac, command line arguments are not currently passed through the sh script, please call `java -jar` manually, instead of the exe.
-`sim` - "Simulation Mode" forces Forge to not start the GUI and automatically runs the AI matches in command line. Enables all other switches for simulation mode.
-`-d <deck1[.dck]> ... <deckX[.dck]>` - Space separated list of deck files, in `-f` game type path. (For example; If `-f` is set to Commander, decks from `<userdata>/decks/commander/` will be searched. If `-f` is not set then default is `<userdata>/decks/constructed/`.) Names must use quote marks when they contain spaces.
-`deck1.dck` - Literal deck file name, when the value has ".dck" extension.
-`deck` - A meta deck name of a deck file.
-`-D [path]` - [path] is absolute directory path to load decks from. (Overrides path for `-d`.)
-`-n [N]` - [N] number of games, just flat test the AI multiple times. Default is 1.
-`-m [M]` - [M] number of matches, best of [M] matches. (Overrides -n) Recommended 1, 3, or 5. Default is 1.
-`-f [F]` - Runs [F] format of game. Default is "constructed" (other options may not work, list extracted from code)
-`Commander`
-`Oathbreaker`
-`TinyLeaders`
-`Brawl`
-`MomirBasic`
-`Vanguard`
-`MoJhoSto`
-`-t [T]` - for Tournament Mode, [T] for type of tournament.
-`Bracket` - See wikipedia for [Bracket Tournament](https://en.wikipedia.org/wiki/Bracket_(tournament))
-`RoundRobin` - See wikipedia for [Round Robin Tournaments](https://en.wikipedia.org/wiki/Round-robin_tournament)
-`Swiss` - See wikipedia for [Swiss Pairing Tournaments](https://en.wikipedia.org/wiki/Swiss-system_tournament)
-`-p [P]` - [P] number of players paired, only used in tournament mode. Default is 2.
-`-q` - Quiet Mode, only prints the result not the entire log.
## Examples
In linux and macos you must run forge by evoking java and calling the jar, currently command line parameters are not passed through the script. The forge jar filename is truncated in these examples from `forge-whatever-version-youre-on.jar` to `forge.jar`.
In Windows, if you use the EXE file as described below, the simulation runs in the background and output is sent to the forge log file only. If you want to have output to the console, please use the `java -jar` evocation of forge.
To simulate a basic three games of two decks (deck1 and deck2 must be meta deck names of decks in `<userdata>\decks\constructed\`):
Adventure mode is a work-in-progress game mode where you explore the ever-changing landscape of Shandalar, duel creatures to earn gold and new cards to battle the various bosses. You can visit towns to buy equipment and cards, and crawl through dungeons to find artifacts and loot to help you on your journey. Adventure mode is an awesome reimagining of the original "Shandalar" 1997 PC Game in Forge, even though the scope of adventure is much broader and we don't constrain ourselves to the lore of the plane of Shandalar. You can play Shandalar on Desktop (Linux, Windows, IOS) or on Android devices.
# How to run?
1. Step 1: Download the latest version of Forge https://downloads.cardforge.org/dailysnapshots/
2. Step 2: Make sure you have the required version of the JDK (https://www.oracle.com/be/java/technologies/downloads/)
3. Step 3: Launch the adventure mode from the "adventure.exe" file included in the forge version (on android, the adventure version should be embedded into the main Forge program)
4. Step 4: In order to have pictures show up make sure you enable "automatically download missing card images" in the settings. This should automatically download the picture of the card as you encounter them in game.
Card images are assets used to represent the real cards in game. You DO NOT need images to play forge, however representing real cards is a nice ability forge provides. These images can be any image set you like, or custom images too.
Primarily there are two types of images you'll care about; cards, and tokens.
**Cards** - are the primary card image files, and can be generic (as all cards of the same name have the same rules) or per set since there may be different art work in different editions. Typically these are scans of the original cards and are provided by forge's FTP, or scryfall. You can customize a full generic set, or any edition if you desire... (you could for example, blur every card image and play a literal "blind" or "farsighted" game.)
**Tokens** - are the images for the cards replacing a generic "1/1 zombie" for example. These are less frequently updated, and are typically the bulk of what is missing when doing an audit. However, these are probably where the more true "custom" replacements are available, with either custom artwork, or modified of other existing.
# Downloading
Due to charges in Forges hosting and scryfall terms you can no longer predownload card images. Turn on auto download in forge to download card images when first viewed.
## In Forge Auto Download:
**Download Missing Images - Setting**
- This will download the images from the sources as the game requests the image in situ.
- This can be useful if you don't want to have copies of every card... You can do small pre-caching by loading your decks in the deck editor prior to playing to download just those images.
> I provide my site for free for bulk downloading the entire image catalog. So you don't need to give those spam sites more advertising spots. If the server is loaded bandwidth is shared, right now it's not heavily used so please feel free to download the 4+gb zips, or the individual zips if you need sets. They are the images Kev has uploaded to my site, and the Zips are updated nightly automatically.
**(I'm not gatekeeping, please if you have a private location for bulk downloads or for alternate or custom arts, you can update this wiki too or let us know in the discord. I'll be happy to update the wiki page with additional sources.)**
# Storage
Card images are stored in `pics/cards`, and tokens in `pics/tokens`, in the Cache folder for forge:
If you don't care about the edition's version of cards, images can be stored in the root directory of the cards folder.
`/cache/pics/cards/`
If you want the edition's versions of the cards, they need to go under the edition's code subfolder.
`/cache/pics/cards/AFR` for example for Adventures in the Forgotten Realms.
# File Naming
**File Names:**
- Cards file names follow a simple principle: `Card Name#.border.ext`
-`Card Name` - Card Name with spaces.
-`#` - Alternate Art number; if more than one art exists for the card.
-`border` - Border Type; fullborder, crop. (I don't know all of them.)
-`ext` - Extension, jpg or png are supported.
**Alternate images:**
Alternate images are defined as cards with the same name in the set's edition file, if the edition file does not have the alternate listed forge will not see the alternate there!
**Standard Alternate Arts:**
So for example the AFR set (as most sets) shows these 4 versions of swamp;
```
270 L Swamp @Piotr Dura
271 L Swamp @Sarah Finnigan
272 L Swamp @Titus Lunter
273 L Swamp @Adam Paquette
```
The file naming would be represented by a number after the name:
```
Swamp1.fullborder.jpg
Swamp2.fullborder.jpg
Swamp3.fullborder.jpg
Swamp4.fullborder.jpg
```
**Additional Alternate Arts:**
They may also be listed separately as "extended arts", "showcase", or "borderless" in the same editions file:
```
[cards]
90 U Black Dragon @Mark Zug
```
and
```
[borderless]
291 U Black Dragon @Jason A. Engle
```
Where the files are:
```
black dragon1.fullborder.jpg
black dragon2.fullborder.jpg
```
**Forcing an Alternate:**
Renaming and creating a second of an existing card **will not work**, for example creating two "Burning hands" which does not have alternate art;
```
burning hands1.fullborder.jpg
burning hands2.fullborder.jpg
```
Forge will not see either of those, and will probably download the missing `burning hands.fullborder.jpg` for you. Similarly adding a 3rd black dragon `black dragon3.fullborder.jpg` will **not** work either.
In most cases, each AF subclass implements both the Spell and Ability.
Much of the code is shared, so creating the data object will look very similar.
- **AB** is for Activated Abilities
- **SP** is for Spell
- **DB** is for Drawback and many abilities that are subsidiary to other things, like replacements. They are only used to chain AFs together, and will never be the root AF
- **ST** is for Static, this gets used in case the API should resolve without using the stack<br> (e.g. the unique *Circling Vultures* special action is directly implemented in the script this way)
>*NOTE:*
> - these factories are refactored from time to time (often to adapt to new sets), so while some entries could be slightly outdated, the base information should still be correct
> - a few factories also have _*All_ variants, these are slowly being phased out
> - some parameters are only added for very exotic cards, these won't be included here to keep the focus on understanding the general concepts of scripting
> - when in doubt you can always cross check with the [available APIs](https://github.com/Card-Forge/forge/tree/master/forge-game/src/main/java/forge/game/ability/effects) code
# Common Parameters
## Cost / UnlessCost
`Cost$ <AbilityCost>` is the appropriate way to set the cost of the ability. Currently for spells, any additional costs including the original Mana cost need to appear in the Cost parameter in the AbilityFactory. For each card that uses it, the order in which the cost is paid will always be the same.
Secondary abilities such as the DB executed by triggers or replacements (usually) don't need costs. (This is one reason to use DB over AB in these cases.)
Read more about it in [Costs](Costs)
## ValidTgts / Defined
Most effects need to know (at least implicitly) which players or objects they're trying to affect. There are two different ways for that:
-`ValidTgts` will need to be used for abilities that target
- if your ability instead describes on what it's applied use `Defined`
Read more about it in [Affected / Targets](Targeting)
## Restrictions / Conditions
Restrictions limit when abilities can be put on the stack and Conditions apply during resolving. Common examples are Putrid Leech's only activate this once per turn or different cards that can activate from Zones like the Hand or the Graveyard.
Read more about it in [Restriction](Restrictions)
## SpellDescription
SpellDescription is how the text of the ability will display on the card and in the option dialog for cards with multiple abilities.
The SpellDescription for secondary abilities (both AB and DB) is now displayed when (and if) the ability prompts for user input in the prompt pane so it is useful to put some nice text there.
## StackDescription
*(Optional)* StackDescription is the description the ability will have on the stack. This is automatically generated by the effect, but may be overridden using this parameter. This is sometimes needed with complex effects, when the generated text can't handle some details. Properties of the spell can be accessed like this: {c:Targeted}. You can reuse the spell text by just putting `SpellDescription` or `None` to leave it empty.
## Remember*
Remembering is often needed when a card becomes a new object, which is then further affected by the ability. Typical example: [Flicker](https://github.com/Card-Forge/forge/blob/master/forge-gui/res/cardsfolder/f/flicker.txt)<br>
Because cards keep their remembered parts when changing zones manual [cleanup](#Cleanup) is usually required.
## AI params
`IsCurse$ True` - For effects that are normally treated positive e.g. Pump
`AITgts$ BetterThanEvalRating.130`
# Factories (in Alphabetical Order)
## AlterLife
AlterLife is for Abilities that Alter a player's life total.
### GainLife
Have a player gain the specified amount of life.
`A:AB$ GainLife | Cost$ T | LifeAmount$ 1 | SpellDescription$ You gain 1 life.`
LifeAmount$ is required. This is how much life you will gain.
Defined is optional, if it appears the defined player(s) gain life.
Target is optional, if it appears and Defined doesn't then targeted player(s) gain life.
### LoseLife
Have a player lose the specified amount of life.
`A:AB$ LoseLife | Cost$ Sac<1/CARDNAME> | ValidTgts$ Player | TgtPrompt$ Target a player to lose a life | LifeAmount$ 1 | SpellDescription$ Target player loses 1 life.`
`A:SP$ LoseLife | Cost$ 2 B | Defined$ Opponent | LifeAmount$ 2 | SpellDescription$ Each opponent loses 2 life.`
LifeAmount$ is required. This is how much life will be lost.
Target is optional. If Target doesn't appear then Defined will be used.
Remember, if Defined is missing, the default for Players is "You"
Part of resolving sets the **SVar AFLifeLost** to the amount of life lost by all players.
### SetLife
SetLife sets one or both player's life total to a specified value (i.e.
"your life total becomes 20" or "Target player's life total is equal to
the number of cards in your graveyard").
`A:SP$ SetLife | Cost$ 7 W W | ValidTgts$ Player | TgtPrompt$ Select target player | LifeAmount$ 20 | SpellDescription$ Target player's life total becomes 20.`
Parameters:
LifeAmount (required) - the value to set the life total(s) to
Defined is optional. If it exists, it will be used. Target is optional.
If it exists and defined doesn't it will be used. Default player is "You".
### ExchangeLife
ExchangeLife switches the Life total of two players.
`A:AB$ ExchangeLife | Cost$ 6 T | ValidTgts$ Player | TargetMin$ 2 | TargetMax$ 2 | TgtPrompt$ Select target player | SpellDescription$ Two target players exchange life totals.`
One of Defined or Target is required, since there needs to be two
Players exchanging life If Defined it will be used. If Target exists and
defined doesn't it will be used.
If there aren't two determined players by the SA, the activating player is added as the second player.
## Animate
Animate handles animation effects like "This card becomes a 5/5
green creature with flying until end of turn." It is designed to handle
color changing, type changing, P/T setting, and granting/removing abilities.
`A:SP$Animate | Cost$ G | ValidTgts$ Land | TgtPrompt$ Select target land | Power$ 3 | Toughness$ 3 | Types$ Creature | SpellDescription$ Until end of turn, target land becomes a 3/3 creature that's still a land.`
`A:AB$Animate | Cost$ 1 B | Defined$ Self | Power$ 1 | Toughness$ 1 | Types$ Creature,Skeleton | Colors$ Black | Abilities$ ABRegen | SpellDescription$ CARDNAME becomes a 1/1 black Skeleton creature with "B: Regenerate this creature" until end of turn. It's still a land. (If it regenerates, the next time it would be destroyed this turn, it isn't. Instead tap it, remove all damage from it, and remove it from combat.)`
`SVar:ABRegen:AB$Regenerate | Cost$ B | SpellDescription$ Regenerate CARDNAME.`
`A:AB$Animate | Cost$ 2 R G | Defined$ Self | Power$ 3 | Toughness$ 3 | Types$ Creature,Elemental | Colors$ Red,Green | Triggers$ TrigAttack | SpellDescription$ Until end of turn, CARDNAME becomes a 3/3 red and green Elemental creature with "Whenever this creature attacks, put a +1/+1 counter on it." It's still a land.`
`SVar:TrigAttack:Mode$ Attacks | ValidCard$ Creature.Self | Execute$ TrigPutCounter | TriggerDescription$ Whenever CARDNAME attacks, put a +1/+1 counter on it.`
- Power (required) - the power to assign to the animated card
- Toughness (required) - the toughness to assign to the animated card
- Types (optional) - the additional types to give the animated card;
comma delimited
- OverwriteTypes (optional) - set to True if the animated being should
have these types **instead** as opposed to **in addition to**
- RemoveTypes (optional) - a list of types to Remove from the animated
card
- ChosenType (optional) - overrides types before it and just will add
the ChosenType
- Keywords (optional) - a " & " delimited list of keywords to give the
animated being (just like AB$Pump)
- HiddenKeywords (optional) - a " & " delimited list of hidden
keywords to give the animated being (just like AB$Pump)
- RemoveKeywords (optional) - a " & " delimited list of keywords to
remove from the animated being (just like AB$Debuff)
- Colors (optional) - a comma-delimited list of Colors to give to the
animated being (capitalized and spelled out) (ChosenColor accepted)
- Abilities (optional) - a comma-delimited list of SVar names which
contain abilities that should be granted to the animated being
- OverwriteAbilities - Remove Abilities from animated being
- Triggers (optional) - a comma-delimited list of SVar names which
contain triggers that should be granted to the animated being
- OverwriteTriggers - Remove/suppress triggers from animated being
- staticAbilities (optional) - a comma-delimited list of SVar names
which contain static abilities that should be granted to the
animated being
- OverwriteStatics- Remove static abilities from animated being
- OverwriteReplacements - Remove replacement effects from animated
being
- RemoveAllAbilities - Remove all Abilities, Triggers, Statics, and
Replacement effects
- sVars(optional) - a comma-delimited list of SVars that should be
granted to the animated being
- Duration (Default is end of turn)
- Permanent
- UntilEndOfCombat - if the effect should last only until End of Combat instead of End of Turn
- UntilHostLeavesPlay - if the effect should last as long as the host is still in play
- UntilYourNextUpkeep
- UntilControllerNextUntap
- UntilYourNextTurn
Target is optional, will be used if possible. Defined is optional, will be used if no Targets (Self by default)
## Attach
Attach is being used directly only for Auras, primarily for Aura Spells, but also for Auras entering the battlefield by some effect.
`AB$ Attach | Cost$ R R | ValidTgts$ Creature | AILogic$ Pump`
Parameters:
- Object (optional) - This is the Object that will be Attached
(generally the Source Card for Auras)
- AILogic - AI Logic tells the AI which base AI code it should use for
Attaching
- GainControl - Gains Control of the Attached Permanent (Control
Magic)
- Curse - A Generic Curse the AI has a handful of checks to see
what the most appropriate Target is.
- Pump - A Generic Pump. The AI has a handful of checks to see
what the most appropriate Target is.
- ChangeType - For Attachments that change types. Evil Presence is
a good example. This logic should be expanded.
- KeepTapped - For Attachments that keep a Permanent tapped. The
AI will also check for a few things like Vigilance, and another
KeepTapped Aura. Paralyzing Grasp is a good example.
Attach separates the actually granting of abilities from the attaching to permanents to streamline how things work.
## BecomeMonarch
## Bond
Soulbonding two creatures. Only used internally by the engine.
## Branch
Sometimes, an ability might do certain things when a specific condition is true, and other things if not. This can be implemented by using `Branch`.
The branch evaluates the SVar specified by the property `BranchConditionSVar`, using the comparison defined with `BranchConditionSVarCompare` (such as `GTY`, `LT1`, etc). Depending on whether the condition evaluated to true or false, the subability defined by `TrueSubAbility` or `FalseSubAbility` is executed.
The example below is for "Composer of Spring", which allows either a "land" or a "land or creature" to be put on the battlefield, depending on the number of enchantments in play under your control.
This allows cards that have a mode to be chosen to occur after a trigger.
Parameters
- CharmNum - Number of Modes to Choose
- Choices - A Comma delimited list of SVars containing the Modes
## Choose*
These can be used to chain effects together. However for common cases many effects already support this directly, e.g. `PutCounter | Choices$``.<br>
Besides making the script shorter using such shortcuts usually also helps the AI making better use of the effect.
### ChooseType
This can be used when you are asked to choose a card type or creature type.
- Type - Required - Can be Card or Creature
- InvalidTypes - Optional - Use to specify any type that cannot be chosen (ex: "Choose any creature type except Wall")
The Defined is for target players.
## Clash
This AF handles clashing. It takes two special parameters: WinSubAbility and
OtherwiseSubAbility. They are both optional and work the same way,
namely that it contains the name of an SVar that in turn contains a
drawback to be executed. The example below is for Release the Ants.
`A:SP$ DealDamage | Cost$ 1 R | Tgt$ TgtCP | NumDmg$ 1 | SubAbility$ DBClash | SpellDescription$ Release the Ants deals 1 damage to target creature or player. Clash with an opponent. If you win, return CARDNAME to its owner's hand.`
A non-functional, maintenance AF used for Cleaning up certain Variables before a Spell finishes Resolving.
Parameters
- ClearRemembered$ (optional) Set to True to clear this card's
remembered list. Generally useful for Cards that Remember a card, do
something to it, then need to forget it once it's done.
- ClearImprinted$ (optional) Set to True to clear the list of
imprinted cards.
- ClearChosenX$ (optional) Set to True to clear the chosen X value.
- ClearTriggered$ (optional) Set to True to clear any delayed triggers
produced by this card.
- ClearCoinFlips$ (optional) Set to True to clear the remembered coin
flip result.
- ClearChosenCard$ (optional) Set to True to clear the chosen cards.
- ForgetDefined$ (optional) If present, remove the specified cards
from this card's remembered list.
## Control
### GainControl
Example: Act of Aggression
`A:SP$ GainControl | Cost$ 3 PR PR | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls. | LoseControl$ EOT | Untap$ True | AddKWs$ Haste | SpellDescription$ Gain control of target creature an opponent controls until end of turn. Untap that creature. It gains haste until end of turn.`
Parameters:
- NewController(Targeted player, if there is no target player, the
default is the ActivatingPlayer)
- AllValid(all valid types, no targets)
- LoseControl(LeavesPlay, Untap, LoseControl, EOT(end of turn))
### ControlExchange
### ControlSpell
## Copy*
### CopyPermanent
Copies a permanent on the battlefield.
Parameters:
- NumCopies - optional - the number of copies to put onto the
battlefield. Supports SVar:X:????.
- Keywords - optional - a list of keywords to add to the copies
- AtEOT - optional
- Sacrifice - set to this is copy should be sacrificed at End of
Turn
- Exile - set to this is copy should be exiled at End of Turn
### CopySpellAbility
Copies a spell on the stack (Twincast, etc.).
## Counter
Countering Spells or Abilities.
`A:SP$ Counter | Cost$ 1 U | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | UnlessCost$ 3 | SpellDescription$ Counter target spell unless its controller pays 3.`
`A:SP$ Counter | Cost$ U | TgtPrompt$ Select target Activated or Triggered Ability | ValidTgts$ Card | TargetType$ Activated,Triggered | SpellDescription$ Counter target activated or triggered ability.`
`A:SP$ Counter | Cost$ G | TargetType$ Spell | ValidTgts$ Instant,Aura | TargetValidTargeting$ Permanent.YouCtrl | SpellDescription$ Counter target instant or Aura spell that targets a permanent you control. `
Parameters
- TargetType - Can be Spell,Activated,Triggered. If more than one,
just put a comma in between.
- ValidTgts - a "valid" expression for types of source card (if you
don't know what it is it's just "Card")
- TargetValidTargeting- a "valid" expression for targets of this
spell's target
- Destination - send countered spell to: (only applies to Spells; ignored for Abilities)
- Graveyard (Default)
- Exile
- TopDeck
- Hand
- BottomDeck
- Shuffle
## Counters*
Factories to handle counters on cards.
### Poison
Poison gives a player the specified number of poison counters.
`A:AB$ Poison | Cost$ B T | ValidTgts$ Player | TgtPrompt$ Select target player | Num$ 2 | SpellDescription$ Target player gets 2 poison counters.`
Parameters:
- Num (required) - the number of poison counters to give
Target is optional and used if available. Defined is optional and used if no Target (defaults to "You").
### PutCounter
Put any type of counter on a game object.
`A:AB$ PutCounter | Cost$ T | CounterType$ CHARGE | CounterNum$1 | SpellDescription$ Put a charge counter on CARDNAME.`
`A:SP$ PutCounter | Cost$ G | Tgt$ TgtC | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Put a charge counter on CARDNAME.`
Target is optional. If no Target is provided, the permanent will put counters on itself.
- CounterType (required) specifies the type of counter and should
appear in all caps. It should be one of the values in the Counters
enum.
- CounterNum (required) specifies how many counters will be put on
the chosen card.
### PutCounterAll
Put any type of counter on all valid cards.
- CounterType (required) specifies the type of counter and should
appear in all caps. It should be one of the values in the Counters
enum.
- CounterNum (required) specifies how many counters will be put on
the chosen cards.
- ValidCards (required) specifies the cards to add counters to.
### RemoveCounter
Remove any type of counter from a card.
Target is optional. If no Target is provided, the permanent will remove
counters from itself.
- CounterType (required) specifies the type of counter and should
appear in all caps. It should be one of the values in the Counters
enum.
- CounterNum (required) specifies how many counters will be removed
from the chosen card.
- UpTo is optional. If an effect states you may remove "up to X
counters", set this to True.
### RemoveCounterAll
Remove any type of counter from all valid cards.
- CounterType$ (required) specifies the type of counter and should
appear in all caps. It should be one of the values in the Counters
enum.
- CounterNum$ (required) specifies how many counters will be removed
from the chosen cards.
- ValidCards$ (required) specifies the card to remove counters from.
### Proliferate
No own parameters.
### MoveCounters
Used for cards that Move Counters on Resolution, requiring the Host card
to have Counters for the Move to occur.
Parameters
- Source - The Source of the Moving Counters
- Defined - The Destination of the Moving Counters
- CounterType - The type of counter to move.
- CounterNum - The number of counters to move.
## Damage
### DealDamage
Deal damage to a specified player or permanent.
`A:AB$ DealDamage | Cost$ T | Tgt$ TgtCP | NumDmg$ 1 | SpellDescription$ CARDNAME deals 1 damage to target creature or player.`
NumDmg is required. This is the amount of damage dealt.
### EachDamage
## Debuff
Parameters
- Keywords
- Duration
An AbilityFactory for Removing Keywords, either temporarily or for longer durations.
## Destroy
These APIs handles destruction of cards on the battlefield.
`A:SP$Destroy | Cost$ 1 W W | ValidTgts$ Artifact,Enchantment | TgtPrompt$ Select target artifact or enchantment | SpellDescription$ Destroy target artifact or enchantment.`
## Effect
Effect is an oddball of the AF family. Where usually AFs have similarities to each other to help with AI use, Effect doesn't fall under that jurisdiction. In general, an effect is some type of SA that
lasts longer than its resolution.
A good example is High Tide. For the rest of the turn, High Tide makes
all Islands produce an extra mana. It doesn't matter if the Island was
in play, if it turned into an Island after High Tide was cast, any of that.
`A:SP$ Effect | Cost$ U | Name$ High Tide Effect | Triggers$ IslandTrigger | SVars$ TrigMana | SpellDescription$ Until end of turn, whenever a player taps an Island for mana, that player adds U to his or her mana pool (in addition to the mana the land produces).`
`SVar:IslandTrigger:Mode$ TapsForMana | ValidCard$ Island | Execute$ TrigMana | TriggerDescription$ Whenever a player taps an Island for mana, that player adds U to his or her mana pool (in addition to the mana the land produces).`
`SVar:TrigMana:AB$Mana | Cost$ 0 | Produced$ U | Amount$ 1`
Effect is most similar to Token as it creates a pseudo-permanent, except
Effect creates in the command zone rather than the battlefield. It stays
active there for a set Duration.
Parameters
- Abilities,Triggers,SVars are comma separated lists which contain
SVars that point to the appropriate type that the Effect will gain.
- Duration is how long the Effect lasts. Right now, most effects will
last Until End of Turn. In the future, they may have other
conditions.
Duration$ Permanent for effects that have no specific Duration.
- Stackable$ False - Most Effects are assumed to be Stackable. By
setting the Stackable Flag to False, the AI will know having a
second one in play is useless, so will save it's Resource for
something else.
- Image - a file\_name\_without\_extension (image needs to reside in
the tokens directory)
## Explore
## Fight
## Fog
Fog is an ability based on the original Fog spell. "Prevent all combat
damage that would be dealt this turn." While this could be done with an
effect, the specialized nature of the AI gives it its own AF.
## Game outcome
### GameDraw
### GameLoss
### GameWin
### RestartGame
Used in the script of *Karn Liberated*
## Goad
## Investigate
## Mana
For lands or other permanent to produce mana.
`A:AB$ Mana | Cost$ T | Produced$ <ManaType> | SpellDescription$ Add W to your mana pool.`
In this example ManaType would be W.
## Manifest
## PermanentState
API for things that alter a permanent's state.
### Phases
### SetState
Changing a cards State. This is mostly for Flip Cards or the Transform mechanic.
### Tap
`A:AB$ Tap | Cost$ R | ValidTgts$ Wall | TgtPrompt$ Select target wall | SpellDescription$ Tap target wall.`
### TapOrUntap
### Untap
`A:AB$ Untap | Cost$ G | ActivationLimit$ 1| SpellDescription$ Untap CARDNAME. Activate this ability only once each turn.`
- RepeatSubAbility - required - to set up repeat subability
- RepeatCards - to repeat for each valid card (zone: present zone of
the valid repeat cards, default: battlefield)
- DefinedCards
- RepeatPlayers - to repeat for each valid player
- RepeatCounters - to repeat for each valid counters
## Reveal
### RevealHand
Look at a player's hand.
Target or Defined is required.
`A:AB$ RevealHand | Cost$ T | ValidTgts$ Player | TgtPrompt$ Select target player | SpellDescription$ Look at target player's hand.`
### Reveal
`A:AB$ Reveal | Cost$ 2 U T | Defined$ You | RevealValid$ Card.Blue | AnyNumber$ True | RememberRevealed$ True`
Parameters:
- RevealValid: to limit the valid cards.
- AnyNumber
- Random
- RememberRevealed: to remember the cards revealed
### PeekAndReveal
This AF is very similar to things that Dig can do, but handle a much
simpler form, with less complex coding underneath. Similar to how
RearrangeTopOfLibrary could be handled with Dig.
Primarily used with cards that allow you to Peek at the top card of your
library, and allow you to reveal it if it's of a certain type. The
Kinship cards fit this bill perfectly, so they are used to simplify the
complex popups that would be required if using multiple Dig
SubAbilities.
RevealOptional - Whether or not the Reveal is optional.
RememberRevealed - Whether to remember the revealed cards (after
filtering by Valid)
RememberPeeked - Whether to remember the peeked cards (only if they are
not revealed\!)
RevealValid - defaults to Card, but allows you to set a specific
ValidType if you can only have certain things
PeekAmount - defaults to 1, but allows you to peek at multiple cards if
possible
## RollDice
## Sacrifice
Usually you choose a player and that player has to sacrifice something
`A:SP$ Sacrifice | Cost$ 1 B | ValidTgts$ Player | SacValid$ Creature | SacMessage$ Creature | Amount$ 2 | SpellDescription$ Target player sacrifices a creature.`
Destroy$ True - An optional parameter for destroying permanents target
player chooses (eg: Burning of Xinye, or Imperial Edict).
`A:SP$ Sacrifice | Cost$ 1 B | ValidTgts$ Opponent | SacValid$ Creature | SacMessage$ Creature | Destroy$ True | SpellDescription$ Target opponent chooses a creature he or she controls. Destroy it.`
## Scry
`A:AB$ Scry | Cost$ 1 T | ScryNum$ 2`
## StoreSVar
## Token
Token simply lets you create tokens of any type.
`A:SP$ Token | Cost$ 3 W U | TokenImage$ W 1 1 Bird Flying | TokenAmount$ X | TokenName$ Bird | TokenTypes$ Creature,Bird | TokenOwner$ You | TokenColors$ Blue | TokenPower$ 1 | TokenToughness$ 1 | TokenKeywords$ Flying`
This ability factory does not take a target. All the parameters are
mandatory except for TokenKeywords. If you provide a non-integer for
TokenAmount, TokenPower or TokenToughness the AF will attempt to look for
an SVar of that name and interpret it's contents as a Count$ line. Worth
noting is that TokenTypes and TokenColors are simple commaseparated
lists while TokenKeywords is a list where the items are separated by
"\<\>". If TokenImage is not provided, the factory will attempt to
construct a filename on it's own. TokenOwner can use Defined-like
parameters, such as "You" "Opponent" or the new Triggered-Variables.
You can also use the parameters TokenAbilities$, TokenTriggers$ and
TokenSVars$ to give the created tokens any number of either. For
example, here's how Growth Spasm creates an Eldrazi Spawn token complete
with ability.
SVar:DBToken:DB$Token | TokenAmount$ 1 | TokenName$ Eldrazi Spawn | TokenTypes$ Creature,Eldrazi,Spawn | TokenOwner$ You | TokenColors$ Colorless | TokenPower$ 0 | TokenToughness$ 1 | TokenAbilities$ ABMana SVar:ABMana:AB$Mana | Cost$ Sac<1/CARDNAME> | Produced$ 1 | Amount$ 1 | SpellDescription$ Add 1 to your mana pool.
As another example, here's Mitotic Slimes' use of TokenTriggers$:
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigTokenSenior | TriggerDescription$ When CARDNAME is put into a graveyard from the battlefield, put two 2/2 green Ooze creature tokens onto the battlefield. They have "When this creature is put into a graveyard, put two 1/1 green Ooze creature tokens onto the battlefield."
SVar:TriggerJunior:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigTokenJunior | TriggerDescription$ When this creature is put into a graveyard, put two 1/1 green Ooze creature tokens onto the battlefield. SVar:TrigTokenJunior:AB$Token | Cost$ 0 | TokenImage$ g 1 1 ooze | TokenName$ Ooze | TokenTypes$ Creature,Ooze | TokenColors$ Green | TokenOwner$ You | TokenPower$ 1 | TokenToughness$ 1 | TokenAmount$ 2
## Trigger
If possible split the SpellDescription$ of the the effect so the part for the trigger can become the StackDescription directly.
### DelayedTrigger
### ImmediateTrigger
## Turn structure
### AddPhase
### AddTurn
`A:SP$ AddTurn | Cost$ 1 U | NumTurns$ 1 | SpellDescription$ Take an extra turn after this one.`
### EndTurn
### ReverseTurnOrder
### SkipPhase
### SkipTurn
## ZoneAffecting
For specific effects that handle zones in a specific manner
### ChangeZone
ChangeZone is a united front of any card that changes zone. This does
not include: drawing, discarding, destroying, or milling, as these
represent specific words on which triggers and replacements can react.
There are two primary forms, but the distinction is handled mostly in
the codebase. The only thing that is required is to set appropriate parameters.
Origin and Destination are both required.
Origin is where the card is coming from.
Destination is where the card is going to. If Destination is Library, a
LibraryPosition is recommended, but not required. **Default value of the
LibraryPosition is 0.** 0 represents the top of the library, -1 represents the bottom.
There are two primary versions of ChangeZone.
#### Hidden Origin
The first is hidden, generally used for Origin zones that are not known
information, like the Library or the Hand. The choice of "What card is
changing zones?" happens during resolution.
`A:SP$ ChangeZone | Cost$ W | Origin$ Library | Destination$ Library | LibraryPosition$ 0 | ChangeType$ Artifact,Enchantment | ChangeNum$ 1 | SpellDescription$ Search your library for an artifact or enchantment card and reveal that card. Shuffle your library, then put the card on top of it.`
`A:AB$ ChangeZone | Cost$ T | Origin$ Hand | Destination$ Battlefield | ChangeType$ Land | ChangeNum$ 1 | Optional$ True | SpellDescription$ You may put a land card from your hand onto the battlefield.`
For Hidden, things like ChangeType and ChangeNum are used to restrict
what can ChangeZone, and how many do. There are two parameters special
to Hidden Origin:
"Chooser" defines which player has to decide which card changes zone
(example You, Opponent).
"Mandatory" most of these abilities are not mandatory, but some are.
#### Known Origin
The second is known, generally used for Origin zones that are known
information, like the Battlefield or the Graveyard. The choice of "What
card is changing zones?" happens on activation, generally by targeting.
`A:AB$ ChangeZone | Cost$ 1 U T | TgtPrompt$ Choose target artifact card in your graveyard | ValidTgts$ Artifact.YouCtrl | Origin$ Graveyard | Destination$ Library | SpellDescription$ Put target artifact card from your graveyard on top of your library.`
`A:SP$ ChangeZone | Cost$ U U | ValidTgts$ Permanent | TgtPrompt$ Select target permanent | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return target permanent to its owner's hand.`
For Known, since it just uses Target, normal target parameters are used in this Scenario.
### ChangeZoneResolve
This is a helper AF, for chained effects that create multiple permanents which should enter the battlefield at the same time.
To use it, you need to set the param "ChangeZoneTable" on the first effect and then call this at the end.
This is supported by the following effects:
- Amass
- CopyPermanent
- RepeatEach (_NOTE: if you wrap the creation, you don't need to call this AF on its own_)
- Token
### Dig
Dig is for an ability that does basically this: "You look at the X cards
of your Library, put Y of them somewhere, then put the rest somewhere."
Think of Impulse.
- DigNum - Required - look at the top number of cards of your library.
- Reveal - Optional - for abilities that say "Reveal the top X cards
of your library". Default is false.
- SourceZone - Optional - the zone to dig in. Default is Library.
- DestinationZone - Optional - the zone to put the Y cards in. Default
is Hand.
- LibraryPosition - Optional - if DestinationZone is Library, use this
to specify position. Default is -1 (bottom of library).
- ChangeNum - Optional - the number of cards to move to the
DestinationZone (or "All" when it's for things like "put all lands
revealed this way into your hand"). Default is 1.
- ChangeValid - Optional - use this to specify if "you may move an
artifact to DestinationZone". Default is any Card.
- AnyNumber - Optional - use if you can move any number of Cards to
DestinationZone. Default is false. (think of Lead the Stampede)
- Optional - Optional - set this if you "may" move a card to
DestinationZone. Default is false.
- DestinationZone2 - Optional - the zone to put the rest of the cards
in. If it is library, you are prompted for the order. Default is
Library.
- LibraryPosition2 - Optional - if DestinationZone2 is Library, use
this to specify position. Default is -1 (bottom of library).
`A:AB$ Mill | Cost$ 2 T | NumCards$ 2 | ValidTgts$ Player | TgtPrompt$ Choose a player to mill | SpellDescription$ Target player puts the top two cards of his or her library into his or her graveyard.`
### RearrangeTopOfLibrary
### Shuffle
Used for shuffling a player's library
- Optional - Set this parameter if the user should be
prompted Yes/No to shuffle the given library. Default is false.
A reference guide for scripting cards using the API parsed by the Forge engine.
# Base Structure
By opening any file in the /res/cardsfolder folder you can see the basic structure of how the data is created.<br>
Here's an example of a vanilla creature:
```
Name:Vanilla Creature
ManaCost:2 G
Types:Creature Beast
PT:2/2
Oracle:
```
The name of this card is Vanilla Creature.<br>
It's casting cost is {2}{G}.<br>
It has the types Creature and Beast.<br>
It has a Power-Toughness of 2/2.<br>
It will not display any additional text in the card's template.<br>
If a card has two faces, use AlternateMode:{CardStateName} in the front face and separate both by a new line with the text "ALTERNATE".
There are a few other properties that will appear in many cards. These are
| Property | Description
| - | -
|`A`|[Ability effect](AbilityFactory)
|`AI`|RemoveDeck:<br/>* `All`<br/>This will prevent the card from appearing in random AI decks. It is applicable for cards the AI can't use at all like Dark Ritual and also for cards that the AI could use, but only ineffectively like Tortoise Formation. The AI won't draft these cards.<br/>* `Random`<br/> This will prevent the card from appearing in random decks. It is only applicable for cards that are too narrow for random decks like Root Cage or Into the North. The AI won't draft these cards.<br/>* `NonCommander`<br/>
|`Colors`|Color(s) of the card<br/><br/>When a card's color is determined by a color indicator rather than shards in a mana cost, this property must be defined. If no identifier is needed, this property should be omitted.<br/><br/>* `Colors:red` - This is used on Kobolds of Kher Keep, which has a casting cost of {0} and requires a red indicator to make it red.<br/><br/>* `Colors:red,green` - Since Arlinn, Embraced by the Moon has no casting cost (it's the back of a double-faced card), the red and green indicator must be included.
|`DeckHints`|AI-related hints for a deck including this card<br/><br/>To improve synergy this will increase the rank of of all other cards that share some of its DeckHints types. This helps with smoothing the selection so cards without these Entries won't be at an unfair disadvantage.<br/><br/>The relevant code can be found in the [CardRanker](https://git.cardforge.org/core-developers/forge/-/blob/master/forge-gui/src/main/java/forge/gamemodes/limited/CardRanker.java) class.
|`DeckNeeds`|This can be considered a stronger variant when the AI should not put this card into its deck unless it has whatever other type is specified. The way this works is "inverted": it will directly decrease the rank of the card unless other cards are able to satisfy its types.<br/>If a card demands more than one kind of type you can reuse it:<br/>`DeckNeeds:Type$Human & Type$Warrior` will only find Human Warrior compared to `DeckNeeds:Type$Human\|Warrior` which is either
|`DeckHas`|specifies that the deck now has a certain ability (like, token generation or counters) so that the drafting/deckbuilding AI knows that it now meets requirements for DeckHints/DeckNeeds. This is actually very useful since many of these (such as `Ability$Graveyard, Ability$Token, Ability$Counters`) are not deduced by parsing the abilities, so an explicit hint is necessary. Using the other types is also supported in case the implicit parsing wouldn't find it.<br/>It doesn't require exact matching to have an effect but cards that care about multiple entries for a given type will be judged higher if a card seems to provide even "more" synergy for it.<br/>Example:<br/>Chishiro has two abilities so `DeckHas:Ability$Token & Ability$Counters` is used, therefore score for `DeckNeeds:Ability$Token\|Counters` is increased
|`K`|Keyword (see below)
|`Loyalty`|Number of starting loyalty counters
|`ManaCost`|Cost to cast the card shown in mana shards<br/><br/>This property is required. It has a single parameter that is a mana cost.<br/><br/>* `ManaCost:no cost` for cards that cannot be cast<br/>* `ManaCost:1 W W` sets the casting cost to {1}{W}{W}
|`Name`|Name of the card<br/><br/>A string of text that serves as the name of the card. Note that the registered trademark symbol cannot be included, and this property must have at least one character.<br/><br/>Example:<br/>* `Name:A Display of My Dark Power` sets the card's name to "A Display of My Dark Power"
|`Oracle`|The current Oracle text used by the card.<br/><br/>We actually have a Python Script that runs to be able to fill in this information, so don't worry about manually editing a lot of cards when Wizards decides to change the rules. <br/><br/>This field is used by the Deck Editor to allow non-Legendary Creatures to be marked as potential commanders. Make sure "CARDNAME can be your commander." appears in the oracle text.
|`PT`|Power and toughness
|`R`|[Replacement effect](Replacements)
|`S`|[Static ability](static-abilities)
|`SVar`|String variable. Used throughout scripting in a handful of different ways.
|`T`|[Triggered ability](Triggers)
|`Text`|Additional text that needs to be displayed on the CardDetailPanel that doesn't have any spell/ability that generates a description for it, for example "CARDNAME can be your commander." or "X can't be 0.".
|`Types`|Card types and subtypes<br/><br/>Include all card types and subtypes, separated by spaces.<br/><br/>Example:<br/>* `Types:Enchantment Artifact Creature Golem` for a card that reads Enchantment Artifact Creature -- Golem
Rarity and Set info are now defined in edition definition files. These can be found at /res/reditions path.
## Conventions
- filename: all lowercase, skip special characters, underscore for spaces
- Unix(LF) line endings
- use empty lines only when separating multiple faces on a card
- try to avoid writing default params to keep scripts concise
- e.g. just `SP$ Draw` instead of `SP$ Draw | Defined$ You | NumCards$ 1`
# Keywords
All keywords need to be prepended with "K:" to be parsed correctly. Each keyword must appear on a separate line.
## Keywords without Parameters
This section is for Keywords that require no additional parameters and are one or two words long. Most of these you would see exactly on cards in the game.<br>
Cost is a class that attempts to streamline costs throughout all cards. It requires that each cost is separated by a space. I will use examples that could be found in Ability, although certain Keyworded abilities do use Cost too.
# Common
## Description
Description is an optional last parameter in the cost. This is to allow
for complex Type definitions to have a nice Description that is readable.
## CostDesc / PrecostDesc
## UnlessCost
UnlessCost allows the player specified with UnlessPayer (same as
Defined, defaults to TargetedController) to pay mana to prevent the
resolving of the ability. If the script has the param "UnlessSwitched",
then the player pays mana to resolve the ability (usually used to handle
"any player may pay ..." ).
## XChoice
XChoice is the variable that basically means "You can choose whatever
you want for this variable. But you need to decide what X is before you
start paying." This would commonly appear as an SVar definition of X.
## xPaid
xPaid is the amount of Mana Paid for an X Cost. There are a few cards
that will use the X Payment to determine other costs (like Abandon Hope)
This would commonly appear as an SVar definition of X.
## CARDNAME
For Costs that do something to themselves (ex. Discard Self, Sacrifice
Self)
# Types of Cost
## Discard
Discard has two required parameters and one optional in the form
Discard<Num/Type/Description>
- The first is how many cards are being discarded.
- The second is what card types can be discarded. (Hand for the whole
hand, Random for chosen randomly)
## Draw
## Exert
## Exile
Exile has two required parameters and one option in the form of
Exile<Num/Type/Description>
There are also a few sister abilities that all fit under the Exile
umbrella.
- Exile (for cards on the Battlefield)
- ExileFromGraveyard
- ExileFromHand
- ExileFromTop (for cards on top of your library, this doesn't default
Type to Card, so make sure you add it)
Some Examples
- Exile<1/Creature>
- Exile<1/CARDNAME>
- ExileFromHand<1/CARDNAME>
- ExileFromHand<2/Creature>
- ExileFromGrave<1/CARDNAME>
- ExileFromGrave<1/Treefolk>
- ExileFromTop<10/Card>
## FlipCoin
Only used by "Karplusan Minotaur".
## Mana
- Cost$ 2
- 2 colorless mana
- Cost$ B R
- 1 black and 1 red mana
- Cost$ WG
- Hybrid White/Green mana
- Cost$ S
- Snow Mana
- Cost$ Mana<2\\Creature>
- 2 colorless produced by a source with type 'creature'. Note the
backslash - it was chosen because hybrid costs already use slash
Here's some examples:
- Discard<1/Card>
- "Discard 1 Card"
- Discard<0/Hand> (The number is ignored when Hand is used as a
type.)
- Discard your hand
- Discard<2/Random>
- Discard 2 Cards at Random
- Discard<1/CARDNAME>
- Discard Self (CARDNAME)
- Discard<1/Creature.Black/Black Creature>
- Discard 1 Black Creature
## Mill
## Subtract(Remove) Counter
SubCounter has two required parameters in the form of
SubCounter<Num/CounterName>
- SubCounter<2/P1P1>
- SubCounter<1/CHARGE>
Remember the token name should appear all in caps.
As third parameter you can use a ValidCard.
## Sacrifice
Sacrifice has two required parameters and one optional parameter in the
form of Sac<Num/Type/Description>
- Sac<1/Artifact>
- Sac<1/CARDNAME>
## Tap
- Cost$ T
## Untap
- Cost$ Untap
\- or -
- Cost$ Q
## Unattach
## PayEnergy
## PayLife
PayLife has one required parameter in the form of PayLife<Num>
- PayLife<2>
## GainLife
## TapXType
TapXType has two required parameters and one option in the form of
tapXType<Num/Type/Description>
- tapXType<3/Creature.White>
## Return
Return has two required parameters and one optional in the form of
Return<Num/Type/Description>
- Return<1/Land>
- Return<1/CARDNAME>
## Reveal
# Putting it Together
Putting it together is pretty simple. If a card needs to pay mana and tap, it would look like this:
- Cost$1 W T
For a spell that has an additional cost of sacrificing a land, put the
mana cost and the additional cost in the cost:
- Cost$2 G Sac<1/Land>
One of the features of Cost is you can have more than one of the same Cost type:
- Cost$ Sac<1/Swamp> Sac<1/Creature>
There are many examples, but they mostly fall into those categories.
Replacement create replacement effects, as you'd expect. Their script
follows the format introduced by AbilityFactory; simply begin a line
with "R:", to indicate that it's for a replacement effect, followed by a
collection of name-value pairs (name and value are separated by $)
separated by pipes (|). All Replacement effects expect an "Event$"
parameter, which declares what event should be replaced. Most replacement effects will also have a "ReplaceWith$" parameter which points to an SVar which contains what should replace the event. They may have a "Prevent$True" parameter, instead though, which means that nothing happens instead of the event.
Similarly to triggers, the replacing code can access special variables
pertaining to the event it replaced (like triggered-variables). These
are specific to each event, and is listed below. Most replacement effects
will also have a "Description$" parameter which is simply the card text
for the ability.
## DamageDone
DamageDone events are checked when damage is about to be assigned to a
card or player. There are 5 special parameters:
- ValidSource - The damage source must match this for the event to be
replaced.
- ValidTarget - The damage target must match this for the event to be
replaced.
- DamageAmount - The amount of damage must match this.
- IsCombat - If true, the damage must be combat damage, if false, it
can't be.
- IsEquipping - If true, the host card must be equipping something.
There are 3 Replaced-variables:
- DamageAmount - The amount of damage to be assigned.
- Target - The target of the damage.
- Source - The source of the damage.
## Discard
Discard events are checked when a player is about to discard a card.
There are 4 special parameters:
- ValidPlayer - The player who would discard must match this.
- ValidCard - The the card that would be discarded must match this.
- ValidSource - The card causing the discard must match this.
- DiscardFromEffect - If true, only discards caused by spells/effects
will be replaced. Cleanup/statebased discards will not.
There are 2 Replaced-variables:
- Card - The card that would be discarded.
- Player - The player that would have discarded
## Draw
Draw events are checked when a player is about to draw a card. There is
1 special parameter:
- ValidPlayer - The player who would draw must match this.
There are no Replaced-variables.
## GainLife
GainLife events are checked when a player would gain life. There is 1
special parameter:
- ValidPlayer - The player who would gain life must match this.
There is 1 Replaced-variable:
- LifeGained - The amount of damage to be gained.
## GameLoss
GameLoss events are checked when a player would lose. There is 1 special
parameter:
- ValidPlayer - The player who would lose must match this.
There are no Replaced-variables.
## Moved
Moved events are checked when a card would be moved between zones. There
are 3 special parameters:
- ValidCard - The moving card must match this.
- Origin - The card must be moving from this zone.
- Destination - The card must be moving to this zone.
For example: No Rest for the Wicked and the like would use:
`AB$ ChangeZone | Cost$ Sac<1/CARDNAME> | Defined$ ThisTurnEntered Graveyard from Battlefield Creature.YouCtrl | SpellDescription$ Return to your hand all creature cards in your graveyard that were put there from the battlefield this turn.`
### Targeted
Targeted will only appear on a [SubAbility][]. It means "Do this action
to whatever a parent Ability targeted"
That may sound confusing so here's an example.
If you had a spell that says "Untap target Creature. It gains +1/+1
until end of turn" it would look like similar to this.
Depending on which Mode is specified, other parameters may be expected.
Below are the currently available modes.
The script has access to many things that were previously internal to
triggers. These things are accessed via Triggered-variables. Triggered
variables are always of the form "Triggered<VariableName>\[Controller/Owner\]" and are specific to each trigger mode. You can use Triggered-variables that return a card or a player directly in Defined$ parameters or to grab extra info from (like you use "Targeted" for, for instance "SVar:X:TriggeredCard$CardPower").
You can get the controller or owner of a card returned by a Triggered-variable by appending "Controller" or "Owner" to the variable. Triggered-variables that return an integer can only be accessed from Count$, i.e. "SVar:X:Count$TriggeredLifeAmount".
Other parameters that triggers can use are:
- Secondary - If a trigger has Secondary$ True set, it means that it's
trigger description won't show up in a card's text box. This can be
used if you need to use several triggers to emulate a single effect
on a real card.
- Static - This parameter is mainly used for "As CARDNAME enters the
battlefield..." type things. It causes the triggered ability to
resolve right away, instead of going on the stack.
- ResolvingCheck
- NoResolvingCheck - makes a trigger not recheck its condition to resolve
## Always
Always-triggers represent State triggers, a special kind of triggers. These triggers will not do any checks for intervening if-clauses and will not go on the stack if an instance of it has already been put there. They are checked every time state effects are checked.
Examples: Emperor Crocodile.
There are no special parameters and no triggered-variables.
## Attached
Attached-triggers go off when a card is attached, via enchanting or
equipping, to another card.
Examples: Bramble Elemental, Kithkin Armor.
There are 2 special parameters:
- ValidSource - The card that is being attached to another must match
this for the trigger to go off.
- ValidTarget - The card that is having another card attached to it
must match this.
There are 2 Triggered-variables:
- Source - The card that is being attached.
- Target - The card that is being attached to.
## AttackerBlocked
AttackerBlocked-triggers go off when a creature becomes blocked. It goes
off only once (no matter how many blockers there are) right after the
declare blockers step.
Examples: AEther Membrane, Alley Grifters.
There are 2 special parameters:
- ValidCard - The attacking creature must match this for the trigger
to go off.
- ValidBlocker - The blocking creature must match this for the trigger
to go off.
There are 3 Triggered-variables:
- Attacker - The card of the attacking creature.
- Blocker - The card of the blocking creaure.
- NumBlockers - The number of things blocking the attacker
## AttackerUnblocked
AttackerUnblocked-triggers go off when a creature attacks and is not
blocked, right after the declare blockers step.
Examples: Abyssal Nightstalker, Dauthi Mindripper
There is 1 special parameter:
- ValidCard - The attacking creature must match this for the trigger
to go off.
There is 1 Triggered-variable:
- Attacker - The card of the attacking creature.
## AttackersDeclared
Goes off after attackers are declared, if any attackers were declared,
once a combat only.
Examples: Lightmine Field, Curse of Inertia. There are 2 special
parameters:
- AttackingPlayer - The attacking player must match this.
- AttackedTarget - One of the game entities in TriggeredAttackedTarget
must match this.
There are 3 Triggered-variables:
- Attackers - Collection of attacking creatures.
- AttackingPlayer - The targeted object.
- AttackedTarget - Collection of game entities that each attacker is attacking.
## Attacks
Attacks-triggers go off when a creature attacks. That is, it goes off
once for each creature that attacks during your each combat phase (Right
after the declare attackers step).
Examples: Accorder Paladin, Trepanation Blade.
There are 2 special parameters:
- ValidCard - The attacking creature must match this for the trigger
to go off.
- Alone - If this is True, the trigger will only go off if the
creature attacks alone.
There is 1 Triggered-variable:
- Attacker - The card of the attacking creature.
## BecomeMonstrous
BecomeMonstrous-triggers go off when a creature becomes Monstrous,
naturally.
Examples: Arbor Colossus, Ember Swallower.
There is 1 special parameter:
- ValidCard - The card that becomes monstrous must match this.
There is 1 Triggered-variable:
- Card - The card that became monstrous.
## BecomesTarget
BecomesTarget-triggers go off when a spell or ability (or either) is put
on the stack targeting something that matches a Valid-expression.
Logical screen with/height, changing this would require to change all ui elements and won't increase resolution.
## **skin**
path to the used skin for adventure
## **playerBaseSpeed**
base speed of player character
## **minDeckSize**
minimum deck size for matches, decks with lesser cards will be filled with wastes.
## **starterDecks**
string list of all starter decks
## **restrictedCards**
string list of restricted cards, those cards won't appear in random shops or rewards but it it still possible to get those cards if the plane specifically drops it.
## **restrictedEditions**
string list of restricted editions, behaves the same as restricedCards but with editions.
## **difficulties**
list of DifficultyData
## **legalCards**
RewardData for legal cards, behaves similar as restrictedCards only as white list and not black ist.
Also it is defined as RewardData see [Create-Rewards](https://github.com/Card-Forge/forge/wiki/Create-Rewards) for syntax
In order to edit which sets will show up in random rewards and shop you will need to edit the config file. You can find it here "forgefolder"\res\adventure\common\config.json. Add the sets you want to have restricted to the "restricted edition" section
Forge provides an in-game console in adventure mode.
You can access (and close) the console while exploring by pressing F9 (or Fn-F9).
To scroll the console window, click and drag the text box.
## Available commands
| Command Example | Description |
| -- | -- |
| resetMapQuests | Resets the map quests, resulting in all side-quest progress being lost and all side-quest types being re-picked |
| give gold 1000 | Give 1000 gold |
| give shards 1000 | Give 1000 shards |
| give print lea 232 | Add an alpha (LEA set code) black lotus (232 collector number) |
| give item <itemnameorcode?> | Adds an in game item such as leather boots |
| give set sld | Give 4 copies of every card in the Secret Lair Drop (set code SLD), flagged as having no sell value |
| give nosell card forest | Gives a forest with no sell value |
| give boosters leb | Add a booster from beta (LEB set code) |
| give quest 123 | Add the quest by its number ID |
| give life 10 | Add 10 life to yourself |
| give card forest | Adds a forest to your inventory |
| debug collision | Displays bounding boxes around entities |
| debug map | TODO |
| debug off | Turns off previously enable debugging |
| teleport to 6000 5000 | Moves you 6000 tiles east and 5000 tiles north from the left bottom corner |
| fullHeal | Returns your health back to baseline |
| sprint 100 | Increases your speed for 100 seconds |
| setColorId R | Sets the player color identity; Probably used for testing and shops |
| clearnosell | Clears the no sell value flag from all cards you own that are not used in a deck |
| remove enemy abc | Remove the enemy from the map with the map ID abc |
| remove enemy all | Remove all the enemies from the map |
| dumpEnemyDeckList | Print the enemy deck lists to terminal output stream |
| getShards amount 100 | Similar to give shards command; Gives 100 shards to the player |
| resetQuests | Resets the world quests. In progress quests are not abandonned or reset including dungeons. Does not reroll abandoned quests. Not really sure what this does. |
| hide 100 | Enemies do not chase you for 100 seconds |
| fly 100 | You can walk over obstacles for 100 seconds |
| crack | Cracks a random item you are wearing |
| spawn enemy Sliver | Spawns a Sliver on your screen |
| listPOI | Prints all locations in terminal output stream as ID-type pairings |
| leave | Gets you out of the current town/dungeon/cave |
| dumpEnemyColorIdentity | Prints all enemies, their colour affinity and deck name to terminal output |
| heal | Recover your full health |
| dumpEnemyDeckColors | Prints all decks available to enemies and their affinities |
All Enemies are stored under `res/<AdventureName>/world/enemies.json`
Enemies spawned on the overworld map or on map stages will use this exact template to define their base behavior. These values can be modified or added to with additional settings on an individual enemy basis, details of which can be found within [map instance](Create-new-Maps).
Some ideas for custom enemy cards:
- basic (CR or at least Alchemy conform) effects for normal mobs to add flavor
- advanced + all UN stuff. but their mechanics could be combined in new ways not seen in print
- boss/rare ones that are balanced around meta mechanics (=quests)
The Json file contains an Array of Objects and each Object is one enemy.
Name of the enemy, every time an other object will use an enemy, it will refer to this name.
## **nameOverride**
String - If provided, this will be displayed in any references to this enemy in the game. If not provided, Name will be used.
## **sprite**
String - Path to the sprite atlas for the enemy (from `res/<AdventureName>`)
In fights against the enemy, the sprite under "Avatar" will be used as avatar picture.
Every sprite under
"Idle","Walk","Attack","Hit","Death"
will be used as animation for the corresponding action.
direction can be added to alter the animation depending on the direction like "IdleRight"
Supported directions are "Right","Left","Up","Down","RightDown","LeftDown","LeftUp","RightUp"
## **deck**
Array of strings containing paths to the decks used for this enemy (from `res/<AdventureName>`)
If no decks are defined then the enemy will act like a treasure chest and give the rewards without a fight.
(only for enemies in dungeons)
The format for the deck file can be the normal forge *.dck syntax or a json file that will behave like a collection of [rewards](Create-Rewards) to get a random generated deck.
## **randomizeDeck**
Boolean - if true then the enemy deck will be randomly selected from the deck array. If false, an algorithm will select a deck in sequential order based on the player's prior win/loss ratio against that opponent (discouraged and currently unused due to wild swings in ratio at low game count).
## **ai**
String - Currently unused, this appears to be intended to allow different playstyles to be associated with this enemy.
## **boss**
Boolean - Not used to any great extent at this time, but a value of true in this field indicates that this is a boss-level enemy for which the match presentation can be changed if desired. Currently, this causes capable Android devices to vibrate for a longer period on collision with the enemy sprite.
## **flying**
Boolean - If true, this enemy ignores terrain collisions and can travel freely in their intended movement direction.
## **spawnRate**
Decimal - Relative frequency with which this enemy will be picked to spawn in appropriate biomes (which are set in the biome json file). Existing values range from 0 to 1.0.
## **difficulty**
Decimal - Relative estimated difficulty associated with this enemy. Currently unused, but will likely be factored in as a part of filtering enemies into early/late game appropriate opponents. Existing values range from 0 to 1.0.
## **speed**
Integer - Movement speed of this enemy in overworld or on a [map instance](Create-new-Maps). For comparison, the player's base speed is set at a value of 32 (before any equipment / ability modifiers).
## **scale**
Decimal - Default 1.0. For enemies whose sprites are too large or small for their intended usage, this serves as multiplier for the enemy's visual dimensions & collision area. By default, we work with 16x16 pixel sprites for most entities - this can be replicated with a more detailed 32x32 sprite by setting a scale of 0.5 for the enemy entry.
## **life**
Integer - Base starting life total. This is modified universally by a value determined by the player's chosen difficulty, and can be adjusted further at the enemy object level on [map instances](Create-new-Maps).
## **rewards**
Array - A collection of the rewards to be granted for defeating the enemy.
see [Create Rewards](Create-Rewards) for the syntax.
## **equipment**
Array - A collection of strings representing [equipment items](adventure-items) normally intended for player use that this enemy will have. Not used widely, usually when an enemy will drop that [equipment](adventure-items) and it does not use [mana shards](mana-shards).
## **colors**
String - Any combination of "B" (Black), "U" (Blue), "C" (Colorless), "G" (Green), "R" Red, and "W" (White). Used to display color identity alongside the sprite when an active ability allows it.
## **questTags**
Array- A collection of strings associated with this entity for filtering in regards to quests.
Rewards represent anything that can be obtained by the player in Adventure Mode. Cards are the most common type of reward, but rewards can also represent a pieces of equipment, gold, mana shards, maximum life increases, keys, or generic items to be interacted with via dialogs and quests.
Rewards are associated with...
* Enemies - The loot they drop when defeated.
* Lootable items - Treasure chests, piles of gold, spellbooks, or mana shards on dungeon maps.
* Shops - The items available for purchase.
* Dialog - Items given to the player during a dialog sequence OR required in order to have access to an option.
* Quests - Rewards for completion of a quest.
* Events - Rewards for completion of an event.
As a multipurpose concept, Reward data has a large number of fields, but almost all are optional and no one reward will use all of the fields.
The expected fields and behavior for the rewards are set based on the reward's `type` value. This is the one field that all reward data must contain, as it tells the game what other fields to expect data in and how to process them.
The simplest types are `gold` (give the player X gold), `shards` (give the player X [mana shards](mana-shards), and `life` (increase the player's current and maximum life total by this amount [given very sparingly]). Beyond their types, these three items only require a `count` field. Optional fields for these types include `probability` and `addMaxCount`, (see full list at bottom). An example of a `gold` reward that will grant the player 50 gold follows:
```json
{
"type":"gold",
"count":50
}
```
`Item` rewards are slightly more specific, but still relatively simple, requiring `type`, `count`, and EITHER `itemName` or `itemNames`, examples of both follow. Optional parameters again include `probability` and `addMaxCount`. If `itemName` is specified, then `count` copies of the named item will be granted to the player. If `itemNames` is specified, `count` copies of items randomly selected from the list of named items provided will be granted instead.
`Card` rewards are potentially the most complex from a definition standpoint, but almost all of the fields are optional. A simple and a complex example follow below.
Granting the player one completely random card that is legal for use in Adventure:
```json
{
"type":"card",
"count":1
}
```
As a contrasting and complicated example that will return no matching results, consider the card reward data below. 80% of the time, this will try to grant the player 1-9 (modified further by difficulty) cards that are rare, multicolor, contain both red and blue in their color identity, are from M21 or M22 editions, are either instants or creatures, have the Elf subtype, contain the word "dragon" in the card name, contain the word "destroy" and/or "exile" in the card text. Details on the individual fields can be found in the reference list at the bottom of this page.
```json
{
"type":"card",
"probability":0.8,
"count":1,
"addMaxCount":8,
"colors":["red","blue"],
"rarity":["rare"],
"editions":["M22","M21"],
"cardTypes":["Instant","Creature"],
"colorType":"MultiColor",
"subTypes":["Elf"],
"superTypes":["Legendary"]
"cardName":"dragon",
"cardText":"destroy|exile"
}
```
`Union` reward types are a purely structural element. This reward type does nothing on its own, but it is used as wrappers for multiple `card` instances that has distinctly separate parameters. Required elements are `type` (Union), `count`, and `cardUnion` (which contains additional `card` reward definitions). Optional parameters are, once again `addMaxCount` and `probability`. As an example, the following would award the player with a single red dragon from M21 OR a single green sorcery from M22, but never a red sorcery, M22 dragon, or so on. The individual card pools are conjoined, giving an equal chance of all possible cards across the pools.
```json
{
"count":1,
"type":"Union",
"cardUnion":[
{
"count":1,
"editions":["M21"],
"subTypes":["Dragon"],
"colors":["red"]
},
{
"count":1,
"editions":["M22"],
"cardTypes":["Sorcery"],
"colors":["green"]
}
]
}
```
# Fields:
## **type**
Defines the type of reward.
Valid options are:
*`gold` will reward the player with gold.
*`life` will increase the maximum life of the player.
*`shards` will reward the player with [mana shards](mana-shards).
*`item` will give items to be added to the player's inventory.
*`card` will create one or more cards matching a detailed set of filters to follow.
*`union` is a wrapper for multiple `card` instances that can have mutually exclusive filters.
*`deckCard` is only used with rewards from [enemies](Create-Enemies), this functions as a `card` reward that is limited to cards found in that enemy's deck.
`{"type": "card", ...}`
## **probability**
The probability of this reward being given out, on a decimal scale from 0 to 1. (Defaults to 1 if not provided)
`{..., "probability": 0.5, ...}`
## **count**
If given at all (see `probability`, above), get at least this many of the reward.
`{..., "count": 10, ...}`
## **addMaxCount**
If given at all, an additional "addMaxCount" instances of the reward will be added. A pre-set multiplier based on the chosen difficulty is applied to this parameter. On normal difficulty, you will get the reward "count" to "addMaxCount" times, `{"type": gold", "count":10, "addMaxCount":5"}` would give anywhere from 10 to 15 gold. (Defaults to 0 if not provided)
`{..., "addMaxCount": 5, ...}`
## **colors**
An array of the possible colors for `card` and `deckCard`.
`{..., "colors": ["red", "black"], ...}`
## **rarity**
An array of the possible raritys for `card` and `deckCard`.
A regular expression defining text that must appear on the card. A sequence of plain text alphanumeric (A-Z, 0-9) characters can function here if you are unfamiliar with regular expressions, but many special characters will change the functionality. Useful for denoting keywords or identifying helper cards that are associated with but do not actually have a given type.
`{..., "cardText": "reveal the top card of", ...}`
`{..., "cardText": "cast (an instant|a sorcery) from your hand", ...}`
## **deckNeeds**
This is a functional but partially implemented concept at best, as the result set is currently limited. Card scripts can be tagged as having certain attributes that are used for creating synergies in decks constructed by the game. If/when this feature is expanded to more of our script library, the using those tags will become more and more useful to use for defining rewards so as to allow "concepts" to be awarded as opposed to specific card names or text contents.
`{..., "deckNeeds": ["Ability$Graveyard"], ...}`
## **cardPack**
This element should not be seen or used in JSON definitions of reward data. `cardPack` rewards are a type that represent a collection of cards whose contents have been pre-determined in game logic (such as a drafted deck that can be kept after an event), but that were not pre-determined at the time when the reward data was written. To manually implement granting specific or randomized cards we should use one or more `card` rewards, detailed above. When granted, this will create an item in the player's inventory that can subsequently be opened by the player to award the individual card contents of the associated deck.
This will allow you to edit the maps and tile sets.
To interact with the player, objects needs to be added to the Objects layer.
Objects templates are stored in the "obj" folder, but are not necessary.
Impotent are the types of the object and his properties.
## Object types
# enemy
will spawn an Enemy on the map. On collide with the player a magic duel will be started.
If the player win, the enemy will be removed from the map and the player will get the reward.
If the player loose, then the player will move 1 step back and receive the standard penalty.
Loot is also defined as enemy without a deck, then the player will receive the reward right away.
Properties:
`enemy` name of the enemies
# shop
Will spawn an shop on the map. On collide the player will enter the shop.
Properties:
`shopList` List of possible shop, leave it empty for all shops.
`signXOffset` x offset for the shop sign.
`signYOffset` y offset for the shop sign.
# inn
Will spawn an inn the map. On collide the player will enter the inn.
Properties:
# entry
Will be used as the map entry and exit. On collide the player will be teleported to an other map or the over world.
Properties:
`direction` the position where to spawn. up means the player will be teleported to the upper edge of the object rectangle.
`teleport` The map where the player gets teleported. If the property is empty, then the player will be teleported to the over world.
`teleportObjectId` the object id where the player will be teleported. If empty then it will search for an entry object, that would teleport the player back to the source map.
Using the Forge API you can create your own custom cards and sets. This guide will walk you through the process of creating custom cards via the Force Custom Card Script.
The next tutorial will walk you through the process of adding your new cards to a custom set.
Create a new .txt file using all lowercase letters. Spaces are represented with "_" and you can leave out special characters like commas or apostrophes.
> goblin_card_guide.txt
Now we need to define our card. Add the following to your Goblin_Card_Guide text file:
```
Name:Goblin Card Guide
ManaCost:1 R
Types:Creature Goblin
PT:2/2
K:Haste
Oracle:Haste
```
Let's break our card down:
- Name - The name as it appears on the card.
- ManaCost - The card's cost with colorless mana first and spaces between different mana symbols.
- Types - The card's type and then any subtypes seperated by spaces.
- PT - Power and Toughness, which is only used for Creatures (or some cards that turn into creatures like Vehicles)
- K - A Keyword that gives our creature an ability.
- Oracle - The actual text that appears on that card.
For a refrence of possible fields and their descriptions please see the [Card Scripting API](Card-scripting-API) document. Note that maintaining an up to date list of every ability of every Magic Card ever printed is not feasable. The API is meant to serve as a guide and includes most common used information.
3. Add Card to a Set
Now that we have a new card we need to add it to a set so that it will be included in the game. For the purposes of this turorial we'll add out card to an existing set. If you wish to create your own set you can follow this guide: `Guide Coming Soon`
Navigate to your Custom Editions folder and open the text file for the set you would like to add your card to. Let's add our card to the "DCI Promos" set.
In the "CDI Promos.txt" file we can see a list of the cards in the set. Add a new entry at the end of the list for our new card:
```
78 R Circle of Flame @James Paick
79 U Stormblood Berserker @Greg Staples
80 R Dungrove Elder @Scott Chou
81 R Goblin Card Guide @Forge Team
```
The first number is the collector number for the card in the set. This must be unique.
The next field is the card's rarity in the set - we've made our card a rare.
Next is the card's name and finally the name of the artist for the cards artwork.
Speaking of which - our card doesn't have any artwork. Let's fix that.
4. Add Card Image
Open your Card Images folder. Find the code for the set you added your new card to and open the corrosponding subfolder. For us this will be:
So we've created a creature card and added it to the game. However our creature is a little... boring, so let's update it to have a triggered ability.
"Whenever Goblin Card Guide deals damage to an opponent, draw a card."
Open the txt file again:
> goblin_card_guide.txt
And update the contents to the following:
```
Name:Goblin Card Guide
ManaCost:1 R
Types:Creature Goblin
PT:2/2
K:Haste
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Opponent | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Whenever CARDNAME deals damage to an opponent, draw a card.
SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1
Oracle:Haste
\nWhenever Goblin Card Guide deals damage to an opponent, draw a card.
```
Let's take a look at our changes:
- T:Mode$ DamageDone - Trigger when Damage is Done.
- ValidSource$ Card.Self - The source of the trigger. "Self" means our our card must be the source of the damage for the trigger to occur.
- ValidTarget$ Opponent - The target of the trigger. "Oppenent" means our oppenent must receive damage for the trigger to occur.
- TriggerZones$ Battlefield - Means the card must be on the battlefield for the trigger to occur.
- Execute$ TrigDraw - What happens then the trigger occurs - in this case trigger a Draw effect.
- SVar:TrigDraw:DB$ Draw - A String Variable used by the script. This one takes the Triggered Draw (TrigDraw) and tells the script to draw a card. "DB$ Draw" means "Drawback" "Draw".*
- Defined$ You - Who is drawing the card(s), in this case the controller of the card.
- NumCards$ 1 - Draw 1 card. This could be changed to any number to trigger drawing that many card.
- TriggerDescription$ - The description of the trigger.
*"Drawback" is a connotation that has since been replaced with "SubAbility".
The original connotation differentiated between AB (Ability), SP (Spell) and DB (Drawback).
AP and SP require costs, whereas Drawback do not. The card scripts still use the "DB$" connotation.
6. Updating our Image
Finally since we createda new effect on our card, we need to update the image. Card images are simply .jpg files and don't update or read from any scripts or gamefiles.
I used the same online card creator to make the change to our card:
You can check the [Abilities](AbilityFactory) and [Triggers](Triggers) documentation for more information on this topic. These documents are meant as a guide and are unlikely to contain information about every ability in the game.
The sinplest method for creating an effect on your card is to find another card that does the same thing and copying the ability. These can be found in your Forge folder:
>./Forge/res/cardsfolder/cardsfolder.zip
Unzipping this file will sllow you to search for any card in the containing subfolders.
This is a tutorial to start creating your own custom set, or implementing an existing one. We'll take you step by step into implementing a few cards from [MSEM Champions](https://msem-instigator.herokuapp.com/set/MPS_MSE). This is a basic guide to help you get started.
**Note:** This tutorial is currently for **Windows only**.
## Where do the files go?
### Non-image files
Everything but your cards images go into `%appdata%/Forge/custom`. You will need to put the files in the correct directories in order for the game to load them correctly.
The important folders are the following (you can create them if they don't exist):
* **cards**: Your card rules (logic) go inside this folder (.txt). I suggest you create subfolders inside it to keep everything clean.
* **editions**: Your editions (sets) definition files (.txt) go inside this folder.
* **tokens**: Your tokens definition files (.txt) go inside this folder.
### Image files
Your card and token images go into `%localappdata%/Forge/Cache/pics`.
The important folders are the following:
* **cards**: This is where you will put your card images. You'll want to create a folder with the Code of your edition. The images are named using this convention `Card Name.full.jpg`. If you have multiple art for the same card (something like basic lands, or maybe alternate art of the same card), then you can name them `Card Name1.full.jpg`, `Card Name2.full.jpg`, and so forth.
* **tokens**: Same as the cards folder, your tokens will go inside this folder. The naming convention is `token_script_name.jpg`. So if your token script name is `b_5_5_golem_trample`, then you can put your token image inside your edition folder named `b_5_5_golem_trample.jpg`. If there is a collector number, append it at the beginning, such as `1_b_5_5_golem_trample.jpg`
## Creating your edition definition file
As mentioned in the opening section, we'll be partially implementing the **MSEM Champions** set. Let's create a new text file (.txt) inside `%appdata%/Forge/custom/editions`. Let's name it `MSEM Champions.txt`.
> Note: The file's name don't matter, but it'll be easier to find it if you ever need to edit anything.
Let's paste the following inside it:
```
[metadata]
Code=MSEM_CHAMPIONS
Name=MSEM Champions
Date=2017-10-01
Type=Custom
[cards]
7 M Master Chef
33 M Golden Touch
34 M Avatar of Basat
35 M Exeunt
62 M Unearth
78 M Fox of the Orange Orchard
78★ S Fox of the Orange Orchard
107 M Inked Summoner
107★ S Inked Summoner
130 M Plains
131 M Island
132 M Swamp
133 M Mountain
134 M Forest
[tokens]
b_1_1_bird_flying
b_3_3_cat_deathtouch
b_5_5_golem_trample
[CreatureTypes]
Artist:Artists
```
Let's break it down.
```
[metadata]
Code=MSEM_CHAMPIONS
Name=MSEM Champions
Date=2017-10-01
Type=Custom
```
The **[metadata]** section contains the information about your edition.
* **Code** is an **unique** identifier for your edition.
* **Name** is the name of your edition.
* **Date** is the date the set was first released/created.
* **Type** should be `Custom` as Forge do things differently for them, including but not limited to skipping the automatic download of the images.
```
[cards]
7 M Master Chef
34 M Avatar of Basat
35 M Exeunt
62 M Unearth
78 M Fox of the Orange Orchard
78★ S Fox of the Orange Orchard
107 M Inked Summoner
107★ S Inked Summoner
130 L Plains
131 L Island
132 L Swamp
133 L Mountain
134 L Forest
```
The **[cards]** section contains the cards of your edition. Any card appearing under this section has a chance to appear as a reward and in shops.
Each line is as follow: `CollectorNumber Rarity CardName @ArtistName`.
* The collector number should be unique in a given set. You can see some numbers have a ★ next to their name. In this case it denotes an alternate art of a card, but it could also have been a different number altogether.
> Note: While it generally doesn't matter what collector number you use, avoid using the collector number F followed by only digits (ie. `F001`) as it represents a "Funny" card inside a normal edition. (Think of Funny cards as Un- sets cards)
* The rarity is a one letter representation. It can be L (Basic Land), C (Common), U (Uncommon), R (Rare), M (Mythic Rare), S (Special).
* The card name is self-explanatory. It should match the name of the corresponding card rule. (More of that later)
* The artist is optional, but it should be `@Artist Name` if present. We'll omit it in this tutorial.
> Note: You can put the cards in the list even if they aren't scripted yet. Forge will skip over them.
```
[tokens]
b_1_1_bird_flying
b_3_3_cat_deathtouch
b_5_5_golem_trample
```
The **[tokens]** section is optional, and only needed if you want to use specific token images in this set. They should be named using the name of their token script. `b_1_1_bird_flying` means it is a black 1/1 bird with flying. More on that later.
If you load the game with just file, you'll be able to see that Master Chef, Unearth and the basic lands can already be found in game. That's because they share a name with existing Magic the Gathering cards. **However**, [Master Chef](https://msem-instigator.herokuapp.com/card?q=Master+Chef) from MSEM and [Master Chef](https://scryfall.com/card/clb/241/master-chef) from MTG are two different cards! You must ensure that your custom cards do not have the same name as an existing one, unless you just want it to be another print, just like [Unearth](https://msem-instigator.herokuapp.com/card/CHAMPIONS/62/Unearth) and the basic lands in this example.
Let's comment out Master Chef to avoid a name conflict with an existing MTG card:
```
[cards]
#7 M Master Chef
33 M Golden Touch
...
```
Save your file, and let's move onto another step.
## Scripting your first cards
As mentioned earlier, your custom card rules need to be located inside `%appdata%/Forge/custom/cards`. I recommend creating subfolders for each starting letter (`Forge/custom/cards/a`, `Forge/custom/cards/b`, etc.) to quickly find if a card has a duplicate name.
Now, you might remember than Unearth was an existing MTG card so we do not need to create a custom card rule for it. Let's create a few others.
> This tutorial will not teach you to script your cards, and they might not be perfect. Please check out [Creating a Custom Card](https://github.com/Card-Forge/forge/wiki/Creating-a-Custom-Card) if you want more info, or look at the existing cards to learn more complex scripting.
A:SP$ Sacrifice | SacValid$ Creature | Defined$ Player | SpellDescription$ Each player sacrifices a creature.
AI:RemoveDeck:All
Oracle:Each player sacrifices a creature.
```
fox_of_the_orange_orchard.txt
```
Name:Fox of the Orange Orchard
ManaCost:1 W
Types:Creature Fox Spirit
PT:3/1
Oracle:
```
inked_summoner.txt
```
Name:Inked Summoner
ManaCost:1 B
Types:Creature Human Warlock Artist
PT:1/2
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigBranch | CheckSVar$ X | SVarCompare$ GE2 | TriggerDescription$ At the beginning of your end step, if you lost 2 or more life this turn, create a 1/1 black Bird creature token with flying. If you lost 4 or more life this turn, instead create a 3/3 black Cat creature token with deathtouch. If you lost 6 or more life this turn, instead create a 5/5 black Golem creature token with trample.
Oracle:At the beginning of your end step, if you lost 2 or more life this turn, create a 1/1 black Bird creature token with flying.\nIf you lost 4 or more life this turn, instead create a 3/3 black Cat creature token with deathtouch.\nIf you lost 6 or more life this turn, instead create a 5/5 black Golem creature token with trample.
```
If you load your game now, you should be able to find these cards you just scripted! You'll also notice that Inked Summoner is only listed as a Human Warlock, missing the Artist subtype. That's because Artist is not a real MTG subtype. You can add custom types directing inside the set definition file by following the sections found inside the `res/lists/TypeLists.txt` file. Duplicates will be ignored.
```
[CreatureTypes]
Artist:Artists
```
Oh no! If you play with Inked Summoner now, you will crash when summoning a token. That's because they don't exist in MTG and we need to define them! Let's go onto the next step!
## Scripting custom tokens
The token scripts are located at `%appdata%/Forge/custom/tokens`.
Let's add the new tokens we need to make Inked Summoner work!
> Just like for card scripting, this tutorial will not teach you about scripting them.
b_1_1_bird.flying.txt
```
Name:Bird Token
ManaCost:no cost
Colors:black
Types:Creature Bird
PT:1/1
K:Flying
Oracle:Flying
```
b_3_3_cat_deathtouch.txt
```
Name:Cat Token
ManaCost:no cost
Colors:black
Types:Creature Cat
PT:3/3
K:Deathtouch
Oracle:Deathtouch
```
b_5_5_golem_trample.txt
```
Name:Golem Token
ManaCost:no cost
Colors:black
Types:Creature Golem
PT:5/5
K:Trample
Oracle:Trample
```
Great! Now Inked Summoner no longer make the game crash! Now let's add some images to spice it all up.
## Adding card and token images
You can find the card images for the MSEM Champions edition [here](https://msem-instigator.herokuapp.com/set/CHAMPIONS). Find the ones you need and save them inside `%appdata%/../Local/Forge/Cache/pics/cards/MSEM_CHAMPIONS` Remember the filename format should be something like `Swamp.full.jpg` if you only have one variant in your edition. If you have multiples, then it should be something like `Fox of the Orange Orchard1.full.jpg`, `Fox of the Orange Orchard2.full.jpg`, etc. You can find the alternate images from [here](https://msem-instigator.herokuapp.com/set/MPS_MSE) if you want.
For the tokens, we can deposit them inside `%localappdata%/Forge/Cache/pics/tokens/MSEM_CHAMPIONS`. They should be named the same as their token script so `b_1_1_bird_flying.jpg` and so forth.
You can now start your game again, and see that the art loads correctly now.
## 🎉 Congratulations!
You’ve just added your first custom set in Forge! There's still much more to explore — scripting advanced abilities, custom mechanics, and set structures — but you now have a solid foundation to build from.
1. Quest icons created by Teekatas, from his Legendora set - [Link](http://raindropmemory.deviantart.com/)
1. Thanks to the XMage team for permission to use their targeting arrows.
1. Thanks to http://www.freesound.org/browse/ for providing some sound files.
1. Credit to Kevin Macleod for the royalty free music that is used.
1. Credit to Juhani Junkala for the royalty free music for boss battles - [Link](https://opengameart.org/content/boss-battle-music)
## Adventure Mode Sprites and tiles.
Some of the sprites and tiles that are used in Adventure mode come from external artists. These fall under the ownership of their respective artists and may be used only under the conditions laid out in their respective licence.
1. Credit to Reddit user [wampastompah](https://www.reddit.com/user/wampastompah) for the sprites of iconic MTG characters that are used in Adventure mode
1. Credit to [Aleksandr Makarov's](https://itch.io/profile/iknowkingrabbit) pixelart (tiles and sprites) that are used in Adventure mode.
1. Credit to [Elthen](https://linktr.ee/elthen) for his sprites that are used in adventure mode.
1. Credit to [DeepDiveGameStudio](https://deepdivegamestudio.itch.io) for some of the sprites that are used in adventure mode.
1. Credit to [Krishna Palacio](https://itch.io/profile/krishna-palacio) for some of the sprites and tiles that are used in adventure mode.
1. Credit to https://opengameart.org/ for some of the tiles. More in dept creditation of the tiles used can be found [Here](https://docs.google.com/spreadsheets/d/e/2PACX-1vTD0L8yDltZnn4025CZ9exhziCj0rbpd2aKE-0qeHikVBz51OKaAHLsgFuaDuIWrkrqIJicxzdn3SG1/pubhtml)
1. Inn= Wild Boar's Inn - Brandon Fiechter// Black Rock - Logan Epic Canto //The Land of Gnomes - Logan Epic Canto//Dance of the Red Lights- Logan Epic Canto // Poland - Brandon Fiechter // Tir Nan Og - Logan Epic Canto // Village Consort -Kevin Macleod
You can add lands by clicking the triple dots icon in the right top of the deck building interface.
Initially you only have access to jumpstart basic land arts - to get more, you need to purchase the landscape sketch books from the basic land shop (The Cartographers Guild).
# 40-card deck recommendation
40-card decks give you a much more predictable curve.
In a 40-card deck, each individual card has a 2.5% chance of being drawn.
In a 60-card deck, each individual card has a 1.6% chance of being drawn.
When you use a smaller deck, the significance of each individual card is much higher and will give you more predictable performance.
# Autosell
When you click a card that is not part of any decks, you get the option to move it to auto-sell.
The numbers in the interface indicate how many copies you have.
To sell the cards you have marked, go to any town and enter the Inn.
In the Inn, the middle icon of the coin called Sell (E) will sell all your cards you have marked to sell.
Individual card values vary by rarity and reputation with the town you sell them in.
Since dying will cause you to lose a percentage of your gold, you may want to not sell all your cards immediately.
In order to build and sign the android release, you will need the `forge.keystore` file (which is not present in the repository). This file will need to be placed in the `forge-gui-android` folder. This file should **never** be committed to the repository.
In preparation for the android release, update the version recorded in the following files:
```
forge-gui-android/pom.xml
forge-gui-ios/pom.xml
forge-gui-mobile/src/forge/Forge.java
```
In the first two, you're looking for `alpha-version` (~line 10 in both files) and setting the string accordingly.
In the last one, you're looking for the declaration of `CURRENT_VERSION` (~line 37) and setting it to the same value as the previous two files.
Commit the changes to these three files to the repository with an appropriately descriptive commit message.
A script such as the following will compile and sign the android build:
```
export ANDROID_HOME=/opt/android-sdk/
export _JAVA_OPTIONS="-Xmx2g"
mvn -U -B clean \\
-P android-release-build,android-release-sign \\
install \\
-Dsign.keystore=forge.keystore \\
-Dsign.alias=Forge \\
-Dsign.storepass=${FORGE_STOREPASS} \\
-Dsign.keypass=${FORGE_KEYPASS}
```
Once the above build has successfully completed and passed any desired testing, the following will build, sign, and publish the build:
1) use Agetian's getmtgdecks.sh scripts to download recent decks from mtgdecks.net and convert them to forge format and separate into Standard/Modern/Pioneer using his python tools (deck2forge_3.5.py and deckSorter_1.4a)
2) copy the new decks into forge-gui/res/deckgendecks/$format for each format and remove the $format.lda.dat and $format.raw.dat files from that folder
3) check out the Austin/ldastream branch and merge it with the latest
4) run the forge.deck.generate.LDAModelGenerator class in the forge-gui-desktop module
this will generate updated .raw.dat and .lda.dat files that can then be checked in
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.