diff --git a/aurorastation.dme b/aurorastation.dme
index 818854107fd..fc29ab16927 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -20,6 +20,7 @@
#include "code\__defines\_macros.dm"
#include "code\__defines\admin.dm"
#include "code\__defines\atmos.dm"
+#include "code\__defines\battle_monsters.dm"
#include "code\__defines\callback.dm"
#include "code\__defines\chemistry.dm"
#include "code\__defines\damage_organs.dm"
@@ -153,6 +154,7 @@
#include "code\controllers\subsystems\ao.dm"
#include "code\controllers\subsystems\arrivals.dm"
#include "code\controllers\subsystems\assets.dm"
+#include "code\controllers\subsystems\battle_monsters.dm"
#include "code\controllers\subsystems\cargo.dm"
#include "code\controllers\subsystems\chemistry.dm"
#include "code\controllers\subsystems\effects.dm"
@@ -1139,6 +1141,17 @@
#include "code\modules\awaymissions\pamphlet.dm"
#include "code\modules\awaymissions\trigger.dm"
#include "code\modules\awaymissions\zlevel.dm"
+#include "code\modules\battlemonsters\datum_core.dm"
+#include "code\modules\battlemonsters\datum_elements.dm"
+#include "code\modules\battlemonsters\datum_monsters.dm"
+#include "code\modules\battlemonsters\datum_spells.dm"
+#include "code\modules\battlemonsters\datum_titles.dm"
+#include "code\modules\battlemonsters\datum_traps.dm"
+#include "code\modules\battlemonsters\items\card.dm"
+#include "code\modules\battlemonsters\items\core.dm"
+#include "code\modules\battlemonsters\items\deck.dm"
+#include "code\modules\battlemonsters\items\wrapped.dm"
+#include "code\modules\battlemonsters\items\furniture\dueling_area.dm"
#include "code\modules\blob\blob.dm"
#include "code\modules\cargo\bounty.dm"
#include "code\modules\cargo\export_scanner.dm"
diff --git a/code/__defines/battle_monsters.dm b/code/__defines/battle_monsters.dm
new file mode 100644
index 00000000000..b3737dae408
--- /dev/null
+++ b/code/__defines/battle_monsters.dm
@@ -0,0 +1,74 @@
+//Rarity
+#define BATTLE_MONSTERS_RARITY_COMMON 100
+#define BATTLE_MONSTERS_RARITY_UNCOMMON 25
+#define BATTLE_MONSTERS_RARITY_RARE 5
+#define BATTLE_MONSTERS_RARITY_MYTHICAL 2.5
+#define BATTLE_MONSTERS_RARITY_LEGENDARY 1
+
+//Card Types
+#define BATTLE_MONSTERS_CARDTYPE_MONSTER 0
+#define BATTLE_MONSTERS_CARDTYPE_TRAP 1
+#define BATTLE_MONSTERS_CARDTYPE_SPELL 1
+
+//Attack Types
+#define BATTLE_MONSTERS_ATTACKTYPE_NONE 0x0
+#define BATTLE_MONSTERS_ATTACKTYPE_SPELLCASTER 0x1
+#define BATTLE_MONSTERS_ATTACKTYPE_SWORDSMAN 0x2
+#define BATTLE_MONSTERS_ATTACKTYPE_ARMY 0x4
+#define BATTLE_MONSTERS_ATTACKTYPE_CLAWS 0x8
+#define BATTLE_MONSTERS_ATTACKTYPE_CLUB 0x10
+#define BATTLE_MONSTERS_ATTACKTYPE_TEETH 0x20
+#define BATTLE_MONSTERS_ATTACKTYPE_SHIELD 0x40
+
+//Defense Types
+#define BATTLE_MONSTERS_DEFENSETYPE_NONE 0x0
+#define BATTLE_MONSTERS_DEFENSETYPE_HUMAN 0x1
+#define BATTLE_MONSTERS_DEFENSETYPE_GOD 0x2
+#define BATTLE_MONSTERS_DEFENSETYPE_MACHINE 0x4
+#define BATTLE_MONSTERS_DEFENSETYPE_CREATURE 0x8
+#define BATTLE_MONSTERS_DEFENSETYPE_FLYING 0x10
+#define BATTLE_MONSTERS_DEFENSETYPE_COLOSSUS 0x20
+#define BATTLE_MONSTERS_DEFENSETYPE_REPTILE 0x40
+#define BATTLE_MONSTERS_DEFENSETYPE_FELINE 0x80
+#define BATTLE_MONSTERS_DEFENSETYPE_INSECT 0x100
+#define BATTLE_MONSTERS_DEFENSETYPE_DEMON 0x200
+
+
+#define BATTLE_MONSTERS_DEFENSETYPE_ANTMAN BATTLE_MONSTERS_DEFENSETYPE_INSECT | BATTLE_MONSTERS_DEFENSETYPE_HUMAN
+#define BATTLE_MONSTERS_DEFENSETYPE_CATMAN BATTLE_MONSTERS_DEFENSETYPE_FELINE | BATTLE_MONSTERS_DEFENSETYPE_HUMAN
+#define BATTLE_MONSTERS_DEFENSETYPE_LIZARDMAN BATTLE_MONSTERS_DEFENSETYPE_REPTILE | BATTLE_MONSTERS_DEFENSETYPE_HUMAN
+#define BATTLE_MONSTERS_DEFENSETYPE_DEMIGOD BATTLE_MONSTERS_DEFENSETYPE_GOD | BATTLE_MONSTERS_DEFENSETYPE_HUMAN
+#define BATTLE_MONSTERS_DEFENSETYPE_CYBORG BATTLE_MONSTERS_DEFENSETYPE_MACHINE | BATTLE_MONSTERS_DEFENSETYPE_HUMAN
+#define BATTLE_MONSTERS_DEFENSETYPE_HYBRID BATTLE_MONSTERS_DEFENSETYPE_CREATURE | BATTLE_MONSTERS_DEFENSETYPE_HUMAN
+
+#define BATTLE_MONSTERS_DEFENSETYPE_GIANT BATTLE_MONSTERS_DEFENSETYPE_COLOSSUS | BATTLE_MONSTERS_DEFENSETYPE_HUMAN
+
+#define BATTLE_MONSTERS_DEFENSETYPE_DRAGON BATTLE_MONSTERS_DEFENSETYPE_REPTILE | BATTLE_MONSTERS_DEFENSETYPE_FLYING
+#define BATTLE_MONSTERS_DEFENSETYPE_DRAGONHYBRID BATTLE_MONSTERS_DEFENSETYPE_DRAGON | BATTLE_MONSTERS_DEFENSETYPE_HUMAN
+#define BATTLE_MONSTERS_DEFENSETYPE_FERALDRAGON BATTLE_MONSTERS_DEFENSETYPE_CREATURE | BATTLE_MONSTERS_DEFENSETYPE_DRAGON
+#define BATTLE_MONSTERS_DEFENSETYPE_GIANT_DRAGON BATTLE_MONSTERS_DEFENSETYPE_COLOSSUS | BATTLE_MONSTERS_DEFENSETYPE_DRAGON
+
+//Element Types
+#define BATTLE_MONSTERS_ELEMENT_NONE 0x0
+#define BATTLE_MONSTERS_ELEMENT_NEUTRAL 0x1
+#define BATTLE_MONSTERS_ELEMENT_FIRE 0x2
+#define BATTLE_MONSTERS_ELEMENT_ENERGY 0x4
+#define BATTLE_MONSTERS_ELEMENT_WATER 0x8
+#define BATTLE_MONSTERS_ELEMENT_ICE 0x10
+#define BATTLE_MONSTERS_ELEMENT_EARTH 0x20
+#define BATTLE_MONSTERS_ELEMENT_STONE 0x40
+#define BATTLE_MONSTERS_ELEMENT_DARK 0x80
+#define BATTLE_MONSTERS_ELEMENT_LIGHT 0x100
+#define BATTLE_MONSTERS_ELEMENT_ALL 0xFFFF
+
+//MISC
+#define BATTLE_MONSTERS_FORMAT_STAR "
"
+
+
+//Generation Helpers
+#define BATTLE_MONSTERS_GENERATION_NAME 0x1
+#define BATTLE_MONSTERS_GENERATION_DESCRIPTION 0x2
+#define BATTLE_MONSTERS_GENERATION_SPECIALEFFECTS 0x4
+#define BATTLE_MONSTERS_GENERATION_FORM 0x8
+#define BATTLE_MONSTERS_GENERATION_STATS 0x10
+#define BATTLE_MONSTERS_GENERATION_ALL 0xFFFF //Everything
diff --git a/code/controllers/subsystems/battle_monsters.dm b/code/controllers/subsystems/battle_monsters.dm
new file mode 100644
index 00000000000..7609f99f552
--- /dev/null
+++ b/code/controllers/subsystems/battle_monsters.dm
@@ -0,0 +1,492 @@
+#define BATTLE_MONSTERS_GEN_PREFIX 1
+#define BATTLE_MONSTERS_GEN_ROOT 2
+#define BATTLE_MONSTERS_GEN_SUFFIX 3
+#define BATTLE_MONSTERS_GEN_TRAP 4
+#define BATTLE_MONSTERS_GEN_SPELL 5
+
+var/datum/controller/subsystem/battle_monsters/SSbattlemonsters
+
+/datum/controller/subsystem/battle_monsters
+ name = "Battle Monsters"
+ init_order = SS_INIT_MISC_FIRST
+ flags = SS_NO_FIRE
+
+ var/list/monster_elements
+ var/list/monster_roots
+ var/list/monster_titles
+
+ var/list/monster_elements_rng
+ var/list/monster_roots_rng
+ var/list/monster_titles_rng
+
+ var/list/traps
+ var/list/spells
+
+ var/list/traps_rng
+ var/list/spells_rng
+
+
+/datum/controller/subsystem/battle_monsters/New()
+ NEW_SS_GLOBAL(SSbattlemonsters)
+
+/datum/controller/subsystem/battle_monsters/Initialize()
+ GenerateDatum(BATTLE_MONSTERS_GEN_PREFIX)
+ GenerateDatum(BATTLE_MONSTERS_GEN_ROOT)
+ GenerateDatum(BATTLE_MONSTERS_GEN_SUFFIX)
+ GenerateDatum(BATTLE_MONSTERS_GEN_TRAP)
+ GenerateDatum(BATTLE_MONSTERS_GEN_SPELL)
+
+/datum/controller/subsystem/battle_monsters/proc/CreateCard(var/identifier,var/turf/cardloc)
+ var/list/splitstring = dd_text2List(identifier,",")
+ var/obj/item/battle_monsters/card/new_card
+
+ if(splitstring[1] == "spell_type")
+ new_card = new(cardloc,null,null,null,null,splitstring[2])
+ else if(splitstring[1] == "trap_type")
+ new_card = new(cardloc,null,null,null,splitstring[2],null)
+ else
+ new_card = new(cardloc,splitstring[1],splitstring[2],splitstring[3],null,null)
+
+ return new_card
+
+/datum/controller/subsystem/battle_monsters/proc/GenerateDatum(var/generation_type)
+
+ var/list/datum_paths
+
+ switch(generation_type)
+ if(BATTLE_MONSTERS_GEN_PREFIX)
+ datum_paths = subtypesof(/datum/battle_monsters/element)
+ if(BATTLE_MONSTERS_GEN_ROOT)
+ datum_paths = subtypesof(/datum/battle_monsters/monster)
+ if(BATTLE_MONSTERS_GEN_SUFFIX)
+ datum_paths = subtypesof(/datum/battle_monsters/title)
+ if(BATTLE_MONSTERS_GEN_TRAP)
+ datum_paths = subtypesof(/datum/battle_monsters/trap)
+ if(BATTLE_MONSTERS_GEN_SPELL)
+ datum_paths = subtypesof(/datum/battle_monsters/spell)
+
+ var/list/id_datum_list = list()
+ var/list/id_rng_list = list()
+
+ for(var/path in datum_paths)
+ var/datum/battle_monsters/generated = new path()
+ if(!generated.id)
+ continue
+ id_datum_list[generated.id] = generated
+ id_rng_list[generated.id] = generated.rarity
+
+ switch(generation_type)
+ if(BATTLE_MONSTERS_GEN_PREFIX)
+ monster_elements = id_datum_list
+ monster_elements_rng = id_rng_list
+ if(BATTLE_MONSTERS_GEN_ROOT)
+ monster_roots = id_datum_list
+ monster_roots_rng = id_rng_list
+ if(BATTLE_MONSTERS_GEN_SUFFIX)
+ monster_titles = id_datum_list
+ monster_titles_rng = id_rng_list
+ if(BATTLE_MONSTERS_GEN_TRAP)
+ traps = id_datum_list
+ traps_rng = id_rng_list
+ if(BATTLE_MONSTERS_GEN_SPELL)
+ spells = id_datum_list
+ spells_rng = id_rng_list
+
+/datum/controller/subsystem/battle_monsters/proc/GetRandomPrefix()
+ return FindMatchingPrefix(pickweight(monster_elements_rng))
+
+/datum/controller/subsystem/battle_monsters/proc/GetRandomRoot()
+ return FindMatchingRoot(pickweight(monster_roots_rng))
+
+/datum/controller/subsystem/battle_monsters/proc/GetRandomSuffix()
+ return FindMatchingSuffix(pickweight(monster_titles_rng))
+
+/datum/controller/subsystem/battle_monsters/proc/GetRandomTrap()
+ return FindMatchingTrap(pickweight(traps_rng))
+
+/datum/controller/subsystem/battle_monsters/proc/GetRandomSpell()
+ return FindMatchingSpell(pickweight(spells_rng))
+
+/datum/controller/subsystem/battle_monsters/proc/GetRandomPrefix_Filtered(var/raremin,var/raremax)
+ var/list/edited_list = list()
+
+ for(var/id in monster_elements_rng)
+ var/score = monster_elements_rng[id]
+ if(score > raremax)
+ continue
+ if(score < raremin)
+ continue
+ edited_list[id] = score
+
+ if(edited_list.len <= 0)
+ edited_list = monster_elements_rng
+
+ return FindMatchingPrefix(pickweight(edited_list))
+
+/datum/controller/subsystem/battle_monsters/proc/GetRandomRoot_Filtered(var/raremin,var/raremax)
+ var/list/edited_list = list()
+
+ for(var/id in monster_roots_rng)
+ var/score = monster_roots_rng[id]
+ if(score > raremax)
+ continue
+ if(score < raremin)
+ continue
+ edited_list[id] = score
+
+ if(edited_list.len <= 0)
+ edited_list = monster_roots_rng
+
+ return FindMatchingRoot(pickweight(edited_list))
+
+/datum/controller/subsystem/battle_monsters/proc/GetRandomSuffix_Filtered(var/raremin,var/raremax)
+ var/list/edited_list = list()
+
+ for(var/id in monster_titles_rng)
+ var/score = monster_titles_rng[id]
+ if(score > raremax)
+ continue
+ if(score < raremin)
+ continue
+ edited_list[id] = score
+
+ if(edited_list.len <= 0)
+ edited_list = monster_titles_rng
+
+ return FindMatchingSuffix(pickweight(edited_list))
+
+
+/datum/controller/subsystem/battle_monsters/proc/FindMatchingPrefix(var/text,var/failsafe = FALSE)
+ if(monster_elements[text])
+ return monster_elements[text]
+ else if(failsafe)
+ return GetRandomPrefix()
+ else
+ return
+
+/datum/controller/subsystem/battle_monsters/proc/FindMatchingRoot(var/text,var/failsafe = FALSE)
+ if(monster_roots[text])
+ return monster_roots[text]
+ else if(failsafe)
+ return GetRandomRoot()
+ else
+ return
+
+/datum/controller/subsystem/battle_monsters/proc/FindMatchingSuffix(var/text,var/failsafe = FALSE)
+ if(monster_titles[text])
+ return monster_titles[text]
+ else if(failsafe)
+ return GetRandomSuffix()
+ else
+ return
+
+/datum/controller/subsystem/battle_monsters/proc/FindMatchingTrap(var/text,var/failsafe = FALSE)
+ if(traps[text])
+ return traps[text]
+ else if(failsafe)
+ return GetRandomTrap()
+ else
+ return
+
+/datum/controller/subsystem/battle_monsters/proc/FindMatchingSpell(var/text,var/failsafe = FALSE)
+ if(spells[text])
+ return spells[text]
+ else if(failsafe)
+ return GetRandomSpell()
+ else
+ return
+
+/datum/controller/subsystem/battle_monsters/proc/GetSpeciesGeneral(var/card_defense_type)
+ switch(card_defense_type)
+ if(BATTLE_MONSTERS_DEFENSETYPE_NONE)
+ return "monster"
+ if(BATTLE_MONSTERS_DEFENSETYPE_GIANT_DRAGON)
+ return "giant dragon"
+ if(BATTLE_MONSTERS_DEFENSETYPE_FERALDRAGON)
+ return "feral dragon"
+ if(BATTLE_MONSTERS_DEFENSETYPE_DRAGONHYBRID)
+ return "human-dragon hybrid"
+ if(BATTLE_MONSTERS_DEFENSETYPE_DRAGON)
+ return "dragon"
+ if(BATTLE_MONSTERS_DEFENSETYPE_DEMIGOD)
+ return "demi-god"
+ if(BATTLE_MONSTERS_DEFENSETYPE_CYBORG)
+ return "cyborg"
+ if(BATTLE_MONSTERS_DEFENSETYPE_HYBRID)
+ return "hybrid"
+ if(BATTLE_MONSTERS_DEFENSETYPE_CATMAN)
+ return "catman"
+ if(BATTLE_MONSTERS_DEFENSETYPE_LIZARDMAN)
+ return "lizardman"
+ if(BATTLE_MONSTERS_DEFENSETYPE_ANTMAN)
+ return "antman"
+ if(BATTLE_MONSTERS_DEFENSETYPE_GIANT)
+ return "colossus"
+ if(BATTLE_MONSTERS_DEFENSETYPE_REPTILE)
+ return "reptile"
+ if(BATTLE_MONSTERS_DEFENSETYPE_HUMAN)
+ return "human"
+ if(BATTLE_MONSTERS_DEFENSETYPE_INSECT)
+ return "insect"
+ if(BATTLE_MONSTERS_DEFENSETYPE_GOD)
+ return "god"
+ if(BATTLE_MONSTERS_DEFENSETYPE_FELINE)
+ return "feline"
+ if(BATTLE_MONSTERS_DEFENSETYPE_MACHINE)
+ return "machine"
+ if(BATTLE_MONSTERS_DEFENSETYPE_CREATURE)
+ return "creature"
+ if(BATTLE_MONSTERS_DEFENSETYPE_COLOSSUS)
+ return "colossus"
+ if(BATTLE_MONSTERS_DEFENSETYPE_FLYING)
+ return "winged monster"
+
+ return "unknown"
+
+/datum/controller/subsystem/battle_monsters/proc/GetSpecies(card_defense_type, var/and_text = " and ")
+
+ //This list looks odd to prevent runtime errors related to out of bounds indexes
+ var/list/translations = list(
+ "Human" = BATTLE_MONSTERS_DEFENSETYPE_HUMAN,
+ "God" = BATTLE_MONSTERS_DEFENSETYPE_GOD,
+ "Machine" = BATTLE_MONSTERS_DEFENSETYPE_MACHINE,
+ "Creature" = BATTLE_MONSTERS_DEFENSETYPE_CREATURE,
+ "Flying" = BATTLE_MONSTERS_DEFENSETYPE_FLYING,
+ "Colossus" = BATTLE_MONSTERS_DEFENSETYPE_COLOSSUS,
+ "Reptile" = BATTLE_MONSTERS_DEFENSETYPE_REPTILE,
+ "Feline" = BATTLE_MONSTERS_DEFENSETYPE_FELINE,
+ "Insect" = BATTLE_MONSTERS_DEFENSETYPE_INSECT,
+ "Demon" = BATTLE_MONSTERS_DEFENSETYPE_DEMON
+ )
+
+ var/list/included_elements = list()
+
+ for(var/translation in translations)
+ if(!(translations[translation] & card_defense_type))
+ continue
+ included_elements.Add(translation)
+
+ return english_list(included_elements, nothing_text = "Monster", and_text = and_text)
+
+/datum/controller/subsystem/battle_monsters/proc/GetAttackType(var/card_attack_type, var/and_text = " and ")
+
+ //This list looks odd to prevent runtime errors related to out of bounds indexes
+ var/list/translations = list(
+ "Spellcaster" = BATTLE_MONSTERS_ATTACKTYPE_SPELLCASTER,
+ "Warrior" = BATTLE_MONSTERS_ATTACKTYPE_SWORDSMAN,
+ "Commander" = BATTLE_MONSTERS_ATTACKTYPE_ARMY,
+ "Warrior" = BATTLE_MONSTERS_ATTACKTYPE_CLAWS,
+ "Warrior" = BATTLE_MONSTERS_ATTACKTYPE_TEETH,
+ "Warrior" = BATTLE_MONSTERS_ATTACKTYPE_CLUB,
+ "Defender" = BATTLE_MONSTERS_ATTACKTYPE_SHIELD
+ )
+
+ var/list/included_elements = list()
+
+ for(var/translation in translations)
+ if(!(translations[translation] & card_attack_type))
+ continue
+ if(translation in included_elements)
+ continue
+ included_elements.Add(translation)
+
+ return english_list(included_elements, nothing_text = "Regular", and_text = and_text)
+
+/datum/controller/subsystem/battle_monsters/proc/GetWeapons(var/card_attack_type, var/and_text = " and ")
+
+ //This list looks odd to prevent runtime errors related to out of bounds indexes
+ var/list/translations = list(
+ "staff" = BATTLE_MONSTERS_ATTACKTYPE_SPELLCASTER,
+ "sword" = BATTLE_MONSTERS_ATTACKTYPE_SWORDSMAN,
+ "army" = BATTLE_MONSTERS_ATTACKTYPE_ARMY,
+ "claws" = BATTLE_MONSTERS_ATTACKTYPE_CLAWS,
+ "club" = BATTLE_MONSTERS_ATTACKTYPE_CLUB,
+ "fangs" = BATTLE_MONSTERS_ATTACKTYPE_TEETH,
+ "shield" = BATTLE_MONSTERS_ATTACKTYPE_SHIELD
+ )
+
+ var/list/included_elements = list()
+
+ for(var/translation in translations)
+ if(!(translations[translation] & card_attack_type))
+ continue
+ included_elements.Add(translation)
+
+ return english_list(included_elements, nothing_text = "fists", and_text = and_text)
+
+/datum/controller/subsystem/battle_monsters/proc/GetElements(var/card_elements,var/and_text = " and ")
+
+ //This list looks odd to prevent runtime errors related to out of bounds indexes
+ var/list/translations = list(
+ "Neutral" = BATTLE_MONSTERS_ELEMENT_NEUTRAL,
+ "Fire" = BATTLE_MONSTERS_ELEMENT_FIRE,
+ "Energy" = BATTLE_MONSTERS_ELEMENT_ENERGY,
+ "Water" = BATTLE_MONSTERS_ELEMENT_WATER,
+ "Ice" = BATTLE_MONSTERS_ELEMENT_ICE,
+ "Earth" = BATTLE_MONSTERS_ELEMENT_EARTH,
+ "Stone" = BATTLE_MONSTERS_ELEMENT_STONE,
+ "Dark" = BATTLE_MONSTERS_ELEMENT_DARK,
+ "Light" = BATTLE_MONSTERS_ELEMENT_LIGHT,
+ )
+
+ var/list/included_elements = list()
+
+ for(var/translation in translations)
+ if(!(translations[translation] & card_elements))
+ continue
+ included_elements.Add(translation)
+
+ return english_list(included_elements, nothing_text = "Neutral", and_text = and_text)
+
+/datum/controller/subsystem/battle_monsters/proc/GetStarLevel(var/power_rating)
+ return min(10,1 + round( (power_rating^1.25) * 0.00012))
+
+/datum/controller/subsystem/battle_monsters/proc/FormatMonsterText(var/text,var/datum/battle_monsters/element/prefix_datum,var/datum/battle_monsters/monster/root_datum,var/datum/battle_monsters/title/suffix_datum)
+
+ var/list/generated_stats = SSbattlemonsters.GenerateMonsterStats(prefix_datum,root_datum,suffix_datum)
+
+ if(!generated_stats || generated_stats.len == 0)
+ return "Something went wrong... go bother the monsterous devs about it."
+
+ var/list/replacements = list(
+ "%NAME" = generated_stats["name"],
+
+ "%DESCRIPTION" = generated_stats["desc"],
+ "%SPECIAL_EFFECTS" = generated_stats["special_effects"],
+
+ "%ELEMENT_AND" = GetElements(generated_stats["elements"]),
+ "%ELEMENT_OR" = GetElements(generated_stats["elements"], " or "),
+ "%ELEMENT_LIST" = GetElements(generated_stats["elements"], ", "),
+
+ "%TYPE" = GetAttackType(generated_stats["attack_type"]),
+ "%ATTACKTYPE_LIST" = GetAttackType(generated_stats["attack_type"], ", "),
+ "%WEAPON_AND" = GetWeapons(generated_stats["attack_type"]),
+
+ "%SPECIES_C" = capitalize(GetSpeciesGeneral(generated_stats["defense_type"])),
+ "%SPECIES_LIST" = GetSpecies(generated_stats["defense_type"], ", "),
+ "%SPECIES" = GetSpeciesGeneral(generated_stats["defense_type"]),
+
+ "%ATTACK_POINTS" = generated_stats["attack_points"],
+ "%DEFENSE_POINTS" = generated_stats["defense_points"],
+
+ "%STARLEVEL" = GetStarLevel(generated_stats["power"])
+ )
+
+ for(var/word in replacements)
+ text = replacetext(text,word,replacements[word])
+
+ for(var/word in replacements)//2 passes for good measure.
+ text = replacetext(text,word,replacements[word])
+
+ return text
+
+/datum/controller/subsystem/battle_monsters/proc/FormatSpellText(var/text,var/datum/battle_monsters/spell_datum)
+ var/list/generated_stats = SSbattlemonsters.GenerateSpellStats(spell_datum)
+
+ if(!generated_stats || generated_stats.len == 0)
+ return "Something went wrong... go bother the wizardly devs about it."
+
+ var/list/replacements = list(
+ "%NAME" = generated_stats["name"],
+
+ "%DESCRIPTION" = generated_stats["desc"],
+ "%SPECIAL_EFFECTS" = generated_stats["special_effects"],
+
+ "%ELEMENT_AND" = GetElements(generated_stats["elements"]),
+ "%ELEMENT_OR" = GetElements(generated_stats["elements"], " or "),
+ "%ELEMENT_LIST" = GetElements(generated_stats["elements"], ", ")
+ )
+
+ for(var/word in replacements)
+ text = replacetext(text,word,replacements[word])
+
+ for(var/word in replacements)//2 passes for good measure.
+ text = replacetext(text,word,replacements[word])
+
+ return text
+
+/datum/controller/subsystem/battle_monsters/proc/GetMonsterFormatting()
+ return "%NAME | %ELEMENT_LIST %TYPE | %SPECIES_C
\
+ Keywords: %SPECIES_LIST
\
+ ATK: %ATTACK_POINTS | DEF: %DEFENSE_POINTS
\
+ %SPECIAL_EFFECTS
\
+ The card depicts %DESCRIPTION"
+
+/datum/controller/subsystem/battle_monsters/proc/GetSpellFormatting()
+ return "%NAME | Spell | %ELEMENT_LIST
\
+ %SPECIAL_EFFECTS
\
+ The card depicts %DESCRIPTION"
+
+/datum/controller/subsystem/battle_monsters/proc/GetTrapFormatting()
+ return "%NAME | Trap | %ELEMENT_LIST
\
+ %SPECIAL_EFFECTS
\
+ The card depicts %DESCRIPTION"
+
+/datum/controller/subsystem/battle_monsters/proc/ExamineMonsterCard(var/mob/user,var/datum/battle_monsters/element/prefix_datum,var/datum/battle_monsters/monster/root_datum,var/datum/battle_monsters/title/suffix_datum)
+ to_chat(user,FormatMonsterText(GetMonsterFormatting(),prefix_datum,root_datum,suffix_datum))
+
+/datum/controller/subsystem/battle_monsters/proc/ExamineSpellCard(var/mob/user,var/datum/battle_monsters/spell/spell_datum)
+ to_chat(user,FormatSpellText(GetSpellFormatting(),spell_datum))
+
+/datum/controller/subsystem/battle_monsters/proc/ExamineTrapCard(var/mob/user,var/datum/battle_monsters/trap/trap_datum)
+ to_chat(user,FormatSpellText(GetTrapFormatting(),trap_datum))
+
+/datum/controller/subsystem/battle_monsters/proc/GenerateMonsterStats(var/datum/battle_monsters/element/prefix_datum,var/datum/battle_monsters/monster/root_datum,var/datum/battle_monsters/title/suffix_datum, var/limiter = BATTLE_MONSTERS_GENERATION_ALL)
+
+ var/returning_list[0] //Byond documentation told me this is how you make an assoc list.
+
+ returning_list["name"] = root_datum.name
+ if(prefix_datum.name)
+ returning_list["name"] = "[prefix_datum.name] [returning_list["name"]]"
+ if(suffix_datum.name)
+ returning_list["name"] = "[returning_list["name"]], [suffix_datum.name]"
+
+ returning_list["desc"] = root_datum.description
+ if(prefix_datum.description)
+ returning_list["desc"] += " [prefix_datum.description]"
+ if(suffix_datum.description)
+ returning_list["desc"] += "
[suffix_datum.description]"
+
+ returning_list["special_effects"] = ""
+ if(prefix_datum.special_effects)
+ returning_list["special_effects"] = trim("[returning_list["special_effects"]][prefix_datum.special_effects]
")
+ if(root_datum.special_effects)
+ returning_list["special_effects"] = trim("[returning_list["special_effects"]][root_datum.special_effects]
")
+ if(suffix_datum.special_effects)
+ returning_list["special_effects"] = trim("[returning_list["special_effects"]][suffix_datum.special_effects]
")
+
+ returning_list["elements"] = prefix_datum.elements | root_datum.elements | suffix_datum.elements
+ returning_list["attack_type"] = prefix_datum.attack_type | root_datum.attack_type | suffix_datum.attack_type
+ returning_list["defense_type"] = prefix_datum.defense_type | root_datum.defense_type | suffix_datum.defense_type
+
+ returning_list["rarity_score"] = prefix_datum.rarity_score + root_datum.rarity_score + suffix_datum.rarity_score
+ returning_list["attack_points"] = (prefix_datum.attack_add + root_datum.attack_add + suffix_datum.attack_add) * (prefix_datum.attack_mul * root_datum.attack_mul * suffix_datum.attack_mul)
+ returning_list["defense_points"] = (prefix_datum.defense_add + root_datum.defense_add + suffix_datum.defense_add) * (prefix_datum.defense_mul * root_datum.defense_mul * suffix_datum.defense_mul)
+ returning_list["power"] = (prefix_datum.power_add + root_datum.power_add + suffix_datum.power_add) * (prefix_datum.power_mul * root_datum.power_mul * suffix_datum.power_mul)
+ if(returning_list["attack_points"] >= returning_list["defense_points"])
+ returning_list["defense_points"] = returning_list["defense_points"]/(returning_list["attack_points"] + returning_list["defense_points"])
+ returning_list["attack_points"] = 1 - returning_list["defense_points"]
+ else
+ returning_list["attack_points"] = returning_list["attack_points"]/(returning_list["attack_points"] + returning_list["defense_points"])
+ returning_list["defense_points"] = 1 - returning_list["attack_points"]
+
+ returning_list["attack_points"] = round(returning_list["power"] * returning_list["attack_points"],100)
+ returning_list["defense_points"] = round(returning_list["power"] * returning_list["defense_points"],100)
+ returning_list["star_level"] = GetStarLevel(returning_list["power"])
+
+ return returning_list
+
+/datum/controller/subsystem/battle_monsters/proc/GenerateSpellStats(var/datum/battle_monsters/spell_datum)
+ var/returning_list[0] //Byond documentation told me this is how you make an assoc list.
+ returning_list["name"] = spell_datum.name
+ returning_list["desc"] = spell_datum.description
+ returning_list["special_effects"] = spell_datum.special_effects
+ returning_list["elements"] = spell_datum.elements
+ returning_list["rarity_score"] = spell_datum.rarity_score
+ return returning_list
+
+#undef BATTLE_MONSTERS_GEN_PREFIX
+#undef BATTLE_MONSTERS_GEN_ROOT
+#undef BATTLE_MONSTERS_GEN_SUFFIX
+#undef BATTLE_MONSTERS_GEN_TRAP
+#undef BATTLE_MONSTERS_GEN_SPELL
\ No newline at end of file
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 54285dfdaea..8a395730de5 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -54,6 +54,7 @@
'sound/ambience/ambigen14.ogg'
)
var/list/forced_ambience = null
+ var/loop_ambience = TRUE
var/sound_env = STANDARD_STATION
var/turf/base_turf //The base turf type of the area, which can be used to override the z-level's base turf
var/no_light_control = 0 // if 1, lights in area cannot be toggled with light controller
@@ -338,12 +339,12 @@ var/list/mob/living/forced_ambiance_list = new
if(!L.client.ambience_playing)
L.client.ambience_playing = 1
- L << sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = 2)
+ L << sound('sound/ambience/shipambience.ogg', repeat = loop_ambience, wait = 0, volume = 35, channel = 2)
if(forced_ambience)
if(forced_ambience.len)
forced_ambiance_list |= L
- L << sound(pick(forced_ambience), repeat = 1, wait = 0, volume = 25, channel = 1)
+ L << sound(pick(forced_ambience), repeat = loop_ambience, wait = 0, volume = 25, channel = 1)
else
L << sound(null, channel = 1)
else if(src.ambience.len && prob(35))
diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm
index a7cb46c5589..f2601c3045f 100644
--- a/code/game/objects/items/weapons/manuals.dm
+++ b/code/game/objects/items/weapons/manuals.dm
@@ -1416,4 +1416,14 @@
201: Means that the frame is not rated to handle the entirety of the heat energy of the components. Either upgrade the frame, or downgrade the components.