first commit

This commit is contained in:
dtookey 2020-08-14 12:02:03 -04:00
parent 8f6b455daf
commit 9e19ed5126
48 changed files with 3677 additions and 0 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Gremious
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

52
README.md Normal file
View File

@ -0,0 +1,52 @@
# StS-Default Mod Base
Welcome to this extremely over-commented Slay the Spire modding base.
This is a minimal "default clean slate" for creating Slay the spire mods.
Use it to make your own mod of any type - If you want to add any standard in-game content (character, cards, relics, events, etc.), this is a good starting point.
It features 1 character (the Default) with a minimal set of things: 1 card of each type, 1 debuff, 1 relic, etc.
If you're new to modding, you basically *need* the BaseMod wiki for whatever you wish to add, and you can work your way thought it with this base. Another very important thing is to look at other mods (get them from their github!), as well as the base-game code, and see how they do things.
https://github.com/daviscook477/BaseMod/wiki
This base itself isn't going to help you code or anything!! Nor does it provide basic Java learning - if you need that go through the codeacademy free java course! While I have been putting comments about what does what, this is just a nice starting point if you need a place to start learning from that's not an absolute empty canvas, or an overly-complicated, difficult to understand mod. But you still need to learn how the in-game code works and how to piece things together on your own. (i.e. this base will show you where to put the code for double-tap, but not what it is/how to write it. Look up the actual cards and backward-engineer them for that.)
Feel free to use this in any way you like, of course.
If you have any issues or you want to recommend and/or add something to the mod that you feel would be helpful, feel free to submit an issue or a PR!
Happy modding!
---
## Check the wiki to get started:
https://github.com/Gremious/StS-DefaultModBase/wiki
---
## Know what you're doing?
Hop on over to the Quick Start branch. It's a fully cloneable, uncommented version of the Default ready for jump-starting a brand new mod!
---
## Some HD Slay the Spire art assets:
Includes:
- Empty Relic Template feat. empty bottle
- Empty Card Template
- Color-Changable cardback
- A couple of HD Monster vectors (Louse, Nob, Sentry, Sneaky Gremlin)
- A coupe of HD items (J.A.X., A Coin)
- 2 people silhouettes
- A curse Background
https://mega.nz/#F!gfpgTCyK!2oFOjVFKyOreKv7zdY1fEQ
(If you want to contribute to this collection feel free to send me a message through [#modding](https://www.megacrit.com/) or a github issue.)
---

113
constructTheArena/pom.xml Normal file
View File

@ -0,0 +1,113 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--Hello! You'll basically should only need to change these names and the steam path (just below)-->
<!--The author name(s) as they appear in MTS and any other comments are in your ModTheSpire.json-->
<groupId>xyz.geniuscartel</groupId>
<artifactId>ConstructTheArena</artifactId>
<name>Construct The Arena</name>
<version>0.0.1</version>
<description>A default base to start your own mod from.</description>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<SlayTheSpire.version>01-23-2019</SlayTheSpire.version>
<ModTheSpire.version>3.8.0</ModTheSpire.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--CHANGE THIS TO YOUR STEAM INSTALLATION-->
<Steam.path>/home/dtookey/.steam/debian-installation/steamapps</Steam.path>
</properties>
<dependencies>
<dependency>
<groupId>com.megacrit.cardcrawl</groupId>
<artifactId>slaythespire</artifactId>
<version>${SlayTheSpire.version}</version>
<scope>system</scope>
<systemPath>${Steam.path}/common/SlayTheSpire/desktop-1.0.jar</systemPath>
<!--<systemPath>${basedir}/../lib/desktop-1.0.jar</systemPath>-->
</dependency>
<dependency>
<groupId>com.evacipated.cardcrawl</groupId>
<artifactId>modthespire</artifactId>
<version>${ModTheSpire.version}</version>
<scope>system</scope>
<systemPath>${Steam.path}/workshop/content/646570/1605060445/ModTheSpire.jar</systemPath>
<!--<systemPath>${basedir}/../lib/ModTheSpire.jar</systemPath>-->
</dependency>
<dependency>
<groupId>basemod</groupId>
<artifactId>basemod</artifactId>
<version>5.0.0</version>
<scope>system</scope>
<systemPath>${Steam.path}/workshop/content/646570/1605833019/BaseMod.jar</systemPath>
<!--<systemPath>${basedir}/../lib/BaseMod.jar</systemPath>-->
</dependency>
<dependency>
<groupId>com.evacipated.cardcrawl.mod</groupId>
<artifactId>StSLib</artifactId>
<version>1.3.2</version>
<scope>system</scope>
<systemPath>${Steam.path}/workshop/content/646570/1609158507/StSLib.jar</systemPath>
<!--<systemPath>${basedir}/../lib/StSLib.jar</systemPath>-->
</dependency>
</dependencies>
<!-- This is how your code is packaged into the jar file-->
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<excludes>
<exclude>**/*.psd</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<phase>package</phase>
<configuration>
<target>
<!-- This moves your mod into a common folder where all mods you make can go. -->
<copy file="target/${project.artifactId}.jar" tofile="${Steam.path}/common/SlayTheSpire/mods/${project.artifactId}.jar"/>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>ModTheSpire.json</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>ModTheSpire.json</include>
</includes>
</resource>
</resources>
</build>
</project>

View File

@ -0,0 +1,515 @@
package constructTheArena;
import basemod.BaseMod;
import basemod.ModLabeledToggleButton;
import basemod.ModPanel;
import basemod.helpers.RelicType;
import basemod.interfaces.*;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.evacipated.cardcrawl.mod.stslib.Keyword;
import com.evacipated.cardcrawl.modthespire.lib.SpireConfig;
import com.evacipated.cardcrawl.modthespire.lib.SpireInitializer;
import com.google.gson.Gson;
import com.megacrit.cardcrawl.core.Settings;
import com.megacrit.cardcrawl.dungeons.TheCity;
import com.megacrit.cardcrawl.helpers.CardHelper;
import com.megacrit.cardcrawl.helpers.FontHelper;
import com.megacrit.cardcrawl.localization.*;
import com.megacrit.cardcrawl.unlock.UnlockTracker;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import constructTheArena.cards.*;
import constructTheArena.characters.TheDefault;
import constructTheArena.events.IdentityCrisisEvent;
import constructTheArena.potions.PlaceholderPotion;
import constructTheArena.relics.BottledPlaceholderRelic;
import constructTheArena.relics.DefaultClickableRelic;
import constructTheArena.relics.PlaceholderRelic;
import constructTheArena.relics.PlaceholderRelic2;
import constructTheArena.util.IDCheckDontTouchPls;
import constructTheArena.util.TextureLoader;
import constructTheArena.variables.DefaultCustomVariable;
import constructTheArena.variables.DefaultSecondMagicNumber;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
// Please don't just mass replace "theDefault" with "yourMod" everywhere.
// It'll be a bigger pain for you. You only need to replace it in 3 places.
// I comment those places below, under the place where you set your ID.
//TODO: FIRST THINGS FIRST: RENAME YOUR PACKAGE AND ID NAMES FIRST-THING!!!
// Right click the package (Open the project pane on the left. Folder with black dot on it. The name's at the very top) -> Refactor -> Rename, and name it whatever you wanna call your mod.
// Scroll down in this file. Change the ID from "theDefault:" to "yourModName:" or whatever your heart desires (don't use spaces). Dw, you'll see it.
// In the JSON strings (resources>localization>eng>[all them files] make sure they all go "yourModName:" rather than "theDefault". You can ctrl+R to replace in 1 file, or ctrl+shift+r to mass replace in specific files/directories (Be careful.).
// Start with the DefaultCommon cards - they are the most commented cards since I don't feel it's necessary to put identical comments on every card.
// After you sorta get the hang of how to make cards, check out the card template which will make your life easier
/*
* With that out of the way:
* Welcome to this super over-commented Slay the Spire modding base.
* Use it to make your own mod of any type. - If you want to add any standard in-game content (character,
* cards, relics), this is a good starting point.
* It features 1 character with a minimal set of things: 1 card of each type, 1 debuff, couple of relics, etc.
* If you're new to modding, you basically *need* the BaseMod wiki for whatever you wish to add
* https://github.com/daviscook477/BaseMod/wiki - work your way through with this base.
* Feel free to use this in any way you like, of course. MIT licence applies. Happy modding!
*
* And pls. Read the comments.
*/
@SpireInitializer
public class DefaultMod implements
EditCardsSubscriber,
EditRelicsSubscriber,
EditStringsSubscriber,
EditKeywordsSubscriber,
EditCharactersSubscriber,
PostInitializeSubscriber {
// Make sure to implement the subscribers *you* are using (read basemod wiki). Editing cards? EditCardsSubscriber.
// Making relics? EditRelicsSubscriber. etc., etc., for a full list and how to make your own, visit the basemod wiki.
public static final Logger logger = LogManager.getLogger(DefaultMod.class.getName());
private static String modID;
// Mod-settings settings. This is if you want an on/off savable button
public static Properties theDefaultDefaultSettings = new Properties();
public static final String ENABLE_PLACEHOLDER_SETTINGS = "enablePlaceholder";
public static boolean enablePlaceholder = true; // The boolean we'll be setting on/off (true/false)
//This is for the in-game mod settings panel.
private static final String MODNAME = "Construct The Arena";
private static final String AUTHOR = "Virgil"; // And pretty soon - You!
private static final String DESCRIPTION = "A deck constructor mod for concept testing and general shenanigans.";
// =============== INPUT TEXTURE LOCATION =================
// Colors (RGB)
// Character Color
public static final Color DEFAULT_GRAY = CardHelper.getColor(64.0f, 70.0f, 70.0f);
// Potion Colors in RGB
public static final Color PLACEHOLDER_POTION_LIQUID = CardHelper.getColor(209.0f, 53.0f, 18.0f); // Orange-ish Red
public static final Color PLACEHOLDER_POTION_HYBRID = CardHelper.getColor(255.0f, 230.0f, 230.0f); // Near White
public static final Color PLACEHOLDER_POTION_SPOTS = CardHelper.getColor(100.0f, 25.0f, 10.0f); // Super Dark Red/Brown
// ONCE YOU CHANGE YOUR MOD ID (BELOW, YOU CAN'T MISS IT) CHANGE THESE PATHS!!!!!!!!!!!
// ONCE YOU CHANGE YOUR MOD ID (BELOW, YOU CAN'T MISS IT) CHANGE THESE PATHS!!!!!!!!!!!
// ONCE YOU CHANGE YOUR MOD ID (BELOW, YOU CAN'T MISS IT) CHANGE THESE PATHS!!!!!!!!!!!
// ONCE YOU CHANGE YOUR MOD ID (BELOW, YOU CAN'T MISS IT) CHANGE THESE PATHS!!!!!!!!!!!
// ONCE YOU CHANGE YOUR MOD ID (BELOW, YOU CAN'T MISS IT) CHANGE THESE PATHS!!!!!!!!!!!
// ONCE YOU CHANGE YOUR MOD ID (BELOW, YOU CAN'T MISS IT) CHANGE THESE PATHS!!!!!!!!!!!
// Card backgrounds - The actual rectangular card.
private static final String ATTACK_DEFAULT_GRAY = "constructTheArenaResources/images/512/bg_attack_default_gray.png";
private static final String SKILL_DEFAULT_GRAY = "constructTheArenaResources/images/512/bg_skill_default_gray.png";
private static final String POWER_DEFAULT_GRAY = "constructTheArenaResources/images/512/bg_power_default_gray.png";
private static final String ENERGY_ORB_DEFAULT_GRAY = "constructTheArenaResources/images/512/card_default_gray_orb.png";
private static final String CARD_ENERGY_ORB = "constructTheArenaResources/images/512/card_small_orb.png";
private static final String ATTACK_DEFAULT_GRAY_PORTRAIT = "constructTheArenaResources/images/1024/bg_attack_default_gray.png";
private static final String SKILL_DEFAULT_GRAY_PORTRAIT = "constructTheArenaResources/images/1024/bg_skill_default_gray.png";
private static final String POWER_DEFAULT_GRAY_PORTRAIT = "constructTheArenaResources/images/1024/bg_power_default_gray.png";
private static final String ENERGY_ORB_DEFAULT_GRAY_PORTRAIT = "constructTheArenaResources/images/1024/card_default_gray_orb.png";
// Character assets
private static final String THE_DEFAULT_BUTTON = "constructTheArenaResources/images/charSelect/DefaultCharacterButton.png";
private static final String THE_DEFAULT_PORTRAIT = "constructTheArenaResources/images/charSelect/DefaultCharacterPortraitBG.png";
public static final String THE_DEFAULT_SHOULDER_1 = "constructTheArenaResources/images/char/defaultCharacter/shoulder.png";
public static final String THE_DEFAULT_SHOULDER_2 = "constructTheArenaResources/images/char/defaultCharacter/shoulder2.png";
public static final String THE_DEFAULT_CORPSE = "constructTheArenaResources/images/char/defaultCharacter/corpse.png";
//Mod Badge - A small icon that appears in the mod settings menu next to your mod.
public static final String BADGE_IMAGE = "constructTheArenaResources/images/Badge.png";
// Atlas and JSON files for the Animations
public static final String THE_DEFAULT_SKELETON_ATLAS = "constructTheArenaResources/images/char/defaultCharacter/skeleton.atlas";
public static final String THE_DEFAULT_SKELETON_JSON = "constructTheArenaResources/images/char/defaultCharacter/skeleton.json";
// =============== MAKE IMAGE PATHS =================
public static String makeCardPath(String resourcePath) {
return getModID() + "Resources/images/cards/" + resourcePath;
}
public static String makeRelicPath(String resourcePath) {
return getModID() + "Resources/images/relics/" + resourcePath;
}
public static String makeRelicOutlinePath(String resourcePath) {
return getModID() + "Resources/images/relics/outline/" + resourcePath;
}
public static String makeOrbPath(String resourcePath) {
return getModID() + "Resources/orbs/" + resourcePath;
}
public static String makePowerPath(String resourcePath) {
return getModID() + "Resources/images/powers/" + resourcePath;
}
public static String makeEventPath(String resourcePath) {
return getModID() + "Resources/images/events/" + resourcePath;
}
// =============== /MAKE IMAGE PATHS/ =================
// =============== /INPUT TEXTURE LOCATION/ =================
// =============== SUBSCRIBE, CREATE THE COLOR_GRAY, INITIALIZE =================
public DefaultMod() {
logger.info("Subscribe to BaseMod hooks");
BaseMod.subscribe(this);
/*
( ( /( ( ( /( ( ( ` ( /( )\ ) )\ ))\ )
)\ )\()) )\ )\()))\ ) ( )\))( )\()|()/( (()/(()/(
(((_)((_)((((_)( ((_)\(()/( )\ ((_)()\((_)\ /(_)) /(_))(_))
)\___ _((_)\ _ )\ _((_)/(_))_((_) (_()((_) ((_|_))_ _(_))(_))_
((/ __| || (_)_\(_) \| |/ __| __| | \/ |/ _ \| \ |_ _|| (_)
| (__| __ |/ _ \ | .` | (_ | _| | |\/| | (_) | |) | | | | |) |
\___|_||_/_/ \_\|_|\_|\___|___| |_| |_|\___/|___/ |___||___(_)
*/
setModID("constructTheArena");
// cool
// TODO: NOW READ THIS!!!!!!!!!!!!!!!:
// 1. Go to your resources folder in the project panel, and refactor> rename theDefaultResources to
// yourModIDResources.
// 2. Click on the localization > eng folder and press ctrl+shift+r, then select "Directory" (rather than in Project)
// replace all instances of theDefault with yourModID.
// Because your mod ID isn't the default. Your cards (and everything else) should have Your mod id. Not mine.
// 3. FINALLY and most importantly: Scroll up a bit. You may have noticed the image locations above don't use getModID()
// Change their locations to reflect your actual ID rather than theDefault. They get loaded before getID is a thing.
logger.info("Done subscribing");
logger.info("Creating the color " + TheDefault.Enums.COLOR_GRAY.toString());
BaseMod.addColor(TheDefault.Enums.COLOR_GRAY, DEFAULT_GRAY, DEFAULT_GRAY, DEFAULT_GRAY,
DEFAULT_GRAY, DEFAULT_GRAY, DEFAULT_GRAY, DEFAULT_GRAY,
ATTACK_DEFAULT_GRAY, SKILL_DEFAULT_GRAY, POWER_DEFAULT_GRAY, ENERGY_ORB_DEFAULT_GRAY,
ATTACK_DEFAULT_GRAY_PORTRAIT, SKILL_DEFAULT_GRAY_PORTRAIT, POWER_DEFAULT_GRAY_PORTRAIT,
ENERGY_ORB_DEFAULT_GRAY_PORTRAIT, CARD_ENERGY_ORB);
logger.info("Done creating the color");
logger.info("Adding mod settings");
// This loads the mod settings.
// The actual mod Button is added below in receivePostInitialize()
theDefaultDefaultSettings.setProperty(ENABLE_PLACEHOLDER_SETTINGS, "FALSE"); // This is the default setting. It's actually set...
try {
SpireConfig config = new SpireConfig("defaultMod", "theDefaultConfig", theDefaultDefaultSettings); // ...right here
// the "fileName" parameter is the name of the file MTS will create where it will save our setting.
config.load(); // Load the setting and set the boolean to equal it
enablePlaceholder = config.getBool(ENABLE_PLACEHOLDER_SETTINGS);
} catch (Exception e) {
e.printStackTrace();
}
logger.info("Done adding mod settings");
}
// ====== NO EDIT AREA ======
// DON'T TOUCH THIS STUFF. IT IS HERE FOR STANDARDIZATION BETWEEN MODS AND TO ENSURE GOOD CODE PRACTICES.
// IF YOU MODIFY THIS I WILL HUNT YOU DOWN AND DOWNVOTE YOUR MOD ON WORKSHOP
public static void setModID(String ID) { // DON'T EDIT
Gson coolG = new Gson(); // EY DON'T EDIT THIS
// String IDjson = Gdx.files.internal("IDCheckStringsDONT-EDIT-AT-ALL.json").readString(String.valueOf(StandardCharsets.UTF_8)); // i hate u Gdx.files
InputStream in = DefaultMod.class.getResourceAsStream("/IDCheckStringsDONT-EDIT-AT-ALL.json"); // DON'T EDIT THIS ETHER
IDCheckDontTouchPls EXCEPTION_STRINGS = coolG.fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), IDCheckDontTouchPls.class); // OR THIS, DON'T EDIT IT
logger.info("You are attempting to set your mod ID as: " + ID); // NO WHY
if (ID.equals(EXCEPTION_STRINGS.DEFAULTID)) { // DO *NOT* CHANGE THIS ESPECIALLY, TO EDIT YOUR MOD ID, SCROLL UP JUST A LITTLE, IT'S JUST ABOVE
throw new RuntimeException(EXCEPTION_STRINGS.EXCEPTION); // THIS ALSO DON'T EDIT
} else if (ID.equals(EXCEPTION_STRINGS.DEVID)) { // NO
modID = EXCEPTION_STRINGS.DEFAULTID; // DON'T
} else { // NO EDIT AREA
modID = ID; // DON'T WRITE OR CHANGE THINGS HERE NOT EVEN A LITTLE
} // NO
logger.info("Success! ID is " + modID); // WHY WOULD U WANT IT NOT TO LOG?? DON'T EDIT THIS.
} // NO
public static String getModID() { // NO
return modID; // DOUBLE NO
} // NU-UH
private static void pathCheck() { // ALSO NO
Gson coolG = new Gson(); // NOPE DON'T EDIT THIS
// String IDjson = Gdx.files.internal("IDCheckStringsDONT-EDIT-AT-ALL.json").readString(String.valueOf(StandardCharsets.UTF_8)); // i still hate u btw Gdx.files
InputStream in = DefaultMod.class.getResourceAsStream("/IDCheckStringsDONT-EDIT-AT-ALL.json"); // DON'T EDIT THISSSSS
IDCheckDontTouchPls EXCEPTION_STRINGS = coolG.fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), IDCheckDontTouchPls.class); // NAH, NO EDIT
String packageName = DefaultMod.class.getPackage().getName(); // STILL NO EDIT ZONE
FileHandle resourcePathExists = Gdx.files.internal(getModID() + "Resources"); // PLEASE DON'T EDIT THINGS HERE, THANKS
if (!modID.equals(EXCEPTION_STRINGS.DEVID)) { // LEAVE THIS EDIT-LESS
if (!packageName.equals(getModID())) { // NOT HERE ETHER
throw new RuntimeException(EXCEPTION_STRINGS.PACKAGE_EXCEPTION + getModID()); // THIS IS A NO-NO
} // WHY WOULD U EDIT THIS
if (!resourcePathExists.exists()) { // DON'T CHANGE THIS
throw new RuntimeException(EXCEPTION_STRINGS.RESOURCE_FOLDER_EXCEPTION + getModID() + "Resources"); // NOT THIS
}// NO
}// NO
}// NO
// ====== YOU CAN EDIT AGAIN ======
@SuppressWarnings("unused")
public static void initialize() {
logger.info("========================= Initializing Default Mod. Hi. =========================");
DefaultMod defaultmod = new DefaultMod();
logger.info("========================= /Default Mod Initialized. Hello World./ =========================");
}
// ============== /SUBSCRIBE, CREATE THE COLOR_GRAY, INITIALIZE/ =================
// =============== LOAD THE CHARACTER =================
@Override
public void receiveEditCharacters() {
logger.info("Beginning to edit characters. " + "Add " + TheDefault.Enums.THE_DEFAULT.toString());
BaseMod.addCharacter(new TheDefault("the Default", TheDefault.Enums.THE_DEFAULT),
THE_DEFAULT_BUTTON, THE_DEFAULT_PORTRAIT, TheDefault.Enums.THE_DEFAULT);
receiveEditPotions();
logger.info("Added " + TheDefault.Enums.THE_DEFAULT.toString());
}
// =============== /LOAD THE CHARACTER/ =================
// =============== POST-INITIALIZE =================
@Override
public void receivePostInitialize() {
logger.info("Loading badge image and mod options");
// Load the Mod Badge
Texture badgeTexture = TextureLoader.getTexture(BADGE_IMAGE);
// Create the Mod Menu
ModPanel settingsPanel = new ModPanel();
// Create the on/off button:
ModLabeledToggleButton enableNormalsButton = new ModLabeledToggleButton("This is the text which goes next to the checkbox.",
350.0f, 700.0f, Settings.CREAM_COLOR, FontHelper.charDescFont, // Position (trial and error it), color, font
enablePlaceholder, // Boolean it uses
settingsPanel, // The mod panel in which this button will be in
(label) -> {}, // thing??????? idk
(button) -> { // The actual button:
enablePlaceholder = button.enabled; // The boolean true/false will be whether the button is enabled or not
try {
// And based on that boolean, set the settings and save them
SpireConfig config = new SpireConfig("defaultMod", "theDefaultConfig", theDefaultDefaultSettings);
config.setBool(ENABLE_PLACEHOLDER_SETTINGS, enablePlaceholder);
config.save();
} catch (Exception e) {
e.printStackTrace();
}
});
settingsPanel.addUIElement(enableNormalsButton); // Add the button to the settings panel. Button is a go.
BaseMod.registerModBadge(badgeTexture, MODNAME, AUTHOR, DESCRIPTION, settingsPanel);
// =============== EVENTS =================
// This event will be exclusive to the City (act 2). If you want an event that's present at any
// part of the game, simply don't include the dungeon ID
// If you want to have a character-specific event, look at slimebound (CityRemoveEventPatch).
// Essentially, you need to patch the game and say "if a player is not playing my character class, remove the event from the pool"
BaseMod.addEvent(IdentityCrisisEvent.ID, IdentityCrisisEvent.class, TheCity.ID);
// =============== /EVENTS/ =================
logger.info("Done loading badge Image and mod options");
}
// =============== / POST-INITIALIZE/ =================
// ================ ADD POTIONS ===================
public void receiveEditPotions() {
logger.info("Beginning to edit potions");
// Class Specific Potion. If you want your potion to not be class-specific,
// just remove the player class at the end (in this case the "TheDefaultEnum.THE_DEFAULT".
// Remember, you can press ctrl+P inside parentheses like addPotions)
BaseMod.addPotion(PlaceholderPotion.class, PLACEHOLDER_POTION_LIQUID, PLACEHOLDER_POTION_HYBRID, PLACEHOLDER_POTION_SPOTS, PlaceholderPotion.POTION_ID, TheDefault.Enums.THE_DEFAULT);
logger.info("Done editing potions");
}
// ================ /ADD POTIONS/ ===================
// ================ ADD RELICS ===================
@Override
public void receiveEditRelics() {
logger.info("Adding relics");
// This adds a character specific relic. Only when you play with the mentioned color, will you get this relic.
BaseMod.addRelicToCustomPool(new PlaceholderRelic(), TheDefault.Enums.COLOR_GRAY);
BaseMod.addRelicToCustomPool(new BottledPlaceholderRelic(), TheDefault.Enums.COLOR_GRAY);
BaseMod.addRelicToCustomPool(new DefaultClickableRelic(), TheDefault.Enums.COLOR_GRAY);
// This adds a relic to the Shared pool. Every character can find this relic.
BaseMod.addRelic(new PlaceholderRelic2(), RelicType.SHARED);
// Mark relics as seen (the others are all starters so they're marked as seen in the character file
UnlockTracker.markRelicAsSeen(BottledPlaceholderRelic.ID);
logger.info("Done adding relics!");
}
// ================ /ADD RELICS/ ===================
// ================ ADD CARDS ===================
@Override
public void receiveEditCards() {
logger.info("Adding variables");
//Ignore this
pathCheck();
// Add the Custom Dynamic Variables
logger.info("Add variables");
// Add the Custom Dynamic variables
BaseMod.addDynamicVariable(new DefaultCustomVariable());
BaseMod.addDynamicVariable(new DefaultSecondMagicNumber());
logger.info("Adding cards");
// Add the cards
// Don't comment out/delete these cards (yet). You need 1 of each type and rarity (technically) for your game not to crash
// when generating card rewards/shop screen items.
BaseMod.addCard(new OrbSkill());
BaseMod.addCard(new DefaultSecondMagicNumberSkill());
BaseMod.addCard(new DefaultCommonAttack());
BaseMod.addCard(new DefaultAttackWithVariable());
BaseMod.addCard(new DefaultCommonSkill());
BaseMod.addCard(new DefaultCommonPower());
BaseMod.addCard(new DefaultUncommonSkill());
BaseMod.addCard(new DefaultUncommonAttack());
BaseMod.addCard(new DefaultUncommonPower());
BaseMod.addCard(new DefaultRareAttack());
BaseMod.addCard(new DefaultRareSkill());
BaseMod.addCard(new DefaultRarePower());
logger.info("Making sure the cards are unlocked.");
// Unlock the cards
// This is so that they are all "seen" in the library, for people who like to look at the card list
// before playing your mod.
UnlockTracker.unlockCard(OrbSkill.ID);
UnlockTracker.unlockCard(DefaultSecondMagicNumberSkill.ID);
UnlockTracker.unlockCard(DefaultCommonAttack.ID);
UnlockTracker.unlockCard(DefaultAttackWithVariable.ID);
UnlockTracker.unlockCard(DefaultCommonSkill.ID);
UnlockTracker.unlockCard(DefaultCommonPower.ID);
UnlockTracker.unlockCard(DefaultUncommonSkill.ID);
UnlockTracker.unlockCard(DefaultUncommonAttack.ID);
UnlockTracker.unlockCard(DefaultUncommonPower.ID);
UnlockTracker.unlockCard(DefaultRareAttack.ID);
UnlockTracker.unlockCard(DefaultRareSkill.ID);
UnlockTracker.unlockCard(DefaultRarePower.ID);
logger.info("Done adding cards!");
}
// There are better ways to do this than listing every single individual card, but I do not want to complicate things
// in a "tutorial" mod. This will do and it's completely ok to use. If you ever want to clean up and
// shorten all the imports, go look take a look at other mods, such as Hubris.
// ================ /ADD CARDS/ ===================
// ================ LOAD THE TEXT ===================
@Override
public void receiveEditStrings() {
logger.info("You seeing this?");
logger.info("Beginning to edit strings for mod with ID: " + getModID());
// CardStrings
BaseMod.loadCustomStringsFile(CardStrings.class,
getModID() + "Resources/localization/eng/DefaultMod-Card-Strings.json");
// PowerStrings
BaseMod.loadCustomStringsFile(PowerStrings.class,
getModID() + "Resources/localization/eng/DefaultMod-Power-Strings.json");
// RelicStrings
BaseMod.loadCustomStringsFile(RelicStrings.class,
getModID() + "Resources/localization/eng/DefaultMod-Relic-Strings.json");
// Event Strings
BaseMod.loadCustomStringsFile(EventStrings.class,
getModID() + "Resources/localization/eng/DefaultMod-Event-Strings.json");
// PotionStrings
BaseMod.loadCustomStringsFile(PotionStrings.class,
getModID() + "Resources/localization/eng/DefaultMod-Potion-Strings.json");
// CharacterStrings
BaseMod.loadCustomStringsFile(CharacterStrings.class,
getModID() + "Resources/localization/eng/DefaultMod-Character-Strings.json");
// OrbStrings
BaseMod.loadCustomStringsFile(OrbStrings.class,
getModID() + "Resources/localization/eng/DefaultMod-Orb-Strings.json");
logger.info("Done edittting strings");
}
// ================ /LOAD THE TEXT/ ===================
// ================ LOAD THE KEYWORDS ===================
@Override
public void receiveEditKeywords() {
// Keywords on cards are supposed to be Capitalized, while in Keyword-String.json they're lowercase
//
// Multiword keywords on cards are done With_Underscores
//
// If you're using multiword keywords, the first element in your NAMES array in your keywords-strings.json has to be the same as the PROPER_NAME.
// That is, in Card-Strings.json you would have #yA_Long_Keyword (#y highlights the keyword in yellow).
// In Keyword-Strings.json you would have PROPER_NAME as A Long Keyword and the first element in NAMES be a long keyword, and the second element be a_long_keyword
Gson gson = new Gson();
String json = Gdx.files.internal(getModID() + "Resources/localization/eng/DefaultMod-Keyword-Strings.json").readString(String.valueOf(StandardCharsets.UTF_8));
com.evacipated.cardcrawl.mod.stslib.Keyword[] keywords = gson.fromJson(json, com.evacipated.cardcrawl.mod.stslib.Keyword[].class);
if (keywords != null) {
for (Keyword keyword : keywords) {
BaseMod.addKeyword(getModID().toLowerCase(), keyword.PROPER_NAME, keyword.NAMES, keyword.DESCRIPTION);
// getModID().toLowerCase() makes your keyword mod specific (it won't show up in other cards that use that word)
}
}
}
// ================ /LOAD THE KEYWORDS/ ===================
// this adds "ModName:" before the ID of any card/relic/power etc.
// in order to avoid conflicts if any other mod uses the same ID.
public static String makeID(String idText) {
return getModID() + ":" + idText;
}
}

