diff --git a/code/__DEFINES/~doppler_defines/TESHARI_WAR_defines.dm b/code/__DEFINES/~doppler_defines/TESHARI_WAR_defines.dm
new file mode 100644
index 00000000000000..bfca153ed5734c
--- /dev/null
+++ b/code/__DEFINES/~doppler_defines/TESHARI_WAR_defines.dm
@@ -0,0 +1,5 @@
+// Factions
+#define WAR_FACTION_TIZIRA "Tizira"
+#define WAR_FACTION_TESHARI "Teshari Soverignty"
+#define WAR_FACTION_ISOLATIONIST "4CA Isolationist"
+#define WAR_FACTION_NEUTRAL "Neutral"
diff --git a/code/game/objects/effects/posters/poster.dm b/code/game/objects/effects/posters/poster.dm
index ebfc3a791bb2f1..8e6859a4840291 100644
--- a/code/game/objects/effects/posters/poster.dm
+++ b/code/game/objects/effects/posters/poster.dm
@@ -155,6 +155,7 @@
poster_item_desc = initial(selected.poster_item_desc)
poster_item_icon_state = initial(selected.poster_item_icon_state)
ruined = initial(selected.ruined)
+ randomise_further(selected) // DOPPLER EDIT ADDITIION - War posters
if(length(GLOB.holidays) && prob(30)) // its the holidays! lets get festive
apply_holiday()
update_appearance()
diff --git a/code/modules/cargo/exports/large_objects.dm b/code/modules/cargo/exports/large_objects.dm
index ed65563b02ca36..920297e42d3622 100644
--- a/code/modules/cargo/exports/large_objects.dm
+++ b/code/modules/cargo/exports/large_objects.dm
@@ -8,6 +8,7 @@
/obj/structure/closet/crate/large,
/obj/structure/closet/crate/mail,
/obj/structure/closet/crate/wooden,
+ /obj/structure/closet/crate/donation, // DOPPLER EDIT ADDITION - Dono crates are in loadout and are craftable... do not want infinite money glitch
)
/datum/export/large/crate/total_printout(datum/export_report/ex, notes = TRUE) // That's why a goddamn metal crate costs that much.
diff --git a/code/modules/cargo/orderconsole.dm b/code/modules/cargo/orderconsole.dm
index d5984da9ec9828..c4b3cd16752a32 100644
--- a/code/modules/cargo/orderconsole.dm
+++ b/code/modules/cargo/orderconsole.dm
@@ -189,6 +189,7 @@
packs += list(list(
"name" = pack.name,
"cost" = pack.get_cost() * get_discount(),
+ "shortagemult" = pack.get_shortage_price_mult(), // DOPPLER EDIT ADDITION - TESHARI_WAR STORY MODULE
"id" = pack_id,
"desc" = pack.desc || pack.name, // If there is a description, use it. Otherwise use the pack's name.
"first_item_icon" = first_item?.icon,
diff --git a/code/modules/modular_computers/file_system/programs/dept_order.dm b/code/modules/modular_computers/file_system/programs/dept_order.dm
index e640d5ecb3710c..f0952e5b151e73 100644
--- a/code/modules/modular_computers/file_system/programs/dept_order.dm
+++ b/code/modules/modular_computers/file_system/programs/dept_order.dm
@@ -85,9 +85,18 @@ GLOBAL_VAR(department_cd_override)
if(!islist(supply_data[pack.group]) || !can_see_pack(pack))
continue
+ // DOPPLER EDIT ADDITION BEGIN - TESHARI_WAR STORY MODULE
+ var/shortage_text = ""
+ if (pack.is_unavailable())
+ shortage_text = " (Shortages: Unavailable!)"
+ else if (pack.get_shortage_price_mult() != 1)
+ shortage_text = " (Shortages: [pack.get_shortage_price_mult()]x cooldown)"
+ // DOPPLER EDIT ADDITION END
UNTYPED_LIST_ADD(supply_data[pack.group], list(
"name" = pack.name,
"cost" = pack.get_cost(),
+ "unavailable" = pack.is_unavailable(), // DOPPLER EDIT ADDITION - TESHARI_WAR STORY MODULE
+ "shortage_text" = shortage_text, // DOPPLER EDIT ADDITION - TESHARI_WAR STORY MODULE
"id" = pack.id,
"desc" = pack.desc || pack.name, // If there is a description, use it. Otherwise use the pack's name.
))
diff --git a/config/config.txt b/config/config.txt
index 35b7e9a352d176..ebf61685b778a5 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -3,6 +3,7 @@
$include game_options.txt
$include dbconfig.txt
$include doppler/config_doppler.txt
+$include doppler/STORY_SUPPLY_SHORTAGE.txt
$include comms.txt
$include logging.txt
$include resources.txt
diff --git a/config/doppler/STORY_SUPPLY_SHORTAGE.txt b/config/doppler/STORY_SUPPLY_SHORTAGE.txt
new file mode 100644
index 00000000000000..dfbc7ba520b870
--- /dev/null
+++ b/config/doppler/STORY_SUPPLY_SHORTAGE.txt
@@ -0,0 +1,10 @@
+## Instructions - write SUPPLY_SHORTAGES, followed by this format: SUPPLYPACKTYPEPATH,MULT to change the price of
+## a supply pack.
+## 1 has no effect.
+## -1 will make the item completely unavailable.
+## Mults below 1, including 0, are possible, but highly inadvisable.
+
+## Examples:
+#SUPPLY_SHORTAGES /datum/supply_pack/goody/laser_single,1.5
+#SUPPLY_SHORTAGES /datum/supply_pack/security/armory/laser,-1
+#SUPPLY_SHORTAGES /datum/supply_pack/security/stingpack,4
diff --git a/modular_doppler/STORY_teshari_war/config.dm b/modular_doppler/STORY_teshari_war/config.dm
new file mode 100644
index 00000000000000..58caf63c53739d
--- /dev/null
+++ b/modular_doppler/STORY_teshari_war/config.dm
@@ -0,0 +1,11 @@
+/datum/config_entry/keyed_list/supply_shortages
+ key_mode = KEY_MODE_TYPE
+ value_mode = VALUE_MODE_NUM
+ splitter = ","
+
+/datum/config_entry/keyed_list/supply_shortages/validate_config_key(key)
+ var/type = text2path(key)
+ if (type in get_usable_supply_packs())
+ return key
+ log_config("ERROR: [key] is not a valid supply pack typepath.")
+ return null
diff --git a/modular_doppler/STORY_teshari_war/mind.dm b/modular_doppler/STORY_teshari_war/mind.dm
new file mode 100644
index 00000000000000..e1c852097cf25c
--- /dev/null
+++ b/modular_doppler/STORY_teshari_war/mind.dm
@@ -0,0 +1,3 @@
+/datum/mind
+ /// The stance this mind takes on the tizira/teshari war. By default, they dont care at all.
+ var/war_faction = WAR_FACTION_NEUTRAL // i just wanna grill
diff --git a/modular_doppler/STORY_teshari_war/preferences.dm b/modular_doppler/STORY_teshari_war/preferences.dm
new file mode 100644
index 00000000000000..9d27c79c4753a6
--- /dev/null
+++ b/modular_doppler/STORY_teshari_war/preferences.dm
@@ -0,0 +1,30 @@
+GLOBAL_LIST_INIT(teshari_war_factions, list(
+ WAR_FACTION_TIZIRA,
+ WAR_FACTION_TESHARI,
+ WAR_FACTION_ISOLATIONIST,
+ WAR_FACTION_NEUTRAL
+))
+
+/datum/preference/choiced/doppler_war_faction
+ savefile_key = "doppler_war_faction"
+ main_feature_name = "War Alignment"
+ savefile_identifier = PREFERENCE_CHARACTER
+ category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
+ priority = PREFERENCE_PRIORITY_DEFAULT
+
+/datum/preference/choiced/doppler_war_faction/init_possible_values()
+ return GLOB.teshari_war_factions
+
+/datum/preference/choiced/doppler_war_faction/create_default_value()
+ return WAR_FACTION_NEUTRAL
+
+/datum/preference/choiced/doppler_war_faction/apply_to_human(mob/living/carbon/human/target, value)
+ RegisterSignal(target, COMSIG_MOB_MIND_INITIALIZED, PROC_REF(apply_mind_variable))
+ return
+
+/datum/preference/choiced/doppler_war_faction/proc/apply_mind_variable(mob/living/carbon/human/target, datum/mind/new_mind)
+ SIGNAL_HANDLER
+
+ new_mind.war_faction = target.client.prefs.read_preference(/datum/preference/choiced/doppler_war_faction)
+ UnregisterSignal(target, COMSIG_MOB_MIND_INITIALIZED)
+
diff --git a/modular_doppler/STORY_teshari_war/readme.md b/modular_doppler/STORY_teshari_war/readme.md
new file mode 100644
index 00000000000000..9b3f2eb3793b17
--- /dev/null
+++ b/modular_doppler/STORY_teshari_war/readme.md
@@ -0,0 +1,35 @@
+## Title: Teshari/Tizira war
+
+MODULE ID: TESHARI_WAR
+
+### Description:
+
+TEMPORARY STORY MODULE. WILL BE REVERTED WHEN THE ARC ENDS.
+
+### TG Proc Changes:
+
+code/modules/cargo/orderconsole.dm - L192, added "shortagemult" = pack.get_shortage_price_mult(),
+code/modules/modular_computers/file_system/programs/dept_order.dm - L88 - L99
+
+tgui/packages/tgui/interfaces/NtosDeptOrder.tsx - L31-L32, L211-213, L223
+tgui/packages/tgui/interfaces/Cargo/CargoCatalog.tsx - L201-L204, L221-L223, L241-L245
+tgui/packages/tgui/interfaces/Cargo/types.ts - L42
+
+config/config.txt - L6
+
+### Defines:
+
+code\_\_DEFINES\~doppler_defines\TESHARI_WAR_defines.dm
+
+### Master file additions
+
+N/A
+
+### Included files that are not contained in this module:
+
+config/doppler/STORY_SUPPLY_SHORTAGE.txt
+tgui\packages\tgui\interfaces\PreferencesMenu\preferences\features\dopplershift_preferences\war_faction.tsx
+
+### Credits:
+
+Niko
diff --git a/modular_doppler/STORY_teshari_war/supply_pack.dm b/modular_doppler/STORY_teshari_war/supply_pack.dm
new file mode 100644
index 00000000000000..01426caef5fd8a
--- /dev/null
+++ b/modular_doppler/STORY_teshari_war/supply_pack.dm
@@ -0,0 +1,32 @@
+/// Returns a list of non-abstract supply packs.
+/proc/get_usable_supply_packs()
+ RETURN_TYPE(/list)
+ var/list/packs = list()
+ for (var/datum/supply_pack/iter_path as anything in subtypesof(/datum/supply_pack))
+ if (iter_path::abstract_type == iter_path)
+ continue
+ packs += iter_path
+ return packs
+
+/// Returns the assoc list of stringified supply pack typepaths to their price mult.
+/proc/get_price_mults()
+ RETURN_TYPE(/list)
+ return CONFIG_GET(keyed_list/supply_shortages)
+
+// If this proc returns -1, the item is wholly unavailable.
+/// Returns the actual price mult of the supply pack. If -1, the item is completely unavailable. Use get_cost() for actual price calculations.
+/datum/supply_pack/proc/get_shortage_price_mult()
+ var/mult = get_price_mults()["[type]"]
+ if (isnull(mult))
+ return 1
+ return mult
+
+/// Returns TRUE if our shortage price mult is -1.
+/datum/supply_pack/proc/is_unavailable()
+ return get_shortage_price_mult() == -1
+
+/datum/supply_pack/get_cost()
+ . = ..()
+
+ if (!is_unavailable())
+ . *= get_shortage_price_mult()
diff --git a/modular_doppler/donation_box/donation_box.dm b/modular_doppler/donation_box/donation_box.dm
new file mode 100644
index 00000000000000..4cfd8fd1897488
--- /dev/null
+++ b/modular_doppler/donation_box/donation_box.dm
@@ -0,0 +1,59 @@
+/obj/structure/closet/crate/donation
+ name = "donation box"
+ desc = "A steel crate, modified into a donation box via a small slot on the top. Allows insertion of items without allowing removal."
+ icon = 'modular_doppler/donation_box/icons/donation_box.dmi'
+ icon_state = "donation_box"
+ base_icon_state = "donation_box"
+
+/obj/structure/closet/crate/donation/secure
+ name = "secure donation box"
+ desc = "A steel crate, modified into a donation box via a small slot on the top. Allows insertion of items without allowing removal."
+ icon = 'modular_doppler/donation_box/icons/donation_box.dmi'
+ icon_state = "securedonation_box"
+ base_icon_state = "securedonation_box"
+ secure = TRUE
+ card_reader_installed = TRUE
+
+/obj/structure/closet/crate/donation/examine(mob/user)
+ . = ..()
+
+ . += span_smallnotice("When closed, you can [EXAMINE_HINT("Right-Click")] with an item to place it in the box, if able.")
+
+/obj/structure/closet/crate/donation/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if (opened)
+ return NONE
+ if (user.combat_mode)
+ return NONE
+ if (!LAZYACCESS(modifiers, RIGHT_CLICK))
+ return NONE
+ if (!insertion_allowed(tool))
+ balloon_alert(user, "can't fit it in!")
+ return NONE
+
+ user.balloon_alert_to_viewers("inserts [tool]", "inserted [tool]")
+ user.visible_message(
+ span_notice("[user] inserts [tool] into the small slot on [src]."),
+ span_notice("You insert [tool] into the small slot on [src].")
+ )
+
+ if (insert(tool))
+ return ITEM_INTERACT_SUCCESS
+ return NONE
+
+/obj/item/donation_box_kit
+ icon = 'icons/obj/storage/box.dmi'
+ icon_state = "plasticbox"
+ name = "donation kit"
+ desc = "Contains a folded donation box, with an access lock and a few tools inside."
+
+/obj/item/donation_box_kit/attack_self(mob/user, modifiers)
+ user.balloon_alert_to_viewers("deploying...")
+ if (!do_after(user, 3 SECONDS, src))
+ return FALSE
+ var/obj/structure/closet/crate/donation/secure/box = new /obj/structure/closet/crate/donation/secure(get_turf(user))
+ new /obj/item/hand_labeler(box)
+ playsound(src, 'sound/machines/terminal/terminal_eject.ogg', 70, TRUE)
+ qdel(src)
+
+ return TRUE
+
diff --git a/modular_doppler/donation_box/icons/donation_box.dmi b/modular_doppler/donation_box/icons/donation_box.dmi
new file mode 100644
index 00000000000000..757eb8ffa64287
Binary files /dev/null and b/modular_doppler/donation_box/icons/donation_box.dmi differ
diff --git a/modular_doppler/donation_box/loadout.dm b/modular_doppler/donation_box/loadout.dm
new file mode 100644
index 00000000000000..ae6eb4e8a82617
--- /dev/null
+++ b/modular_doppler/donation_box/loadout.dm
@@ -0,0 +1,3 @@
+/datum/loadout_item/pocket_items/equipment/donation_kit
+ name = "Donation Kit"
+ item_path = /obj/item/donation_box_kit
diff --git a/modular_doppler/modular_crafting/code/sheet_types.dm b/modular_doppler/modular_crafting/code/sheet_types.dm
index 6f546d20248b44..5978e0041c4997 100644
--- a/modular_doppler/modular_crafting/code/sheet_types.dm
+++ b/modular_doppler/modular_crafting/code/sheet_types.dm
@@ -25,6 +25,7 @@ GLOBAL_LIST_INIT(doppler_metal_recipes, list(
new/datum/stack_recipe("forge", /obj/structure/reagent_forge, 10, time = 2 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_TOOLS),
new/datum/stack_recipe("throwing wheel", /obj/structure/throwing_wheel, 10, time = 2 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_TOOLS),
new/datum/stack_recipe("metal shelf", /obj/structure/shelf, 1, time = 2 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_STRUCTURE),
+ new/datum/stack_recipe("donation box", /obj/structure/closet/crate/donation, 10, time = 3 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_CONTAINERS),
))
GLOBAL_LIST_INIT(doppler_metal_airlock_recipes, list(
diff --git a/modular_doppler/war_posters/code/loadout.dm b/modular_doppler/war_posters/code/loadout.dm
new file mode 100644
index 00000000000000..e69de29bb2d1d6
diff --git a/modular_doppler/war_posters/code/mood.dm b/modular_doppler/war_posters/code/mood.dm
new file mode 100644
index 00000000000000..87d7c4fe71985f
--- /dev/null
+++ b/modular_doppler/war_posters/code/mood.dm
@@ -0,0 +1,13 @@
+#define WAR_POSTER_MOOD_CAT "poster_mood"
+
+/datum/mood_event/war_poster_sympathy
+ description = "I saw a poster that reinforces my beliefs. It's good to have like-minded people around."
+ mood_change = 2
+ category = WAR_POSTER_MOOD_CAT
+
+/datum/mood_event/war_poster_wrong
+ description = "I saw a poster that really pissed me off! How can people like that be allowed to work here?!"
+ mood_change = -4
+ category = WAR_POSTER_MOOD_CAT
+
+#undef WAR_POSTER_MOOD_CAT
diff --git a/modular_doppler/war_posters/code/posters.dm b/modular_doppler/war_posters/code/posters.dm
new file mode 100644
index 00000000000000..7908ebb8b53f70
--- /dev/null
+++ b/modular_doppler/war_posters/code/posters.dm
@@ -0,0 +1,141 @@
+#define WAR_POSTER_MOOD "war_poster"
+#define WAR_POSTER_RANGE 7
+
+/obj/structure/sign/poster/proc/randomise_further(obj/structure/sign/poster/selected_path)
+ return
+
+/obj/structure/sign/poster/doppler_war
+ name = "abstract war poster"
+ desc = "Surely, if this was coded correctly, you would be angry."
+ abstract_type = /obj/structure/sign/poster/doppler_war
+ icon = 'modular_doppler/war_posters/icons/posters.dmi'
+ icon_state = "tesh_unity"
+
+ /// Those of our aligned faction recieve a small mood buff when seeing this poster - other factions get a debuff. Neutral doesn't care at all.
+ var/aligned_faction = WAR_FACTION_NEUTRAL
+ /// Assoc list of (WAR_FACTION -> list(/datum/war_demoralisation_reaction, chance)). Used in pickweight to determine what people think when they see the poster, depending on
+ var/list/faction_reactions = list()
+ var/list/faction_moods = list()
+ /// Proximity sensor for the above vars
+ var/datum/proximity_monitor/advanced/war_demoraliser/demoraliser
+
+/obj/structure/sign/poster/doppler_war/randomise_further(obj/structure/sign/poster/doppler_war/selected_path)
+ . = ..()
+
+ faction_reactions = selected_path::faction_reactions
+ faction_moods = selected_path::faction_moods
+
+/obj/structure/sign/poster/doppler_war/on_placed_poster()
+ demoraliser = new(src, WAR_POSTER_RANGE, TRUE, aligned_faction, WAR_POSTER_MOOD, faction_reactions, faction_moods, READING_CHECK_LIGHT)
+ return ..()
+
+/obj/item/poster/random_teshari
+ name = "random teshari poster"
+ poster_type = /obj/structure/sign/poster/doppler_war/teshari/random
+ icon = 'modular_doppler/war_posters/icons/posters.dmi'
+ icon_state = "rolled_tesh"
+
+/obj/structure/sign/poster/doppler_war/teshari
+ name = "teshari war poster"
+ poster_item_name = "teshari war poster"
+ poster_item_desc = "A pro-teshari propoganda poster, espousing the evils of the Talunan empire and the virtue of Sirsiai's defense. Aisi Tarischi."
+ poster_item_icon_state = "rolled_tesh"
+ printable = TRUE
+ aligned_faction = WAR_FACTION_TESHARI
+ abstract_type = /obj/structure/sign/poster/doppler_war/teshari
+
+/obj/structure/sign/poster/doppler_war/teshari/random
+ name = "random teshari poster"
+ random_basetype = /obj/structure/sign/poster/doppler_war/teshari
+ icon_state = "tesh_unity"
+ never_random = TRUE
+
+/obj/structure/sign/poster/doppler_war/teshari/unity
+ name = "Ilisime. (Unity.)"
+ desc = "A poster encouraging the teshari diaspora - from stars, from sea, from earth, from dust - to unite in defense of their shattered homeworld, Sirisai."
+ icon_state = "tesh_unity"
+ faction_reactions = list(
+ WAR_FACTION_TESHARI = list(
+ "All for Sirisai. Aisi Tarischi!" = 10,
+ "Sophonts in arms, we can save Teshari kind." = 10,
+ "Unity...? I'm not alone here." = 8
+ ),
+ WAR_FACTION_TIZIRA = list(
+
+ ),
+ WAR_FACTION_ISOLATIONIST = list(
+ "Ugh! More propoganda!" = 10,
+ "Keep your war posters out of my workspace." = 10
+ )
+ )
+ faction_moods = list(
+ WAR_FACTION_TESHARI = /datum/mood_event/war_poster_sympathy,
+ WAR_FACTION_TIZIRA = /datum/mood_event/war_poster_wrong,
+ WAR_FACTION_ISOLATIONIST = /datum/mood_event/war_poster_wrong
+ )
+
+/obj/structure/sign/poster/doppler_war/teshari/execution
+ name = "Schatara Shitilushu. (Our Execution.)"
+ desc = "A kneeled teshari is presented for execution by a Tiziran blade. You could be next on the block. Act now!"
+ icon_state = "tesh_warn"
+ faction_reactions = list(
+ WAR_FACTION_TESHARI = list(
+ "We will shatter their Tiziran steel with our resolve..." = 10,
+ "I won't be next. Noone will. We can stop the Tizirans here and now." = 10,
+ ),
+ WAR_FACTION_TIZIRA = list(
+
+ ),
+ WAR_FACTION_ISOLATIONIST = list(
+ "Seriously? An execution on a poster? That's just tasteless." = 10,
+ "They're so desparate to get the 4CA involved, they post fake executions on our walls?" = 10
+ )
+ )
+ faction_moods = list(
+ WAR_FACTION_TESHARI = /datum/mood_event/war_poster_sympathy,
+ WAR_FACTION_TIZIRA = /datum/mood_event/war_poster_wrong,
+ WAR_FACTION_ISOLATIONIST = /datum/mood_event/war_poster_wrong
+ )
+
+/obj/structure/sign/poster/doppler_war/tiziran
+ name = "tiziran war poster"
+ poster_item_name = "tiziran war poster"
+ poster_item_desc = "A pro-tizira propoganda poster, generally encouraging enlistment and patriotism."
+ poster_item_icon_state = "rolled_tizira"
+ printable = TRUE
+ abstract_type = /obj/structure/sign/poster/doppler_war/tiziran
+ aligned_faction = WAR_FACTION_TIZIRA
+
+/obj/structure/sign/poster/doppler_war/tiziran/enlist
+ name = "Za'Haious Rar! (Enlist!)"
+ desc = "While many Tizirans face obligate military service, few reach professional status. This poster encourages the reader to fully commit themselves to the Talunan ranks, and earn their prestige in white."
+ icon_state = "tizira_enlist"
+ faction_reactions = list(
+ WAR_FACTION_TESHARI = list(
+ "We will shatter their Tiziran steel with our resolve..." = 10,
+ "I won't be next. Noone will. We can stop the Tizirans here and now." = 10,
+ ),
+ WAR_FACTION_TIZIRA = list(
+
+ ),
+ WAR_FACTION_ISOLATIONIST = list(
+ "Ugh! More propoganda!" = 10,
+ "Keep your war posters out of my workspace." = 10
+ )
+ )
+ faction_moods = list(
+ WAR_FACTION_TESHARI = /datum/mood_event/war_poster_wrong,
+ WAR_FACTION_TIZIRA = /datum/mood_event/war_poster_sympathy,
+ WAR_FACTION_ISOLATIONIST = /datum/mood_event/war_poster_wrong
+ )
+
+/obj/structure/sign/poster/doppler_war/isolationist
+ name = "isolationist war poster"
+ poster_item_name = "isolationist war poster"
+ poster_item_desc = "An isolationist propoganda poster, condeming the 4CA for its attempted involvement in the tizira/teshari war."
+ poster_item_icon_state = "rolled_iso"
+ abstract_type = /obj/structure/sign/poster/doppler_war/isolationist
+ aligned_faction = WAR_FACTION_ISOLATIONIST
+
+#undef WAR_POSTER_MOOD
+#undef WAR_POSTER_RANGE
diff --git a/modular_doppler/war_posters/code/war_moralisation.dm b/modular_doppler/war_posters/code/war_moralisation.dm
new file mode 100644
index 00000000000000..32b19430e8c4b1
--- /dev/null
+++ b/modular_doppler/war_posters/code/war_moralisation.dm
@@ -0,0 +1,83 @@
+/datum/proximity_monitor/advanced/war_demoraliser
+ /// The faction, using defines from TESHARI_WAR_defines.dm. Do NOT use neutral.
+ var/faction
+ /// Mood category to apply to moods
+ var/mood_category
+ /// Assoc list of (WAR_FACTION -> list(/datum/war_demoralisation_reaction, chance)). Used in pickweight to determine what people think when they see the poster, depending on
+ var/list/faction_reactions = list()
+ var/list/faction_moods = list()
+ /// For literacy checks
+ var/reading_requirements = READING_CHECK_LIGHT
+
+/datum/proximity_monitor/advanced/war_demoraliser/New(atom/_host, range, _ignore_if_not_on_turf = TRUE, faction, mood_category, list/faction_reactions, list/faction_moods, reading_requirements)
+ . = ..()
+
+ if (faction == WAR_FACTION_NEUTRAL)
+ CRASH("War demoralizers should not be neutral. That's like, their entire point. They're propoganda.")
+ RegisterSignal(host, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
+
+ src.faction = faction
+ src.mood_category = mood_category
+ src.faction_reactions = faction_reactions
+ src.faction_moods = faction_moods
+ src.reading_requirements = reading_requirements
+
+/datum/proximity_monitor/advanced/war_demoraliser/field_turf_crossed(atom/movable/crossed, turf/old_location, turf/new_location)
+ if (!isliving(crossed))
+ return
+ if (!can_see(crossed, host, current_range))
+ return
+ on_seen(crossed)
+
+/*
+ * Signal proc for [COMSIG_ATOM_EXAMINE].
+ * Immediately tries to apply a mood to the examiner, ignoring the proximity check.
+ * If someone wants to make themselves sad through a camera that's their choice I guess.
+ */
+/datum/proximity_monitor/advanced/war_demoraliser/proc/on_examine(datum/source, mob/examiner)
+ SIGNAL_HANDLER
+ if (isliving(examiner))
+ on_seen(examiner)
+
+/**
+ * Called when someone is looking at a war-related demoralizer.
+ * Applies a mood if they are conscious and don't already have it.
+ * Different moods are applied based on their faction.
+ *
+ * Arguments
+ * * viewer - Whoever is looking at this.
+ */
+/datum/proximity_monitor/advanced/war_demoraliser/proc/on_seen(mob/living/viewer)
+ if (!viewer.mind)
+ return
+ // If you're not conscious you're too busy or dead to look at propaganda
+ if (viewer.stat != CONSCIOUS)
+ return
+ if(viewer.is_blind())
+ return
+ if (!should_demoralise(viewer))
+ return
+ if(!viewer.can_read(host, reading_requirements, TRUE)) //if it's a text based demoralization datum, make sure the mob has the capability to read. if it's only an image, make sure it's just bright enough for them to see it.
+ return
+
+ var/target_faction = viewer.mind.war_faction
+ var/datum/mood_event/mood = faction_moods[target_faction]
+ if (isnull(mood))
+ return
+ viewer.add_mood_event(mood_category, mood)
+ var/reaction = faction_reactions[target_faction]
+ if (isnull(reaction))
+ return
+ to_chat(viewer, span_notice(reaction))
+
+/**
+ * Returns true if user is capable of experiencing moods and doesn't already have the one relevant to this datum, false otherwise.
+ *
+ * Arguments
+ * * viewer - Whoever just saw the parent.
+ */
+/datum/proximity_monitor/advanced/war_demoraliser/proc/should_demoralise(mob/living/viewer)
+ if (!viewer.mob_mood)
+ return FALSE
+
+ return !viewer.mob_mood.has_mood_of_category(mood_category)
diff --git a/modular_doppler/war_posters/icons/posters.dmi b/modular_doppler/war_posters/icons/posters.dmi
new file mode 100644
index 00000000000000..6aa0fc62d5976d
Binary files /dev/null and b/modular_doppler/war_posters/icons/posters.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index fd750de46c9da8..9241c8b1965ba4 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -464,6 +464,7 @@
#include "code\__DEFINES\~doppler_defines\species_clothing_paths.dm"
#include "code\__DEFINES\~doppler_defines\speech_channels.dm"
#include "code\__DEFINES\~doppler_defines\strippable.dm"
+#include "code\__DEFINES\~doppler_defines\TESHARI_WAR_defines.dm"
#include "code\__DEFINES\~doppler_defines\text.dm"
#include "code\__DEFINES\~doppler_defines\traits.dm"
#include "code\__DEFINES\~doppler_defines\vehicles.dm"
@@ -6934,6 +6935,8 @@
#include "modular_doppler\deforest_medical_items\code\vulnerable_status_effect.dm"
#include "modular_doppler\deforest_medical_items\code\chemicals\demoneye.dm"
#include "modular_doppler\disable_suicide\config_entries.dm"
+#include "modular_doppler\donation_box\donation_box.dm"
+#include "modular_doppler\donation_box\loadout.dm"
#include "modular_doppler\doppler_command_uniforms\hop\overrides.dm"
#include "modular_doppler\emotes\code\emotes.dm"
#include "modular_doppler\emotes\code\hologram.dm"
@@ -7720,6 +7723,10 @@
#include "modular_doppler\starter_resources\ore_vent.dm"
#include "modular_doppler\stone\code\ore_veins.dm"
#include "modular_doppler\stone\code\stone.dm"
+#include "modular_doppler\STORY_teshari_war\config.dm"
+#include "modular_doppler\STORY_teshari_war\mind.dm"
+#include "modular_doppler\STORY_teshari_war\supply_pack.dm"
+#include "modular_doppler\STORY_teshari_war\preferences.dm"
#include "modular_doppler\super_glasses\code\glasses_stats_thief.dm"
#include "modular_doppler\super_glasses\code\techno_visors.dm"
#include "modular_doppler\suuuper_trustworthy_item_orders\code\chem_containers.dm"
@@ -7767,6 +7774,10 @@
#include "modular_doppler\verbs\code\preferences.dm"
#include "modular_doppler\verbs\code\say.dm"
#include "modular_doppler\verbs\code\subtle.dm"
+#include "modular_doppler\war_posters\code\loadout.dm"
+#include "modular_doppler\war_posters\code\mood.dm"
+#include "modular_doppler\war_posters\code\posters.dm"
+#include "modular_doppler\war_posters\code\war_moralisation.dm"
#include "modular_doppler\wargaming\code\game_kit.dm"
#include "modular_doppler\wargaming\code\holograms.dm"
#include "modular_doppler\wargaming\code\projectors.dm"
diff --git a/tgui/packages/tgui/interfaces/Cargo/CargoCatalog.tsx b/tgui/packages/tgui/interfaces/Cargo/CargoCatalog.tsx
index dbc019cacf081b..f20f13a1392bcc 100644
--- a/tgui/packages/tgui/interfaces/Cargo/CargoCatalog.tsx
+++ b/tgui/packages/tgui/interfaces/Cargo/CargoCatalog.tsx
@@ -198,6 +198,10 @@ function CatalogList(props: CatalogListProps) {
}
const privateBuy = (self_paid && !pack.goody) || app_cost;
+ const unavailable = pack.shortagemult === -1;
+ const shortageString = unavailable
+ ? 'UNAVAILABLE!'
+ : `${pack.shortagemult}x cost`;
const tooltipIcon = (content: string, icon: string, color: string) => (
@@ -214,7 +218,9 @@ function CatalogList(props: CatalogListProps) {
dmIconState={pack.first_item_icon_state}
imageSize={32}
color={color}
- disabled={(amount_by_name[pack.name] || 0) >= max_order}
+ disabled={
+ (amount_by_name[pack.name] || 0) >= max_order || unavailable
+ }
buttonsAlt={
+ {pack.shortagemult !== 1 && (
+
+ Shortages: {shortageString}
+
+ )}
{(!!pack.small_item || !!pack.access || !!pack.contraband) && (
diff --git a/tgui/packages/tgui/interfaces/Cargo/types.ts b/tgui/packages/tgui/interfaces/Cargo/types.ts
index de1a839e410ba5..f6843026295943 100644
--- a/tgui/packages/tgui/interfaces/Cargo/types.ts
+++ b/tgui/packages/tgui/interfaces/Cargo/types.ts
@@ -39,6 +39,7 @@ export type Supply = {
small_item: BooleanLike;
contraband: BooleanLike;
contains: SupplyItem[];
+ shortagemult: number;
};
type SupplyItem = {
diff --git a/tgui/packages/tgui/interfaces/NtosDeptOrder.tsx b/tgui/packages/tgui/interfaces/NtosDeptOrder.tsx
index a622efb5c532f9..6438c47d29ed88 100644
--- a/tgui/packages/tgui/interfaces/NtosDeptOrder.tsx
+++ b/tgui/packages/tgui/interfaces/NtosDeptOrder.tsx
@@ -28,6 +28,8 @@ type typePath = string;
type Pack = {
name: string;
desc: string;
+ unavailable: BooleanLike;
+ shortage_text: string;
cost: number;
id: typePath;
};
@@ -206,6 +208,9 @@ const DepartmentCatalog = () => {
+
+ {pack.shortage_text}
+
@@ -215,6 +220,7 @@ const DepartmentCatalog = () => {
id: pack.id,
})
}
+ disabled={pack.unavailable}
>
Order
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/war_faction.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/war_faction.tsx
new file mode 100644
index 00000000000000..595ee790bd93fc
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/war_faction.tsx
@@ -0,0 +1,13 @@
+import { FeatureChoiced, FeatureValueProps, FeatureChoicedServerData } from "../base";
+import { FeatureDropdownInput } from "../dropdowns";
+
+export const doppler_war_faction: FeatureChoiced = {
+ name: 'War Alignment',
+ description:
+ 'The stance your character takes on the Tizira/Teshari war.',
+ component: (
+ props: FeatureValueProps,
+ ) => {
+ return ;
+ },
+};