diff --git a/code/__DEFINES/gamemode.dm b/code/__DEFINES/gamemode.dm
index 621b7659a16..f5b94829d6a 100644
--- a/code/__DEFINES/gamemode.dm
+++ b/code/__DEFINES/gamemode.dm
@@ -67,6 +67,7 @@
#define SPECIAL_ROLE_HERETIC "Heretic"
#define SPECIAL_ROLE_EVENTMISC "Event Role"
#define SPECIAL_ROLE_NINJA "Space Ninja"
+#define SPECIAL_ROLE_IRRADIATED_MOUSE "Irradiated Mouse"
// Constants used by code which checks the status of nuclear blasts during a
// round, regardless of original game mode, e.g. setting the ending cinematic.
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index 397a4ad16dd..7c178907bd8 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -46,6 +46,7 @@
#define ROLE_GHOST "ghost role"
#define ROLE_ELITE "lavaland elite"
#define ROLE_NINJA "ninja"
+#define ROLE_IRRADIATED_MOUSE "irradiated mouse"
#define ROLE_UPLIFTED_PRIMITIVE "uplifted primitive"
// Misc jobban categories
@@ -74,6 +75,8 @@ GLOBAL_LIST_INIT(special_roles_antags, list(
ROLE_MIND_FLAYER,
ROLE_HERETIC,
ROLE_NINJA,
+ ROLE_IRRADIATED_MOUSE,
+
ROLE_UPLIFTED_PRIMITIVE,
// UNUSED/BROKEN ANTAGS
// ROLE_HOG_GOD = /datum/game_mode/hand_of_god,
diff --git a/code/game/gamemodes/miniantags/irradiated_mouse/irradiated_mouse.dm b/code/game/gamemodes/miniantags/irradiated_mouse/irradiated_mouse.dm
new file mode 100644
index 00000000000..596489a6482
--- /dev/null
+++ b/code/game/gamemodes/miniantags/irradiated_mouse/irradiated_mouse.dm
@@ -0,0 +1,188 @@
+/datum/event/spawn_irradiated_mouse
+ name = "Irradiated Mouse Spawn"
+ noAutoEnd = TRUE
+ nominal_severity = EVENT_LEVEL_MODERATE
+ role_weights = list(ASSIGNMENT_CREW = 1, ASSIGNMENT_MEDICAL = 5)
+ role_requirements = list(ASSIGNMENT_CREW = 10, ASSIGNMENT_MEDICAL = 1)
+
+/datum/event/spawn_irradiated_mouse/start()
+ INVOKE_ASYNC(src, PROC_REF(spawn_mouse))
+
+/datum/event/spawn_irradiated_mouse/proc/spawn_mouse()
+ // poll for ghosts
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as an irradiated mouse?", ROLE_IRRADIATED_MOUSE, TRUE, source = /mob/living/basic/mouse/irradiated_mouse)
+ if(!length(candidates))
+ kill()
+ return
+
+ var/mob/candidate = pick(candidates)
+ var/obj/vents = get_valid_vent_spawns(TRUE, TRUE, 0) // find an unwelded vent with nobody nearby
+ if(!length(vents))
+ message_admins("Warning: No suitable vents detected for spawning an irradiated mouse.")
+ return
+
+ // pick a vent and spawn a mouse inside of it
+ var/obj/vent = pick(vents)
+ var/mob/living/basic/mouse/irradiated_mouse/spawned_mouse = new /mob/living/basic/mouse/irradiated_mouse(vent.loc)
+ spawned_mouse.forceMove(vent)
+ spawned_mouse.add_ventcrawl(vent)
+
+ // put the ghost inside the mouse
+ spawned_mouse.ckey = candidate.ckey
+ dust_if_respawnable(spawned_mouse)
+
+ // objectives
+ spawned_mouse.mind = new
+ spawned_mouse.mind.bind_to(spawned_mouse)
+ spawned_mouse.mind.set_original_mob(spawned_mouse)
+ spawned_mouse.mind.wipe_memory()
+ spawned_mouse.mind.assigned_role = SPECIAL_ROLE_IRRADIATED_MOUSE
+ spawned_mouse.mind.special_role = SPECIAL_ROLE_IRRADIATED_MOUSE
+ SSticker.mode.traitors |= spawned_mouse.mind
+
+ // start sound + intro message
+ SEND_SOUND(spawned_mouse, sound('sound/items/geiger/ext1.ogg'))
+ spawned_mouse.mind.add_mind_objective(/datum/objective/irradiated_mouse_objective)
+ spawned_mouse.give_intro_text()
+
+/datum/objective/irradiated_mouse_objective
+ explanation_text = "Enjoy the remainder of your life the best you can! Whether by gorging on cheese, pestering the crew or exploring the station."
+ completed = TRUE
+ needs_target = FALSE
+
+/mob/living/basic/mouse/irradiated_mouse
+ desc = "It's a small, disease-ridden rodent... Thats glowing?"
+ maxHealth = 150
+ health = 150
+ butcher_results = list(/obj/item/food/meat = 1, /obj/item/stack/sheet/mineral/uranium = 1)
+ gold_core_spawnable = NO_SPAWN
+ minimum_survivable_temperature = 0
+ initial_traits = list(TRAIT_SHOCKIMMUNE, TRAIT_AI_PAUSED, TRAIT_RADIMMUNE) // shock immune so you can chew on those yummy wires
+ mouse_color = "green"
+ icon_state = "mouse_green"
+ a_intent = INTENT_HARM
+
+ var/available_upgrades = 0
+ var/upgrade_cooldown_in_seconds = 120
+ var/radiation_upgrades = 0
+ var/speed_upgrades = 0
+ var/damage_upgrades = 0
+ var/level_cap = 3
+
+ var/alpha_rad = 0
+ var/beta_rad = 0
+ var/gamma_rad = 0
+ var/alpha_rad_per_level = 500
+ var/beta_rad_per_level = 500
+ var/gamma_rad_per_level = 0
+ var/radiation_cooldown = 0.1
+ var/produce_radioactive_sludge = FALSE
+
+ var/speed_per_level = -0.5
+ var/speed_capstone_alpha = 50
+
+ var/has_exited_vents = FALSE
+ var/seconds_time_till_death = 60 * 15
+
+ var/datum/spell/irradiated_mouse_spell/upgrade_radiation/upgrade_radiation_spell
+ var/datum/spell/irradiated_mouse_spell/upgrade_speed/upgrade_speed_spell
+ var/datum/spell/irradiated_mouse_spell/upgrade_damage/upgrade_damage_spell
+
+/mob/living/basic/mouse/irradiated_mouse/Initialize(mapload)
+ . = ..()
+ add_language("Galactic Common")
+ set_default_language(GLOB.all_languages["Galactic Common"])
+
+ upgrade_radiation_spell = new()
+ upgrade_speed_spell = new()
+ upgrade_damage_spell = new()
+ AddSpell(upgrade_radiation_spell)
+ AddSpell(upgrade_speed_spell)
+ AddSpell(upgrade_damage_spell)
+
+/mob/living/basic/mouse/irradiated_mouse/update_desc()
+ . = ..()
+ desc = initial(desc) // we dont want the standard description auto added by mice
+
+/mob/living/basic/mouse/irradiated_mouse/proc/give_intro_text()
+ var/list/messages = list()
+ messages.Add(SPAN_USERDANGER("
You are an Irradiated Mouse!"))
+ messages.Add("[SPAN_NOTICE("Due to your proximity to radioactive material laying around you've started rapidly mutating! Unfortunately this comes at the cost of your life: [SPAN_BOLDNOTICE("once you exit the vents you will have 15 minutes to live.")]")]")
+ messages.Add(mind.prepare_announce_objectives(FALSE))
+ messages.Add("[SPAN_MOTD("For more information, check the wiki page: ([GLOB.configuration.url.wiki_url]/index.php/Irradiated_Mouse) ")]")
+ to_chat(src, chat_box_red(messages.Join("
")))
+
+/mob/living/basic/mouse/irradiated_mouse/remove_ventcrawl()
+ . = ..()
+ if(!has_exited_vents)
+ upgrade_radiation()
+ upgrade_speed()
+ upgrade_damage()
+ has_exited_vents = TRUE
+
+/mob/living/basic/mouse/irradiated_mouse/Life(seconds, times_fired)
+ . = ..()
+ if(stat != CONSCIOUS)
+ return
+
+ if(!has_exited_vents)
+ return
+
+ // telegraph the radiation by giving tiles harmless alpha radiation
+ for(var/turf/turf in range(1, src))
+ contaminate_target(turf, src, alpha_rad, ALPHA_RAD)
+
+ // reduce health by a constant so the mouse eventually dies
+ adjustBruteLoss(maxHealth * seconds / seconds_time_till_death)
+
+ // subtract time after you've left a vent
+ upgrade_cooldown_in_seconds -= seconds
+ if(upgrade_cooldown_in_seconds <= 0)
+ upgrade_cooldown_in_seconds += initial(upgrade_cooldown_in_seconds)
+ available_upgrades++
+ to_chat(src, SPAN_NOTICE("You have [available_upgrades] upgrades available."))
+
+ if(!produce_radioactive_sludge)
+ return
+
+ var/chance = 50 - (health/maxHealth * 50) // more likely as health drops, fastest you can get this is after 6 minutes, giving a 20% spawn chance per 2 seconds
+ if(prob(chance))
+ new /obj/effect/decal/cleanable/radioactive_sludge(get_turf(src))
+
+/mob/living/basic/mouse/irradiated_mouse/proc/upgrade_radiation()
+ radiation_upgrades++
+ alpha_rad = alpha_rad_per_level * radiation_upgrades
+ beta_rad = beta_rad_per_level * radiation_upgrades
+ gamma_rad = gamma_rad_per_level * radiation_upgrades
+
+ // update your radiation component
+ DeleteComponentsType(/datum/component/inherent_radioactivity)
+ AddComponent(/datum/component/inherent_radioactivity, alpha_rad, beta_rad, gamma_rad, radiation_cooldown)
+
+/mob/living/basic/mouse/irradiated_mouse/proc/upgrade_speed()
+ speed_upgrades++
+ speed = initial(speed) + speed_per_level * speed_upgrades // this will tend towards a negative value (due to speed_per_level being negative) which is faster
+ if(speed_upgrades > level_cap)
+ alpha = speed_capstone_alpha
+ RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_movement)) // handles afterimages
+
+/mob/living/basic/mouse/irradiated_mouse/proc/upgrade_damage()
+ damage_upgrades++
+ melee_damage_lower = damage_upgrades * 5
+ melee_damage_upper = melee_damage_lower + 5
+ if(damage_upgrades > level_cap)
+ environment_smash = ENVIRONMENT_SMASH_WALLS // what allows wall smashing
+
+/mob/living/basic/mouse/irradiated_mouse/proc/on_movement(mob/living/mob, atom/old_loc)
+ if(stat != CONSCIOUS)
+ return
+
+ new /obj/effect/temp_visual/decoy/irradiated_mouse_afterimage(old_loc, mob)
+
+/obj/effect/temp_visual/decoy/irradiated_mouse_afterimage
+ duration = 0.75 SECONDS
+
+/obj/effect/temp_visual/decoy/irradiated_mouse_afterimage/Initialize(mapload, atom/mimiced_atom)
+ . = ..()
+ animate(src, alpha = 0, time = duration, easing = EASE_OUT) // gradually fading out after image
+
diff --git a/code/game/gamemodes/miniantags/irradiated_mouse/irradiated_mouse_spells.dm b/code/game/gamemodes/miniantags/irradiated_mouse/irradiated_mouse_spells.dm
new file mode 100644
index 00000000000..16e4967addf
--- /dev/null
+++ b/code/game/gamemodes/miniantags/irradiated_mouse/irradiated_mouse_spells.dm
@@ -0,0 +1,69 @@
+/datum/spell/irradiated_mouse_spell/
+ action_background_icon_state = "bg_irradiated_mouse"
+ clothes_req = FALSE
+ base_cooldown = 5 SECONDS
+
+/datum/spell/irradiated_mouse_spell/create_new_targeting()
+ return new /datum/spell_targeting/self
+
+/datum/spell/irradiated_mouse_spell/proc/has_upgrades(mob/living/basic/mouse/irradiated_mouse/user)
+ if(!user.available_upgrades)
+ to_chat(user, SPAN_WARN("You dont have any available upgrades"))
+ return FALSE
+ return TRUE
+
+/datum/spell/irradiated_mouse_spell/upgrade_radiation
+ name = "Upgrade Radiation"
+ desc = "Upgrade the amount of radiation you emit. You will start producing radioactive sludge at level 3."
+ action_icon_state = "irradiated_mouse_radiation"
+
+/datum/spell/irradiated_mouse_spell/upgrade_radiation/cast(list/targets, mob/living/basic/mouse/irradiated_mouse/user)
+ . = ..()
+ if(!has_upgrades(user))
+ return
+
+ to_chat(user, SPAN_NOTICE("You upgrade your the amount of radiation you give off."))
+
+ user.available_upgrades--
+ user.upgrade_radiation()
+ if(user.radiation_upgrades > user.level_cap)
+ user.RemoveSpell(user.upgrade_radiation_spell)
+ user.produce_radioactive_sludge = TRUE
+ to_chat(user, SPAN_NOTICE("You feel like shedding some weight."))
+
+/datum/spell/irradiated_mouse_spell/upgrade_speed
+ name = "Upgrade Speed"
+ desc = "Upgrade your speed. You will become semi-transparent at level 3."
+ action_icon_state = "irradiated_mouse_speed"
+
+/datum/spell/irradiated_mouse_spell/upgrade_speed/cast(list/targets, mob/living/basic/mouse/irradiated_mouse/user)
+ . = ..()
+ if(!has_upgrades(user))
+ return
+
+ to_chat(user, SPAN_NOTICE("You upgrade your speed."))
+
+ user.available_upgrades--
+ user.upgrade_speed()
+ if(user.speed_upgrades > user.level_cap)
+ user.RemoveSpell(user.upgrade_speed_spell)
+ to_chat(user, SPAN_NOTICE("You feel like the wind."))
+
+/datum/spell/irradiated_mouse_spell/upgrade_damage
+ name = "Upgrade Damage"
+ desc = "Upgrade your damage. You will become able to damage walls and windows at level 3."
+ action_icon_state = "irradiated_mouse_damage"
+
+/datum/spell/irradiated_mouse_spell/upgrade_damage/cast(list/targets, mob/living/basic/mouse/irradiated_mouse/user)
+ . = ..()
+ if(!has_upgrades(user))
+ return
+
+ to_chat(user, SPAN_NOTICE("You upgrade your damage."))
+
+ user.available_upgrades--
+ user.upgrade_damage()
+ if(user.damage_upgrades > user.level_cap)
+ user.RemoveSpell(user.upgrade_damage_spell)
+ to_chat(user, SPAN_NOTICE("You feel much stronger."))
+
diff --git a/code/game/objects/effects/decals/Cleanable/radioactive_sludge.dm b/code/game/objects/effects/decals/Cleanable/radioactive_sludge.dm
new file mode 100644
index 00000000000..6192c9c2c5c
--- /dev/null
+++ b/code/game/objects/effects/decals/Cleanable/radioactive_sludge.dm
@@ -0,0 +1,10 @@
+/obj/effect/decal/cleanable/radioactive_sludge
+ gender = PLURAL
+ name = "Radioactive Sludge"
+ desc = "You should probably clean this up."
+ icon_state = "greenglow"
+
+/obj/effect/decal/cleanable/radioactive_sludge/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/inherent_radioactivity, 800, 800, 0, 1.5)
+
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index 5808ed6f478..4e7e4b9ace3 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -4,6 +4,7 @@ GLOBAL_LIST_INIT(special_role_times, list(
ROLE_GOLEM = 0,
ROLE_PAI = 0,
ROLE_GUARDIAN = 0,
+ ROLE_IRRADIATED_MOUSE = 7,
ROLE_TRAITOR = 7,
ROLE_CHANGELING = 14,
ROLE_WIZARD = 14,
diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm
index 1fa1a65902d..8300801df9c 100644
--- a/code/modules/events/event_container.dm
+++ b/code/modules/events/event_container.dm
@@ -201,6 +201,7 @@ GLOBAL_LIST_EMPTY(event_last_fired)
new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/market_crash, 20),
new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/cargo_pods, 50),
new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/spawn_floor_cluwne, 3),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/spawn_irradiated_mouse, 20),
)
/datum/event_container/major
diff --git a/code/modules/mob/living/basic/friendly/mouse.dm b/code/modules/mob/living/basic/friendly/mouse.dm
index d2dd4189d2e..fb5b5b7d84c 100644
--- a/code/modules/mob/living/basic/friendly/mouse.dm
+++ b/code/modules/mob/living/basic/friendly/mouse.dm
@@ -125,10 +125,10 @@
cable.deconstruct()
/mob/living/basic/mouse/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)// Prevents mouse from pulling things
- if(istype(AM, /obj/item/food/sliced/cheesewedge))
+ if(istype(AM, /obj/item/food/sliced/cheesewedge) || istype(AM, /mob/living/basic/mouse))
return ..() // Get dem
if(show_message)
- to_chat(src, SPAN_WARNING("You are too small to pull anything except cheese."))
+ to_chat(src, SPAN_WARNING("You are too small to pull anything except cheese and other mice."))
return
/mob/living/basic/mouse/proc/on_atom_entered(datum/source, atom/movable/entered)
diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm
index 87e6bd635d6..b96cc093a50 100644
--- a/code/modules/mob/living/simple_animal/friendly/cat.dm
+++ b/code/modules/mob/living/simple_animal/friendly/cat.dm
@@ -140,14 +140,23 @@
//MICE!
if(eats_mice && isturf(loc) && !incapacitated())
for(var/mob/living/basic/mouse/M in view(1, src))
- if(!M.stat && Adjacent(M))
- custom_emote(EMOTE_VISIBLE, "splats \the [M]!")
- M.death()
- M.splat()
- movement_target = null
- walk(src, 0)
- stop_automated_movement = FALSE
- break
+ if(M.stat == DEAD || !Adjacent(M))
+ continue
+
+ if(istype(M, /mob/living/basic/mouse/irradiated_mouse))
+ if(prob(50))
+ death()
+ return
+ else
+ name = "Schrödinger's [name]"
+
+ custom_emote(EMOTE_VISIBLE, "splats \the [M]!")
+ M.death()
+ M.splat()
+ movement_target = null
+ walk(src, 0)
+ stop_automated_movement = FALSE
+ break
for(var/obj/item/toy/cattoy/T in view(1, src))
if(T.cooldown < (world.time - 400))
custom_emote(EMOTE_VISIBLE, "bats \the [T] around with its paw!")
diff --git a/icons/mob/actions/actions.dmi b/icons/mob/actions/actions.dmi
index 8382b7c9150..9fd56c2c428 100644
Binary files a/icons/mob/actions/actions.dmi and b/icons/mob/actions/actions.dmi differ
diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi
index d5af2ac9594..39adc734cc7 100644
Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.dmi differ
diff --git a/paradise.dme b/paradise.dme
index a69e093519d..212034021fd 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -977,6 +977,8 @@
#include "code\game\gamemodes\miniantags\guardian\types\protector.dm"
#include "code\game\gamemodes\miniantags\guardian\types\ranged.dm"
#include "code\game\gamemodes\miniantags\guardian\types\standard_guardian.dm"
+#include "code\game\gamemodes\miniantags\irradiated_mouse\irradiated_mouse.dm"
+#include "code\game\gamemodes\miniantags\irradiated_mouse\irradiated_mouse_spells.dm"
#include "code\game\gamemodes\miniantags\morph\morph.dm"
#include "code\game\gamemodes\miniantags\morph\morph_event.dm"
#include "code\game\gamemodes\miniantags\morph\spells\ambush.dm"
@@ -1216,6 +1218,7 @@
#include "code\game\objects\effects\decals\Cleanable\fuel.dm"
#include "code\game\objects\effects\decals\Cleanable\humans.dm"
#include "code\game\objects\effects\decals\Cleanable\misc_cleanables.dm"
+#include "code\game\objects\effects\decals\Cleanable\radioactive_sludge.dm"
#include "code\game\objects\effects\decals\Cleanable\robots.dm"
#include "code\game\objects\effects\decals\Cleanable\tar.dm"
#include "code\game\objects\effects\decals\Cleanable\tracks.dm"