View File

@ -0,0 +1,59 @@
package constructTheArena.actions;
import com.megacrit.cardcrawl.actions.AbstractGameAction;
import com.megacrit.cardcrawl.actions.common.ApplyPowerAction;
import com.megacrit.cardcrawl.cards.DamageInfo;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import com.megacrit.cardcrawl.relics.ChemicalX;
import com.megacrit.cardcrawl.ui.panels.EnergyPanel;
import constructTheArena.powers.CommonPower;
public class UncommonPowerAction extends AbstractGameAction {
private boolean freeToPlayOnce;
private int magicNumber;
private AbstractPlayer p;
private int energyOnUse;
private boolean upgraded;
public UncommonPowerAction(final AbstractPlayer p, final AbstractMonster m,
final int magicNumber, final boolean upgraded,
final DamageInfo.DamageType damageTypeForTurn, final boolean freeToPlayOnce,
final int energyOnUse) {
this.freeToPlayOnce = false;
this.p = p;
this.magicNumber = magicNumber;
this.freeToPlayOnce = freeToPlayOnce;
actionType = ActionType.SPECIAL;
this.energyOnUse = energyOnUse;
this.upgraded = upgraded;
}
@Override
public void update() {
int effect = EnergyPanel.totalCount;
if (energyOnUse != -1) {
effect = energyOnUse;
}
if (p.hasRelic(ChemicalX.ID)) {
effect += 2;
p.getRelic(ChemicalX.ID).flash();
}
if (upgraded) {
++effect;
}
if (effect > 0) {
for (int i = 0; i < effect; ++i) {
AbstractDungeon.actionManager.addToBottom(new ApplyPowerAction(p, p,
new CommonPower(p, p, magicNumber), magicNumber,
AttackEffect.BLUNT_LIGHT));
}
if (!freeToPlayOnce) {
p.energy.use(EnergyPanel.totalCount);
}
}
isDone = true;
}
}

View File

@ -0,0 +1,55 @@
package constructTheArena.cards;
import basemod.abstracts.CustomCard;
public abstract class AbstractDefaultCard extends CustomCard {
// Custom Abstract Cards can be a bit confusing. While this is a simple base for simply adding a second magic number,
// if you're new to modding I suggest you skip this file until you know what unique things that aren't provided
// by default, that you need in your own cards.
// In this example, we use a custom Abstract Card in order to define a new magic number. From here on out, we can
// simply use that in our cards, so long as we put "extends AbstractDynamicCard" instead of "extends CustomCard" at the start.
// In simple terms, it's for things that we don't want to define again and again in every single card we make.
public int defaultSecondMagicNumber; // Just like magic number, or any number for that matter, we want our regular, modifiable stat
public int defaultBaseSecondMagicNumber; // And our base stat - the number in it's base state. It will reset to that by default.
public boolean upgradedDefaultSecondMagicNumber; // A boolean to check whether the number has been upgraded or not.
public boolean isDefaultSecondMagicNumberModified; // A boolean to check whether the number has been modified or not, for coloring purposes. (red/green)
public AbstractDefaultCard(final String id,
final String name,
final String img,
final int cost,
final String rawDescription,
final CardType type,
final CardColor color,
final CardRarity rarity,
final CardTarget target) {
super(id, name, img, cost, rawDescription, type, color, rarity, target);
// Set all the things to their default values.
isCostModified = false;
isCostModifiedForTurn = false;
isDamageModified = false;
isBlockModified = false;
isMagicNumberModified = false;
isDefaultSecondMagicNumberModified = false;
}
public void displayUpgrades() { // Display the upgrade - when you click a card to upgrade it
super.displayUpgrades();
if (upgradedDefaultSecondMagicNumber) { // If we set upgradedDefaultSecondMagicNumber = true in our card.
defaultSecondMagicNumber = defaultBaseSecondMagicNumber; // Show how the number changes, as out of combat, the base number of a card is shown.
isDefaultSecondMagicNumberModified = true; // Modified = true, color it green to highlight that the number is being changed.
}
}
public void upgradeDefaultSecondMagicNumber(int amount) { // If we're upgrading (read: changing) the number. Note "upgrade" and NOT "upgraded" - 2 different things. One is a boolean, and then this one is what you will usually use - change the integer by how much you want to upgrade.
defaultBaseSecondMagicNumber += amount; // Upgrade the number by the amount you provide in your card.
defaultSecondMagicNumber = defaultBaseSecondMagicNumber; // Set the number to be equal to the base value.
upgradedDefaultSecondMagicNumber = true; // Upgraded = true - which does what the above method does.
}
}

View File

@ -0,0 +1,27 @@
package constructTheArena.cards;
import static com.megacrit.cardcrawl.core.CardCrawlGame.languagePack;
public abstract class AbstractDynamicCard extends AbstractDefaultCard {
// "How come DefaultCommonAttack extends CustomCard and not DynamicCard like all the rest?"
// Well every card, at the end of the day, extends CustomCard.
// Abstract Default Card extends CustomCard and builds up on it, adding a second magic number. Your card can extend it and
// bam - you can have a second magic number in that card (Learn Java inheritance if you want to know how that works).
// Abstract Dynamic Card builds up on Abstract Default Card even more and makes it so that you don't need to add
// the NAME and the DESCRIPTION into your card - it'll get it automatically. Of course, this functionality could have easily
// Been added to the default card rather than creating a new Dynamic one, but was done so to deliberately.
public AbstractDynamicCard(final String id,
final String img,
final int cost,
final CardType type,
final CardColor color,
final CardRarity rarity,
final CardTarget target) {
super(id, languagePack.getCardStrings(id).NAME, img, cost, languagePack.getCardStrings(id).DESCRIPTION, type, color, rarity, target);
}
}

