### Starting Conditions and Loadout Source: https://github.com/daviscook477/basemod/wiki/Migrating-to-5.0 Methods for defining the character's starting card, deck, relics, and loadout information. ```APIDOC ## Starting Conditions and Loadout ### getStartCardForEvent Returns an instance of a basic rarity card to appear in the "gremlin match" event. ```java @Override public AbstractCard getStartCardForEvent() {} ``` ### getStartingDeck Returns the character's starting deck. This method is no longer static. ```java @Override public ArrayList getStartingDeck() {} ``` ### getStartingRelics Returns the character's starting relics. This method is no longer static. ```java @Override public ArrayList getStartingRelics() {} ``` ### getLoadout Returns the character's loadout information. This method is no longer static and should call `this` instead of your class enum for its 8th parameter. ```java @Override public CharSelectInfo getLoadout() {} ``` ``` -------------------------------- ### Implement getStartingDeck and getStartingRelics Source: https://github.com/daviscook477/basemod/wiki/Migrating-to-5.0 Returns the starting deck and relics; these methods are no longer static. ```Java @Override public ArrayList getStartingDeck() {}, public ArrayList getStartingRelics() {} ``` -------------------------------- ### Basic Pluralization Examples Source: https://github.com/daviscook477/basemod/wiki/Dynamic-Text-Blocks Examples showing how to conditionally append 's' or change words based on a variable value. ```json "{@@}Draw !M! {!M!|@=card.|>1=cards.}" ``` ```json "{@@}Draw !M! card{!M!|>1=s}." ``` -------------------------------- ### Custom Reward Implementation Example Source: https://github.com/daviscook477/basemod/wiki/Custom-Rewards This section provides an example of how to implement a custom reward, including patching the `RewardType` enum and creating the `CustomReward` class. ```APIDOC ## Custom Reward Implementation Example ### 1. Patching RewardType Enum First, create a class to patch the `RewardItem.RewardType` enum to include your new reward type. #### Code Example: ```java import com.evacipated.cardcrawl.modthespire.lib.SpireEnum; import com.megacrit.cardcrawl.rewards.RewardItem; public class HpRewardTypePatch { @SpireEnum public static RewardItem.RewardType MYMOD_HPREWARD; } ``` ### 2. Creating the CustomReward Class Next, create your actual reward class that extends `CustomReward`. #### Code Example: ```java import basemod.abstracts.CustomReward; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Texture; import com.megacrit.cardcrawl.dungeons.AbstractDungeon; public class HpReward extends CustomReward { private static final Texture ICON = new Texture(Gdx.files.internal("[pathtotexturefile]")); public int amount; public HpReward(int amount) { super(ICON, "Heal " + amount + " hp.", HpRewardTypePatch.MYMOD_HPREWARD); this.amount = amount; } @Override public boolean claimReward() { AbstractDungeon.player.heal(this.amount); return true; } } ``` ### 3. Registering the Reward Finally, register this new reward with `BaseMod.registerCustomReward` in your initializer class, as shown in the 'Register Custom Reward' snippet. ``` -------------------------------- ### TopPanelItem Implementation Source: https://github.com/daviscook477/basemod/wiki/Custom-Top-Panel-Items Example of how to create a custom top panel item by extending the TopPanelItem class. ```APIDOC ### Implementation Example ```Java public class TopPanelItemExample extends TopPanelItem { private static final Texture IMG = new Texture("yourmodresources/images/icon.png"); public static final String ID = "yourmodname:TopPanelItemExample"; public TopPanelItemExample() { super(IMG, ID); } @Override protected void onClick() { // your onclick code } } ``` ``` -------------------------------- ### Example: Basic Auto-Adding Cards Source: https://github.com/daviscook477/basemod/wiki/AutoAdd Demonstrates how to use AutoAdd to automatically find and register all cards within a specified package, with options for default visibility. ```APIDOC ## Example: Basic Auto-Adding Cards ```java @Override public void receiveEditCards() { // This finds and adds all cards in the same package (or sub-package) as MyAbstractCard // along with marking all added cards as seen new AutoAdd(MyModID) .packageFilter(MyAbstractCard.class) .setDefaultSeen(true) .cards(); } ``` ``` -------------------------------- ### Implement Event Subscribers with BaseMod Source: https://github.com/daviscook477/basemod/wiki/Starting-Your-Mod Implement BaseMod interfaces to subscribe to game events. This example shows how to subscribe to PostExhaust, PostBattle, and PostDungeonInitialize events. ```Java package example_mod; import com.evacipated.cardcrawl.modthespire.lib.SpireInitializer; import com.megacrit.cardcrawl.cards.AbstractCard; import com.megacrit.cardcrawl.rooms.AbstractRoom; import basemod.BaseMod; import basemod.interfaces.PostBattleSubscriber; import basemod.interfaces.PostDungeonInitializeSubscriber; import basemod.interfaces.PostExhaustSubscriber; @SpireInitializer public class ExampleMod implements PostExhaustSubscriber, PostBattleSubscriber, PostDungeonInitializeSubscriber { public ExampleMod() { BaseMod.subscribe(this); } public static void initialize() { new ExampleMod(); } @Override public void receivePostExhaust(AbstractCard c) { } @Override public void receivePostBattle(AbstractRoom r) { } @Override public void receivePostDungeonInitialize() { } } ``` -------------------------------- ### Implement a Custom Screen Post Processor Source: https://github.com/daviscook477/basemod/wiki/Screen-Rendering-Post-Processor An example of a post processor that flips the screen horizontally and defines a custom priority. ```java import basemod.interfaces.ScreenPostProcessor; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureRegion; public class SampleScreenPostProcessor implements ScreenPostProcessor { @Override public void postProcess(SpriteBatch sb, TextureRegion textureRegion, OrthographicCamera camera) { sb.setColor(Color.WHITE); sb.setBlendFunction(GL20.GL_ONE, GL20.GL_ZERO); textureRegion.flip(true, false); // Render flipped screen sb.draw(textureRegion, 0, 0); textureRegion.flip(true, false); } // This method is optional. // Post processors with higher priority run after those with lower priority. @Override public int priority() { return 50; } } ``` -------------------------------- ### Modify Ironclad Starting Relics with Basemod Source: https://github.com/daviscook477/basemod/wiki/Examples Implement `PostCreateStartingRelicsSubscriber` to add or change starting relics for a character. This example adds 'Black Blood' to the Ironclad's starting inventory. Ensure the mod is initialized using `@SpireInitializer` and `BaseMod.subscribe(this)`. ```java import java.util.ArrayList; import basemod.ModLabel; import com.megacrit.cardcrawl.characters.AbstractPlayer; import com.megacrit.cardcrawl.characters.Ironclad; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.badlogic.gdx.graphics.Texture; import com.evacipated.cardcrawl.modthespire.lib.SpireInitializer; import com.megacrit.cardcrawl.core.Settings; import com.megacrit.cardcrawl.unlock.UnlockTracker; import com.megacrit.cardcrawl.characters.AbstractPlayer.PlayerClass; import basemod.BaseMod; import basemod.ModPanel; import basemod.interfaces.PostCreateStartingRelicsSubscriber; import basemod.interfaces.PostInitializeSubscriber; @SpireInitializer // this annotation tells ModTheSpire to look at this class to initialize our mod public class SampleMod implements PostCreateStartingRelicsSubscriber, PostInitializeSubscriber { public static final Logger logger = LogManager.getLogger(SampleMod.class.getName()); // lets us log output public static final String MODNAME = "Sample Mod"; // mod name public static final String AUTHOR = "You"; // your name public static final String DESCRIPTION = "v1.0.0 - makes the Ironclad OP"; // description (w/ version # if you want) public SampleMod() { logger.info("subscribing to PostCreateStartingRelics and postInitialize events"); // when our mod is loaded we tell BaseMod that we want to do something when the starting relics are created for the character when they start a run // as well as when the game as finished initializing itself. these are defined earlier (in implements) BaseMod.subscribe(this); } public static void initialize() { // ModTheSpire will call this method to initialize because of the annotation we put at the top of the class definition @SuppressWarnings("unused") SampleMod mod = new SampleMod(); } @Override public void receivePostInitialize() { // Mod badge Texture badgeTexture = new Texture("badge_img.png"); ModPanel settingsPanel = new ModPanel(); ModLabel label = new ModLabel("My mod does not have any settings (yet)!", 400.0f, 700.0f, settingsPanel, (me) -> {}); settingsPanel.addUIElement(label); BaseMod.registerModBadge(badgeTexture, MODNAME, AUTHOR, DESCRIPTION, settingsPanel); // once the game has initialized we want to set up a **mod badge** which is a little icon on the main menu screen that tells users that our mod has been loaded } @Override public void receivePostCreateStartingRelics(PlayerClass playerClass, ArrayList relicsToAdd) { if (playerClass == PlayerClass.IRONCLAD) { // only give the relic if the class is ironclad relicsToAdd.add("Black Blood"); // here we simply give the player black blood in addition to their other starting relics UnlockTracker.markRelicAsSeen("Black Blood"); } } } ``` -------------------------------- ### Initialize and Subscribe to BaseMod Events Source: https://context7.com/daviscook477/basemod/llms.txt Implement subscriber interfaces and register your mod class with BaseMod to receive callbacks for various game events. This example shows how to subscribe to card exhaust, battle completion, card/relic editing, and post-initialization events. ```java import basemod.BaseMod; import basemod.interfaces.*; import com.evacipated.cardcrawl.modthespire.lib.SpireInitializer; import com.megacrit.cardcrawl.cards.AbstractCard; import com.megacrit.cardcrawl.rooms.AbstractRoom; @SpireInitializer public class MyMod implements PostExhaustSubscriber, PostBattleSubscriber, EditCardsSubscriber, EditRelicsSubscriber, PostInitializeSubscriber { public static void initialize() { new MyMod(); } public MyMod() { // Subscribe to all implemented interfaces at once BaseMod.subscribe(this); } @Override public void receivePostExhaust(AbstractCard card) { // Called after a card is exhausted System.out.println("Card exhausted: " + card.name); } @Override public void receivePostBattle(AbstractRoom room) { // Called after combat ends (doesn't trigger on loss) System.out.println("Battle complete!"); } @Override public void receiveEditCards() { // Register custom cards here BaseMod.addCard(new MyCustomCard()); } @Override public void receiveEditRelics() { // Register custom relics here BaseMod.addRelic(new MyCustomRelic(), RelicType.SHARED); } @Override public void receivePostInitialize() { // Called after game initialization - register events, monsters, badges BaseMod.addEvent(MyEvent.ID, MyEvent.class); } } ``` -------------------------------- ### Initialize Mod with SpireInitializer Source: https://github.com/daviscook477/basemod/wiki/Starting-Your-Mod Boilerplate code to initialize your mod with ModTheSpire. Ensures your mod is recognized and its initialization method is called before the game starts. ```Java package example_mod; import com.evacipated.cardcrawl.modthespire.lib.SpireInitializer; @SpireInitializer public class ExampleMod { public ExampleMod() { // TODO: make an awesome mod! } public static void initialize() { new ExampleMod(); } } ``` -------------------------------- ### Maven pom.xml Configuration Source: https://github.com/daviscook477/basemod/wiki/IntelliJ-Environment-Setup This is an example pom.xml file to configure your Slay The Spire mod project. Ensure you import changes after pasting this into your project's pom.xml. ```xml // tofile location is where you can find your compiled .jar file ``` -------------------------------- ### Set Animation in Slay the Spire Source: https://github.com/daviscook477/basemod/wiki/Custom-Character-Animations Use this to start an animation after setting it up in the constructor. Ensure 'YourAnimationName' matches the name defined in your animation software. ```Java AnimationState.TrackEntry e = state.setAnimation(0, "YourAnimationName", true); ``` -------------------------------- ### Define Additional Shader Uniforms Source: https://github.com/daviscook477/basemod/wiki/Screen-Rendering-Post-Processor Example of uniforms provided by BaseMod for use in custom shaders. ```glsl uniform float u_scale; uniform vec2 u_screenSize; // These uniforms are defined below: // u_scale = com.megacrit.cardcrawl.core.Settings.scale; // u_screenSize = vec2(com.megacrit.cardcrawl.core.Settings.WIDTH, com.megacrit.cardcrawl.core.Settings.HEIGHT); ``` -------------------------------- ### Example: Auto-Adding Character Specific Relics Source: https://github.com/daviscook477/basemod/wiki/AutoAdd Illustrates how to use AutoAdd to find and register relics for a specific character, with conditional logic based on annotations. ```APIDOC ## Example: Auto-Adding Character Specific Relics for a Modded Character ```java @Override public void receiveEditRelics() { // This finds and adds all relics inheriting from CustomRelic that are in the same package // as MyRelic, keeping all as unseen except those annotated with @AutoAdd.Seen new AutoAdd(MyModID) .packageFilter(MyRelic.class) .any(CustomRelic.class, (info, relic) -> { BaseMod.addRelicToCustomPool(relic, MY_CHARACTER_COLOR); if (info.seen) { UnlockTracker.markRelicAsSeen(relic.relicId); } }); } ``` ``` -------------------------------- ### Implement Custom Character Source: https://context7.com/daviscook477/basemod/llms.txt Extend CustomPlayer to define character stats, animations, and starting loadout, then register via receiveEditCharacters. ```java import basemod.abstracts.CustomPlayer; import com.esotericsoftware.spine.AnimationState; import com.megacrit.cardcrawl.characters.AbstractPlayer; import com.megacrit.cardcrawl.core.EnergyManager; import com.megacrit.cardcrawl.core.Settings; import com.megacrit.cardcrawl.screens.CharSelectInfo; public class MyCharacter extends CustomPlayer { public static final int ENERGY_PER_TURN = 3; public static final int STARTING_HP = 75; public static final int MAX_HP = 75; public static final int STARTING_GOLD = 99; public static final int HAND_SIZE = 5; public static final int ORB_SLOTS = 0; public MyCharacter(String name) { super(name, MyPlayerClassEnum.MY_PLAYER_CLASS); this.dialogX = this.drawX + 0.0F * Settings.scale; this.dialogY = this.drawY + 220.0F * Settings.scale; initializeClass(null, "img/char/shoulder2.png", // Campfire shoulder 1 "img/char/shoulder1.png", // Campfire shoulder 2 "img/char/corpse.png", // Corpse image getLoadout(), 20.0F, -10.0F, 220.0F, 290.0F, new EnergyManager(ENERGY_PER_TURN)); // Load spine animation loadAnimation("img/char/skeleton.atlas", "img/char/skeleton.json", 1.0F); AnimationState.TrackEntry e = this.state.setAnimation(0, "idle", true); } public static ArrayList getStartingDeck() { ArrayList retVal = new ArrayList<>(); retVal.add("myModID:Strike"); retVal.add("myModID:Strike"); retVal.add("myModID:Strike"); retVal.add("myModID:Strike"); retVal.add("myModID:Defend"); retVal.add("myModID:Defend"); retVal.add("myModID:Defend"); retVal.add("myModID:Defend"); retVal.add("myModID:SpecialCard"); return retVal; } public static ArrayList getStartingRelics() { ArrayList retVal = new ArrayList<>(); retVal.add("myModID:StarterRelic"); return retVal; } public static CharSelectInfo getLoadout() { return new CharSelectInfo("My Character", "A mysterious warrior from distant lands.", STARTING_HP, MAX_HP, ORB_SLOTS, STARTING_GOLD, HAND_SIZE, MyPlayerClassEnum.MY_PLAYER_CLASS, getStartingRelics(), getStartingDeck(), false); } } // Registration in receiveEditCharacters: @Override public void receiveEditCharacters() { BaseMod.addCharacter(new MyCharacter(CardCrawlGame.playerName), "img/charselect/button.png", "img/charselect/portrait.png", MyPlayerClassEnum.MY_PLAYER_CLASS); } ``` -------------------------------- ### Dynamic Text Usage Examples Source: https://github.com/daviscook477/basemod/wiki/Dynamic-Text-Blocks Various implementations of dynamic text blocks for card descriptions, including energy icons, pluralization, and variable-based logic. ```json "DESCRIPTION": "{@@}Gain !B! Block. NL Draw !M! card{!M!|>1=s}." ``` ```json "DESCRIPTION": "{@@}Gain{!M!|repeat= [E]|>4= !M! [E]}." ``` ```json "DESCRIPTION": "{@@}The first {!M!|@=Orb|>1=!M! Orbs} you Channel each turn {!M!|@=is|>1=are} Evoked." ``` ```json "DESCRIPTION": "{@@}Apply !M! Vulnerable and Lock-On. NL Draw !M10Robot:SecondMagic! card{!M10Robot:SecondMagic!|>1=s}." ``` ```json "DESCRIPTION": "{@@}Deal !D! damage. NL Channel !M! m10robot:Bit{!M!|>1=s} for each Attack played this turn." ``` ```json "DESCRIPTION": "{@@}Deal !D! damage for each Skill played this turn.{!Location!|0= NL (Deals !M10Robot:SecondDamage! damage)}" ``` -------------------------------- ### CustomRelic Constructor Example Source: https://github.com/daviscook477/basemod/wiki/Custom-Relics Extends `CustomRelic` for easier relic creation. Handles texture loading. Ensure relic texture is 128x128px with the image centered. ```Java public Blueberries() { super(ID, MyMod.getBlueberriesTexture(), // you could create the texture in this class if you wanted too RelicTier.UNCOMMON, LandingSound.MAGICAL); // this relic is uncommon and sounds magic when you click it } ``` -------------------------------- ### Define a Custom Player Class Source: https://github.com/daviscook477/basemod/wiki/Custom-Characters Extends CustomPlayer to define character attributes, animations, starting deck, and loadout. Requires proper initialization of textures and energy management. ```java import java.util.ArrayList; import com.badlogic.gdx.math.MathUtils; import com.esotericsoftware.spine.AnimationState; import com.megacrit.cardcrawl.actions.utility.ExhaustAllEtherealAction; import com.megacrit.cardcrawl.characters.AbstractPlayer; import com.megacrit.cardcrawl.core.EnergyManager; import com.megacrit.cardcrawl.core.Settings; import com.megacrit.cardcrawl.dungeons.AbstractDungeon; import com.megacrit.cardcrawl.powers.AbstractPower; import com.megacrit.cardcrawl.screens.CharSelectInfo; import com.megacrit.cardcrawl.unlock.UnlockTracker; public class MyCharacter extends CustomPlayer { public static final int ENERGY_PER_TURN = 3; // how much energy you get every turn public static final String MY_CHARACTER_SHOULDER_2 = "img/char/shoulder2.png"; // campfire pose public static final String MY_CHARACTER_SHOULDER_1 = "img/char/shoulder1.png"; // another campfire pose public static final String MY_CHARACTER_CORPSE = "img/char/corpse.png"; // dead corpse public static final String MY_CHARACTER_SKELETON_ATLAS = "img/char/skeleton.atlas"; // spine animation atlas public static final String MY_CHARACTER_SKELETON_JSON = "img/char/skeleton.json"; // spine animation json public MyCharacter (String name) { super(name, MyPlayerClassEnum.MY_PLAYER_CLASS); this.dialogX = (this.drawX + 0.0F * Settings.scale); // set location for text bubbles this.dialogY = (this.drawY + 220.0F * Settings.scale); // you can just copy these values initializeClass(null, MY_CHARACTER_SHOULDER_2, // required call to load textures and setup energy/loadout MY_CHARACTER_SHOULDER_1, MY_CHARACTER_CORPSE, getLoadout(), 20.0F, -10.0F, 220.0F, 290.0F, new EnergyManager(ENERGY_PER_TURN)); loadAnimation(MY_CHARACTER_SKELETON_ATLAS, MY_CHARACTER_SKELETON_JSON, 1.0F); // if you're using modified versions of base game animations or made animations in spine make sure to include this bit and the following lines AnimationState.TrackEntry e = this.state.setAnimation(0, "animation", true); e.setTime(e.getEndTime() * MathUtils.random()); } public static ArrayList getStartingDeck() { // starting deck 'nuff said ArrayList retVal = new ArrayList<>(); retVal.add("MyCard0"); retVal.add("MyCard0"); retVal.add("MyCard0"); retVal.add("MyCard0"); retVal.add("MyCard1"); retVal.add("MyCard1"); retVal.add("MyCard1"); retVal.add("MyCard1"); retVal.add("MyCard2"); return retVal; } public static ArrayList getStartingRelics() { // starting relics - also simple ArrayList retVal = new ArrayList<>(); retVal.add("MyRelic"); UnlockTracker.markRelicAsSeen("MyRelic"); return retVal; } public static final int STARTING_HP = 75; public static final int MAX_HP = 75; public static final int STARTING_GOLD = 99; public static final int HAND_SIZE = 5; public static CharSelectInfo getLoadout() { // the rest of the character loadout so includes your character select screen info plus hp and starting gold return new CharSelectInfo("My Character", "My character is a person from the outer worlds. He makes magic stuff happen.", STARTING_HP, MAX_HP, ORB_SLOTS, STARTING_GOLD, HAND_SIZE, this, getStartingRelics(), getStartingDeck(), false); } } ``` -------------------------------- ### Console Access Source: https://github.com/daviscook477/basemod/wiki/Console How to open and enable the in-game console. ```APIDOC ## Console Access ### Description Use the `` ` `` key to open the console. The console must be enabled in the in-game mod config for BaseMod. The keybind can also be changed in the config. ### Method N/A (Keybind) ### Endpoint N/A ``` -------------------------------- ### Get Potions to Remove Source: https://github.com/daviscook477/basemod/wiki/Custom-Potions Retrieves a list of potions that are marked for removal. ```APIDOC ## GET /api/potions/remove/list ### Description Retrieves a list of potion IDs that are scheduled for removal from the game. ### Method GET ### Endpoint /api/potions/remove/list ### Response #### Success Response (200) - **potionsToRemove** (ArrayList) - A list of potion IDs to be removed. #### Response Example ```json { "potionsToRemove": [ "old_potion_1", "deprecated_potion_2" ] } ``` ``` -------------------------------- ### Create Mod Configuration Panels Source: https://context7.com/daviscook477/basemod/llms.txt Use EasyConfigPanel for simple settings or a custom ModPanel for complex layouts. Register the panel using BaseMod.registerModBadge. ```java import basemod.BaseMod; import basemod.EasyConfigPanel; import basemod.ModPanel; // Simple config using EasyConfigPanel: public class MyModConfig extends EasyConfigPanel { public static boolean enableFeature = true; public static int damageMultiplier = 2; public static String playerName = "Hero"; public MyModConfig() { super(MyMod.modID, MyMod.makeID("MyModConfig")); setNumberRange("damageMultiplier", 1, 10); } } // Register in receivePostInitialize: @Override public void receivePostInitialize() { Texture badge = new Texture("img/badge.png"); BaseMod.registerModBadge(badge, "My Mod", "Author", "Description of my mod", new MyModConfig()); } // Custom ModPanel with buttons and labels: ModPanel panel = new ModPanel(); panel.addLabel("Settings", 400f, 700f, (label) -> {}); panel.addButton(350f, 650f, (button) -> { // Button click handler MyModConfig.enableFeature = !MyModConfig.enableFeature; }); BaseMod.registerModBadge(badge, "My Mod", "Author", "Description", panel); ``` -------------------------------- ### Adding a Character with Basemod Source: https://github.com/daviscook477/basemod/wiki/Migrating-to-5.0 Example of how to add a custom character using `BaseMod.addCharacter` with its new parameters. ```APIDOC ## Adding a Character with Basemod `BaseMod.addCharacter` now requires an instance of the class you're adding, the card color enum, the file path to the character button, the file path to the character portrait, and your custom class enum. Example: ```java BaseMod.addCharacter( new MyCustomCharacter(CardCrawlGame.playerName), MyCardColorEnum.CHARACTER_COLOR, PATH_TO_CHAR_BUTTON, PATH_TO_CHAR_PORTRAIT, MyCharacterEnum.CHARACTER_CLASS ); ``` ``` -------------------------------- ### UIStrings for Config Panel Labels Source: https://github.com/daviscook477/basemod/wiki/Mod-Config-and-Panel Create a UIStrings JSON file to provide user-friendly names for your configuration options. The keys in the `TEXT_DICT` should match the static field names in your config class, and the values will be displayed in the config panel. ```json "${modID}:MyModConfig": { "TEXT_DICT": { "enableSomething": "A Toggle", "nameSomething": "A Name:", "setSomething": "A Number" } } ``` -------------------------------- ### Load and Initialize G3DJ Model Source: https://github.com/daviscook477/basemod/wiki/Custom-Character-Animations Initializes the model loader, applies transparency settings, and sets up the animation controller for a model instance. ```java // Model loader needs a binary json reader to decode JsonReader jsonReader = new JsonReader(); // Create a model loader passing in our json reader G3dModelLoader modelLoader = new G3dModelLoader(jsonReader); // Now load the model by name myModel = modelLoader.loadModel(Gdx.files.internal("data/seeker.g3dj")); // Necessary to get transparent textures working - I don't know why for (Material mat : myModel.materials) { mat.set(new BlendingAttribute(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA)); } // Now create an instance. Instance holds the positioning data, etc // of an instance of your model myInstance = new ModelInstance(myModel, 0, 0, 10.0f); // fbx-conv is supposed to perform this rotation for you... it // doesnt seem to myInstance.transform.rotate(1, 0, 0, -90); // You use an AnimationController to um, control animations. Each // control is tied to the model instance controller = new AnimationController(myInstance); // Pick the current animation by name controller.setAnimation("bottom|idle", 1, new AnimationListener() { @Override public void onEnd(AnimationDesc animation) { // this will be called when the current animation is done. // queue up another animation called "Jump". // Passing a negative to loop count loops forever. 1f for // speed is normal speed. controller.queue("bottom|idle", -1, 1f, null, 0f); } @Override public void onLoop(AnimationDesc animation) { // TODO Auto-generated method stub } }); ``` -------------------------------- ### Implement newInstance Source: https://github.com/daviscook477/basemod/wiki/Migrating-to-5.0 Returns a new instance of the character. ```Java @Override public AbstractPlayer newInstance() {} ``` -------------------------------- ### CustomRelic Class Definition Source: https://github.com/daviscook477/basemod/wiki/Custom-Relics Example of extending `CustomRelic` to create a new relic. Requires implementing the constructor, `getUpdatedDescription`, `onEquip`, and `makeCopy` methods. ```Java public class Blueberries extends CustomRelic { public static final String ID = "Blueberries"; private static final int HP_PER_CARD = 1; public Blueberries() { super(ID, MyMod.getBlueberriesTexture(), // you could create the texture in this class if you wanted too RelicTier.UNCOMMON, LandingSound.MAGICAL); // this relic is uncommon and sounds magic when you click it } @Override public String getUpdatedDescription() { return DESCRIPTIONS[0] + HP_PER_CARD + DESCRIPTIONS[1]; // DESCRIPTIONS pulls from your localization file } @Override public void onEquip() { int count = 0; for (AbstractCard c : AbstractDungeon.player.masterDeck.group) { if (c.isEthereal) { // when equipped (picked up) this relic counts how many ethereal cards are in the player's deck count++; } } AbstractDungeon.player.increaseMaxHp(count * HP_PER_CARD, true); } @Override abstract public AbstractRelic makeCopy() { // always override this method to return a new instance of your relic return new Blueberries(); } } ``` -------------------------------- ### Extend EasyConfigPanel Source: https://github.com/daviscook477/basemod/wiki/Mod-Config-and-Panel Create a class that extends EasyConfigPanel to define your mod's configuration structure. Ensure the constructor calls the superclass constructor with the mod ID and a unique panel ID. ```java public class MyModConfig extends EasyConfigPanel { public MyModConfig() { super(MyMod.modID, MyMod.makeID("MyModConfig")); } } ``` -------------------------------- ### Create a custom TopPanelItem in Java Source: https://github.com/daviscook477/basemod/wiki/Custom-Top-Panel-Items Extend the TopPanelItem class to define a custom icon and click behavior. Ensure the texture path is correct for your mod's resources. ```Java public class TopPanelItemExample extends TopPanelItem { private static final Texture IMG = new Texture("yourmodresources/images/icon.png"); public static final String ID = "yourmodname:TopPanelItemExample"; public TopPanelItemExample() { super(IMG, ID); } @Override protected void onClick() { // your onclick code } } ``` -------------------------------- ### CustomRelic onEquip Logic Source: https://github.com/daviscook477/basemod/wiki/Custom-Relics Overrides `onEquip` to implement logic executed when the relic is picked up. This example counts ethereal cards and increases max HP. ```Java @Override public void onEquip() { int count = 0; for (AbstractCard c : AbstractDungeon.player.masterDeck.group) { if (c.isEthereal) { // when equipped (picked up) this relic counts how many ethereal cards are in the player's deck count++; } } AbstractDungeon.player.increaseMaxHp(count * HP_PER_CARD, true); } ``` -------------------------------- ### Register Mod Badge with Config Panel Source: https://github.com/daviscook477/basemod/wiki/Mod-Config-and-Panel Register your mod's badge and associate it with your EasyConfigPanel in the `receivePostInitialize` method. If `registerModBadge` is already called, modify the last parameter to instantiate your config panel. ```java @Override public void receivePostInitialize() { /* other code */ BaseMod.registerModBadge(badge texture, mod name, mod author, mod description, new MyModConfig()); } ``` -------------------------------- ### Creating a Custom Console Command Source: https://github.com/daviscook477/basemod/wiki/Console This snippet shows the basic structure of a custom console command by extending the ConsoleCommand abstract class and setting constructor parameters. ```APIDOC ## Creating a Custom Console Command All commands must be extensions of the `Abstract class basemod.devcommands.ConsoleCommand`. The class has a few parameters that you can set in the constructor that do the handling of your command. Note that none of these are mandatory and all have default values. ### Constructor Parameters - `maxExtraTokens` (int) - How many additional words can come after this one. Default: 1. - `minExtraTokens` (int) - How many additional words have to come after this one. Default: 0. - `requiresPlayer` (boolean) - If true, the command can only be executed if during a run. Default: false. - `simpleCheck` (boolean) - If true and you don't implement your own logic overriding the command syntax check function, it checks if what is typed in is in the options you said the command has. This only applies to the autocompletion feature of the console, and has no bearing on what the command does when executed! Default: false. ### Follow-up Commands - `followup.put("keyword", CommandClass.class)` - Adds a keyword as a possible follow-up to the current command, passing control to the specified `CommandClass`. You may add as many as you like. ```java public class YourCommand extends ConsoleCommand { public YourCommand() { maxExtraTokens = 2; minExtraTokens = 0; requiresPlayer = true; simpleCheck = true; followup.put("whateveryouwantmetobebaby", YourSecondCommand.class); } ... } ``` ``` -------------------------------- ### Registering and Adding Monster Encounters Source: https://github.com/daviscook477/basemod/wiki/Custom-Monsters Demonstrates how to register single or group monster encounters and add them to dungeon pools within the receivePostInitialize method. ```Java public void receivePostInitialize() { // Add a single monster encounter BaseMod.addMonster(MyMonster.ID, () -> new MyMonster()); // Add a multi-monster encounter BaseMod.addMonster("MyMonsters", () -> new MonsterGroup(new AbstractMonster[] { new MyMonster(), new MyMonster2() })); BaseMod.addMonsterEncounter(TheCity.ID, new MonsterInfo(MyMonster.ID, 5)); BaseMod.addStrongMonsterEncounter(TheBeyond.ID, new MonsterInfo("MyMonsters", 10)); } ``` -------------------------------- ### Define YourCommand Class Source: https://github.com/daviscook477/basemod/wiki/Console Extend ConsoleCommand and configure parameters like maxExtraTokens, minExtraTokens, and requiresPlayer in the constructor. Use followup.put to define subcommands. ```java public class YourCommand extends ConsoleCommand { public YourCommand() { maxExtraTokens = 2; //How many additional words can come after this one. If unspecified, maxExtraTokens = 1. minExtraTokens = 0; //How many additional words have to come after this one. If unspecified, minExtraTokens = 0. requiresPlayer = true; //if true, means the command can only be executed if during a run. If unspecified, requiresplayer = false. simpleCheck = true; /** * If this flag is true and you don't implement your own logic overriding the command syntax check function, it checks if what is typed in is in the options you said the command has. * Note that this only applies to the autocompletion feature of the console, and has no bearing on what the command does when executed! * If unspecified, simpleCheck = false. */ followup.put("whateveryouwantmetobebaby", YourSecondCommand.class); /** * Doing this adds this word as a possible followup to your current command, and passes it to YourSecondCommand. * You may add as many of these as you like. */ } ... } ``` -------------------------------- ### Get Card Strings by ID Source: https://github.com/daviscook477/basemod/wiki/Custom-Strings Retrieve card name and description strings using the card's ID from `CardStrings`. This is useful for displaying card information dynamically. ```java private static CardStrings cardStrings = CardCrawlGame.languagePack.getCardStrings(myCardID); // Gets the name and descriptions of a card by its ID from your CardStrings. ``` -------------------------------- ### Implement getStartCardForEvent Source: https://github.com/daviscook477/basemod/wiki/Migrating-to-5.0 Returns an instance of a basic rarity card for the gremlin match event. ```Java @Override public AbstractCard getStartCardForEvent() {} ``` -------------------------------- ### Implement getLoadout Source: https://github.com/daviscook477/basemod/wiki/Migrating-to-5.0 Returns the character loadout; method is no longer static. ```Java @Override public CharSelectInfo getLoadout() {} ``` -------------------------------- ### Use Custom Check in Dynamic Text Source: https://github.com/daviscook477/basemod/wiki/Dynamic-Text-Blocks Example of how to use the registered custom check '!YourMod:hasStrength!' within a card's dynamic text description. It defines different text outputs for the -1, 0, and 1 return values of the custom check. ```json { "${ModID}:Strike": { "NAME": "Strike", "DESCRIPTION": "{@@}Deal !D! damage.{!YourMod:hasStrength!|1= NL I am very strong!|-1 = NL Plz don't hurt me.}" } } ``` -------------------------------- ### Java Custom Card with Modal Choice Source: https://github.com/daviscook477/basemod/wiki/Modal-Choice-Cards Implement a custom card that opens a modal choice for the player. The modal options are defined using ModalChoiceBuilder, and the selected option triggers a specific action via the optionSelected callback method. This example shows how to add a random card of a chosen color (Red, Green, or Colorless) to the player's hand. ```Java import basemod.abstracts.CustomCard; import basemod.helpers.ModalChoice; import basemod.helpers.ModalChoiceBuilder; import basemod.helpers.TooltipInfo; import com.megacrit.cardcrawl.actions.common.MakeTempCardInHandAction; import com.megacrit.cardcrawl.cards.AbstractCard; import com.megacrit.cardcrawl.characters.AbstractPlayer; import com.megacrit.cardcrawl.dungeons.AbstractDungeon; import com.megacrit.cardcrawl.helpers.CardLibrary; import com.megacrit.cardcrawl.monsters.AbstractMonster; import java.util.List; public class ModalTest extends CustomCard implements ModalChoice.Callback { public static final String ID = "ModalTest"; public static final String NAME = "Modal Test"; public static final String DESCRIPTION = "Choose Red, Green, or Colorless. NL Gain a random card of that color."; private static final int COST = 0; private ModalChoice modal; public ModalTest() { super(ID, NAME, null, COST, DESCRIPTION, CardType.SKILL, CardColor.COLORLESS, CardRarity.BASIC, CardTarget.NONE); modal = new ModalChoiceBuilder() .setCallback(this) // Sets callback of all the below options to this .setColor(CardColor.RED) // Sets color of any following cards to red .addOption("Add a random Red card to your hand.", CardTarget.NONE) .setColor(CardColor.GREEN) // Sets color of any following cards to green .addOption("Add a random Green card to your hand.", CardTarget.NONE) .setColor(CardColor.COLORLESS) // Sets color of any following cards to colorless .addOption("Add a random Colorless card to your hand.", CardTarget.NONE) .create(); } // Uses the titles and descriptions of the option cards as tooltips for this card @Override public List getCustomTooltips() { return modal.generateTooltips(); } @Override public void use(AbstractPlayer p, AbstractMonster m) { modal.open(); } // This is called when one of the option cards us chosen @Override public void optionSelected(AbstractPlayer p, AbstractMonster m, int i) { CardColor color; switch (i) { case 0: color = CardColor.RED; break; case 1: color = CardColor.GREEN; break; case 2: color = CardColor.COLORLESS; break; default: return; } AbstractCard c; if (color == CardColor.COLORLESS) { c = AbstractDungeon.returnTrulyRandomColorlessCard(AbstractDungeon.cardRandomRng).makeCopy(); } else { c = CardLibrary.getColorSpecificCard(color, AbstractDungeon.cardRandomRng).makeCopy(); } AbstractDungeon.actionManager.addToBottom(new MakeTempCardInHandAction(c, true)); } @Override public void upgrade() { if (!upgraded) { upgradeName(); } } @Override public AbstractCard makeCopy() { return new ModalTest(); } } ``` -------------------------------- ### Register and Open a Custom Screen Source: https://github.com/daviscook477/basemod/wiki/Custom-Screens Register the screen instance during initialization and trigger it using the defined enum value. ```java // Register BaseMod.addCustomScreen(new MyScreen()); // Open BaseMod.openCustomScreen(MyScreen.Enum.MY_SCREEN, "foobar", new Shiv()); ``` -------------------------------- ### Implement Custom Reward Logic Source: https://github.com/daviscook477/basemod/wiki/Custom-Rewards Create a class that extends CustomReward to define the behavior of your custom reward. This includes setting its icon, display text, and implementing the claimReward method. ```Java import basemod.abstracts.CustomReward; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Texture; import com.megacrit.cardcrawl.dungeons.AbstractDungeon; public class HpReward extends CustomReward { private static final Texture ICON = new Texture(Gdx.files.internal("[pathtotexturefile]")); public int amount; public HpReward(int amount) { super(ICON, "Heal " + amount + " hp.", HpRewardTypePatch.MYMOD_HPREWARD); this.amount = amount; } @Override public boolean claimReward() { AbstractDungeon.player.heal(this.amount); return true; } } ``` -------------------------------- ### Registering a Command Source: https://github.com/daviscook477/basemod/wiki/Console Register your custom command with Basemod by calling `ConsoleCommand.addCommand` in your mod's `receivePostInitialize` method. ```APIDOC ## Registering your command In your mod's main file, in the `receivePostInitialize` method, call the following static function: ```java public void receivePostInitialize() { // ... other initialization code ... ConsoleCommand.addCommand("yourphrasehere", YourCommand.class); // ... } ``` - **`"yourphrasehere"`**: The keyword used to invoke your command in the console. It can only consist of letters and colons. - **`YourCommand.class`**: The class object of your custom command. If the keyword is already taken or invalid, Basemod will not register your command and print an error message in the BaseMod log window, but it will not crash the game. ``` -------------------------------- ### Registering a Mod Badge and Settings Panel Source: https://github.com/daviscook477/basemod/wiki/Mod-Badges Configures a custom settings panel with labels and buttons, then registers it alongside a mod badge. ```java ModPanel settingsPanel = new ModPanel(); settingsPanel.addLabel("", 475.0f, 700.0f, (me) -> { if (me.parent.waitingOnEvent) { me.text = "Press key"; } else { me.text = "Change console hotkey (" + Keys.toString(DevConsole.toggleKey) + ")"; } }); settingsPanel.addButton(350.0f, 650.0f, (me) -> { me.parent.waitingOnEvent = true; oldInputProcessor = Gdx.input.getInputProcessor(); Gdx.input.setInputProcessor(new InputAdapter() { @Override public boolean keyUp(int keycode) { DevConsole.toggleKey = keycode; me.parent.waitingOnEvent = false; Gdx.input.setInputProcessor(oldInputProcessor); return true; } }); }); Texture badgeTexture = new Texture(Gdx.files.internal("img/BaseModBadge.png")); registerModBadge(badgeTexture, MODNAME, AUTHOR, DESCRIPTION, settingsPanel); ``` -------------------------------- ### Define Config Options with EasyConfigPanel Source: https://github.com/daviscook477/basemod/wiki/Mod-Config-and-Panel Define static fields in your EasyConfigPanel subclass for boolean, String, or numeric options. These fields will automatically appear in the config panel with their initial values as defaults. Use `setNumberRange` for numeric fields to specify their valid range. ```java public class MyModConfig extends EasyConfigPanel { public static boolean enableSomething = true; public static String nameSomething = "foo"; public static int setSomething = 5; public MyModConfig() { super(MyMod.modID, MyMod.makeID("MyModConfig")); setNumberRange("setSomething", 1, 10); } } ``` -------------------------------- ### Configure event parameters with AddEventParams Source: https://github.com/daviscook477/basemod/wiki/Custom-Events Use the Builder pattern to define specific spawning conditions or advanced event configurations. ```java BaseMod.addEvent(new AddEventParams.Builder(MySecondEvent.ID, MySecondEvent.class).dungeonID(TheCity.ID).create()); ``` -------------------------------- ### Register Command with Basemod Source: https://github.com/daviscook477/basemod/wiki/Console In your mod's main file, call ConsoleCommand.addCommand in receivePostInitialize to register your custom command with a unique keyword. ```java public void receivePostInitialize() { ... ConsoleCommand.addCommand("yourphrasehere", YourCommand.class); ... } ``` -------------------------------- ### Register events in receivePostInitialize Source: https://github.com/daviscook477/basemod/wiki/Custom-Events Use BaseMod.addEvent to add custom events to specific or all dungeon pools. ```java @Override public void receivePostInitialize() { BaseMod.addEvent(MyFirstEvent.ID, MyFirstEvent.class); BaseMod.addEvent(MySecondEvent.ID, MySecondEvent.class, TheCity.ID); } ``` -------------------------------- ### Character Instantiation Source: https://github.com/daviscook477/basemod/wiki/Migrating-to-5.0 Method for creating a new instance of the custom character. ```APIDOC ## Character Instantiation ### newInstance Returns a new instance of your character, passing `this.name` as its name parameter. ```java @Override public AbstractPlayer newInstance() {} ``` ``` -------------------------------- ### Adding Custom Commands Source: https://github.com/daviscook477/basemod/wiki/Console Information on how to add your own custom commands to BaseMod. ```APIDOC ## Adding your own commands ### Description Instructions for adding custom commands to BaseMod. ### Requirements Doing this requires 2 things: A class that handles your command, and an unoccupied key that triggers it in the console. ```