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"