View File

@ -0,0 +1,86 @@
package constructTheArena.cards;
import com.megacrit.cardcrawl.actions.AbstractGameAction;
import com.megacrit.cardcrawl.actions.common.DamageAction;
import com.megacrit.cardcrawl.cards.DamageInfo;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import constructTheArena.DefaultMod;
import constructTheArena.characters.TheDefault;
import static constructTheArena.DefaultMod.makeCardPath;
// public class ${NAME} extends AbstractDynamicCard
public class CardTemplate extends AbstractDynamicCard {
/*
* "Hey, I wanna make a bunch of cards now." - You, probably.
* ok cool my dude no problem here's the most convenient way to do it:
*
* Copy all of the code here (Ctrl+A > Ctrl+C)
* Ctrl+Shift+A and search up "file and code template"
* Press the + button at the top and name your template whatever it is for - "AttackCard" or "PowerCard" or something up to you.
* Read up on the instructions at the bottom. Basically replace anywhere you'd put your cards name with ${NAME}
* And then you can do custom ones like ${DAMAGE} and ${TARGET} if you want.
* I'll leave some comments on things you might consider replacing with what.
*
* Of course, delete all the comments and add anything you want (For example, if you're making a skill card template you'll
* likely want to replace that new DamageAction with a gain Block one, and add baseBlock instead, or maybe you want a
* universal template where you delete everything unnecessary - up to you)
*
* You can create templates for anything you ever want to. Cards, relics, powers, orbs, etc. etc. etc.
*/
// TEXT DECLARATION
// public static final String ID = DefaultMod.makeID(${NAME}.class.getSimpleName()); // USE THIS ONE FOR THE TEMPLATE;
public static final String ID = DefaultMod.makeID("DefaultCommonAttack"); // DELETE THIS ONE.
public static final String IMG = makeCardPath("Attack.png");// "public static final String IMG = makeCardPath("${NAME}.png");
// This does mean that you will need to have an image with the same NAME as the card in your image folder for it to run correctly.
// /TEXT DECLARATION/
// STAT DECLARATION
private static final CardRarity RARITY = CardRarity.COMMON; // Up to you, I like auto-complete on these
private static final CardTarget TARGET = CardTarget.ENEMY; // since they don't change much.
private static final CardType TYPE = CardType.ATTACK; //
public static final CardColor COLOR = TheDefault.Enums.COLOR_GRAY;
private static final int COST = 1; // COST = ${COST}
private static final int UPGRADED_COST = 0; // UPGRADED_COST = ${UPGRADED_COST}
private static final int DAMAGE = 7; // DAMAGE = ${DAMAGE}
private static final int UPGRADE_PLUS_DMG = 2; // UPGRADE_PLUS_DMG = ${UPGRADED_DAMAGE_INCREASE}
// /STAT DECLARATION/
public CardTemplate() { // public ${NAME}() - This one and the one right under the imports are the most important ones, don't forget them
super(ID, IMG, COST, TYPE, COLOR, RARITY, TARGET);
baseDamage = DAMAGE;
}
// Actions the card should do.
@Override
public void use(AbstractPlayer p, AbstractMonster m) {
AbstractDungeon.actionManager.addToBottom(
new DamageAction(m, new DamageInfo(p, damage, damageTypeForTurn), AbstractGameAction.AttackEffect.SLASH_HORIZONTAL));
}
// Upgraded stats.
@Override
public void upgrade() {
if (!upgraded) {
upgradeName();
upgradeDamage(UPGRADE_PLUS_DMG);
upgradeBaseCost(UPGRADED_COST);
initializeDescription();
}
}
}

View File

@ -0,0 +1,76 @@
package constructTheArena.cards;
import com.megacrit.cardcrawl.actions.AbstractGameAction;
import com.megacrit.cardcrawl.actions.common.DamageAction;
import com.megacrit.cardcrawl.cards.DamageInfo;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import com.megacrit.cardcrawl.ui.panels.EnergyPanel;
import constructTheArena.DefaultMod;
import constructTheArena.characters.TheDefault;
import static constructTheArena.DefaultMod.makeCardPath;
public class DefaultAttackWithVariable extends AbstractDynamicCard {
/*
* Wiki-page: https://github.com/daviscook477/BaseMod/wiki/Custom-Cards
*
* Special Strike: Deal 7 (*) damage times the energy you currently have.
*/
// TEXT DECLARATION
public static final String ID = DefaultMod.makeID(DefaultAttackWithVariable.class.getSimpleName());
public static final String IMG = makeCardPath("Attack.png");
// /TEXT DECLARATION/
// STAT DECLARATION
private static final CardRarity RARITY = CardRarity.COMMON;
private static final CardTarget TARGET = CardTarget.ENEMY;
private static final CardType TYPE = CardType.ATTACK;
public static final CardColor COLOR = TheDefault.Enums.COLOR_GRAY;
private static final int COST = 1;
private static final int DAMAGE = 7;
private static final int UPGRADE_PLUS_DMG = 1;
public int specialDamage;
// /STAT DECLARATION/
public DefaultAttackWithVariable() {
super(ID, IMG, COST, TYPE, COLOR, RARITY, TARGET);
baseDamage = DAMAGE;
isMultiDamage = true;
}
// Actions the card should do.
@Override
public void use(AbstractPlayer p, AbstractMonster m) {
// Create an int which equals to your current energy.
int effect = EnergyPanel.totalCount;
// For each energy, create 1 damage action.
for (int i = 0; i < effect; i++) {
AbstractDungeon.actionManager.addToBottom(
new DamageAction(m, new DamageInfo(p, damage, damageTypeForTurn),
AbstractGameAction.AttackEffect.FIRE));
}
}
// Upgraded stats.
@Override
public void upgrade() {
if (!upgraded) {
upgradeName();
upgradeDamage(UPGRADE_PLUS_DMG);
initializeDescription();
}
}
}

View File

@ -0,0 +1,109 @@
package constructTheArena.cards;
import basemod.abstracts.CustomCard;
import com.megacrit.cardcrawl.actions.AbstractGameAction;
import com.megacrit.cardcrawl.actions.common.DamageAction;
import com.megacrit.cardcrawl.cards.DamageInfo;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.localization.CardStrings;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import constructTheArena.DefaultMod;
import constructTheArena.characters.TheDefault;
import static constructTheArena.DefaultMod.makeCardPath;
// "How come this card extends CustomCard and not DynamicCard like all the rest?"
// Skip this question until you start figuring out the AbstractDefaultCard/AbstractDynamicCard and just extend DynamicCard
// for your own ones like all the other cards.
// Well every card, at the end of the day, extends CustomCard.
// Abstract Default Card extends CustomCard and builds up on it, adding a second magic number. Your card can extend it and
// bam - you can have a second magic number in that card (Learn Java inheritance if you want to know how that works).
// Abstract Dynamic Card builds up on Abstract Default Card even more and makes it so that you don't need to add
// the NAME and the DESCRIPTION into your card - it'll get it automatically. Of course, this functionality could have easily
// Been added to the default card rather than creating a new Dynamic one, but was done so to deliberately.
public class DefaultCommonAttack extends CustomCard {
/*
* Wiki-page: https://github.com/daviscook477/BaseMod/wiki/Custom-Cards
*
* Strike Deal 7(9) damage.
*/
// TEXT DECLARATION
public static final String ID = DefaultMod.makeID(DefaultCommonAttack.class.getSimpleName());
private static final CardStrings cardStrings = CardCrawlGame.languagePack.getCardStrings(ID);
public static final String IMG = makeCardPath("Attack.png");
// Setting the image as as easy as can possibly be now. You just need to provide the image name
// and make sure it's in the correct folder. That's all.
// There's makeCardPath, makeRelicPath, power, orb, event, etc..
// The list of all of them can be found in the main DefaultMod.java file in the
// ==INPUT TEXTURE LOCATION== section under ==MAKE IMAGE PATHS==
public static final String NAME = cardStrings.NAME;
public static final String DESCRIPTION = cardStrings.DESCRIPTION;
// /TEXT DECLARATION/
// STAT DECLARATION
private static final CardRarity RARITY = CardRarity.BASIC;
private static final CardTarget TARGET = CardTarget.ENEMY;
private static final CardType TYPE = CardType.ATTACK;
public static final CardColor COLOR = TheDefault.Enums.COLOR_GRAY;
private static final int COST = 1;
private static final int DAMAGE = 6;
private static final int UPGRADE_PLUS_DMG = 3;
// Hey want a second damage/magic/block/unique number??? Great!
// Go check out DefaultAttackWithVariable and theDefault.variable.DefaultCustomVariable
// that's how you get your own custom variable that you can use for anything you like.
// Feel free to explore other mods to see what variables they personally have and create your own ones.
// /STAT DECLARATION/
public DefaultCommonAttack() {
super(ID, NAME, IMG, COST, DESCRIPTION, TYPE, COLOR, RARITY, TARGET);
// Aside from baseDamage/MagicNumber/Block there's also a few more.
// Just type this.base and let intelliJ auto complete for you, or, go read up AbstractCard
baseDamage = DAMAGE;
this.tags.add(CardTags.STARTER_STRIKE); //Tag your strike, defend and form (Wraith form, Demon form, Echo form, etc.) cards so that they function correctly.
this.tags.add(CardTags.STRIKE);
}
// Actions the card should do.
@Override
public void use(AbstractPlayer p, AbstractMonster m) {
AbstractDungeon.actionManager.addToBottom( // The action managed queues all the actions a card should do.
// addToTop - first
// addToBottom - last
// 99.99% of the time you just want to addToBottom all of them.
// Please do that unless you need to add to top for some specific reason.
new DamageAction(m, new DamageInfo(p, damage, damageTypeForTurn),
// a list of existing actions can be found at com.megacrit.cardcrawl.actions but
// Chances are you'd instead look at "hey my card is similar to this basegame card"
// Let's find out what action *it* uses.
// I.e. i want energy gain or card draw, lemme check out Adrenaline
// P.s. if you want to damage ALL enemies OUTSIDE of a card, check out the custom orb.
AbstractGameAction.AttackEffect.SLASH_HORIZONTAL)); // The animation the damage action uses to hit.
}
// Upgraded stats.
@Override
public void upgrade() {
if (!upgraded) {
upgradeName();
upgradeDamage(UPGRADE_PLUS_DMG);
initializeDescription();
}
}
}

View File

@ -0,0 +1,88 @@
package constructTheArena.cards;
import com.megacrit.cardcrawl.actions.common.ApplyPowerAction;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.localization.CardStrings;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import constructTheArena.DefaultMod;
import constructTheArena.characters.TheDefault;
import constructTheArena.powers.CommonPower;
import static constructTheArena.DefaultMod.makeCardPath;
public class DefaultCommonPower extends AbstractDynamicCard {
/*
* Wiki-page: https://github.com/daviscook477/BaseMod/wiki/Custom-Cards
*
* Hold Place Gain 1(2) Keywords(s).
*/
// TEXT DECLARATION
public static final String ID = DefaultMod.makeID(DefaultCommonPower.class.getSimpleName());
public static final String IMG = makeCardPath("Power.png");
private static final CardStrings cardStrings = CardCrawlGame.languagePack.getCardStrings(ID);
public static final String UPGRADE_DESCRIPTION = cardStrings.UPGRADE_DESCRIPTION;
// /TEXT DECLARATION/
// STAT DECLARATION
private static final CardRarity RARITY = CardRarity.COMMON;
private static final CardTarget TARGET = CardTarget.SELF;
private static final CardType TYPE = CardType.POWER;
public static final CardColor COLOR = TheDefault.Enums.COLOR_GRAY;
private static final int COST = 1;
private static final int MAGIC = 1;
private static final int UPGRADE_MAGIC = 1;
// Hey want a second magic/damage/block/unique number??? Great!
// Go check out DefaultAttackWithVariable and theDefault.variable.DefaultCustomVariable
// that's how you get your own custom variable that you can use for anything you like.
// Feel free to explore other mods to see what variables they personally have and create your own ones.
// /STAT DECLARATION/
public DefaultCommonPower() {
super(ID, IMG, COST, TYPE, COLOR, RARITY, TARGET);
magicNumber = baseMagicNumber = MAGIC;
}
// Actions the card should do.
@Override
public void use(AbstractPlayer p, AbstractMonster m) {
AbstractDungeon.actionManager.addToBottom(new ApplyPowerAction(p, p,
new CommonPower(p, p, magicNumber), magicNumber));
/*
Hey do you see this "amount" and "stackAmount" up here^ (press ctrl+p inside the parentheses to see parameters)
THIS DOES NOT MEAN APPLY 1 POWER 1 TIMES. If you put 2 in both numbers it would apply 2. NOT "2 STACKS, 2 TIMES".
The stackAmount is for telling ApplyPowerAction what to do if a stack already exists. Which means that it will go
"ah, I see this power has an ID ("") that matches the power I received. I will therefore instead add the stackAmount value
to this existing power's amount" (Thank you Johnny)
Which is why if we want to apply 2 stacks with this card every time, want 2 in both numbers -
"Always add 2, even if the player already has this power."
*/
}
//Upgraded stats.
@Override
public void upgrade() {
if (!upgraded) {
upgradeName();
upgradeMagicNumber(UPGRADE_MAGIC);
rawDescription = UPGRADE_DESCRIPTION;
initializeDescription();
}
}
}

View File

@ -0,0 +1,66 @@
package constructTheArena.cards;
import com.megacrit.cardcrawl.actions.common.GainBlockAction;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import constructTheArena.DefaultMod;
import constructTheArena.characters.TheDefault;
import static constructTheArena.DefaultMod.makeCardPath;
public class DefaultCommonSkill extends AbstractDynamicCard {
/*
* Wiki-page: https://github.com/daviscook477/BaseMod/wiki/Custom-Cards
*
* Defend Gain 5 (8) block.
*/
// TEXT DECLARATION
public static final String ID = DefaultMod.makeID(DefaultCommonSkill.class.getSimpleName());
public static final String IMG = makeCardPath("Skill.png");
// /TEXT DECLARATION/
// STAT DECLARATION
private static final CardRarity RARITY = CardRarity.BASIC;
private static final CardTarget TARGET = CardTarget.SELF;
private static final CardType TYPE = CardType.SKILL;
public static final CardColor COLOR = TheDefault.Enums.COLOR_GRAY;
private static final int COST = 1;
private static final int BLOCK = 5;
private static final int UPGRADE_PLUS_BLOCK = 3;
// /STAT DECLARATION/
public DefaultCommonSkill() {
super(ID, IMG, COST, TYPE, COLOR, RARITY, TARGET);
baseBlock = BLOCK;
this.tags.add(CardTags.STARTER_DEFEND); //Tag your strike, defend and form (Wraith form, Demon form, Echo form, etc.) cards so that they function correctly.
}
// Actions the card should do.
@Override
public void use(AbstractPlayer p, AbstractMonster m) {
AbstractDungeon.actionManager.addToBottom(new GainBlockAction(p, p, block));
}
//Upgraded stats.
@Override
public void upgrade() {
if (!upgraded) {
upgradeName();
upgradeBlock(UPGRADE_PLUS_BLOCK);
initializeDescription();
}
}
}

View File

@ -0,0 +1,74 @@
package constructTheArena.cards;
import com.megacrit.cardcrawl.actions.AbstractGameAction;
import com.megacrit.cardcrawl.actions.animations.VFXAction;
import com.megacrit.cardcrawl.actions.common.DamageAction;
import com.megacrit.cardcrawl.cards.DamageInfo;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import com.megacrit.cardcrawl.vfx.combat.WeightyImpactEffect;
import constructTheArena.DefaultMod;
import constructTheArena.characters.TheDefault;
import static constructTheArena.DefaultMod.makeCardPath;
public class DefaultRareAttack extends AbstractDynamicCard {
/*
* Wiki-page: https://github.com/daviscook477/BaseMod/wiki/Custom-Cards
*
* TOUCH Deal 30(35) damage.
*/
// TEXT DECLARATION
public static final String ID = DefaultMod.makeID(DefaultRareAttack.class.getSimpleName());
public static final String IMG = makeCardPath("Attack.png");
// /TEXT DECLARATION/
// STAT DECLARATION
private static final CardRarity RARITY = CardRarity.RARE;
private static final CardTarget TARGET = CardTarget.ENEMY;
private static final CardType TYPE = CardType.ATTACK;
public static final CardColor COLOR = TheDefault.Enums.COLOR_GRAY;
private static final int COST = 2;
private static final int DAMAGE = 30;
private static final int UPGRADE_PLUS_DMG = 5;
// /STAT DECLARATION/
public DefaultRareAttack() {
super(ID, IMG, COST, TYPE, COLOR, RARITY, TARGET);
baseDamage = DAMAGE;
}
// Actions the card should do.
@Override
public void use(AbstractPlayer p, AbstractMonster m) {
if (m != null) {
AbstractDungeon.actionManager.addToBottom(new VFXAction(new WeightyImpactEffect(m.hb.cX, m.hb.cY)));
}
AbstractDungeon.actionManager.addToBottom(
new DamageAction(m, new DamageInfo(p, damage, damageTypeForTurn),
AbstractGameAction.AttackEffect.NONE));
}
//Upgraded stats.
@Override
public void upgrade() {
if (!upgraded) {
upgradeName();
upgradeDamage(UPGRADE_PLUS_DMG);
}
}
}

View File

@ -0,0 +1,70 @@
package constructTheArena.cards;
import basemod.helpers.BaseModCardTags;
import com.megacrit.cardcrawl.actions.common.ApplyPowerAction;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import constructTheArena.DefaultMod;
import constructTheArena.characters.TheDefault;
import constructTheArena.powers.RarePower;
import static constructTheArena.DefaultMod.makeCardPath;
public class DefaultRarePower extends AbstractDynamicCard {
/*
* Wiki-page: https://github.com/daviscook477/BaseMod/wiki/Custom-Cards
*
* In-Progress Form At the start of your turn, play a TOUCH.
*/
// TEXT DECLARATION
public static final String ID = DefaultMod.makeID(DefaultRarePower.class.getSimpleName());
public static final String IMG = makeCardPath("Power.png");
// /TEXT DECLARATION/
// STAT DECLARATION
private static final CardRarity RARITY = CardRarity.RARE;
private static final CardTarget TARGET = CardTarget.SELF;
private static final CardType TYPE = CardType.POWER;
public static final CardColor COLOR = TheDefault.Enums.COLOR_GRAY;
private static final int COST = 3;
private static final int UPGRADE_COST = 2;
private static final int MAGIC = 1;
// /STAT DECLARATION/
public DefaultRarePower() {
super(ID, IMG, COST, TYPE, COLOR, RARITY, TARGET);
magicNumber = baseMagicNumber = MAGIC;
this.tags.add(BaseModCardTags.FORM); //Tag your strike, defend and form cards so that they work correctly.
}
// Actions the card should do.
@Override
public void use(AbstractPlayer p, AbstractMonster m) {
AbstractDungeon.actionManager.addToBottom(
new ApplyPowerAction(p, p, new RarePower(p, p, magicNumber), magicNumber));
}
//Upgraded stats.
@Override
public void upgrade() {
if (!upgraded) {
upgradeName();
upgradeBaseCost(UPGRADE_COST);
initializeDescription();
}
}
}

