diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm index 30321f5546..396eb87723 100644 --- a/code/_globalvars/lists/maintenance_loot.dm +++ b/code/_globalvars/lists/maintenance_loot.dm @@ -118,3 +118,21 @@ GLOBAL_LIST_INIT(maintenance_loot, list( /obj/item/clothing/gloves/tackler/offbrand = 1, "" = 3 )) + +GLOBAL_LIST_INIT(ratking_trash, list(//Garbage: used by the regal rat mob when spawning garbage. + /obj/item/cigbutt, + /obj/item/trash/cheesie, + /obj/item/trash/candy, + /obj/item/trash/chips, + /obj/item/trash/pistachios, + /obj/item/trash/plate, + /obj/item/trash/popcorn, + /obj/item/trash/raisins, + /obj/item/trash/sosjerky, + /obj/item/trash/syndi_cakes)) + +GLOBAL_LIST_INIT(ratking_coins, list(//Coins: Used by the regal rat mob when spawning coins. + /obj/item/coin/iron, + /obj/item/coin/silver, + /obj/item/coin/plastic, + /obj/item/coin/titanium)) \ No newline at end of file diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm index 79b110a78a..7e7d939c39 100644 --- a/code/controllers/configuration/entries/game_options.dm +++ b/code/controllers/configuration/entries/game_options.dm @@ -435,6 +435,10 @@ config_entry_value = 64 min_val = 0 +/datum/config_entry/number/ratcap + config_entry_value = 64 + min_val = 0 + /datum/config_entry/flag/disable_stambuffer /datum/config_entry/keyed_list/box_random_engine diff --git a/code/controllers/subsystem/mobs.dm b/code/controllers/subsystem/mobs.dm index 8caf2a4623..885587ff55 100644 --- a/code/controllers/subsystem/mobs.dm +++ b/code/controllers/subsystem/mobs.dm @@ -8,6 +8,7 @@ SUBSYSTEM_DEF(mobs) var/static/list/clients_by_zlevel[][] var/static/list/dead_players_by_zlevel[][] = list(list()) // Needs to support zlevel 1 here, MaxZChanged only happens when z2 is created and new_players can login before that. var/static/list/cubemonkeys = list() + var/static/list/cheeserats = list() /datum/controller/subsystem/mobs/stat_entry() ..("P:[GLOB.mob_living_list.len]") diff --git a/code/modules/food_and_drinks/food/snacks_other.dm b/code/modules/food_and_drinks/food/snacks_other.dm index 2463ed3ced..bc80ffe621 100644 --- a/code/modules/food_and_drinks/food/snacks_other.dm +++ b/code/modules/food_and_drinks/food/snacks_other.dm @@ -12,6 +12,17 @@ tastes = list("cheese" = 1) foodtype = DAIRY + + +/obj/item/reagent_containers/food/snacks/royalcheese + name = "royal cheese" + desc = "Ascend the throne. Consume the wheel. Feel the POWER." + icon_state = "royalcheese" + list_reagents = list(/datum/reagent/consumable/nutriment = 15, /datum/reagent/consumable/nutriment/vitamin = 5, /datum/reagent/gold = 20, /datum/reagent/toxin/mutagen = 5) + w_class = WEIGHT_CLASS_BULKY + tastes = list("cheese" = 4, "royalty" = 1) + foodtype = DAIRY + /obj/item/reagent_containers/food/snacks/cheesewedge name = "cheese wedge" desc = "A wedge of delicious Cheddar. The cheese wheel it was cut from can't have gone far." diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm index 689345b04f..359fadfb70 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm @@ -194,3 +194,14 @@ ) result = /mob/living/simple_animal/hostile/bear/butter subcategory = CAT_MISCFOOD + +/datum/crafting_recipe/food/royalcheese + name = "Royal Cheese" + reqs = list( + /obj/item/reagent_containers/food/snacks/store/cheesewheel = 1, + /obj/item/clothing/head/crown = 1, + /datum/reagent/medicine/strange_reagent = 5, + /datum/reagent/toxin/mutagen = 5 + ) + result = /obj/item/reagent_containers/food/snacks/royalcheese + subcategory = CAT_MISCFOOD \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index 50d1e44d2a..51c903ee16 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -29,6 +29,7 @@ var/body_color //brown, gray and white, leave blank for random gold_core_spawnable = FRIENDLY_SPAWN var/chew_probability = 1 + faction = list("rat") /mob/living/simple_animal/mouse/Initialize() . = ..() @@ -59,11 +60,15 @@ else ..(gibbed) + /mob/living/simple_animal/mouse/Crossed(AM as mob|obj) if( ishuman(AM) ) if(!stat) var/mob/M = AM to_chat(M, "[icon2html(src, M)] Squeak!") + if(istype(AM, /obj/item/reagent_containers/food/snacks/royalcheese)) + evolve() + qdel(AM) ..() /mob/living/simple_animal/mouse/handle_automated_action() @@ -84,6 +89,40 @@ C.deconstruct() visible_message("[src] chews through the [C].") + for(var/obj/item/reagent_containers/food/snacks/cheesewedge/cheese in range(1, src)) + if(prob(10)) + be_fruitful() + qdel(cheese) + return + for(var/obj/item/reagent_containers/food/snacks/royalcheese/bigcheese in range(1, src)) + qdel(bigcheese) + evolve() + return + +/** + *Checks the mouse cap, if it's above the cap, doesn't spawn a mouse. If below, spawns a mouse and adds it to cheeserats. + */ + +/mob/living/simple_animal/mouse/proc/be_fruitful() + var/cap = CONFIG_GET(number/ratcap) + if(LAZYLEN(SSmobs.cheeserats) >= cap) + visible_message("[src] carefully eats the cheese, hiding it from the [cap] mice on the station!") + return + var/mob/living/newmouse = new /mob/living/simple_animal/mouse(loc) + SSmobs.cheeserats += newmouse + visible_message("[src] nibbles through the cheese, attracting another mouse!") + +/** + *Spawns a new regal rat, says some good jazz, and if sentient, transfers the relivant mind. + */ +/mob/living/simple_animal/mouse/proc/evolve() + var/mob/living/simple_animal/hostile/regalrat = new /mob/living/simple_animal/hostile/regalrat(loc) + visible_message("[src] devours the cheese! He morphs into something... greater!") + regalrat.say("RISE, MY SUBJECTS! SCREEEEEEE!") + if(mind) + mind.transfer_to(regalrat) + qdel(src) + /* * Mouse types */ @@ -100,6 +139,10 @@ body_color = "brown" icon_state = "mouse_brown" +/mob/living/simple_animal/mouse/Destroy() + SSmobs.cheeserats -= src + return ..() + GLOBAL_VAR(tom_existed) //TOM IS ALIVE! SQUEEEEEEEE~K :) diff --git a/code/modules/mob/living/simple_animal/hostile/regalrat.dm b/code/modules/mob/living/simple_animal/hostile/regalrat.dm new file mode 100644 index 0000000000..e21514b37e --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/regalrat.dm @@ -0,0 +1,239 @@ +/mob/living/simple_animal/hostile/regalrat + name = "regal rat" + desc = "An evolved rat, created through some strange science. It leads nearby rats with deadly efficiency to protect its kingdom. Not technically a king." + icon_state = "regalrat" + icon_living = "regalrat" + icon_dead = "regalrat_dead" + gender = NEUTER + speak_chance = 0 + turns_per_move = 5 + maxHealth = 70 + health = 70 + see_in_dark = 5 + obj_damage = 10 + butcher_results = list(/obj/item/clothing/head/crown = 1,) + response_help_continuous = "glares at" + response_help_simple = "glare at" + response_disarm_continuous = "skoffs at" + response_disarm_simple = "skoff at" + response_harm_continuous = "slashes" + response_harm_simple = "slash" + melee_damage_lower = 10 + melee_damage_upper = 12 + attack_verb_continuous = "slashes" + attack_verb_simple = "slash" + attack_sound = 'sound/weapons/punch1.ogg' + ventcrawler = VENTCRAWLER_ALWAYS + unique_name = TRUE + faction = list("rat") + var/datum/action/cooldown/coffer + var/datum/action/cooldown/riot + ///Number assigned to rats and mice, checked when determining infighting. + +/mob/living/simple_animal/hostile/regalrat/Initialize() + . = ..() + coffer = new /datum/action/cooldown/coffer + coffer.Grant(src) + riot = new /datum/action/cooldown/riot + riot.Grant(src) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Royal Rat, cheesey be his crown?", ROLE_SENTIENCE, null, FALSE, 100, POLL_IGNORE_SENTIENCE_POTION) + if(LAZYLEN(candidates) && !mind) + var/mob/dead/observer/C = pick(candidates) + key = C.key + notify_ghosts("All rise for the rat king, ascendant to the throne in \the [get_area(src)].", source = src, action = NOTIFY_ORBIT, flashwindow = FALSE) + +/mob/living/simple_animal/hostile/regalrat/handle_automated_action() + if(prob(20)) + riot.Trigger() + else if(prob(50)) + coffer.Trigger() + return ..() + +/mob/living/simple_animal/hostile/regalrat/CanAttack(atom/the_target) + if(istype(the_target,/mob/living/simple_animal)) + var/mob/living/A = the_target + if(istype(the_target, /mob/living/simple_animal/hostile/regalrat) && A.stat == CONSCIOUS) + return TRUE + if(istype(the_target, /mob/living/simple_animal/hostile/rat) && A.stat == CONSCIOUS) + var/mob/living/simple_animal/hostile/rat/R = the_target + if(R.faction_check_mob(src, TRUE)) + return FALSE + else + return TRUE + return ..() + +/mob/living/simple_animal/hostile/regalrat/examine(mob/user) + . = ..() + if(istype(user,/mob/living/simple_animal/hostile/rat)) + var/mob/living/simple_animal/hostile/rat/ratself = user + if(ratself.faction_check_mob(src, TRUE)) + . += "This is your king. Long live his majesty!" + else + . += "This is a false king! Strike him down!" + else if(istype(user,/mob/living/simple_animal/hostile/regalrat)) + . += "Who is this foolish false king? This will not stand!" + +/** + *This action creates trash, money, dirt, and cheese. + */ + +/datum/action/cooldown/coffer + name = "Fill Coffers" + desc = "Your newly granted regality and poise let you scavenge for lost junk, but more importantly, cheese." + icon_icon = 'icons/mob/actions/actions_animal.dmi' + background_icon_state = "bg_clock" + button_icon_state = "coffer" + cooldown_time = 50 + +/datum/action/cooldown/coffer/Trigger() + . = ..() + if(!.) + return + var/turf/T = get_turf(owner) + var/loot = rand(1,100) + switch(loot) + if(1 to 5) + to_chat(owner, "Score! You find some cheese!") + new /obj/item/reagent_containers/food/snacks/cheesewedge(T) + if(6 to 10) + var/pickedcoin = pick(GLOB.ratking_coins) + to_chat(owner, "You find some leftover coins. More for the royal treasury!") + for(var/i = 1 to rand(1,3)) + new pickedcoin(T) + if(11) + to_chat(owner, "You find a... Hunh. This coin doesn't look right.") + var/rarecoin = rand(1,2) + if (rarecoin == 1) + new /obj/item/coin/twoheaded(T) + else + new /obj/item/coin/antagtoken(T) + if(12 to 40) + var/pickedtrash = pick(GLOB.ratking_trash) + to_chat(owner, "You just find more garbage and dirt. Lovely, but beneath you now.") + new /obj/effect/decal/cleanable/dirt(T) + new pickedtrash(T) + if(41 to 100) + to_chat(owner, "Drat. Nothing.") + new /obj/effect/decal/cleanable/dirt(T) + StartCooldown() + +/** + *This action checks all nearby mice, and converts them into hostile rats. If no mice are nearby, creates a new one. + */ + +/datum/action/cooldown/riot + name = "Raise Army" + desc = "Raise an army out of the hordes of mice and pests crawling around the maintenance shafts." + icon_icon = 'icons/mob/actions/actions_animal.dmi' + button_icon_state = "riot" + background_icon_state = "bg_clock" + cooldown_time = 80 + ///Checks to see if there are any nearby mice. Does not count Rats. + +/datum/action/cooldown/riot/Trigger() + . = ..() + if(!.) + return + var/cap = CONFIG_GET(number/ratcap) + var/something_from_nothing = FALSE + for(var/mob/living/simple_animal/mouse/M in oview(owner, 5)) + var/mob/living/simple_animal/hostile/rat/new_rat = new(get_turf(M)) + something_from_nothing = TRUE + if(M.mind && M.stat == CONSCIOUS) + M.mind.transfer_to(new_rat) + if(istype(owner,/mob/living/simple_animal/hostile/regalrat)) + var/mob/living/simple_animal/hostile/regalrat/giantrat = owner + new_rat.faction = giantrat.faction + qdel(M) + if(!something_from_nothing) + if(LAZYLEN(SSmobs.cheeserats) >= cap) + to_chat(owner,"There's too many mice on this station to beckon a new one! Find them first!") + return + new /mob/living/simple_animal/mouse(owner.loc) + owner.visible_message("[owner] commands a mouse to its side!") + else + owner.visible_message("[owner] commands its army to action, mutating them into rats!") + StartCooldown() + +/mob/living/simple_animal/hostile/rat + name = "rat" + desc = "It's a nasty, ugly, evil, disease-ridden rodent with anger issues." + icon_state = "mouse_gray" + icon_living = "mouse_gray" + icon_dead = "mouse_gray_dead" + speak = list("Skree!","SKREEE!","Squeak?") + speak_emote = list("squeaks") + emote_hear = list("Hisses.") + emote_see = list("runs in a circle.", "stands on its hind legs.") + melee_damage_lower = 3 + melee_damage_upper = 5 + obj_damage = 5 + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + maxHealth = 15 + health = 15 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 1) + density = FALSE + ventcrawler = VENTCRAWLER_ALWAYS + pass_flags = PASSTABLE | PASSGRILLE | PASSMOB + mob_size = MOB_SIZE_TINY + mob_biotypes = MOB_ORGANIC|MOB_BEAST + faction = list("rat") + +/mob/living/simple_animal/hostile/rat/Initialize() + . = ..() + SSmobs.cheeserats += src + +/mob/living/simple_animal/hostile/rat/Destroy() + SSmobs.cheeserats -= src + return ..() + +/mob/living/simple_animal/hostile/rat/examine(mob/user) + . = ..() + if(istype(user,/mob/living/simple_animal/hostile/rat)) + var/mob/living/simple_animal/hostile/rat/ratself = user + if(ratself.faction_check_mob(src, TRUE)) + . += "You both serve the same king." + else + . += "This fool serves a different king!" + else if(istype(user,/mob/living/simple_animal/hostile/regalrat)) + var/mob/living/simple_animal/hostile/regalrat/ratking = user + if(ratking.faction_check_mob(src, TRUE)) + . += "This rat serves under you." + else + . += "This peasant serves a different king! Strike him down!" + +/mob/living/simple_animal/hostile/rat/CanAttack(atom/the_target) + if(istype(the_target,/mob/living/simple_animal)) + var/mob/living/A = the_target + if(istype(the_target, /mob/living/simple_animal/hostile/regalrat) && A.stat == CONSCIOUS) + var/mob/living/simple_animal/hostile/regalrat/ratking = the_target + if(ratking.faction_check_mob(src, TRUE)) + return FALSE + else + return TRUE + if(istype(the_target, /mob/living/simple_animal/hostile/rat) && A.stat == CONSCIOUS) + var/mob/living/simple_animal/hostile/rat/R = the_target + if(R.faction_check_mob(src, TRUE)) + return FALSE + else + return TRUE + return ..() + +/mob/living/simple_animal/hostile/rat/handle_automated_action() + . = ..() + if(prob(40)) + var/turf/open/floor/F = get_turf(src) + if(istype(F) && !F.intact) + var/obj/structure/cable/C = locate() in F + if(C && prob(15)) + if(C.avail()) + visible_message("[src] chews through the [C]. It's toast!") + playsound(src, 'sound/effects/sparks2.ogg', 100, TRUE) + C.deconstruct() + death() + else if(C && C.avail()) + visible_message("[src] chews through the [C]. It looks unharmed!") + playsound(src, 'sound/effects/sparks2.ogg', 100, TRUE) + C.deconstruct() diff --git a/config/game_options.txt b/config/game_options.txt index f65c1bfff5..405ec0405a 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -603,6 +603,9 @@ NIGHTSHIFT_TOGGLE_PUBLIC_REQUIRES_AUTH ## A cap on how many monkeys may be created via monkey cubes MONKEYCAP 64 +## A cap on how many mice can be bred via cheese wedges +RATCAP 64 + ## Enable the capitalist agenda on your server. ECONOMY diff --git a/icons/mob/actions/actions_animal.dmi b/icons/mob/actions/actions_animal.dmi index aefa471dd2..78eb9f5fe5 100644 Binary files a/icons/mob/actions/actions_animal.dmi and b/icons/mob/actions/actions_animal.dmi differ diff --git a/icons/obj/food/food.dmi b/icons/obj/food/food.dmi index 9affc07f88..641bd61018 100644 Binary files a/icons/obj/food/food.dmi and b/icons/obj/food/food.dmi differ diff --git a/tgstation.dme b/tgstation.dme index eba550d70b..9c24f3549c 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2563,6 +2563,7 @@ #include "code\modules\mob\living\simple_animal\hostile\nanotrasen.dm" #include "code\modules\mob\living\simple_animal\hostile\netherworld.dm" #include "code\modules\mob\living\simple_animal\hostile\pirate.dm" +#include "code\modules\mob\living\simple_animal\hostile\regalrat.dm" #include "code\modules\mob\living\simple_animal\hostile\russian.dm" #include "code\modules\mob\living\simple_animal\hostile\sharks.dm" #include "code\modules\mob\living\simple_animal\hostile\skeleton.dm"