View File

@ -0,0 +1,78 @@
package constructTheArena.cards;
import com.megacrit.cardcrawl.actions.common.ApplyPowerAction;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.localization.CardStrings;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import com.megacrit.cardcrawl.powers.VulnerablePower;
import constructTheArena.DefaultMod;
import constructTheArena.characters.TheDefault;
import static constructTheArena.DefaultMod.makeCardPath;
public class DefaultRareSkill extends AbstractDynamicCard {
/*
* Wiki-page: https://github.com/daviscook477/BaseMod/wiki/Custom-Cards
*
* For Each Loop x2" "Apply 1 Vulnerable to all enemies, 2(3) times.
*/
// TEXT DECLARATION
public static final String ID = DefaultMod.makeID(DefaultRareSkill.class.getSimpleName());
public static final String IMG = makeCardPath("Skill.png");
private static final CardStrings cardStrings = CardCrawlGame.languagePack.getCardStrings(ID);
public static final String UPGRADE_DESCRIPTION = cardStrings.UPGRADE_DESCRIPTION;
// /TEXT DECLARATION/
// STAT DECLARATION
private static final CardRarity RARITY = CardRarity.RARE;
private static final CardTarget TARGET = CardTarget.ALL_ENEMY;
private static final CardType TYPE = CardType.SKILL;
public static final CardColor COLOR = TheDefault.Enums.COLOR_GRAY;
private static final int COST = 1;
private int TIMES = 2;
private final int UPGRADE_TIMES = 3;
private int AMOUNT = 1;
// /STAT DECLARATION/
public DefaultRareSkill() {
super(ID, IMG, COST, TYPE, COLOR, RARITY, TARGET);
baseMagicNumber = magicNumber = AMOUNT;
}
// Actions the card should do.
@Override
public void use(AbstractPlayer p, AbstractMonster m) {
for (int i = 0; i < TIMES; i++) {
for (final AbstractMonster mo : AbstractDungeon.getCurrRoom().monsters.monsters) {
AbstractDungeon.actionManager.addToBottom(new ApplyPowerAction(mo, p,
new VulnerablePower(mo, magicNumber, false), magicNumber));
}
}
}
//Upgraded stats.
@Override
public void upgrade() {
if (!upgraded) {
upgradeName();
rawDescription = UPGRADE_DESCRIPTION;
TIMES = UPGRADE_TIMES;
initializeDescription();
}
}
}

View File

@ -0,0 +1,84 @@
package constructTheArena.cards;
import com.megacrit.cardcrawl.actions.common.ApplyPowerAction;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import com.megacrit.cardcrawl.powers.PoisonPower;
import com.megacrit.cardcrawl.powers.VulnerablePower;
import constructTheArena.DefaultMod;
import constructTheArena.characters.TheDefault;
import static constructTheArena.DefaultMod.makeCardPath;
public class DefaultSecondMagicNumberSkill extends AbstractDynamicCard {
/*
* Wiki-page: https://github.com/daviscook477/BaseMod/wiki/Custom-Cards
*
* Using the second magic number isn't very confusing, you just declare and use it the absolutely the same way you would your
* your other ones (attack, block, magic, etc.)
*
* For how to create it, check out:
* https://github.com/daviscook477/BaseMod/wiki/Dynamic-Variables
* The files in this base that detail this are:
* variables.DefaultSecondMagicNumber and cards.AbstractDefaultCard
*
* Apply 2(5) vulnerable and 4(9) poison to an enemy.
*/
// TEXT DECLARATION
public static final String ID = DefaultMod.makeID(DefaultSecondMagicNumberSkill.class.getSimpleName());
public static final String IMG = makeCardPath("Skill.png");
// /TEXT DECLARATION/
// STAT DECLARATION
private static final CardRarity RARITY = CardRarity.COMMON;
private static final CardTarget TARGET = CardTarget.ENEMY;
private static final CardType TYPE = CardType.SKILL;
public static final CardColor COLOR = TheDefault.Enums.COLOR_GRAY;
private static final int COST = 1;
private static final int VULNERABLE = 2;
private static final int UPGRADE_PLUS_VULNERABLE = 3;
private static final int POISON = 4;
private static final int UPGRADE_PLUS_POISON = 5;
// /STAT DECLARATION/
public DefaultSecondMagicNumberSkill() {
super(ID, IMG, COST, TYPE, COLOR, RARITY, TARGET);
magicNumber = baseMagicNumber = VULNERABLE;
defaultSecondMagicNumber = defaultBaseSecondMagicNumber = POISON;
}
// Actions the card should do.
@Override
public void use(AbstractPlayer p, AbstractMonster m) {
AbstractDungeon.actionManager.addToBottom(
new ApplyPowerAction(m, p, new VulnerablePower(m, magicNumber, false), this.magicNumber));
AbstractDungeon.actionManager.addToBottom(
new ApplyPowerAction(m, p, new PoisonPower(m, p, this.defaultSecondMagicNumber), this.defaultSecondMagicNumber));
}
// Upgraded stats.
@Override
public void upgrade() {
if (!this.upgraded) {
this.upgradeName();
this.upgradeMagicNumber(UPGRADE_PLUS_VULNERABLE);
this.upgradeDefaultSecondMagicNumber(UPGRADE_PLUS_POISON);
this.initializeDescription();
}
}
}

View File

@ -0,0 +1,69 @@
package constructTheArena.cards;
import com.megacrit.cardcrawl.actions.AbstractGameAction;
import com.megacrit.cardcrawl.actions.common.DamageAction;
import com.megacrit.cardcrawl.cards.DamageInfo;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import constructTheArena.DefaultMod;
import constructTheArena.characters.TheDefault;
import static constructTheArena.DefaultMod.makeCardPath;
public class DefaultUncommonAttack extends AbstractDynamicCard {
/*
* Wiki-page: https://github.com/daviscook477/BaseMod/wiki/Custom-Cards
*
* Big Slap Deal 10(15)) damage.
*/
// TEXT DECLARATION
public static final String ID = DefaultMod.makeID(DefaultUncommonAttack.class.getSimpleName());
public static final String IMG = makeCardPath("Attack.png");
// /TEXT DECLARATION/
// STAT DECLARATION
private static final CardRarity RARITY = CardRarity.UNCOMMON;
private static final CardTarget TARGET = CardTarget.ENEMY;
private static final CardType TYPE = CardType.ATTACK;
public static final CardColor COLOR = TheDefault.Enums.COLOR_GRAY;
private static final int COST = 1;
private static final int DAMAGE = 10;
private static final int UPGRADE_PLUS_DMG = 5;
// /STAT DECLARATION/
public DefaultUncommonAttack() {
super(ID, IMG, COST, TYPE, COLOR, RARITY, TARGET);
baseDamage = DAMAGE;
}
// Actions the card should do.
@Override
public void use(AbstractPlayer p, AbstractMonster m) {
AbstractDungeon.actionManager.addToBottom(
new DamageAction(m, new DamageInfo(p, damage, damageTypeForTurn),
AbstractGameAction.AttackEffect.BLUNT_LIGHT));
}
//Upgraded stats.
@Override
public void upgrade() {
if (!upgraded) {
upgradeName();
upgradeDamage(UPGRADE_PLUS_DMG);
initializeDescription();
}
}
}

View File

@ -0,0 +1,71 @@
package constructTheArena.cards;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.localization.CardStrings;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import com.megacrit.cardcrawl.ui.panels.EnergyPanel;
import constructTheArena.DefaultMod;
import constructTheArena.actions.UncommonPowerAction;
import constructTheArena.characters.TheDefault;
import static constructTheArena.DefaultMod.makeCardPath;
public class DefaultUncommonPower extends AbstractDynamicCard {
/*
* Wiki-page: https://github.com/daviscook477/BaseMod/wiki/Custom-Cards
*
* Weirdness Apply X (+1) keywords to yourself.
*/
// TEXT DECLARATION
public static final String ID = DefaultMod.makeID(DefaultUncommonPower.class.getSimpleName());
public static final String IMG = makeCardPath("Power.png");
private static final CardStrings cardStrings = CardCrawlGame.languagePack.getCardStrings(ID);
public static final String UPGRADE_DESCRIPTION = cardStrings.UPGRADE_DESCRIPTION;
// /TEXT DECLARATION/
// STAT DECLARATION
private static final CardRarity RARITY = CardRarity.UNCOMMON;
private static final CardTarget TARGET = CardTarget.SELF;
private static final CardType TYPE = CardType.POWER;
public static final CardColor COLOR = TheDefault.Enums.COLOR_GRAY;
private static final int COST = -1;
private static final int MAGIC = 1;
// /STAT DECLARATION/
public DefaultUncommonPower() {
super(ID, IMG, COST, TYPE, COLOR, RARITY, TARGET);
magicNumber = baseMagicNumber = MAGIC;
}
// Actions the card should do.
@Override
public void use(final AbstractPlayer p, final AbstractMonster m) {
if (energyOnUse < EnergyPanel.totalCount) {
energyOnUse = EnergyPanel.totalCount;
}
AbstractDungeon.actionManager.addToBottom(new UncommonPowerAction(p, m, magicNumber,
upgraded, damageTypeForTurn, freeToPlayOnce, energyOnUse));
}
//Upgraded stats.
@Override
public void upgrade() {
if (!upgraded) {
upgradeName();
rawDescription = UPGRADE_DESCRIPTION;
initializeDescription();
}
}
}

View File

@ -0,0 +1,66 @@
package constructTheArena.cards;
import com.megacrit.cardcrawl.actions.common.ApplyPowerAction;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import com.megacrit.cardcrawl.powers.PlatedArmorPower;
import constructTheArena.DefaultMod;
import constructTheArena.characters.TheDefault;
import static constructTheArena.DefaultMod.makeCardPath;
public class DefaultUncommonSkill extends AbstractDynamicCard {
/*
* Wiki-page: https://github.com/daviscook477/BaseMod/wiki/Custom-Cards
*
* A Better Defend Gain 1 Plated Armor. Affected by Dexterity.
*/
// TEXT DECLARATION
public static final String ID = DefaultMod.makeID(DefaultUncommonSkill.class.getSimpleName());
public static final String IMG = makeCardPath("Skill.png");
// /TEXT DECLARATION/
// STAT DECLARATION
private static final CardRarity RARITY = CardRarity.UNCOMMON;
private static final CardTarget TARGET = CardTarget.SELF;
private static final CardType TYPE = CardType.SKILL;
public static final CardColor COLOR = TheDefault.Enums.COLOR_GRAY;
private static final int COST = 1;
private static final int UPGRADE_REDUCED_COST = 0;
private static final int BLOCK = 1;
private static final int UPGRADE_PLUS_BLOCK = 2;
// /STAT DECLARATION/
public DefaultUncommonSkill() {
super(ID, IMG, COST, TYPE, COLOR, RARITY, TARGET);
baseBlock = BLOCK;
}
// Actions the card should do.
@Override
public void use(AbstractPlayer p, AbstractMonster m) {
AbstractDungeon.actionManager.addToBottom(
new ApplyPowerAction(p, p, new PlatedArmorPower(p, block), block));
}
// Upgraded stats.
@Override
public void upgrade() {
if (!upgraded) {
upgradeName();
upgradeBlock(UPGRADE_PLUS_BLOCK);
upgradeBaseCost(UPGRADE_REDUCED_COST);
initializeDescription();
}
}
}

View File

@ -0,0 +1,68 @@
package constructTheArena.cards;
import com.megacrit.cardcrawl.actions.defect.ChannelAction;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.localization.CardStrings;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import constructTheArena.DefaultMod;
import constructTheArena.characters.TheDefault;
import constructTheArena.orbs.DefaultOrb;
import static constructTheArena.DefaultMod.makeCardPath;
public class OrbSkill extends AbstractDynamicCard {
/*
* Orb time.
*
* Channel 1 Default Orb.
*/
// TEXT DECLARATION
public static final String ID = DefaultMod.makeID(OrbSkill.class.getSimpleName());
private static final CardStrings cardStrings = CardCrawlGame.languagePack.getCardStrings(ID);
public static final String IMG = makeCardPath("Skill.png");
public static final String NAME = cardStrings.NAME;
public static final String DESCRIPTION = cardStrings.DESCRIPTION;
// /TEXT DECLARATION/
// STAT DECLARATION
private static final CardRarity RARITY = CardRarity.UNCOMMON;
private static final CardTarget TARGET = CardTarget.SELF;
private static final CardType TYPE = CardType.SKILL;
public static final CardColor COLOR = TheDefault.Enums.COLOR_GRAY;
private static final int COST = 1;
// /STAT DECLARATION/
public OrbSkill() {
super(ID, IMG, COST, TYPE, COLOR, RARITY, TARGET);
}
// Actions the card should do.
@Override
public void use(AbstractPlayer p, AbstractMonster m) {
AbstractDungeon.actionManager.addToBottom(new ChannelAction(new DefaultOrb())); // Channel a Default Orb.
}
// Upgraded stats.
@Override
public void upgrade() {
if (!this.upgraded) {
this.upgradeName();
this.initializeDescription();
}
}
}

View File

@ -0,0 +1,296 @@
package constructTheArena.characters;
import basemod.abstracts.CustomPlayer;
import basemod.animations.SpriterAnimation;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.math.MathUtils;
import com.esotericsoftware.spine.AnimationState;
import com.evacipated.cardcrawl.modthespire.lib.SpireEnum;
import com.megacrit.cardcrawl.actions.AbstractGameAction;
import com.megacrit.cardcrawl.cards.AbstractCard;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.core.EnergyManager;
import com.megacrit.cardcrawl.core.Settings;
import com.megacrit.cardcrawl.helpers.CardLibrary;
import com.megacrit.cardcrawl.helpers.FontHelper;
import com.megacrit.cardcrawl.helpers.ScreenShake;
import com.megacrit.cardcrawl.localization.CharacterStrings;
import com.megacrit.cardcrawl.screens.CharSelectInfo;
import com.megacrit.cardcrawl.unlock.UnlockTracker;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import constructTheArena.DefaultMod;
import constructTheArena.cards.*;
import constructTheArena.relics.DefaultClickableRelic;
import constructTheArena.relics.PlaceholderRelic;
import constructTheArena.relics.PlaceholderRelic2;
import java.util.ArrayList;
import static constructTheArena.DefaultMod.*;
import static constructTheArena.characters.TheDefault.Enums.COLOR_GRAY;
//Wiki-page https://github.com/daviscook477/BaseMod/wiki/Custom-Characters
//and https://github.com/daviscook477/BaseMod/wiki/Migrating-to-5.0
//All text (starting description and loadout, anything labeled TEXT[]) can be found in DefaultMod-character-Strings.json in the resources
public class TheDefault extends CustomPlayer {
public static final Logger logger = LogManager.getLogger(DefaultMod.class.getName());
// =============== CHARACTER ENUMERATORS =================
// These are enums for your Characters color (both general color and for the card library) as well as
// an enum for the name of the player class - IRONCLAD, THE_SILENT, DEFECT, YOUR_CLASS ...
// These are all necessary for creating a character. If you want to find out where and how exactly they are used
// in the basegame (for fun and education) Ctrl+click on the PlayerClass, CardColor and/or LibraryType below and go down the
// Ctrl+click rabbit hole
public static class Enums {
@SpireEnum
public static AbstractPlayer.PlayerClass THE_DEFAULT;
@SpireEnum(name = "DEFAULT_GRAY_COLOR") // These two HAVE to have the same absolutely identical name.
public static AbstractCard.CardColor COLOR_GRAY;
@SpireEnum(name = "DEFAULT_GRAY_COLOR") @SuppressWarnings("unused")
public static CardLibrary.LibraryType LIBRARY_COLOR;
}
// =============== CHARACTER ENUMERATORS =================
// =============== BASE STATS =================
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 CARD_DRAW = 9;
public static final int ORB_SLOTS = 3;
// =============== /BASE STATS/ =================
// =============== STRINGS =================
private static final String ID = makeID("DefaultCharacter");
private static final CharacterStrings characterStrings = CardCrawlGame.languagePack.getCharacterString(ID);
private static final String[] NAMES = characterStrings.NAMES;
private static final String[] TEXT = characterStrings.TEXT;
// =============== /STRINGS/ =================
// =============== TEXTURES OF BIG ENERGY ORB ===============
public static final String[] orbTextures = {
"constructTheArenaResources/images/char/defaultCharacter/orb/layer1.png",
"constructTheArenaResources/images/char/defaultCharacter/orb/layer2.png",
"constructTheArenaResources/images/char/defaultCharacter/orb/layer3.png",
"constructTheArenaResources/images/char/defaultCharacter/orb/layer4.png",
"constructTheArenaResources/images/char/defaultCharacter/orb/layer5.png",
"constructTheArenaResources/images/char/defaultCharacter/orb/layer6.png",
"constructTheArenaResources/images/char/defaultCharacter/orb/layer1d.png",
"constructTheArenaResources/images/char/defaultCharacter/orb/layer2d.png",
"constructTheArenaResources/images/char/defaultCharacter/orb/layer3d.png",
"constructTheArenaResources/images/char/defaultCharacter/orb/layer4d.png",
"constructTheArenaResources/images/char/defaultCharacter/orb/layer5d.png",};
// =============== /TEXTURES OF BIG ENERGY ORB/ ===============
// =============== CHARACTER CLASS START =================
public TheDefault(String name, PlayerClass setClass) {
super(name, setClass, orbTextures,
"constructTheArenaResources/images/char/defaultCharacter/orb/vfx.png", null,
new SpriterAnimation(
"constructTheArenaResources/images/char/defaultCharacter/Spriter/theDefaultAnimation.scml"));
// =============== TEXTURES, ENERGY, LOADOUT =================
initializeClass(null, // required call to load textures and setup energy/loadout.
// I left these in DefaultMod.java (Ctrl+click them to see where they are, Ctrl+hover to see what they read.)
THE_DEFAULT_SHOULDER_2, // campfire pose
THE_DEFAULT_SHOULDER_1, // another campfire pose
THE_DEFAULT_CORPSE, // dead corpse
getLoadout(), 20.0F, -10.0F, 220.0F, 290.0F, new EnergyManager(ENERGY_PER_TURN)); // energy manager
// =============== /TEXTURES, ENERGY, LOADOUT/ =================
// =============== ANIMATIONS =================
loadAnimation(
THE_DEFAULT_SKELETON_ATLAS,
THE_DEFAULT_SKELETON_JSON,
1.0f);
AnimationState.TrackEntry e = state.setAnimation(0, "animation", true);
e.setTime(e.getEndTime() * MathUtils.random());
// =============== /ANIMATIONS/ =================
// =============== TEXT BUBBLE LOCATION =================
dialogX = (drawX + 0.0F * Settings.scale); // set location for text bubbles
dialogY = (drawY + 220.0F * Settings.scale); // you can just copy these values
// =============== /TEXT BUBBLE LOCATION/ =================
}
// =============== /CHARACTER CLASS END/ =================
// Starting description and loadout
@Override
public CharSelectInfo getLoadout() {
return new CharSelectInfo(NAMES[0], TEXT[0],
STARTING_HP, MAX_HP, ORB_SLOTS, STARTING_GOLD, CARD_DRAW, this, getStartingRelics(),
getStartingDeck(), false);
}
// Starting Deck
@Override
public ArrayList<String> getStartingDeck() {
ArrayList<String> retVal = new ArrayList<>();
logger.info("Begin loading starter Deck Strings");
retVal.add(DefaultCommonAttack.ID);
retVal.add(DefaultUncommonAttack.ID);
retVal.add(DefaultRareAttack.ID);
retVal.add(DefaultCommonSkill.ID);
retVal.add(DefaultUncommonSkill.ID);
retVal.add(DefaultRareSkill.ID);
retVal.add(DefaultCommonPower.ID);
retVal.add(DefaultUncommonPower.ID);
retVal.add(DefaultRarePower.ID);
retVal.add(DefaultAttackWithVariable.ID);
retVal.add(DefaultSecondMagicNumberSkill.ID);
retVal.add(OrbSkill.ID);
return retVal;
}
// Starting Relics
public ArrayList<String> getStartingRelics() {
ArrayList<String> retVal = new ArrayList<>();
retVal.add(PlaceholderRelic.ID);
retVal.add(PlaceholderRelic2.ID);
retVal.add(DefaultClickableRelic.ID);
UnlockTracker.markRelicAsSeen(PlaceholderRelic.ID);
UnlockTracker.markRelicAsSeen(PlaceholderRelic2.ID);
UnlockTracker.markRelicAsSeen(DefaultClickableRelic.ID);
return retVal;
}
// character Select screen effect
@Override
public void doCharSelectScreenSelectEffect() {
CardCrawlGame.sound.playA("ATTACK_DAGGER_1", 1.25f); // Sound Effect
CardCrawlGame.screenShake.shake(ScreenShake.ShakeIntensity.LOW, ScreenShake.ShakeDur.SHORT,
false); // Screen Effect
}
// character Select on-button-press sound effect
@Override
public String getCustomModeCharacterButtonSoundKey() {
return "ATTACK_DAGGER_1";
}
// Should return how much HP your maximum HP reduces by when starting a run at
// Ascension 14 or higher. (ironclad loses 5, defect and silent lose 4 hp respectively)
@Override
public int getAscensionMaxHPLoss() {
return 0;
}
// Should return the card color enum to be associated with your character.
@Override
public AbstractCard.CardColor getCardColor() {
return COLOR_GRAY;
}
// Should return a color object to be used to color the trail of moving cards
@Override
public Color getCardTrailColor() {
return constructTheArena.DefaultMod.DEFAULT_GRAY;
}
// Should return a BitmapFont object that you can use to customize how your
// energy is displayed from within the energy orb.
@Override
public BitmapFont getEnergyNumFont() {
return FontHelper.energyNumFontRed;
}
// Should return class name as it appears in run history screen.
@Override
public String getLocalizedCharacterName() {
return NAMES[0];
}
//Which card should be obtainable from the Match and Keep event?
@Override
public AbstractCard getStartCardForEvent() {
return new DefaultCommonAttack();
}
// The class name as it appears next to your player name in-game
@Override
public String getTitle(AbstractPlayer.PlayerClass playerClass) {
return NAMES[1];
}
// Should return a new instance of your character, sending name as its name parameter.
@Override
public AbstractPlayer newInstance() {
return new TheDefault(name, chosenClass);
}
// Should return a Color object to be used to color the miniature card images in run history.
@Override
public Color getCardRenderColor() {
return constructTheArena.DefaultMod.DEFAULT_GRAY;
}
// Should return a Color object to be used as screen tint effect when your
// character attacks the heart.
@Override
public Color getSlashAttackColor() {
return constructTheArena.DefaultMod.DEFAULT_GRAY;
}
// Should return an AttackEffect array of any size greater than 0. These effects
// will be played in sequence as your character's finishing combo on the heart.
// Attack effects are the same as used in DamageAction and the like.
@Override
public AbstractGameAction.AttackEffect[] getSpireHeartSlashEffect() {
return new AbstractGameAction.AttackEffect[]{
AbstractGameAction.AttackEffect.BLUNT_HEAVY,
AbstractGameAction.AttackEffect.BLUNT_HEAVY,
AbstractGameAction.AttackEffect.BLUNT_HEAVY};
}
// Should return a string containing what text is shown when your character is
// about to attack the heart. For example, the defect is "NL You charge your
// core to its maximum..."
@Override
public String getSpireHeartText() {
return TEXT[1];
}
// The vampire events refer to the base game characters as "brother", "sister",
// and "broken one" respectively.This method should return a String containing
// the full text that will be displayed as the first screen of the vampires event.
@Override
public String getVampireText() {
return TEXT[2];
}
}

View File

@ -0,0 +1,143 @@
package constructTheArena.events;
import com.megacrit.cardcrawl.cards.AbstractCard;
import com.megacrit.cardcrawl.cards.CardGroup;
import com.megacrit.cardcrawl.cards.colorless.Apotheosis;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.core.Settings;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.events.AbstractImageEvent;
import com.megacrit.cardcrawl.helpers.RelicLibrary;
import com.megacrit.cardcrawl.helpers.ScreenShake;
import com.megacrit.cardcrawl.localization.EventStrings;
import com.megacrit.cardcrawl.relics.AbstractRelic;
import com.megacrit.cardcrawl.vfx.cardManip.PurgeCardEffect;
import com.megacrit.cardcrawl.vfx.cardManip.ShowCardAndObtainEffect;
import constructTheArena.DefaultMod;
import static constructTheArena.DefaultMod.makeEventPath;
public class IdentityCrisisEvent extends AbstractImageEvent {
public static final String ID = DefaultMod.makeID("IdentityCrisisEvent");
private static final EventStrings eventStrings = CardCrawlGame.languagePack.getEventString(ID);
private static final String NAME = eventStrings.NAME;
private static final String[] DESCRIPTIONS = eventStrings.DESCRIPTIONS;
private static final String[] OPTIONS = eventStrings.OPTIONS;
public static final String IMG = makeEventPath("IdentityCrisisEvent.png");
private int screenNum = 0; // The initial screen we will see when encountering the event - screen 0;
private float HEALTH_LOSS_PERCENTAGE = 0.03F; // 3%
private float HEALTH_LOSS_PERCENTAGE_LOW_ASCENSION = 0.05F; // 5%
private int healthdamage; //The actual number of how much Max HP we're going to lose.
public IdentityCrisisEvent() {
super(NAME, DESCRIPTIONS[0], IMG);
if (AbstractDungeon.ascensionLevel >= 15) { // If the player is ascension 15 or above, lose 5% max hp. Else, lose just 3%.
healthdamage = (int) ((float) AbstractDungeon.player.maxHealth * HEALTH_LOSS_PERCENTAGE);
} else {
healthdamage = (int) ((float) AbstractDungeon.player.maxHealth * HEALTH_LOSS_PERCENTAGE_LOW_ASCENSION);
}
// The first dialogue options available to us.
imageEventText.setDialogOption(OPTIONS[0]); // Inspiration - Gain a Random Starting Relic
imageEventText.setDialogOption(OPTIONS[1] + healthdamage + OPTIONS[2]); // Denial - lose healthDamage Max HP
imageEventText.setDialogOption(OPTIONS[3], new Apotheosis()); // Acceptance - Gain Apotheosis
imageEventText.setDialogOption(OPTIONS[4]); // TOUCH THE MIRROR
}
@Override
protected void buttonEffect(int i) { // This is the event:
switch (screenNum) {
case 0: // While you are on screen number 0 (The starting screen)
switch (i) {
case 0: // If you press button the first button (Button at index 0), in this case: Inspiration.
this.imageEventText.updateBodyText(DESCRIPTIONS[1]); // Update the text of the event
this.imageEventText.updateDialogOption(0, OPTIONS[5]); // 1. Change the first button to the [Leave] button
this.imageEventText.clearRemainingOptions(); // 2. and remove all others
screenNum = 1; // Screen set the screen number to 1. Once we exit the switch (i) statement,
// we'll still continue the switch (screenNum) statement. It'll find screen 1 and do it's actions
// (in our case, that's the final screen, but you can chain as many as you want like that)
AbstractRelic relicToAdd = RelicLibrary.starterList.get(AbstractDungeon.relicRng.random(RelicLibrary.starterList.size() - 1)).makeCopy();
// Get a random starting relic
AbstractDungeon.getCurrRoom().spawnRelicAndObtain((float)(Settings.WIDTH / 2), (float)(Settings.HEIGHT / 2), relicToAdd);
break; // Onto screen 1 we go.
case 1: // If you press button the second button (Button at index 1), in this case: Deinal
CardCrawlGame.screenShake.shake(ScreenShake.ShakeIntensity.MED, ScreenShake.ShakeDur.MED, false);
// Shake the screen
CardCrawlGame.sound.play("BLUNT_FAST"); // Play a hit sound
AbstractDungeon.player.decreaseMaxHealth(healthdamage); // Lose max HP
if (CardGroup.getGroupWithoutBottledCards(AbstractDungeon.player.masterDeck.getPurgeableCards()).size() > 0) {
// If you have cards you can remove - remove a card
AbstractDungeon.gridSelectScreen.open(
CardGroup.getGroupWithoutBottledCards(
AbstractDungeon.player.masterDeck.getPurgeableCards()),
1, OPTIONS[6], false, false, false, true);
}
this.imageEventText.updateBodyText(DESCRIPTIONS[2]);
this.imageEventText.updateDialogOption(0, OPTIONS[5]);
this.imageEventText.clearRemainingOptions();
screenNum = 1;
// Same as before. A note here is that you can also do
// imageEventText.clearAllDialogs();
// imageEventText.setDialogOption(OPTIONS[1]);
// imageEventText.setDialogOption(OPTIONS[4]);
// (etc.)
// And that would also just set them into slot 0, 1, 2... in order, just like what we do in the very beginning
break; // Onto screen 1 we go.
case 2: // If you press button the third button (Button at index 2), in this case: Acceptance
AbstractCard c = new Apotheosis().makeCopy();
AbstractDungeon.effectList.add(new ShowCardAndObtainEffect(c, (float) (Settings.WIDTH / 2), (float) (Settings.HEIGHT / 2)));
this.imageEventText.updateBodyText(DESCRIPTIONS[3]);
this.imageEventText.updateDialogOption(0, OPTIONS[5]);
this.imageEventText.clearRemainingOptions();
screenNum = 1;
break;
case 3: // If you press button the fourth button (Button at index 3), in this case: TOUCH
imageEventText.loadImage("constructTheArenaResources/images/events/IdentityCrisisEvent2.png"); // Change the shown image
// Other than that, this option doesn't do anything special.
this.imageEventText.updateBodyText(DESCRIPTIONS[4]);
this.imageEventText.updateDialogOption(0, OPTIONS[5]);
this.imageEventText.clearRemainingOptions();
screenNum = 1;
break;
}
break;
case 1: // Welcome to screenNum = 1;
switch (i) {
case 0: // If you press the first (and this should be the only) button,
openMap(); // You'll open the map and end the event.
break;
}
break;
}
}
public void update() { // We need the update() when we use grid screens (such as, in this case, the screen for selecting a card to remove)
super.update(); // Do everything the original update()
if (!AbstractDungeon.gridSelectScreen.selectedCards.isEmpty()) { // Once the grid screen isn't empty (we selected a card for removal)
AbstractCard c = (AbstractCard)AbstractDungeon.gridSelectScreen.selectedCards.get(0); // Get the card
AbstractDungeon.topLevelEffects.add(new PurgeCardEffect(c, (float)(Settings.WIDTH / 2), (float)(Settings.HEIGHT / 2))); // Create the card removal effect
AbstractDungeon.player.masterDeck.removeCard(c); // Remove it from the deck
AbstractDungeon.gridSelectScreen.selectedCards.clear(); // Or you can .remove(c) instead of clear,
// if you want to continue using the other selected cards for something
}
}
}

View File

@ -0,0 +1,131 @@
package constructTheArena.orbs;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;
import com.megacrit.cardcrawl.actions.AbstractGameAction;
import com.megacrit.cardcrawl.actions.animations.VFXAction;
import com.megacrit.cardcrawl.actions.common.DamageAllEnemiesAction;
import com.megacrit.cardcrawl.actions.common.DrawCardAction;
import com.megacrit.cardcrawl.actions.utility.SFXAction;
import com.megacrit.cardcrawl.cards.DamageInfo;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.core.Settings;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.localization.OrbStrings;
import com.megacrit.cardcrawl.orbs.AbstractOrb;
import com.megacrit.cardcrawl.vfx.combat.DarkOrbActivateEffect;
import com.megacrit.cardcrawl.vfx.combat.DarkOrbPassiveEffect;
import com.megacrit.cardcrawl.vfx.combat.OrbFlareEffect;
import constructTheArena.DefaultMod;
import constructTheArena.util.TextureLoader;
import static constructTheArena.DefaultMod.makeOrbPath;
public class DefaultOrb extends AbstractOrb {
// Standard ID/Description
public static final String ORB_ID = DefaultMod.makeID("DefaultOrb");
private static final OrbStrings orbString = CardCrawlGame.languagePack.getOrbString(ORB_ID);
public static final String[] DESC = orbString.DESCRIPTION;
private static final Texture IMG = TextureLoader.getTexture(makeOrbPath("default_orb.png"));
// Animation Rendering Numbers - You can leave these at default, or play around with them and see what they change.
private float vfxTimer = 1.0f;
private float vfxIntervalMin = 0.1f;
private float vfxIntervalMax = 0.4f;
private static final float ORB_WAVY_DIST = 0.04f;
private static final float PI_4 = 12.566371f;
public DefaultOrb() {
ID = ORB_ID;
name = orbString.NAME;
img = IMG;
evokeAmount = baseEvokeAmount = 1;
passiveAmount = basePassiveAmount = 3;
updateDescription();
angle = MathUtils.random(360.0f); // More Animation-related Numbers
channelAnimTimer = 0.5f;
}
@Override
public void updateDescription() { // Set the on-hover description of the orb
applyFocus(); // Apply Focus (Look at the next method)
description = DESC[0] + evokeAmount + DESC[1] + passiveAmount + DESC[2]; // Set the description
}
@Override
public void applyFocus() {
passiveAmount = basePassiveAmount;
evokeAmount = baseEvokeAmount;
}
@Override
public void onEvoke() { // 1.On Orb Evoke
AbstractDungeon.actionManager.addToBottom( // 2.Damage all enemies
new DamageAllEnemiesAction(AbstractDungeon.player, DamageInfo.createDamageMatrix(evokeAmount, true, true), DamageInfo.DamageType.THORNS, AbstractGameAction.AttackEffect.NONE));
// The damage matrix is how orb damage all enemies actions have to be assigned. For regular cards that do damage to everyone, check out cleave or whirlwind - they are a bit simpler.
AbstractDungeon.actionManager.addToBottom(new SFXAction("TINGSHA")); // 3.And play a Jingle Sound.
// For a list of sound effects you can use, look under com.megacrit.cardcrawl.audio.SoundMaster - you can see the list of keys you can use there. As far as previewing what they sound like, open desktop-1.0.jar with something like 7-Zip and go to audio. Reference the file names provided. (Thanks fiiiiilth)
}
@Override
public void onStartOfTurn() {// 1.At the start of your turn.
AbstractDungeon.actionManager.addToBottom(// 2.This orb will have a flare effect
new VFXAction(new OrbFlareEffect(this, OrbFlareEffect.OrbFlareColor.FROST), 0.1f));
AbstractDungeon.actionManager.addToBottom(// 3. And draw you cards.
new DrawCardAction(AbstractDungeon.player, passiveAmount));
}
@Override
public void updateAnimation() {// You can totally leave this as is.
// If you want to create a whole new orb effect - take a look at conspire's Water Orb. It includes a custom sound, too!
super.updateAnimation();
angle += Gdx.graphics.getDeltaTime() * 45.0f;
vfxTimer -= Gdx.graphics.getDeltaTime();
if (vfxTimer < 0.0f) {
AbstractDungeon.effectList.add(new DarkOrbPassiveEffect(cX, cY)); // This is the purple-sparkles in the orb. You can change this to whatever fits your orb.
vfxTimer = MathUtils.random(vfxIntervalMin, vfxIntervalMax);
}
}
// Render the orb.
@Override
public void render(SpriteBatch sb) {
sb.setColor(new Color(1.0f, 1.0f, 1.0f, c.a / 2.0f));
sb.draw(img, cX - 48.0f, cY - 48.0f + bobEffect.y, 48.0f, 48.0f, 96.0f, 96.0f, scale + MathUtils.sin(angle / PI_4) * ORB_WAVY_DIST * Settings.scale, scale, angle, 0, 0, 96, 96, false, false);
sb.setColor(new Color(1.0f, 1.0f, 1.0f, this.c.a / 2.0f));
sb.setBlendFunction(770, 1);
sb.draw(img, cX - 48.0f, cY - 48.0f + bobEffect.y, 48.0f, 48.0f, 96.0f, 96.0f, scale, scale + MathUtils.sin(angle / PI_4) * ORB_WAVY_DIST * Settings.scale, -angle, 0, 0, 96, 96, false, false);
sb.setBlendFunction(770, 771);
renderText(sb);
hb.render(sb);
}
@Override
public void triggerEvokeAnimation() { // The evoke animation of this orb is the dark-orb activation effect.
AbstractDungeon.effectsQueue.add(new DarkOrbActivateEffect(cX, cY));
}
@Override
public void playChannelSFX() { // When you channel this orb, the ATTACK_FIRE effect plays ("Fwoom").
CardCrawlGame.sound.play("ATTACK_FIRE", 0.1f);
}
@Override
public AbstractOrb makeCopy() {
return new DefaultOrb();
}
}

View File

@ -0,0 +1,215 @@
package constructTheArena.patches;
import com.evacipated.cardcrawl.modthespire.lib.*;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.helpers.RelicLibrary;
import com.megacrit.cardcrawl.relics.AbstractRelic;
import javassist.CtBehavior;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/*
* Using SpirePatch, also known as patching, allows you to insert your own code into the basegame code.
* It is an extremely powerful and useful tool that can appear complicated at first. If you have no experience with modding StS, and especially
* with Java, I recommend you skip this for a while until you have a semi-decent grip on things/until you feel like you need to use it.
* That being said, at the end of the day, it is not very complex once you understand how it works.
*
* Keep in mind that every patch is very unique so making a "tutorial class" that goes beyond the basics is a bit difficult,
* since there are too many unique cases. I'll leave it up to you to experiment and learn what you need for *your own* patch.
*
* You will ***NEED*** to follow the official SpirePatch documentation here as you read through this patch.
* https://github.com/kiooeht/ModTheSpire/wiki/SpirePatch
* https://github.com/kiooeht/ModTheSpire/wiki/Matcher
* Comments with quotations are taken from the documentation.
*
* This is a good time to Ctrl+Click on AbstractDungeon down there and Ctrl+f for returnRandomRelicKey() - that is the method that we will be patching.
* Have a read through it's code. returnRandomRelicKey() is a method that is passed a rarity (relic tier) and returns the first relic
* from the appropriate pool of that rarity (which are pre-shuffled), as well as removing it from the relic pool so that you never get it again.
*
* This is used whenever any combat gives you a relic reward - it is how the game grabs a random relic.
* (On a sidenote returnEndRandomRelicKey() on the other hand returns the *last* relic form the same list - this is used for shops.)
* That way visiting a shop doesn't change what relics you would get/see otherwise.
*
* Now that we understand how that method works - This patch will do the following:
* We will insert our piece of code above the line "return !RelicLibrary.getRelic(retVal).canSpawn() ? returnEndRandomRelicKey(tier) : retVal;"
* which is at the very end of the method. (If you read through the official documentation, you will also know that you can simply use a postfix patch to do that.)
* Have a read through the documentation as to their differences - they all have their pros and cons.
* For example postfix patches can't use @ByRef and doesn't have localvars. On the other hand, instead of needing to use SpireReturn they can just
* put a return value in their patched method, and they can also be passed the original return value of the patched method.
*
* *NEVER USE REPLACE PATCHES. DON'T REPLACE GAME FILES EITHER (by putting a file with the same name in the same location as a basegame one).*
* *NEVER USE REPLACE PATCHES. DON'T REPLACE GAME FILES EITHER (by putting a file with the same name in the same location as a basegame one).*
* *NEVER USE REPLACE PATCHES. DON'T REPLACE GAME FILES EITHER (by putting a file with the same name in the same location as a basegame one).*
* *NEVER USE REPLACE PATCHES. DON'T REPLACE GAME FILES EITHER (by putting a file with the same name in the same location as a basegame one).*
* *NEVER USE REPLACE PATCHES. DON'T REPLACE GAME FILES EITHER (by putting a file with the same name in the same location as a basegame one).*
* *NEVER USE REPLACE PATCHES. DON'T REPLACE GAME FILES EITHER (by putting a file with the same name in the same location as a basegame one).*
*
* So:
* We will insert our piece of code above the line "return !RelicLibrary.getRelic(retVal).canSpawn() ? returnEndRandomRelicKey(tier) : retVal;"
* We will the put in a logger inside that prints out the the value of the local variable "retVal" of that method.
* That's about it.
*
* Let's get to it!
*/
@SpirePatch( // "Use the @SpirePatch annotation on the patch class."
clz = AbstractDungeon.class, // This is the class where the method we will be patching is. In our case - Abstract Dungeon
method = "returnRandomRelicKey" // This is the name of the method we will be patching.
/*
Now let's imagine for a second that there were two methods named returnRandomRelicKey()
The one we're patching - "String returnRandomRelicKey(RelicTier tier)" - that grabs a relic of specific tier
and a fictional one - "String returnRandomRelicKey(RelicTier tier, LandingSound sound)" - that grabs a relic of a specific tier AND with a specific landing sound.
How would we tell the code which of the two methods to put our patch in? We use paramtypez (read the docs too they have a good example!)
Let's say we wanted to patch the second fictional one - we would add
paramtypez={
AbstractRelic.RelicTier.class,
AbstractRelic.LandingSound.class
}
to this annotation, after the method parameter. (If we wanted to patch the first one, we'd only put "AbstractRelic.RelicTier.class".
*/
)
public class DefaultInsertPatch {// Don't worry about the "never used" warning - *You* usually don't use/call them anywhere. Mod The Spire does.
// You can have as many inner classes with patches as you want inside this one - you don't have to separate each patch into it's own file.
// So if you need to put 4 patches all for 1 purpose (for example they all make a specific relic effect happen) - you can keep them organized together.
// Do keep in mind that "A patch class must be a public static class."
private static final Logger logger = LogManager.getLogger(DefaultInsertPatch.class.getName()); // This is our logger! It prints stuff out in the console.
// It's like a very fancy System.out.println();
@SpireInsertPatch( // This annotation of our patch method specifies the type of patch we will be using. In our case - a Spire Insert Patch
locator = Locator.class, // Spire insert patches require a locator - this isn't something you import - this is something we write.
// (Or as is usually the case with them - copy paste cause they're always nearly the same thing.
// In fact, most insert patches are fairly boiler-plate. You could easily make an insert patch template, if you'd like.)
// You can find our Locator class just below, as an inner class, underneath our actual patch method.
localvars = {"retVal"} // The method we're patching, returnRandomRelicKey(), has a local variable that we'd like to access and manipulate -
// "String retVal = null;". So, we simply write out it's name here and then add it as a parameter to our patch method.
// Keep in mind that localvars can also be used to capture class variables, not just local method ones. This also includes private ones.
)
//"A patch method must be a public static method."
public static void thisIsOurActualPatchMethod(
// 1. "Patch methods are passed all the arguments of the original method,
// 2. as well as the instance if original method is not static (instance first, then parameters).
// 3. localvars are passed as arguments, appearing the in parameter list after the original method's parameters."
// Example: if returnEndRandomRelicKey(RelicTier tier) were NOT static we would write our method parameters as such:
// thisIsOurActualPatchMethod(AbstractDungeon __instance, AbstractRelic.RelicTier tier, String retVal)
// As it stands, that method is static so it's not tied to a specific instance of AbstractDungeon. (Read up on what "static" means in java
// if you don't understand this part).
// As such we write our method parameters like this instead:
AbstractRelic.RelicTier tier, String retVal) {
// Wow time to actually put stuff in the basegame code!!! Everything here will be executed exactly as written, at the line which we specified.
// You can change retVal (using @byRef) to always return the same relic, or return a specific relic if it passes some check.
// You can execute any other static method you have, you can save retVal to your personal public static variable to always be able to
// reference the last relic you grabbed - etc. etc. The possibilities are endless. We're gonna do the following:
logger.info("Hey our patch triggered. The relic we're about to get is " + retVal);
// Incredible.
// Let's talk about @byRef for a bit.
// https://github.com/kiooeht/ModTheSpire/wiki/@ByRef - Read the documentation it is really helpful!
// If you grabbed retVal right now and did:
// retVal = Anchor().relicID
// logger.info("Hey our patch triggered. The relic we're about to get is " + retVal);
// The logger would correctly print out saying "we're about to get Anchor".
// However you wouldn't get anchor. You would get the standard relic roll.
// The reason behind that is because by default, in patches, all variables are passed by Value, not by Reference.
// (You can google what this means in java if you don't understand).
// This means that while we can change retVal within our own method, it won't change in the actual, original basegame method.
// If you want to do that, you'll need to use @byRef on the variable you want to change - this makes it get passed by reference.
// In our case that would be retVal - so we can annotate it with @byRef in our parameters. Another thing to note is that non-array
// objects must be converted to arrays.
// So if our current method parameters are:
// (AbstractRelic.RelicTier tier, String retVal)
// We would instead have:
// (AbstractRelic.RelicTier tier, @ByRef String[] retVal)
// Then, when we want to use it, can just access the (for us) one and only value in that array of Strings - which would be placed at index 0.
// retVal[0] = Anchor().relicID
// Then the retVal would actually be changed outside of this method - inside returnRandomRelicKey();
}
private static class Locator extends SpireInsertLocator { // Hey welcome to our SpireInsertLocator class!
@Override
public int[] Locate(CtBehavior ctMethodToPatch) throws Exception {// All the locator has and needs is an override of the Locate method
// In simple terms, the locator works like this:
// We give is something to match with, and it returns the line number that it finds the ting on,
// inside the method which we specified wayyyy early on in our @SpirePatch annotation.
// The Locate method is of type int[] - it returns an array of ints. These ints are actually the matching line numbers.
// This is where we open up the https://github.com/kiooeht/ModTheSpire/wiki/Matcher documentation.
// The line in the original method, "return !RelicLibrary.getRelic(retVal).canSpawn() ? returnEndRandomRelicKey(tier) : retVal;"
// is just a simple ternary operator, check out http://www.cafeaulait.org/course/week2/43.html
// or https://stackoverflow.com/questions/8898590/short-form-for-java-if-statement or simply ask google.
// if you can't spawn the relic(note the "!"), grab a new relic from the end of the list instead
// (call the returnEndRandomRelicKey() method) - otherwise return the relic.
// We want to insert our code immediately above it so we'll need to use a matcher against something in that line.
// We have a few of options for this 1 particular line. Before you proceed, read the docs and see how many you can personally spot.
// 1. RelicLibrary.getRelic - is calling the the getRelic() method of the RelicLibrary class.
// (You can also see that by Ctrl+clicking on getRelic)
// 2. getRelic(retVal).canSpawn() - is calling the canSpawn() method of the AbstractRelic class.
// (get relic() returns an AbstractRelic so we just use canSpawn() directly from it to check if we can spawn it)
// 3. returnEndRandomRelicKey(tier) - is calling the returnEndRandomRelicKey method of the AbstractDungeon class.
// At the end of the day, all three of these are MethodCallMatchers.
/*
* BUT WAIT!
* Did you know that the third option is *actually* on a completely different line than the first one?
* Decompiling the code with a different decompiler shows that the last line is actually a lot more like this:
*
* if (!RelicLibrary.getRelic(retVal).canSpawn()) {
* return returnEndRandomRelicKey(tier);
* }
* return retVal;
*
* Which means that if we use the third matcher we will insert code inside the if statement, while if we use 1. or 2. - just outside of it.
*
* Follow this guide to learn how to decompile your game.
* https://github.com/daviscook477/BaseMod/wiki/Decompiling-Your-Game
* Essentially - you would want to use JD-GUI and Luyten *both* to get a precise look.
* (You can 100% still totally use the IntelliJ for quick-referencing code, it is still very fast and convenient)
*
* On a sidenote, you should enable debug lines in intelliJ both for bugfixing and seeing what thing is *really* on what line
* To do so:
* 1. Ctrl+Shift+A
* 2. Registry
* 3. Scroll down and set decompiler.dump.original.lines to true
*/
// A good way to choose, usually, would be to pick the matcher least likely to appear elsewhere in the code of the method
// i.e. the rarest one. In this case, it doesn't really matter as it's 3 of the same matcher, and none of their methods
// ever appear again anywhere else, so let's just go for the first one:
// As the documentation says, put the Class type and the method name (as a string) as your parameters:
Matcher finalMatcher = new Matcher.MethodCallMatcher(RelicLibrary.class, "getRelic");
// Now we just have to return the line number corresponding to that particular method call.
// We have 2 options:
// 1. findInOrder - Returns the first line number that matches the description of the matcher
// (i.e. the very first time it finds RelicLibrary.getRelic() in the method we're patching.)
// 2. findAllInOrder - Returns an array of ints - all of the line numbers matching the description.
// (This is, for example, if the method we're patching had used "RelicLibrary.getRelic()" 3 different times,
// and we want to insert our code right before ALL of the matches, or before a particular one of them.)
// In our case "RelicLibrary.getRelic()" is called only once, in that particular return statement, so we can just return it.
return LineFinder.findInOrder(ctMethodToPatch, finalMatcher);
// If we wanted to use findAllInOrder instead, we would do it like this:
// return new int[]{LineFinder.findAllInOrder(ctMethodToPatch, finalMatcher)[0]};
// The [0] in this case indicates the index of the line number in the array (in the order they were found)
// The first (and for us, only) instance of "RelicLibrary.getRelic()" would be at index 0. The second at index 1, and so on.
// Finally, if we wanted to insert our code before *every* line with a match, we would just skip the index and return the whole list of lines:
// return LineFinder.findAllInOrder(ctMethodToPatch, finalMatcher)
}
}
}

View File

@ -0,0 +1,31 @@
package constructTheArena.patches.relics;
import com.evacipated.cardcrawl.modthespire.lib.SpireField;
import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
import com.megacrit.cardcrawl.cards.AbstractCard;
/*
* Patches have a pretty detailed documentation. Go check em out here:
*
* https://github.com/kiooeht/ModTheSpire/wiki/SpirePatch
*/
@SpirePatch(clz = AbstractCard.class, method = SpirePatch.CLASS)
public class BottledPlaceholderField {
public static SpireField<Boolean> inBottledPlaceholderField = new SpireField<>(() -> false);
// SpireField is a wonderful thing that lets us add our own fields to preexisting classes in the game.
// In this scenario we're going to add a boolean named "inBottledPlaceholderField" to the "makeStatEquivalentCopy" method inside AbstractCard
@SpirePatch(clz = AbstractCard.class, method = "makeStatEquivalentCopy")
public static class MakeStatEquivalentCopy {
public static AbstractCard Postfix(AbstractCard result, AbstractCard self) {
// This is a postfix patch, meaning it'll be inserted at the very end of makeStatEquivalentCopy()
inBottledPlaceholderField.set(result, inBottledPlaceholderField.get(self)); // Read:
// set inBottledPlaceholderField to have the card and true/false depending on whether it's bottled or not.
return result; // Return the bottled card.
}
}
}

View File

@ -0,0 +1,81 @@
package constructTheArena.potions;
import com.megacrit.cardcrawl.actions.common.ApplyPowerAction;
import com.megacrit.cardcrawl.core.AbstractCreature;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.helpers.PowerTip;
import com.megacrit.cardcrawl.localization.PotionStrings;
import com.megacrit.cardcrawl.potions.AbstractPotion;
import com.megacrit.cardcrawl.powers.LoseStrengthPower;
import com.megacrit.cardcrawl.powers.StrengthPower;
import com.megacrit.cardcrawl.rooms.AbstractRoom;
public class PlaceholderPotion extends AbstractPotion {
public static final String POTION_ID = constructTheArena.DefaultMod.makeID("PlaceholderPotion");
private static final PotionStrings potionStrings = CardCrawlGame.languagePack.getPotionString(POTION_ID);
public static final String NAME = potionStrings.NAME;
public static final String[] DESCRIPTIONS = potionStrings.DESCRIPTIONS;
public PlaceholderPotion() {
// The bottle shape and inside is determined by potion size and color. The actual colors are the main DefaultMod.java
super(NAME, POTION_ID, PotionRarity.COMMON, PotionSize.M, PotionColor.SMOKE);
// Potency is the damage/magic number equivalent of potions.
potency = getPotency();
// Initialize the Description
description = DESCRIPTIONS[0] + potency + DESCRIPTIONS[2] + DESCRIPTIONS[1] + potency + DESCRIPTIONS[2];
// Do you throw this potion at an enemy or do you just consume it.
isThrown = false;
// Initialize the on-hover name + description
tips.add(new PowerTip(name, description));
}
// See that description? It has DESCRIPTIONS[1] instead of just hard-coding the "text " + potency + " more text" inside.
// DO NOT HARDCODE YOUR STRINGS ANYWHERE, it's really bad practice to have "Strings" in your code:
/*
* 1. It's bad for if somebody likes your mod enough (or if you decide) to translate it.
* Having only the JSON files for translation rather than 15 different instances of "Dexterity" in some random cards is A LOT easier.
*
* 2. You don't have a centralised file for all strings for easy proof-reading. If you ever want to change a string
* you don't have to go through all your files individually/pray that a mass-replace doesn't screw something up.
*
* 3. Without hardcoded strings, editing a string doesn't require a compile, saving you time (unless you clean+package).
*
*/
@Override
public void use(AbstractCreature target) {
target = AbstractDungeon.player;
// If you are in combat, gain strength and the "lose strength at the end of your turn" power, equal to the potency of this potion.
if (AbstractDungeon.getCurrRoom().phase == AbstractRoom.RoomPhase.COMBAT) {
AbstractDungeon.actionManager.addToBottom(new ApplyPowerAction(target, AbstractDungeon.player, new StrengthPower(target, potency), potency));
AbstractDungeon.actionManager.addToBottom(new ApplyPowerAction(target, AbstractDungeon.player, new LoseStrengthPower(target, potency), potency));
}
}
@Override
public AbstractPotion makeCopy() {
return new PlaceholderPotion();
}
// This is your potency.
@Override
public int getPotency(final int potency) {
return 2;
}
public void upgradePotion()
{
potency += 1;
tips.clear();
tips.add(new PowerTip(name, description));
}
}

View File

@ -0,0 +1,117 @@
package constructTheArena.powers;
import basemod.interfaces.CloneablePowerInterface;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.megacrit.cardcrawl.actions.common.ApplyPowerAction;
import com.megacrit.cardcrawl.actions.common.ReducePowerAction;
import com.megacrit.cardcrawl.actions.utility.UseCardAction;
import com.megacrit.cardcrawl.cards.AbstractCard;
import com.megacrit.cardcrawl.core.AbstractCreature;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.localization.PowerStrings;
import com.megacrit.cardcrawl.powers.AbstractPower;
import com.megacrit.cardcrawl.powers.DexterityPower;
import constructTheArena.DefaultMod;
import constructTheArena.util.TextureLoader;
import static constructTheArena.DefaultMod.makePowerPath;
//Gain 1 dex for the turn for each card played.
public class CommonPower extends AbstractPower implements CloneablePowerInterface {
public AbstractCreature source;
public static final String POWER_ID = DefaultMod.makeID("CommonPower");
private static final PowerStrings powerStrings = CardCrawlGame.languagePack.getPowerStrings(POWER_ID);
public static final String NAME = powerStrings.NAME;
public static final String[] DESCRIPTIONS = powerStrings.DESCRIPTIONS;
// We create 2 new textures *Using This Specific Texture Loader* - an 84x84 image and a 32x32 one.
// There's a fallback "missing texture" image, so the game shouldn't crash if you accidentally put a non-existent file.
private static final Texture tex84 = TextureLoader.getTexture(makePowerPath("placeholder_power84.png"));
private static final Texture tex32 = TextureLoader.getTexture(makePowerPath("placeholder_power32.png"));
public CommonPower(final AbstractCreature owner, final AbstractCreature source, final int amount) {
name = NAME;
ID = POWER_ID;
this.owner = owner;
this.amount = amount;
this.source = source;
type = PowerType.BUFF;
isTurnBased = false;
// We load those txtures here.
this.region128 = new TextureAtlas.AtlasRegion(tex84, 0, 0, 84, 84);
this.region48 = new TextureAtlas.AtlasRegion(tex32, 0, 0, 32, 32);
updateDescription();
}
// On use card, apply (amount) of Dexterity. (Go to the actual power card for the amount.)
@Override
public void onUseCard(final AbstractCard card, final UseCardAction action) {
AbstractDungeon.actionManager.addToBottom(new ApplyPowerAction(owner, owner,
new DexterityPower(owner, amount), amount));
}
// Note: If you want to apply an effect when a power is being applied you have 3 options:
//onInitialApplication is "When THIS power is first applied for the very first time only."
//onApplyPower is "When the owner applies a power to something else (only used by Sadistic Nature)."
//onReceivePowerPower from StSlib is "When any (including this) power is applied to the owner."
// At the end of the turn, remove gained Dexterity.
@Override
public void atEndOfTurn(final boolean isPlayer) {
int count = 0;
for (final AbstractCard c : AbstractDungeon.actionManager.cardsPlayedThisTurn) {
// This is how you iterate through arrays (like the one above) and card groups like
// "AbstractDungeon.player.masterDeck.getAttacks().group" - every attack in your actual master deck.
// Read up on java's enhanced for-each loops if you want to know more on how these work.
++count; // At the end of your turn, increase the count by 1 for each card played this turn.
}
if (count > 0) {
flash(); // Makes the power icon flash.
for (int i = 0; i < count; ++i) {
AbstractDungeon.actionManager.addToBottom(
new ReducePowerAction(owner, owner, DexterityPower.POWER_ID, amount));
// Reduce the power by 1 for each count - i.e. for each card played this turn.
// DO NOT HARDCODE YOUR STRINGS ANYWHERE: i.e. don't write any Strings directly i.e. "Dexterity" for the power ID above.
// Use the power/card/relic etc. and fetch it's ID like shown above. It's really bad practice to have "Strings" in your code:
/*
* 1. It's bad for if somebody likes your mod enough (or if you decide) to translate it.
* Having only the JSON files for translation rather than 15 different instances of "Dexterity" in some random cards is A LOT easier.
*
* 2. You don't have a centralised file for all strings for easy proof-reading, and if you ever want to change a string
* you now have to go through all your files individually.
*
* 3. Without hardcoded strings, editing a string doesn't require a compile, saving you time (unless you clean+package).
*
*/
}
}
}
// Update the description when you apply this power. (i.e. add or remove an "s" in keyword(s))
@Override
public void updateDescription() {
if (amount == 1) {
description = DESCRIPTIONS[0] + amount + DESCRIPTIONS[1];
} else if (amount > 1) {
description = DESCRIPTIONS[0] + amount + DESCRIPTIONS[2];
}
}
@Override
public AbstractPower makeCopy() {
return new CommonPower(owner, source, amount);
}
}

View File

@ -0,0 +1,79 @@
package constructTheArena.powers;
import basemod.interfaces.CloneablePowerInterface;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.megacrit.cardcrawl.actions.utility.NewQueueCardAction;
import com.megacrit.cardcrawl.cards.AbstractCard;
import com.megacrit.cardcrawl.core.AbstractCreature;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.localization.PowerStrings;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import com.megacrit.cardcrawl.powers.AbstractPower;
import constructTheArena.DefaultMod;
import constructTheArena.cards.DefaultRareAttack;
import constructTheArena.util.TextureLoader;
public class RarePower extends AbstractPower implements CloneablePowerInterface {
public AbstractCreature source;
public static final String POWER_ID = DefaultMod.makeID("RarePower");
private static final PowerStrings powerStrings = CardCrawlGame.languagePack.getPowerStrings(POWER_ID);
public static final String NAME = powerStrings.NAME;
public static final String[] DESCRIPTIONS = powerStrings.DESCRIPTIONS;
// We create 2 new textures *Using This Specific Texture Loader* - an 84x84 image and a 32x32 one.
private static final Texture tex84 = TextureLoader.getTexture("constructTheArenaResources/images/powers/placeholder_power84.png");
private static final Texture tex32 = TextureLoader.getTexture("constructTheArenaResources/images/powers/placeholder_power32.png");
public RarePower(final AbstractCreature owner, final AbstractCreature source, final int amount) {
name = NAME;
ID = POWER_ID;
this.owner = owner;
this.amount = amount;
this.source = source;
type = PowerType.DEBUFF;
isTurnBased = false;
// We load those textures here.
this.region128 = new TextureAtlas.AtlasRegion(tex84, 0, 0, 84, 84);
this.region48 = new TextureAtlas.AtlasRegion(tex32, 0, 0, 32, 32);
updateDescription();
}
@Override
public void atStartOfTurn() { // At the start of your turn
AbstractCard playCard = new DefaultRareAttack(); // Declare Card - the DefaultRareAttack card. We will name it 'playCard'.
AbstractMonster targetMonster = AbstractDungeon.getRandomMonster(); // Declare Target - Random Monster. We will name the monster 'targetMonster'.
playCard.freeToPlayOnce = true; //Self Explanatory
if (playCard.type != AbstractCard.CardType.POWER) {
playCard.purgeOnUse = true;
}
// Remove completely on use (Not Exhaust). A note - you don't need the '{}' in this if statement,
// as it's just 1 line directly under. You can remove them, if you want. In fact, you can even put it all on 1 line:
// if (playCard.type != AbstractCard.CardType.POWER) playCard.purgeOnUse = true; - works identically
AbstractDungeon.actionManager.addToBottom(new NewQueueCardAction(playCard, targetMonster)); // Play the card on the target.
}
@Override
public void updateDescription() {
if (amount == 1) {
description = DESCRIPTIONS[0] + amount + DESCRIPTIONS[1];
} else if (amount > 1) {
description = DESCRIPTIONS[0] + amount + DESCRIPTIONS[2];
}
}
@Override
public AbstractPower makeCopy() {
return new RarePower(owner, source, amount);
}
}

View File

@ -0,0 +1,185 @@
package constructTheArena.relics;
import basemod.BaseMod;
import basemod.abstracts.CustomBottleRelic;
import basemod.abstracts.CustomRelic;
import basemod.abstracts.CustomSavable;
import com.badlogic.gdx.graphics.Texture;
import com.evacipated.cardcrawl.mod.stslib.actions.common.AutoplayCardAction;
import com.evacipated.cardcrawl.mod.stslib.fields.cards.AbstractCard.AutoplayField;
import com.megacrit.cardcrawl.actions.utility.UseCardAction;
import com.megacrit.cardcrawl.cards.AbstractCard;
import com.megacrit.cardcrawl.cards.CardGroup;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.helpers.FontHelper;
import com.megacrit.cardcrawl.helpers.PowerTip;
import com.megacrit.cardcrawl.relics.AbstractRelic;
import com.megacrit.cardcrawl.rooms.AbstractRoom;
import constructTheArena.DefaultMod;
import constructTheArena.patches.relics.BottledPlaceholderField;
import constructTheArena.util.TextureLoader;
import java.util.Iterator;
import java.util.function.Predicate;
import static constructTheArena.DefaultMod.makeRelicOutlinePath;
import static constructTheArena.DefaultMod.makeRelicPath;
public class BottledPlaceholderRelic extends CustomRelic implements CustomBottleRelic, CustomSavable<Integer> {
// This file will show you how to use 2 things - (Mostly) The Custom Bottle Relic and the Custom Savable - they go hand in hand.
/*
* https://github.com/daviscook477/BaseMod/wiki/Custom-Savable
*
* Choose a card. Whenever you take play any card, draw the chosen card.
*/
// BasemodWiki Says: "When you need to store a value on a card or relic between runs that isn't a relic's counter value
// or a card's misc value, you use a custom savable to save and load it between runs."
private static AbstractCard card; // The field value we wish to save in this case is the card that's going to be in our bottle.
private boolean cardSelected = true; // A boolean to indicate whether or not we selected a card for bottling.
// (It's set to false on Equip)
// ID, images, text.
public static final String ID = DefaultMod.makeID("BottledPlaceholderRelic");
private static final Texture IMG = TextureLoader.getTexture(makeRelicPath("BottledPlaceholder.png"));
private static final Texture OUTLINE = TextureLoader.getTexture(makeRelicOutlinePath("BottledPlaceholder.png"));
public BottledPlaceholderRelic() {
super(ID, IMG, OUTLINE, RelicTier.COMMON, LandingSound.CLINK);
tips.clear();
tips.add(new PowerTip(name, description));
}
// Now, for making Bottled cards we need a small patch - our own custom SpireField
// I've included that already in patches.relics.BottledPlaceholderField
// The basemod wiki I linked above has comments about onSave and onLoad
@Override
public Predicate<AbstractCard> isOnCard() {
return BottledPlaceholderField.inBottledPlaceholderField::get;
}
@Override
public Integer onSave() {
if (card != null) {
return AbstractDungeon.player.masterDeck.group.indexOf(card);
} else {
return -1;
}
}
@Override
public void onLoad(Integer cardIndex) {
if (cardIndex == null) {
return;
}
if (cardIndex >= 0 && cardIndex < AbstractDungeon.player.masterDeck.group.size()) {
card = AbstractDungeon.player.masterDeck.group.get(cardIndex);
if (card != null) {
BottledPlaceholderField.inBottledPlaceholderField.set(card, true);
setDescriptionAfterLoading();
}
}
}
@Override
public void onEquip() { // 1. When we acquire the relic
cardSelected = false; // 2. Tell the relic that we haven't bottled the card yet
if (AbstractDungeon.isScreenUp) { // 3. If the map is open - hide it.
AbstractDungeon.dynamicBanner.hide();
AbstractDungeon.overlayMenu.cancelButton.hide();
AbstractDungeon.previousScreen = AbstractDungeon.screen;
}
AbstractDungeon.getCurrRoom().phase = AbstractRoom.RoomPhase.INCOMPLETE;
// 4. Set the room to INCOMPLETE - don't allow us to use the map, etc.
CardGroup group = CardGroup.getGroupWithoutBottledCards(AbstractDungeon.player.masterDeck); // 5. Get a card group of all currently unbottled cards
AbstractDungeon.gridSelectScreen.open(group, 1, DESCRIPTIONS[3] + name + DESCRIPTIONS[2], false, false, false, false);
// 6. Open the grid selection screen with the cards from the CardGroup we specified above. The description reads "Select a card to bottle for" + (relic name) + "."
}
@Override
public void onUnequip() { // 1. On unequip
if (card != null) { // If the bottled card exists (prevents the game from crashing if we removed the bottled card from our deck for example.)
AbstractCard cardInDeck = AbstractDungeon.player.masterDeck.getSpecificCard(card); // 2. Get the card
if (cardInDeck != null) {
BottledPlaceholderField.inBottledPlaceholderField.set(cardInDeck, false); // In our SpireField - set the card to no longer be bottled. (Unbottle it)
}
}
}
@Override
public void update() {
super.update(); //Do all of the original update() method in AbstractRelic
if (!cardSelected && !AbstractDungeon.gridSelectScreen.selectedCards.isEmpty()) {
// If the card hasn't been bottled yet and we have cards selected in the gridSelectScreen (from onEquip)
cardSelected = true; //Set the cardSelected boolean to be true - we're about to bottle the card.
card = AbstractDungeon.gridSelectScreen.selectedCards.get(0); // The custom Savable "card" is going to equal
// The card from the selection screen (it's only 1, so it's at index 0)
BottledPlaceholderField.inBottledPlaceholderField.set(card, true); // Use our custom spire field to set that card to be bottled.
if (AbstractDungeon.getCurrRoom().phase == AbstractRoom.RoomPhase.INCOMPLETE) {
AbstractDungeon.getCurrRoom().phase = AbstractRoom.RoomPhase.COMPLETE;
}
AbstractDungeon.getCurrRoom().phase = AbstractRoom.RoomPhase.COMPLETE; // The room phase can now be set to complete (From INCOMPLETE in onEquip)
AbstractDungeon.gridSelectScreen.selectedCards.clear(); // Always clear your grid screen after using it.
setDescriptionAfterLoading(); // Set the description to reflect the bottled card (the method is at the bottom of this file)
}
}
// And finally after all that we can code in the actual relic mechanic
public void onUseCard(AbstractCard targetCard, UseCardAction useCardAction) { // Whenever we use any card
boolean fullHandDialog = false; // Create a boolean (to prevent multiple "My hand is full!" dialogues if we have multiple cards bottled)
for (Iterator<AbstractCard> it = AbstractDungeon.player.drawPile.group.iterator(); it.hasNext(); ) {
// Create a new Iterator called "it" that checks for all AbstractCards in our draw pile. For each card:
AbstractCard card = it.next(); // create a new AbstractCard named "card" which is equal to the current card in the for each loop
if (BottledPlaceholderField.inBottledPlaceholderField.get(card)) { // Check if our SpireField matches said card
// Essentially, we end up with: Check if the draw pile has a card that is bottled with this bottle
// So, once we find a card that is bottled:
this.flash(); // The relic flashes
it.remove(); // Remove that card from the iterator (to prevent infinite loops)
if (AbstractDungeon.player.hand.size() < BaseMod.MAX_HAND_SIZE) { // If your hand isn't full
if (AutoplayField.autoplay.get(card)) { // If the card auto-plays - auto play it
AbstractDungeon.actionManager.addToBottom(new AutoplayCardAction(card, AbstractDungeon.player.hand));
}
card.triggerWhenDrawn(); // If the card triggers an effect on being drawn - trigger it
AbstractDungeon.player.drawPile.moveToHand(card, AbstractDungeon.player.drawPile); // Move the card to your hand from your draw pile
for (AbstractRelic r : AbstractDungeon.player.relics) { // And if you have any relics that trigger on card draw - trigger them
r.onCardDraw(card);
}
} else { // If your hand IS full - create a single "My hand is full!" dialogue and move the card to the discard pile instead
if (!fullHandDialog) {
AbstractDungeon.player.createHandIsFullDialog();
fullHandDialog = true;
}
AbstractDungeon.player.drawPile.moveToDiscardPile(card);
}
}
}
}
// Change description after relic is already loaded to reflect the bottled card.
public void setDescriptionAfterLoading() {
this.description = DESCRIPTIONS[1] + FontHelper.colorString(card.name, "y") + DESCRIPTIONS[2];
this.tips.clear();
this.tips.add(new PowerTip(this.name, this.description));
this.initializeTips();
}
// Standard description
@Override
public String getUpdatedDescription() {
return DESCRIPTIONS[0];
}
}

View File

@ -0,0 +1,109 @@
package constructTheArena.relics;
import basemod.abstracts.CustomRelic;
import com.badlogic.gdx.graphics.Texture;
import com.evacipated.cardcrawl.mod.stslib.relics.ClickableRelic;
import com.megacrit.cardcrawl.actions.animations.TalkAction;
import com.megacrit.cardcrawl.actions.animations.VFXAction;
import com.megacrit.cardcrawl.actions.defect.EvokeOrbAction;
import com.megacrit.cardcrawl.actions.utility.SFXAction;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.helpers.PowerTip;
import com.megacrit.cardcrawl.rooms.AbstractRoom;
import com.megacrit.cardcrawl.vfx.CollectorCurseEffect;
import constructTheArena.DefaultMod;
import constructTheArena.util.TextureLoader;
import static constructTheArena.DefaultMod.makeRelicOutlinePath;
import static constructTheArena.DefaultMod.makeRelicPath;
public class DefaultClickableRelic extends CustomRelic implements ClickableRelic { // You must implement things you want to use from StSlib
/*
* https://github.com/daviscook477/BaseMod/wiki/Custom-Relics
* StSLib for Clickable Relics
*
* Usable once per turn. Right click: Evoke your rightmost orb.
*/
// ID, images, text.
public static final String ID = DefaultMod.makeID("DefaultClickableRelic");
private static final Texture IMG = TextureLoader.getTexture(makeRelicPath("default_clickable_relic.png"));
private static final Texture OUTLINE = TextureLoader.getTexture(makeRelicOutlinePath("default_clickable_relic.png"));
private boolean usedThisTurn = false; // You can also have a relic be only usable once per combat. Check out Hubris for more examples, including other StSlib things.
private boolean isPlayerTurn = false; // We should make sure the relic is only activateable during our turn, not the enemies'.
public DefaultClickableRelic() {
super(ID, IMG, OUTLINE, RelicTier.COMMON, LandingSound.CLINK);
tips.clear();
tips.add(new PowerTip(name, description));
}
@Override
public void onRightClick() {// On right click
if (!isObtained || usedThisTurn || !isPlayerTurn) {
// If it has been used this turn, the player doesn't actually have the relic (i.e. it's on display in the shop room), or it's the enemy's turn
return; // Don't do anything.
}
if (AbstractDungeon.getCurrRoom() != null && AbstractDungeon.getCurrRoom().phase == AbstractRoom.RoomPhase.COMBAT) { // Only if you're in combat
usedThisTurn = true; // Set relic as "Used this turn"
flash(); // Flash
stopPulse(); // And stop the pulsing animation (which is started in atPreBattle() below)
AbstractDungeon.actionManager.addToBottom(new TalkAction(true, DESCRIPTIONS[1], 4.0f, 2.0f)); // Player speech bubble saying "YOU ARE MINE!" (See relic strings)
AbstractDungeon.actionManager.addToBottom(new SFXAction("MONSTER_COLLECTOR_DEBUFF")); // Sound Effect Action of The Collector Nails
AbstractDungeon.actionManager.addToBottom(new VFXAction( // Visual Effect Action of the nails applies on a random monster's position.
new CollectorCurseEffect(AbstractDungeon.getRandomMonster().hb.cX, AbstractDungeon.getRandomMonster().hb.cY), 2.0F));
AbstractDungeon.actionManager.addToBottom(new EvokeOrbAction(1)); // Evoke your rightmost orb
}
// See that talk action? It has DESCRIPTIONS[1] instead of just hard-coding "You are mine" inside.
// DO NOT HARDCODE YOUR STRINGS ANYWHERE, it's really bad practice to have "Strings" in your code:
/*
* 1. It's bad for if somebody likes your mod enough (or if you decide) to translate it.
* Having only the JSON files for translation rather than 15 different instances of "Dexterity" in some random cards is A LOT easier.
*
* 2. You don't have a centralised file for all strings for easy proof-reading. If you ever want to change a string
* you don't have to go through all your files individually/pray that a mass-replace doesn't screw something up.
*
* 3. Without hardcoded strings, editing a string doesn't require a compile, saving you time (unless you clean+package).
*
*/
}
@Override
public void atPreBattle() {
usedThisTurn = false; // Make sure usedThisTurn is set to false at the start of each combat.
beginLongPulse(); // Pulse while the player can click on it.
}
public void atTurnStart() {
usedThisTurn = false; // Resets the used this turn. You can remove this to use a relic only once per combat rather than per turn.
isPlayerTurn = true; // It's our turn!
beginLongPulse(); // Pulse while the player can click on it.
}
@Override
public void onPlayerEndTurn() {
isPlayerTurn = false; // Not our turn now.
stopPulse();
}
@Override
public void onVictory() {
stopPulse(); // Don't keep pulsing past the victory screen/outside of combat.
}
// Description
@Override
public String getUpdatedDescription() {
return DESCRIPTIONS[0];
}
}

View File

@ -0,0 +1,54 @@
package constructTheArena.relics;
import basemod.abstracts.CustomRelic;
import com.badlogic.gdx.graphics.Texture;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import constructTheArena.DefaultMod;
import constructTheArena.util.TextureLoader;
import static constructTheArena.DefaultMod.makeRelicOutlinePath;
import static constructTheArena.DefaultMod.makeRelicPath;
public class PlaceholderRelic extends CustomRelic {
/*
* https://github.com/daviscook477/BaseMod/wiki/Custom-Relics
*
* Gain 1 energy.
*/
// ID, images, text.
public static final String ID = DefaultMod.makeID("PlaceholderRelic");
private static final Texture IMG = TextureLoader.getTexture(makeRelicPath("placeholder_relic.png"));
private static final Texture OUTLINE = TextureLoader.getTexture(makeRelicOutlinePath("placeholder_relic.png"));
public PlaceholderRelic() {
super(ID, IMG, OUTLINE, RelicTier.STARTER, LandingSound.MAGICAL);
}
// Flash at the start of Battle.
@Override
public void atBattleStartPreDraw() {
flash();
}
// Gain 1 energy on equip.
@Override
public void onEquip() {
AbstractDungeon.player.energy.energyMaster += 1;
}
// Lose 1 energy on unequip.
@Override
public void onUnequip() {
AbstractDungeon.player.energy.energyMaster -= 1;
}
// Description
@Override
public String getUpdatedDescription() {
return DESCRIPTIONS[0];
}
}

View File

@ -0,0 +1,48 @@
package constructTheArena.relics;
import basemod.abstracts.CustomRelic;
import com.badlogic.gdx.graphics.Texture;
import com.megacrit.cardcrawl.actions.common.ApplyPowerAction;
import com.megacrit.cardcrawl.actions.common.RelicAboveCreatureAction;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.powers.StrengthPower;
import constructTheArena.DefaultMod;
import constructTheArena.util.TextureLoader;
import static constructTheArena.DefaultMod.makeRelicOutlinePath;
import static constructTheArena.DefaultMod.makeRelicPath;
public class PlaceholderRelic2 extends CustomRelic {
/*
* https://github.com/daviscook477/BaseMod/wiki/Custom-Relics
*
* At the start of each combat, gain 1 Strength (i.e. Vajra)
*/
// ID, images, text.
public static final String ID = DefaultMod.makeID("PlaceholderRelic2");
private static final Texture IMG = TextureLoader.getTexture(makeRelicPath("placeholder_relic2.png"));
private static final Texture OUTLINE = TextureLoader.getTexture(makeRelicOutlinePath("placeholder_relic2.png"));
public PlaceholderRelic2() {
super(ID, IMG, OUTLINE, RelicTier.COMMON, LandingSound.FLAT);
}
// Gain 1 Strength on on equip.
@Override
public void atBattleStart() {
flash();
AbstractDungeon.actionManager.addToTop(new ApplyPowerAction(AbstractDungeon.player, AbstractDungeon.player, new StrengthPower(AbstractDungeon.player, 1), 1));
AbstractDungeon.actionManager.addToTop(new RelicAboveCreatureAction(AbstractDungeon.player, this));
}
// Description
@Override
public String getUpdatedDescription() {
return DESCRIPTIONS[0];
}
}

View File

@ -0,0 +1,10 @@
package constructTheArena.util;
public class IDCheckDontTouchPls {
public String DEFAULTID;
public String DEVID;
public String EXCEPTION;
public String PACKAGE_EXCEPTION;
public String RESOURCE_FOLDER_EXCEPTION;
public String RESOURCES;
}

View File

@ -0,0 +1,51 @@
package constructTheArena.util;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.utils.GdxRuntimeException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashMap;
// Thank you Blank The Evil!
// Welcome to the utilities package. This package is for small utilities that make our life easier.
// You honestly don't need to bother with this unless you want to know how we're loading the textures.
public class TextureLoader {
private static HashMap<String, Texture> textures = new HashMap<String, Texture>();
public static final Logger logger = LogManager.getLogger(TextureLoader.class.getName());
/**
* @param textureString - String path to the texture you want to load relative to resources,
* Example: "theDefaultResources/images/ui/missing_texture.png"
* @return <b>com.badlogic.gdx.graphics.Texture</b> - The texture from the path provided
*/
public static Texture getTexture(final String textureString) {
if (textures.get(textureString) == null) {
try {
loadTexture(textureString);
} catch (GdxRuntimeException e) {
logger.error("Could not find texture: " + textureString);
return getTexture("constructTheArenaResources/images/ui/missing_texture.png");
}
}
return textures.get(textureString);
}
/**
* Creates an instance of the texture, applies a linear filter to it, and places it in the HashMap
*
* @param textureString - String path to the texture you want to load relative to resources,
* Example: "img/ui/missingtexture.png"
* @throws GdxRuntimeException
*/
private static void loadTexture(final String textureString) throws GdxRuntimeException {
logger.info("DefaultMod | Loading Texture: " + textureString);
Texture texture = new Texture(textureString);
texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
textures.put(textureString, texture);
}
}

View File

@ -0,0 +1,51 @@
package constructTheArena.variables;
import basemod.abstracts.DynamicVariable;
import com.megacrit.cardcrawl.cards.AbstractCard;
import com.megacrit.cardcrawl.ui.panels.EnergyPanel;
import static constructTheArena.DefaultMod.makeID;
public class DefaultCustomVariable extends DynamicVariable
{ // Custom Dynamic Variables are what you do if you need your card text to display a cool, changing number that the base game doesn't provide.
// If the !D! and !B! (for Damage and Block) etc. are not enough for you, this is how you make your own one. It Changes In Real Time!
// This is what you type in your card string to make the variable show up. Remember to encase it in "!"'s in the json!
@Override
public String key()
{
return makeID("ENERGY_DAMAGE");
}
// Checks whether the current value is different than the base one.
// For example, this will check whether your damage is modified (i.e. by strength) and color the variable appropriately (Green/Red).
@Override
public boolean isModified(AbstractCard card)
{
return card.isDamageModified;
}
// The value the variable should display.
// In our case, it displays the damage the card would do, multiplied by the amount of energy the player currently has.
@Override
public int value(AbstractCard card)
{
return card.damage * EnergyPanel.getCurrentEnergy();
}
// The baseValue the variable should display.
// just like baseBlock or baseDamage, this is what the variable should reset to by default. (the base value before any modifications)
@Override
public int baseValue(AbstractCard card)
{
return card.baseDamage * EnergyPanel.getCurrentEnergy();
}
// If the card has it's damage upgraded, this variable will glow green on the upgrade selection screen as well.
@Override
public boolean upgraded(AbstractCard card)
{
return card.upgradedDamage;
}
}

View File

@ -0,0 +1,41 @@
package constructTheArena.variables;
import basemod.abstracts.DynamicVariable;
import com.megacrit.cardcrawl.cards.AbstractCard;
import constructTheArena.cards.AbstractDefaultCard;
import static constructTheArena.DefaultMod.makeID;
public class DefaultSecondMagicNumber extends DynamicVariable {
//For in-depth comments, check the other variable(DefaultCustomVariable). It's nearly identical.
@Override
public String key() {
return makeID("SecondMagic");
// This is what you put between "!!" in your card strings to actually display the number.
// You can name this anything (no spaces), but please pre-phase it with your mod name as otherwise mod conflicts can occur.
// Remember, we're using makeID so it automatically puts "theDefault:" (or, your id) before the name.
}
@Override
public boolean isModified(AbstractCard card) {
return ((AbstractDefaultCard) card).isDefaultSecondMagicNumberModified;
}
@Override
public int value(AbstractCard card) {
return ((AbstractDefaultCard) card).defaultSecondMagicNumber;
}
@Override
public int baseValue(AbstractCard card) {
return ((AbstractDefaultCard) card).defaultBaseSecondMagicNumber;
}
@Override
public boolean upgraded(AbstractCard card) {
return ((AbstractDefaultCard) card).upgradedDefaultSecondMagicNumber;
}
}

View File

@ -0,0 +1,8 @@
{
"DEFAULTID": "theDefault",
"DEVID": "theDefaultDev",
"EXCEPTION": "Go to your constructor in your class with SpireInitializer and change your mod ID from \"theDefault\"",
"PACKAGE_EXCEPTION": "Rename your theDefault folder (package) to match your mod ID! ",
"RESOURCE_FOLDER_EXCEPTION": "Rename your theDefaultResources folder to match your mod ID!",
"RESOURCES": "Resources"
}

View File

@ -0,0 +1,12 @@
{
"modid": "${project.artifactId}",
"name": "${project.name}",
"author_list": ["Virgil"],
"credits": "Basemod, ModTheSpire",
"description": "${project.description}",
"version": "${project.version}",
"sts_version": "${SlayTheSpire.version}",
"mts_version": "${ModTheSpire.version}",
"dependencies": ["basemod", "stslib"],
"update_json": ""
}

View File

@ -0,0 +1,53 @@
{
"constructTheArena:DefaultCommonAttack": {
"NAME": "Strike",
"DESCRIPTION": "Deal !D! Damage. NL [#0000ff]This[] [#ff0000]Is[] [#ffff00]How[] [#88888]color[] [#ff00ff]works.[]"
},
"constructTheArena:DefaultCommonSkill": {
"NAME": "Defend",
"DESCRIPTION": "Gain !B! Block. NL constructTheArena:Multiword_Keyword."
},
"constructTheArena:DefaultCommonPower": {
"NAME": "Hold Place",
"DESCRIPTION": "Gain !M! constructTheArena:Keyword.",
"UPGRADE_DESCRIPTION": "Gain !M! constructTheArena:Keywords."
},
"constructTheArena:DefaultUncommonAttack": {
"NAME": "Big slap",
"DESCRIPTION": "Deal !D! Damage."
},
"constructTheArena:DefaultUncommonSkill": {
"NAME": "A Better Defend",
"DESCRIPTION": "Gain !B! Plated Armor. Affected by Dexterity."
},
"constructTheArena:DefaultUncommonPower": {
"NAME": "Weirdness",
"DESCRIPTION": "Gain X constructTheArena:Keywords.",
"UPGRADE_DESCRIPTION": "Gain X +1 constructTheArena:Keywords."
},
"constructTheArena:DefaultRareAttack": {
"NAME": "TOUCH",
"DESCRIPTION": "Deal !D! Damage."
},
"constructTheArena:DefaultRareSkill": {
"NAME": "For Each Loop x2",
"DESCRIPTION": "Apply !M! vulnerable to all enemies, 2 times.",
"UPGRADE_DESCRIPTION": "Apply !M! vulnerable to all enemies, 3 times."
},
"constructTheArena:DefaultRarePower": {
"NAME": "In-Progress Form",
"DESCRIPTION": "At the start of your turn, play a TOUCH."
},
"constructTheArena:DefaultAttackWithVariable": {
"NAME": "Special Strike",
"DESCRIPTION": "Deal !D! Damage [E] times. NL (Deal !constructTheArena:ENERGY_DAMAGE! damage total.)"
},
"constructTheArena:DefaultSecondMagicNumberSkill": {
"NAME": "Cool 2 Number Card",
"DESCRIPTION": "Apply !M! vulnerable and !constructTheArena:SecondMagic! poison to an enemy."
},
"constructTheArena:OrbSkill": {
"NAME": "Get Orb",
"DESCRIPTION": "Channel 1 Default Orb."
}
}

View File

@ -0,0 +1,13 @@
{
"constructTheArena:DefaultCharacter": {
"NAMES": [
"The Default",
"the Default"
],
"TEXT": [
"THE_DEFAULT exists and is valid. THE_DEFAULT wishes to T o uuuch# NL Second placeholder line of description text.",
"NL You TOUCH the heart.",
"Navigating an unlit street, you come across several hooded figures in the midst of some dark ritual. As you approach, they turn to you in eerie unison. The tallest among them bares fanged teeth and extends a long, pale hand towards you. NL ~\"Join~ ~us~ ~basic~ ~one,~ ~and~ ~feel~ ~the~ ~warmth~ ~of~ ~the~ ~Spire.\"~"
]
}
}

View File

@ -0,0 +1,21 @@
{
"constructTheArena:IdentityCrisisEvent": {
"NAME": "Identity Crisis",
"DESCRIPTIONS": [
"As you move through ~The~ ~City~ you come across a #pPurple #pMirror. NL You look at it, but you see no reflection.",
"You see no one but #g~You~ #g~wish~ #g~to~ #g~become~ #g~someone.~",
"#r@YOU@ #r@EXIST@ #r@AND@ #r@ARE@ #r@VALID!@",
"#yYou #yare #yat #ypeace #ywith #yyour #ypresence.",
"You have no hands."
],
"OPTIONS": [
"[Inspiration] #gGain #ga #gRandom #gStarting #gRelic.",
"[Denial] #rLose #r",
" #rMax #rHP. #gRemove #ga #gCard #gfrom #gyour #gdeck.",
"[Acceptance] #gGain #gApotheosis.",
"[TOUCH THE MIRROR] TOUCH TOUCH TO#C# TOUCH TO UCH TOUCH #rTOUCH TOUCH",
"[Leave]",
"Select card to remove."
]
}
}

View File

@ -0,0 +1,20 @@
[
{
"NAMES": [
"keyword",
"keywords"
],
"DESCRIPTION": "Whenever you play a card, gain 1 dexterity this turn only."
},
{
"PROPER_NAME": "Multiword Keyword",
"NAMES": [
"Multiword Keyword",
"multiword_keyword",
"multiword_keywords",
"multikey",
"multikeyword"
],
"DESCRIPTION": "Make sure the first element in the NAMES is the same as your PROPER_NAME. The rest is to match keywords like the single word keyword."
}
]

View File

@ -0,0 +1,11 @@
{
"constructTheArena:DefaultOrb": {
"NAME": "Default Orb",
"DESCRIPTION": [
"#yPassive: At the start of your turn, draw #b",
" card.",
" cards.",
"NL #yEvoke: Deal #b damage to all enemies."
]
}
}

View File

@ -0,0 +1,6 @@
{
"constructTheArena:PlaceholderPotion": {
"NAME": "Placeholder Potion",
"DESCRIPTIONS": ["Gain ", " At the end of your turn, lose ", " Strength."]
}
}

View File

@ -0,0 +1,10 @@
{
"constructTheArena:CommonPower": {
"NAME": "Placeholder Power",
"DESCRIPTIONS": ["You applied #b", " keyword.", " keywords."]
},
"constructTheArena:RarePower": {
"NAME": "Placeholder Power",
"DESCRIPTIONS": ["At the start of your turn, TOUCH #b", " enemy." , " enemies."]
}
}

View File

@ -0,0 +1,34 @@
{
"constructTheArena:PlaceholderRelic": {
"NAME": "Placeholder Energy Relic",
"FLAVOR": "The snake is a friend.",
"DESCRIPTIONS": [
"Gain #b1 [E] ."
]
},
"constructTheArena:PlaceholderRelic2": {
"NAME": "Placeholder Relic 2",
"FLAVOR": "You feel stronger.",
"DESCRIPTIONS": [
"At the start of each combat, gain #b1 #yStrength."
]
},
"constructTheArena:DefaultClickableRelic": {
"NAME": "Right-Clicky Relic",
"FLAVOR": "The more you right-click it, the more orbs you've evoked in your lifetime.",
"DESCRIPTIONS": [
"[#2aecd7]Usable [#2aecd7]once [#2aecd7]per [#2aecd7]turn.[] NL #yEvoke your rightmost Orb.",
"YOU ARE MINE!"
]
},
"constructTheArena:BottledPlaceholderRelic": {
"NAME": "Bottled Placeholder",
"FLAVOR": "What is this?",
"DESCRIPTIONS": [
"Choose a card. Whenever you take play any card, draw the chosen card.",
"Whenever you play a card, draw ",
".",
"Select a card to bottle for "
]
}
}