Regal Rat cleanup & minor changes (#91012)

## About The Pull Request

Since #90505 added another entry to it the Regal Rat Riot ability, which
turns maintenance creatures into versions loyal to the rat, has become
sort of unmanageable (and to be honest it was a bit gross to start
with).
Instead of having a big if/else list (which was making the same range
check multiple times...) that sets stats on a bunch of mobs, I delegated
it to the mobs themselves and instead of changing some stats of the
existing mobs we just turn them into a new mob which can be spawned or
placed separately by mappers or admins if they want.

Other stuff I changed:

Riot (the ability which transforms mobs into minions) no longer spawns a
mouse if it fails to find anything. Instead you have a chance to fish
mice out of disposals bins while digging out trash and items.

Domain is now a toggle which activates itself every 6 seconds rather
than a button you manually click every 6 seconds.

Riot makes a visual effect when used.

Rare Pepe randomisation is done via a random spawner instead of the mob
modifying a bunch of its own properties in Initialise.

A bunch of mobs now automatically follow you after being tamed. I wrote
this assuming I was going to add it to the rioted mobs but then didn't
end up doing that because you might want them to immediately attack
someone.
My rule of thumb is that if I think you'd want the mob to attack someone
the moment it is befriended I didn't add this and if you wouldn't I did.

I changed some of the regal rat minion names, and some of them can now
spawn from gold slime which couldn't before.

## Why It's Good For The Game

This proc sucked and now it's nicer.

As for the other changes;
- A tamed mob immediately following you is nice feedback and saves you a
click as it's likely to be your first action. Also removes some admin
panel shitcode I added.
- I changed Domain to a toggle because you generally want to use it on
cooldown and someone suggested it on this PR and it sounded like a good
idea.
- I saw someone in Discord complaining that the previous flow of
recruiting rats by hitting Riot with nothing around to summon one,
waiting, hitting it again to convert one rat, and waiting again was
tedious and annoying which I agree with.
This method improves the quality of life by separating these two actions
but _also_ as a side effect reduces a regal rat's ability to secretly
stockpile 50 rats in a hidden maintenance room because most disposal
bins are in slightly more visible areas, they'll actually need to go and
make a mess somewhere someone can see them.


## Changelog

🆑
balance: Regal Rats can now grab mice out of disposal bins, and no
longer spawn them with the Riot ability.
balance: The Riot ability no longer needs to be used once for each
slightly different kind of mob in your radius.
balance: The Regal Rat Domain ability is now toggled on and off.
balance: Several kinds of mob will immediately start following you once
tamed.
balance: Rats, hostile frogs, and evil snails can be created via gold
slime reaction.
/🆑
This commit is contained in:
Jacquerel
2025-05-15 16:05:41 -04:00
committed by Roxy
parent d98fc7f48f
commit 9968f94721
39 changed files with 493 additions and 358 deletions
@@ -17,3 +17,6 @@
///Called when a /mob/living/simple_animal/hostile fines a new target: (atom/source, new_target)
#define COMSIG_HOSTILE_FOUND_TARGET "comsig_hostile_found_target"
///Called when a regal rat uses their riot ability on a mob
#define COMSIG_REGAL_RAT_RIOTED "comsig_regal_rat_rioted"
+8
View File
@@ -1054,3 +1054,11 @@ GLOBAL_LIST_INIT(layers_to_offset, list(
/// Distance which you can see someone's ID card
/// Short enough that you can inspect over tables (bartender checking age)
#define ID_EXAMINE_DISTANCE 3
GLOBAL_LIST_INIT(regal_rat_minion_commands, list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/protect_owner,
/datum/pet_command/follow,
/datum/pet_command/attack/mouse
))
@@ -417,18 +417,15 @@
/datum/pet_command/idle,
/datum/pet_command/move,
/datum/pet_command/attack,
/datum/pet_command/follow,
/datum/pet_command/follow/start_active,
/datum/pet_command/protect_owner,
)
var/datum/component/obeys_commands/command_component = target.AddComponent(/datum/component/obeys_commands, pet_commands)
target.AddComponent(/datum/component/obeys_commands, pet_commands)
if (isnull(da_boss))
return
target.befriend(da_boss)
// Fuck it we're in admin territory we can do code crimes here
var/datum/pet_command/follow/follow_command = command_component.available_commands["Follow"]
follow_command?.set_command_active(target, da_boss)
/// Whatever it was doing before we fucked with it (mostly, can't do this with total confidence)
/datum/admin_ai_template/reset
@@ -47,6 +47,8 @@
callout_type = /datum/callout_option/move
///the behavior we use to follow
var/follow_behavior = /datum/ai_behavior/pet_follow_friend
///should we activate immediately if we're doing nothing else and gain a friend?
var/activate_on_befriend = FALSE
/datum/pet_command/follow/set_command_active(mob/living/parent, mob/living/commander)
. = ..()
@@ -59,6 +61,18 @@
controller.queue_behavior(follow_behavior, BB_CURRENT_PET_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
/datum/pet_command/follow/add_new_friend(mob/living/tamer)
. = ..()
var/mob/living/parent = weak_parent.resolve()
if (!parent)
return
if (activate_on_befriend && !parent.ai_controller.blackboard_key_exists(BB_ACTIVE_PET_COMMAND))
try_activate_command(tamer)
/// Like follow but start active
/datum/pet_command/follow/start_active
activate_on_befriend = TRUE
/**
* # Pet Command: Play Dead
* Pretend to be dead for a random period of time
+48
View File
@@ -0,0 +1,48 @@
/// Mob which can become evil when exposed to a certain ability
/datum/element/regal_rat_minion
element_flags = ELEMENT_BESPOKE
argument_hash_start_idx = 2
/// Typepath of converted mob
var/converted_path
/// Balloon string to output on conversion
var/success_balloon
/// Commands to give this mob on conversion
var/list/pet_commands
/datum/element/regal_rat_minion/Attach(datum/target, converted_path, success_balloon = "squeak", list/pet_commands)
. = ..()
if (!isliving(target))
return ELEMENT_INCOMPATIBLE
src.converted_path = converted_path
src.success_balloon = success_balloon
src.pet_commands = pet_commands
RegisterSignal(target, COMSIG_REGAL_RAT_RIOTED, PROC_REF(on_rioted))
/datum/element/regal_rat_minion/Detach(datum/source)
. = ..()
UnregisterSignal(source, COMSIG_REGAL_RAT_RIOTED)
/// Makes a mob into a minion
/datum/element/regal_rat_minion/proc/on_rioted(mob/living/minion, mob/living/master)
SIGNAL_HANDLER
INVOKE_ASYNC(src, PROC_REF(do_conversion), minion, master)
/// Actually turn the mob into a different mob
/datum/element/regal_rat_minion/proc/do_conversion(mob/living/minion, mob/living/master)
if (minion.stat == DEAD)
return
var/mob/living/result = converted_path
var/new_name = minion.name == initial(minion.name) ? result::name : minion.name
var/mob/living/new_minion = minion.change_mob_type(converted_path, new_name = new_name, delete_old_mob = TRUE)
if (length(pet_commands))
new_minion.AddComponent(/datum/component/obeys_commands, pet_commands)
qdel(new_minion.GetComponent(/datum/component/tameable)) // Rats don't share
new_minion.befriend(master)
new_minion.faction = master.faction.Copy()
new_minion.balloon_alert_to_viewers(success_balloon)
@@ -84,34 +84,3 @@
/obj/effect/anomaly/bioscrambler/detonate()
COOLDOWN_RESET(src, pulse_cooldown)
anomalyEffect()
/// Visual effect spawned when the bioscrambler scrambles your bio
/obj/effect/temp_visual/circle_wave
icon = 'icons/effects/64x64.dmi'
icon_state = "circle_wave"
pixel_x = -16
pixel_y = -16
duration = 0.5 SECONDS
color = COLOR_LIME
var/max_alpha = 255
///How far the effect would scale in size
var/amount_to_scale = 2
/obj/effect/temp_visual/circle_wave/Initialize(mapload)
transform = matrix().Scale(0.1)
animate(src, transform = matrix().Scale(amount_to_scale), time = duration, flags = ANIMATION_PARALLEL)
animate(src, alpha = max_alpha, time = duration * 0.6, flags = ANIMATION_PARALLEL)
animate(alpha = 0, time = duration * 0.4)
apply_wibbly_filters(src)
return ..()
/obj/effect/temp_visual/circle_wave/bioscrambler
color = COLOR_LIME
/obj/effect/temp_visual/circle_wave/bioscrambler/light
max_alpha = 128
/obj/effect/temp_visual/circle_wave/void_conduit
color = COLOR_FULL_TONER_BLACK
duration = 12 SECONDS
amount_to_scale = 12
@@ -182,7 +182,7 @@
loot = list( // This spawner will scatter water related items around a moist site.
/obj/item/clothing/head/cone = 7,
/obj/item/clothing/suit/caution = 3,
/mob/living/basic/frog = 2,
/obj/effect/spawner/random/frog = 2,
/obj/item/reagent_containers/cup/rag = 2,
/obj/item/reagent_containers/cup/bucket = 2,
/obj/effect/decal/cleanable/blood/old = 2,
@@ -194,9 +194,19 @@
if(mapload)
var/turf/location = get_turf(loc)
if(location.initial_gas_mix != OPENTURF_DEFAULT_ATMOS && location.initial_gas_mix != OPENTURF_DIRTY_ATMOS)
loot -= list(/mob/living/basic/frog, /mob/living/basic/axolotl)
loot -= list(/obj/effect/spawner/random/frog, /mob/living/basic/axolotl)
return ..()
/obj/effect/spawner/random/frog
name = "random frog"
desc = "Spawns a frog, or sometimes a RARE frog."
icon = 'icons/mob/simple/animal.dmi'
icon_state = "frog"
loot = list(
/mob/living/basic/frog = 99,
/mob/living/basic/frog/rare = 1,
)
/obj/effect/spawner/random/trash/graffiti
name = "random graffiti spawner"
icon_state = "rune"
@@ -766,3 +766,34 @@
duration = 5 MINUTES
pixel_x = -16
pixel_y = -8 //32
/// Visual effect spawned when the bioscrambler scrambles your bio
/obj/effect/temp_visual/circle_wave
icon = 'icons/effects/64x64.dmi'
icon_state = "circle_wave"
pixel_x = -16
pixel_y = -16
duration = 0.5 SECONDS
color = COLOR_LIME
var/max_alpha = 255
///How far the effect would scale in size
var/amount_to_scale = 2
/obj/effect/temp_visual/circle_wave/Initialize(mapload)
transform = matrix().Scale(0.1)
animate(src, transform = matrix().Scale(amount_to_scale), time = duration, flags = ANIMATION_PARALLEL)
animate(src, alpha = max_alpha, time = duration * 0.6, flags = ANIMATION_PARALLEL)
animate(alpha = 0, time = duration * 0.4)
apply_wibbly_filters(src)
return ..()
/obj/effect/temp_visual/circle_wave/bioscrambler
color = COLOR_LIME
/obj/effect/temp_visual/circle_wave/bioscrambler/light
max_alpha = 128
/obj/effect/temp_visual/circle_wave/void_conduit
color = COLOR_FULL_TONER_BLACK
duration = 12 SECONDS
amount_to_scale = 12
+1 -1
View File
@@ -204,7 +204,7 @@
cost = CARGO_CRATE_VALUE * 2
contains = list(
/mob/living/basic/axolotl,
/mob/living/basic/frog,
/obj/effect/spawner/random/frog,
)
crate_name = "amphibian crate"
@@ -96,6 +96,7 @@
/mob/living/basic/pet/dog/corgi/puppy = 5,
/mob/living/basic/pet/cat/kitten = 5,
/mob/living/basic/frog = 5,
/mob/living/basic/frog/rare = 1,
/mob/living/basic/axolotl = 1,
/mob/living/basic/pet/dog/corgi/puppy/void = 1,
/mob/living/basic/pet/dog/corgi/puppy/slime = 1,
@@ -201,7 +201,8 @@
/obj/item/fish/tadpole/Initialize(mapload, apply_qualities = TRUE)
. = ..()
AddComponent(/datum/component/fish_growth, /mob/living/basic/frog, rand(2 MINUTES, 3 MINUTES))
var/output_path = prob(99) ? /mob/living/basic/frog : /mob/living/basic/frog/rare
AddComponent(/datum/component/fish_growth, output_path, rand(2 MINUTES, 3 MINUTES))
RegisterSignal(src, COMSIG_FISH_BEFORE_GROWING, PROC_REF(growth_checks))
RegisterSignal(src, COMSIG_FISH_FINISH_GROWING, PROC_REF(on_growth))
@@ -252,7 +253,7 @@
/obj/item/fish/tadpole/proc/gestation(mob/living/user)
if(QDELETED(user) || QDELETED(src))
return
new /mob/living/basic/frog(user)
new /obj/effect/spawner/random/frog(user.drop_location())
user.gib()
qdel(src)
+1 -1
View File
@@ -14,7 +14,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
var/list/return_list = zebra_typecacheof(list(
/datum/data/vending_product = FISH_ICON_COIN,
/mob/living/basic/axolotl = FISH_ICON_CRITTER,
/mob/living/basic/frog = FISH_ICON_CRITTER,
/obj/effect/spawner/random/frog = FISH_ICON_CRITTER,
/mob/living/basic/carp = FISH_ICON_DEF,
/mob/living/basic/mining = FISH_ICON_HOSTILE,
/mob/living/basic/skeleton = FISH_ICON_BONE,
@@ -89,7 +89,7 @@
FISHING_RANDOM_SEED = 16,
/obj/item/seeds/grass = 6,
/obj/item/seeds/random = 1,
/mob/living/basic/frog = 1,
/obj/effect/spawner/random/frog = 1,
/mob/living/basic/axolotl = 1,
/mob/living/basic/turtle = 2,
)
@@ -98,7 +98,7 @@
/obj/item/seeds/grass = 4,
FISHING_RANDOM_SEED = 4,
/obj/item/seeds/random = 1,
/mob/living/basic/frog = 1,
/obj/effect/spawner/random/frog = 1,
/mob/living/basic/axolotl = 1,
)
fishing_difficulty = FISHING_EASY_DIFFICULTY + 5
@@ -107,7 +107,7 @@
FISHING_DUD = 20,
/obj/item/fish/bumpy = 10,
/obj/item/fish/sacabambaspis = 10,
/mob/living/basic/frog = 2,
/obj/effect/spawner/random/frog = 2,
/obj/item/fishing_rod/telescopic/master = 1,
)
fish_counts = list(
@@ -42,7 +42,7 @@
/datum/pet_command/idle,
/datum/pet_command/move,
/datum/pet_command/free,
/datum/pet_command/follow,
/datum/pet_command/follow/start_active,
/datum/pet_command/untargeted_ability/blood_rain,
/datum/pet_command/untargeted_ability/summon_toad,
/datum/pet_command/attack,
@@ -239,7 +239,7 @@
overlay_icon_state = "bg_revenant_border"
spell_requirements = NONE
cooldown_time = 30 SECONDS
summon_type = list(/mob/living/basic/frog/frog_suicide)
summon_type = list(/mob/living/basic/frog/suicide)
summon_radius = 2
summon_amount = 2
max_summons = 2
@@ -52,7 +52,7 @@
var/list/seedling_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/follow,
/datum/pet_command/follow/start_active,
)
/mob/living/basic/seedling/Initialize(mapload)
@@ -213,7 +213,7 @@
seedling_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/follow,
/datum/pet_command/follow/start_active,
/datum/pet_command/attack,
/datum/pet_command/use_ability/solarbeam,
/datum/pet_command/use_ability/rapidseeds,
@@ -35,7 +35,7 @@
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/grub_spit,
/datum/pet_command/follow,
/datum/pet_command/follow/start_active,
/datum/pet_command/fetch,
)
@@ -122,7 +122,7 @@
/datum/pet_command/free,
/datum/pet_command/attack,
/datum/pet_command/breed/gutlunch,
/datum/pet_command/follow,
/datum/pet_command/follow/start_active,
/datum/pet_command/fetch,
/datum/pet_command/mine_walls,
)
@@ -76,7 +76,7 @@
/datum/pet_command/move,
/datum/pet_command/attack,
charge_command,
/datum/pet_command/follow,
/datum/pet_command/follow/start_active,
/datum/pet_command/fish,
)
AddComponent(/datum/component/happiness)
@@ -52,7 +52,7 @@ GLOBAL_LIST_EMPTY(raptor_population)
/datum/pet_command/move,
/datum/pet_command/free,
/datum/pet_command/attack,
/datum/pet_command/follow,
/datum/pet_command/follow/start_active,
/datum/pet_command/fetch,
)
///things we inherited from our parent
@@ -49,7 +49,7 @@
/datum/pet_command/minebot_ability/dump,
/datum/pet_command/automate_mining,
/datum/pet_command/free/minebot,
/datum/pet_command/follow,
/datum/pet_command/follow/start_active,
/datum/pet_command/attack/minebot,
)
///possible colors the bot can have
@@ -49,7 +49,7 @@
var/static/list/pet_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/follow,
/datum/pet_command/follow/start_active,
/datum/pet_command/perform_trick_sequence,
)
@@ -1,5 +1,6 @@
// Add 'walkies' as valid input
/datum/pet_command/follow/dog
activate_on_befriend = TRUE
speech_commands = list("heel", "follow", "walkies")
// Add 'good dog' as valid input
+1 -1
View File
@@ -33,7 +33,7 @@
/datum/pet_command/idle,
/datum/pet_command/move,
/datum/pet_command/free,
/datum/pet_command/follow,
/datum/pet_command/follow/start_active,
/datum/pet_command/attack,
/datum/pet_command/perform_trick_sequence,
)
+26 -3
View File
@@ -19,8 +19,11 @@
verb_exclaim = "gurgles loudly"
verb_yell = "gurgles loudly"
worn_slot_flags = ITEM_SLOT_HEAD
gold_core_spawnable = FRIENDLY_SPAWN
faction = list(FACTION_NEUTRAL, FACTION_MAINT_CREATURES)
ai_controller = /datum/ai_controller/basic_controller/snail
/// What do we turn into if effected by a regal rat?
var/minion_path = /mob/living/basic/snail/angry
/mob/living/basic/snail/Initialize(mapload)
. = ..()
@@ -42,6 +45,9 @@
create_reagents(100, REAGENT_HOLDER_ALIVE)
RegisterSignal(reagents, COMSIG_REAGENTS_HOLDER_UPDATED, PROC_REF(on_reagents_update))
if (minion_path)
AddElement(/datum/element/regal_rat_minion, converted_path = minion_path, success_balloon = "gurgle", pet_commands = GLOB.regal_rat_minion_commands)
/mob/living/basic/snail/proc/on_entered(datum/source, obj/effect/decal/cleanable/food/salt/potential_salt)
SIGNAL_HANDLER
if(istype(potential_salt))
@@ -68,19 +74,36 @@
)
apply_damage(500) //ouch
/mob/living/basic/snail/mob_pickup(mob/living/user)
var/obj/item/clothing/head/mob_holder/snail/holder = new(get_turf(src), src, held_state, head_icon, held_lh, held_rh, worn_slot_flags)
var/display_message = "[user] [HAS_TRAIT(src, TRAIT_MOVE_FLOATING) ? "scoops up [src]" : "peels [src] off the ground"]!"
user.visible_message(span_warning(display_message))
user.put_in_hands(holder)
/mob/living/basic/snail/update_icon_state()
if(stat != DEAD)
icon_state = HAS_TRAIT(src, TRAIT_SHELL_RETREATED) ? "[base_icon_state]_shell" : "[base_icon_state][(faction.Find(FACTION_RAT)) ? "_maints" : ""]"
icon_state = HAS_TRAIT(src, TRAIT_SHELL_RETREATED) ? "snail_shell" : "[base_icon_state]"
return ..()
/// This snail is going to try and beat you up
/mob/living/basic/snail/angry
name = "sewer snail"
gold_core_spawnable = HOSTILE_SPAWN
icon_state = "snail_maints"
icon_living = "snail_maints"
icon_dead = "snail_maints_dead"
base_icon_state = "snail_maints"
health = 40
maxHealth = 40
melee_damage_lower = 5
melee_damage_upper = 8
obj_damage = 8
can_be_held = FALSE
minion_path = null
ai_controller = /datum/ai_controller/basic_controller/snail/trash
///snail's custom holder object
/obj/item/clothing/head/mob_holder/snail
@@ -4,15 +4,21 @@
/datum/action/cooldown/mob_cooldown/domain
name = "Rat King's Domain"
desc = "Corrupts this area to be more suitable for your rat army."
desc = "While enabled, continuously corrupt the surrounding area to be more suitable for your rat army."
check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_INCAPACITATED
click_to_activate = FALSE
cooldown_time = 6 SECONDS
cooldown_time = 1 SECONDS
button_icon = 'icons/mob/actions/actions_animal.dmi'
background_icon_state = "bg_clock"
overlay_icon_state = "bg_clock_border"
button_icon_state = "coffer"
button_icon_state = "coffer_off"
shared_cooldown = NONE
/// Are we currently ticking?
var/is_active = FALSE
/// How often do we make a mess?
var/mess_interval = 6 SECONDS
/// Don't do anything if we're on this cooldown
COOLDOWN_DECLARE(mess_cooldown)
/datum/action/cooldown/mob_cooldown/domain/IsAvailable(feedback = FALSE)
. = ..()
@@ -23,25 +29,65 @@
owner.balloon_alert(owner, "can't use while ventcrawling!")
return FALSE
/datum/action/cooldown/mob_cooldown/domain/proc/domain()
var/turf/location = get_turf(owner)
location.atmos_spawn_air("[GAS_MIASMA]=4;[TURF_TEMPERATURE(T20C)]")
switch (rand(1,10))
if (8)
new /obj/effect/decal/cleanable/vomit(location)
if (9)
new /obj/effect/decal/cleanable/vomit/old(location)
if (10)
new /obj/effect/decal/cleanable/oil/slippery(location)
else
new /obj/effect/decal/cleanable/dirt(location)
StartCooldown()
/datum/action/cooldown/mob_cooldown/domain/Activate(atom/target)
StartCooldown(10 SECONDS)
domain()
set_domain_active(!is_active)
StartCooldown()
/datum/action/cooldown/mob_cooldown/domain/Remove(mob/removed_from)
set_domain_active(FALSE)
return ..()
/datum/action/cooldown/mob_cooldown/domain/update_status_on_signal(datum/source, new_stat, old_stat)
. = ..()
if (!IsAvailable())
set_domain_active(FALSE)
/// Enable or disable the ability
/datum/action/cooldown/mob_cooldown/domain/proc/set_domain_active(should_active)
if (is_active == should_active || isnull(owner))
return
is_active = should_active
if (is_active)
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_MOVE_VENTCRAWLING), PROC_REF(cancel_on_signal))
button_icon_state = "coffer"
spread_domain()
else
UnregisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_MOVE_VENTCRAWLING))
button_icon_state = "coffer_off"
build_all_button_icons(update_flags = UPDATE_BUTTON_ICON)
/// Stop spreading shit when one of these events happens
/datum/action/cooldown/mob_cooldown/domain/proc/cancel_on_signal()
SIGNAL_HANDLER
set_domain_active(FALSE)
/// Create gas and spawn mess
/datum/action/cooldown/mob_cooldown/domain/proc/spread_domain()
if (!is_active || !COOLDOWN_FINISHED(src, mess_cooldown) || !owner)
return
var/turf/our_location = get_turf(owner)
our_location.atmos_spawn_air("[GAS_MIASMA]=4;[TURF_TEMPERATURE(T20C)]")
var/list/available_spots = list(our_location) + get_adjacent_open_turfs(owner)
var/turf/mess_location = pick(available_spots)
switch (rand(1,10))
if (8)
new /obj/effect/decal/cleanable/vomit(mess_location)
if (9)
new /obj/effect/decal/cleanable/vomit/old(mess_location)
if (10)
new /obj/effect/decal/cleanable/oil/slippery(mess_location)
else
new /obj/effect/decal/cleanable/dirt(mess_location)
COOLDOWN_START(src, mess_cooldown, mess_interval) // We use a cooldown AND timer because of the toggle
addtimer(CALLBACK(src, PROC_REF(spread_domain)), mess_interval, TIMER_DELETE_ME)
/**
* This action checks some nearby maintenance animals and makes them your minions.
* If none are nearby, creates a new mouse.
@@ -59,22 +105,6 @@
shared_cooldown = NONE
/// How close does something need to be for us to recruit it?
var/range = 5
/// Commands you can give to your mouse army
var/static/list/mouse_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/protect_owner,
/datum/pet_command/follow,
/datum/pet_command/attack/mouse
)
/// Commands you can give to glockroaches
var/static/list/glockroach_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/protect_owner/glockroach,
/datum/pet_command/follow,
/datum/pet_command/attack/glockroach
)
/datum/action/cooldown/mob_cooldown/riot/IsAvailable(feedback = FALSE)
. = ..()
@@ -98,145 +128,15 @@
* * Spawn a single mouse if below the mouse cap.
*/
/datum/action/cooldown/mob_cooldown/riot/proc/riot()
var/uplifted_mice = FALSE
for(var/mob/living/basic/mouse/nearby_mouse in oview(owner, range))
uplifted_mice = convert_mouse(nearby_mouse) || uplifted_mice
if (uplifted_mice)
owner.visible_message(span_warning("[owner] commands their army to action, mutating them into rats!"))
return
playsound(owner, 'sound/mobs/non-humanoids/mouse/mousesqueek.ogg', vol = 150, frequency = 10000)
var/static/list/converted_check_list = list(FACTION_RAT)
var/uplifted_roach = FALSE
for (var/mob/living/basic/cockroach/nearby_roach in oview(owner, range))
uplifted_roach = convert_roach(nearby_roach, converted_check_list) || uplifted_roach
if (uplifted_roach)
owner.visible_message(span_warning("[owner] commands their army to action, mutating them into sewer roaches!"))
return
new /obj/effect/temp_visual/circle_wave/brown(get_turf(owner))
for (var/mob/living/possible_minion in oview(owner, range))
SEND_SIGNAL(possible_minion, COMSIG_REGAL_RAT_RIOTED, owner)
var/uplifted_frog = FALSE
for (var/mob/living/basic/frog/nearby_frog in oview(owner, range))
uplifted_frog = convert_frog(nearby_frog, converted_check_list) || uplifted_frog
if (uplifted_frog)
owner.visible_message(span_warning("[owner] commands their army to action, mutating them into trash frogs!"))
return
var/uplifted_snail = FALSE
for(var/mob/living/basic/snail/nearby_snail in oview(owner, range))
uplifted_snail = convert_snail(nearby_snail, converted_check_list) || uplifted_snail
if(uplifted_snail)
owner.visible_message(span_warning("[owner] commands their army to action, mutating them into death snails!"))
return
var/rat_cap = CONFIG_GET(number/ratcap)
if (LAZYLEN(SSmobs.cheeserats) >= rat_cap)
to_chat(owner,span_warning("There's too many mice on this station to beckon a new one! Find them first!"))
return
new /mob/living/basic/mouse(owner.loc)
owner.visible_message(span_warning("[owner] commands a mouse to their side!"))
/// Makes a passed mob into our minion
/datum/action/cooldown/mob_cooldown/riot/proc/make_minion(mob/living/new_minion, minion_desc, list/command_list = mouse_commands)
if (isbasicmob(new_minion))
new_minion.AddComponent(/datum/component/obeys_commands, command_list)
qdel(new_minion.GetComponent(/datum/component/tameable)) // Rats don't share
new_minion.befriend(owner)
new_minion.faction = owner.faction.Copy()
// Give a hint in description too
new_minion.desc += minion_desc
new_minion.balloon_alert_to_viewers("squeak")
/// Turns a mouse into an angry mouse
/datum/action/cooldown/mob_cooldown/riot/proc/convert_mouse(mob/living/basic/mouse/nearby_mouse)
// This mouse is already rat controlled, let's not bother with it.
if (istype(nearby_mouse.ai_controller, /datum/ai_controller/basic_controller/mouse/rat))
return FALSE
var/mob/living/basic/mouse/rat/rat_path = /mob/living/basic/mouse/rat
// Change name
if (nearby_mouse.name == "mouse")
nearby_mouse.name = initial(rat_path.name)
// Buffs our combat stats to that of a rat
nearby_mouse.melee_damage_lower = initial(rat_path.melee_damage_lower)
nearby_mouse.melee_damage_upper = initial(rat_path.melee_damage_upper)
nearby_mouse.obj_damage = initial(rat_path.obj_damage)
nearby_mouse.maxHealth = initial(rat_path.maxHealth)
nearby_mouse.health = initial(rat_path.health)
// Replace our AI with a rat one
nearby_mouse.ai_controller = new /datum/ai_controller/basic_controller/mouse/rat(nearby_mouse)
make_minion(nearby_mouse, " ...Except this one looks corrupted and aggressive.")
return TRUE
/// Turns a roach into an angry roach
/datum/action/cooldown/mob_cooldown/riot/proc/convert_roach(mob/living/basic/cockroach/nearby_roach, list/converted_check_list)
// No need to convert when not on the same team.
if (faction_check(nearby_roach.faction, converted_check_list))
return FALSE
var/list/minion_commands = mouse_commands
if (!findtext(nearby_roach.name, "sewer"))
nearby_roach.name = "sewer [nearby_roach.name]"
if (istype(nearby_roach, /mob/living/basic/cockroach/glockroach) || istype(nearby_roach, /mob/living/basic/cockroach/hauberoach))
if (istype(nearby_roach, /mob/living/basic/cockroach/glockroach))
minion_commands = glockroach_commands
nearby_roach.melee_damage_lower += 0.5
nearby_roach.melee_damage_upper += 2
else
nearby_roach.melee_damage_lower += 2
nearby_roach.melee_damage_upper += 4
nearby_roach.obj_damage += 5
nearby_roach.ai_controller = new /datum/ai_controller/basic_controller/cockroach/sewer(nearby_roach)
nearby_roach.melee_attack_cooldown = 0.8 SECONDS
nearby_roach.icon_state += "_sewer"
nearby_roach.maxHealth += 1
nearby_roach.health += 1
make_minion(nearby_roach, " <br>This one looks extra robust.", minion_commands)
return TRUE
/// Turns a frog into a crazy frog. This doesn't do anything interesting and should when it becomes a basic mob.
/datum/action/cooldown/mob_cooldown/riot/proc/convert_frog(mob/living/basic/frog/nearby_frog, list/converted_check_list)
// No need to convert when not on the same team.
if(faction_check(nearby_frog.faction, converted_check_list) || nearby_frog.stat == DEAD)
return FALSE
var/list/minion_commands = mouse_commands
if (!findtext(nearby_frog.name, "trash"))
nearby_frog.name = replacetext(nearby_frog.name, "frog", "trash frog")
nearby_frog.icon_state += "_trash"
nearby_frog.icon_living += "_trash"
nearby_frog.icon_dead = nearby_frog.icon_state + "_dead"
nearby_frog.maxHealth += 10
nearby_frog.health += 10
nearby_frog.melee_damage_lower += 1
nearby_frog.melee_damage_upper += 5
nearby_frog.obj_damage += 10
nearby_frog.ai_controller = new /datum/ai_controller/basic_controller/frog/trash(nearby_frog)
var/crazy_frog_desc = " ...[findtext(nearby_frog.name, "rare") ? "even though" : "perhaps because"] they live in a trash bag."
make_minion(nearby_frog, crazy_frog_desc, minion_commands)
return TRUE
/datum/action/cooldown/mob_cooldown/riot/proc/convert_snail(mob/living/basic/snail/nearby_snail, list/converted_check_list)
// No need to convert when not on the same team.
if(faction_check(nearby_snail.faction, converted_check_list) || nearby_snail.stat == DEAD)
return FALSE
nearby_snail.icon_state = "[nearby_snail.base_icon_state]_maints"
nearby_snail.icon_living = "[nearby_snail.base_icon_state]_maints"
nearby_snail.icon_dead = "[nearby_snail.base_icon_state]_maints_dead"
nearby_snail.maxHealth += 10
nearby_snail.health += 10
nearby_snail.melee_damage_lower += 5
nearby_snail.melee_damage_upper += 8
nearby_snail.can_be_held = FALSE
nearby_snail.obj_damage += 8
nearby_snail.fully_replace_character_name(nearby_snail.name, "trash [nearby_snail.name]")
nearby_snail.ai_controller = new /datum/ai_controller/basic_controller/snail/trash(nearby_snail)
make_minion(nearby_snail, " ...This one doesn't look as timid.", mouse_commands)
nearby_snail.update_appearance()
return TRUE
/obj/effect/temp_visual/circle_wave/brown
color = COLOR_BROWN
amount_to_scale = 4
// Command you can give to a mouse to make it kill someone
/datum/pet_command/attack/mouse
@@ -25,3 +25,9 @@
/datum/ai_planning_subtree/use_mob_ability/domain
ability_key = BB_DOMAIN_ABILITY
/datum/ai_planning_subtree/use_mob_ability/domain/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/datum/action/cooldown/mob_cooldown/domain/domain = controller.blackboard[ability_key]
if (!istype(domain) || domain.is_active)
return
return ..()
@@ -193,7 +193,7 @@
var/static/list/pet_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/follow,
/datum/pet_command/follow/start_active,
/datum/pet_command/perform_trick_sequence,
)
@@ -34,6 +34,10 @@
ai_controller = /datum/ai_controller/basic_controller/cockroach
var/cockroach_cell_line = CELL_LINE_TABLE_COCKROACH
/// What do we turn into if recruited by a regal rat?
var/minion_path = /mob/living/basic/cockroach/sewer
/// Command list given to minionised cockroaches
var/list/minion_commands
/mob/living/basic/cockroach/Initialize(mapload)
var/turf/our_turf = get_turf(src)
@@ -57,6 +61,14 @@
ADD_TRAIT(src, TRAIT_NUKEIMMUNE, INNATE_TRAIT)
ADD_TRAIT(src, TRAIT_RADIMMUNE, INNATE_TRAIT)
if (minion_path)
set_pet_commands()
AddElement(/datum/element/regal_rat_minion, converted_path = minion_path, success_balloon = "hiss", pet_commands = minion_commands)
/// Decide what this should be able to be told to do
/mob/living/basic/cockroach/proc/set_pet_commands()
minion_commands = GLOB.regal_rat_minion_commands
/mob/living/basic/cockroach/ex_act() //Explosions are a terrible way to handle a cockroach.
return FALSE
@@ -64,76 +76,22 @@
/mob/living/basic/cockroach/spawn_gibs()
return
/datum/ai_controller/basic_controller/cockroach
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends,
BB_OWNER_SELF_HARM_RESPONSES = list(
"*me waves its antennae in disapproval.",
"*me chitters sadly."
)
)
ai_traits = STOP_MOVING_WHEN_PULLED
ai_movement = /datum/ai_movement/basic_avoidance
idle_behavior = /datum/idle_behavior/idle_random_walk
planning_subtrees = list(
/datum/ai_planning_subtree/random_speech/insect,
/datum/ai_planning_subtree/find_and_hunt_target/roach,
)
/obj/projectile/glockroachbullet
damage = 10 //same damage as a hivebot
damage_type = BRUTE
/obj/item/ammo_casing/glockroach
name = "0.9mm bullet casing"
desc = "A... 0.9mm bullet casing? What?"
projectile_type = /obj/projectile/glockroachbullet
/mob/living/basic/cockroach/glockroach
name = "glockroach"
desc = "HOLY SHIT, THAT COCKROACH HAS A GUN!"
icon_state = "glockroach"
melee_damage_lower = 2.5
melee_damage_upper = 10
obj_damage = 10
/// Roach which tries to ineffectually attack you
/mob/living/basic/cockroach/sewer
name = "sewer roach"
icon_state = "cockroach_sewer"
desc = "This bug has a really bad attitude."
health = 2
maxHealth = 2 // Wow!
melee_damage_lower = 2
melee_damage_upper = 4
obj_damage = 5
melee_attack_cooldown = 0.8 SECONDS
gold_core_spawnable = HOSTILE_SPAWN
faction = list(FACTION_HOSTILE, FACTION_MAINT_CREATURES)
ai_controller = /datum/ai_controller/basic_controller/cockroach/glockroach
cockroach_cell_line = CELL_LINE_TABLE_GLOCKROACH
///number of burst shots
var/burst_shots
///cooldown between attacks
var/ranged_cooldown = 1 SECONDS
/mob/living/basic/cockroach/glockroach/Initialize(mapload)
. = ..()
AddComponent(\
/datum/component/ranged_attacks,\
casing_type = /obj/item/ammo_casing/glockroach,\
burst_shots = burst_shots,\
cooldown_time = ranged_cooldown,\
)
if (ranged_cooldown <= 1 SECONDS)
AddComponent(/datum/component/ranged_mob_full_auto)
/datum/ai_controller/basic_controller/cockroach/glockroach
planning_subtrees = list(
/datum/ai_planning_subtree/pet_planning,
/datum/ai_planning_subtree/random_speech/insect,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/basic_ranged_attack_subtree/glockroach, //If we are attacking someone, this will prevent us from hunting
/datum/ai_planning_subtree/find_and_hunt_target/roach,
)
/datum/ai_planning_subtree/basic_ranged_attack_subtree/glockroach
ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/glockroach
/datum/ai_behavior/basic_ranged_attack/glockroach //Slightly slower, as this is being made in feature freeze ;)
action_cooldown = 1 SECONDS
minion_path = null
ai_controller = /datum/ai_controller/basic_controller/cockroach/aggro
/// Roach with a spiky hat, like a caltrop
/mob/living/basic/cockroach/hauberoach
name = "hauberoach"
desc = "Is that cockroach wearing a tiny yet immaculate replica 19th century Prussian spiked helmet? ...Is that a bad thing?"
@@ -149,8 +107,9 @@
attack_vis_effect = ATTACK_EFFECT_SLASH
faction = list(FACTION_HOSTILE, FACTION_MAINT_CREATURES)
sharpness = SHARP_POINTY
ai_controller = /datum/ai_controller/basic_controller/cockroach/hauberoach
ai_controller = /datum/ai_controller/basic_controller/cockroach/aggro
cockroach_cell_line = CELL_LINE_TABLE_HAUBEROACH
minion_path = /mob/living/basic/cockroach/hauberoach/imperial
/mob/living/basic/cockroach/hauberoach/Initialize(mapload)
. = ..()
@@ -173,43 +132,92 @@
living_target.visible_message(span_notice("[living_target] squashes [cockroach], not even noticing its spike."), span_notice("You squashed [cockroach], not even noticing its spike."))
return FALSE
/datum/ai_controller/basic_controller/cockroach/hauberoach
planning_subtrees = list(
/datum/ai_planning_subtree/pet_planning,
/datum/ai_planning_subtree/random_speech/insect,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/basic_melee_attack_subtree, //If we are attacking someone, this will prevent us from hunting
/datum/ai_planning_subtree/find_and_hunt_target/roach,
)
/// Regal rat royal escort
/mob/living/basic/cockroach/hauberoach/imperial
name = "imperial hauberoach"
desc = "This cockroach seems to have found employment as a professional royal guard."
health = 2
maxHealth = 2
melee_damage_lower = 3
melee_damage_upper = 12
icon_state = "hauberoach_sewer"
minion_path = null
gold_core_spawnable = NO_SPAWN
/datum/ai_controller/basic_controller/cockroach/sewer
planning_subtrees = list(
/datum/ai_planning_subtree/pet_planning,
/datum/ai_planning_subtree/random_speech/insect,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
/datum/ai_planning_subtree/find_and_hunt_target/roach,
)
/// Roach with a gun
/mob/living/basic/cockroach/glockroach
name = "glockroach"
desc = "HOLY SHIT, THAT COCKROACH HAS A GUN!"
icon_state = "glockroach"
melee_damage_lower = 2.5
melee_damage_upper = 10
obj_damage = 10
gold_core_spawnable = HOSTILE_SPAWN
faction = list(FACTION_HOSTILE, FACTION_MAINT_CREATURES)
ai_controller = /datum/ai_controller/basic_controller/cockroach/glockroach
cockroach_cell_line = CELL_LINE_TABLE_GLOCKROACH
minion_path = /mob/living/basic/cockroach/glockroach/gang
///number of burst shots
var/burst_shots
///cooldown between attacks
var/ranged_cooldown = 1 SECONDS
/mob/living/basic/cockroach/glockroach/Initialize(mapload)
. = ..()
AddComponent(\
/datum/component/ranged_attacks,\
casing_type = /obj/item/ammo_casing/glockroach,\
burst_shots = burst_shots,\
cooldown_time = ranged_cooldown,\
)
if (ranged_cooldown <= 1 SECONDS)
AddComponent(/datum/component/ranged_mob_full_auto)
/mob/living/basic/cockroach/glockroach/set_pet_commands()
var/static/list/glockroach_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/protect_owner/glockroach,
/datum/pet_command/follow,
/datum/pet_command/attack/glockroach
)
minion_commands = glockroach_commands
/mob/living/basic/cockroach/glockroach/gang
name = "gangroach"
desc = "This roach grew up on the wrong side of the streets and has fallen in with the wrong crowd."
icon_state = "glockroach_sewer"
health = 2
maxHealth = 2
minion_path = null
gold_core_spawnable = NO_SPAWN
/obj/projectile/glockroachbullet
damage = 10 //same damage as a hivebot
damage_type = BRUTE
/obj/item/ammo_casing/glockroach
name = "0.9mm bullet casing"
desc = "A... 0.9mm bullet casing? What?"
projectile_type = /obj/projectile/glockroachbullet
/// Roach with an SMG
/mob/living/basic/cockroach/glockroach/mobroach
name = "mobroach"
desc = "WE'RE FUCKED, THAT GLOCKROACH HAS A TOMMYGUN!"
desc = "WE'RE FUCKED, THAT COCKROACH HAS A TOMMYGUN!"
icon_state = "mobroach"
ai_controller = /datum/ai_controller/basic_controller/cockroach/mobroach
burst_shots = 4
ranged_cooldown = 2 SECONDS
minion_path = /mob/living/basic/cockroach/glockroach/mobroach/goon
/datum/ai_controller/basic_controller/cockroach/mobroach
planning_subtrees = list(
/datum/ai_planning_subtree/pet_planning,
/datum/ai_planning_subtree/random_speech/insect,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/basic_ranged_attack_subtree/mobroach, //If we are attacking someone, this will prevent us from hunting
/datum/ai_planning_subtree/find_and_hunt_target/roach,
)
ai_controller = /datum/ai_controller/basic_controller/cockroach/glockroach
/datum/ai_planning_subtree/basic_ranged_attack_subtree/mobroach
ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/mobroach
/mob/living/basic/cockroach/glockroach/mobroach/goon
name = "goonroach"
desc = "You got it, boss."
icon_state = "mobroach_sewer"
health = 2
maxHealth = 2
minion_path = null
gold_core_spawnable = NO_SPAWN
/datum/ai_behavior/basic_ranged_attack/mobroach
action_cooldown = 2 SECONDS
@@ -0,0 +1,61 @@
/// AI controller for normal roach
/datum/ai_controller/basic_controller/cockroach
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends,
BB_OWNER_SELF_HARM_RESPONSES = list(
"*me waves its antennae in disapproval.",
"*me chitters sadly."
)
)
ai_traits = STOP_MOVING_WHEN_PULLED
ai_movement = /datum/ai_movement/basic_avoidance
idle_behavior = /datum/idle_behavior/idle_random_walk
planning_subtrees = list(
/datum/ai_planning_subtree/random_speech/insect,
/datum/ai_planning_subtree/find_and_hunt_target/roach,
)
/// AI controller for aggressive roach
/datum/ai_controller/basic_controller/cockroach/aggro
planning_subtrees = list(
/datum/ai_planning_subtree/pet_planning,
/datum/ai_planning_subtree/random_speech/insect,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
/datum/ai_planning_subtree/find_and_hunt_target/roach,
)
/// AI controller for roach who can shoot at you
/datum/ai_controller/basic_controller/cockroach/glockroach
planning_subtrees = list(
/datum/ai_planning_subtree/pet_planning,
/datum/ai_planning_subtree/random_speech/insect,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/basic_ranged_attack_subtree/glockroach, //If we are attacking someone, this will prevent us from hunting
/datum/ai_planning_subtree/find_and_hunt_target/roach,
)
/datum/ai_planning_subtree/basic_ranged_attack_subtree/glockroach
ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/glockroach
/datum/ai_behavior/basic_ranged_attack/glockroach //Slightly slower, as this is being made in feature freeze ;)
action_cooldown = 1 SECONDS
/// roach who shoots at you slightly slower
/datum/ai_controller/basic_controller/cockroach/mobroach
planning_subtrees = list(
/datum/ai_planning_subtree/pet_planning,
/datum/ai_planning_subtree/random_speech/insect,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/basic_ranged_attack_subtree/mobroach,
/datum/ai_planning_subtree/find_and_hunt_target/roach,
)
/datum/ai_planning_subtree/basic_ranged_attack_subtree/mobroach
ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/mobroach
/datum/ai_behavior/basic_ranged_attack/mobroach
action_cooldown = 2 SECONDS
+58 -19
View File
@@ -45,15 +45,14 @@
var/poison_per_bite = 3
///What reagent the mob injects targets with
var/poison_type = /datum/reagent/drug/space_drugs
///What type do we become if influenced by a regal rat?
var/minion_type = /mob/living/basic/frog/crazy
/mob/living/basic/frog/Initialize(mapload)
. = ..()
add_traits(list(TRAIT_NODROWN, TRAIT_SWIMMER, TRAIT_VENTCRAWLER_ALWAYS), INNATE_TRAIT)
if(prob(1))
make_rare()
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
@@ -61,23 +60,56 @@
AddElement(/datum/element/venomous, poison_type, poison_per_bite)
AddElement(/datum/element/ai_retaliate)
AddElement(/datum/element/swabable, CELL_LINE_TABLE_FROG, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
if (minion_type)
AddElement(/datum/element/regal_rat_minion, converted_path = minion_type, success_balloon = "ribbit", pet_commands = GLOB.regal_rat_minion_commands)
/mob/living/basic/frog/proc/make_rare()
/mob/living/basic/frog/proc/on_entered(datum/source, entered as mob|obj)
SIGNAL_HANDLER
if(stat || !isliving(entered))
return
var/mob/living/entered_mob = entered
if(entered_mob.mob_size > MOB_SIZE_TINY)
playsound(src, stepped_sound, vol = 50, vary = TRUE)
/mob/living/basic/frog/rare
name = "rare frog"
desc = "They seem a little smug."
icon_state = "rare_[icon_state]"
icon_living = "rare_[icon_living]"
icon_dead = "rare_[icon_dead]"
icon_state = "rare_frog"
icon_living = "rare_frog"
icon_dead = "rare_frog_dead"
gold_core_spawnable = NO_SPAWN
butcher_results = list(/obj/item/food/nugget = 5)
poison_type = /datum/reagent/drug/mushroomhallucinogen
minion_type = /mob/living/basic/frog/crazy/rare
/// These frogs would REALLY rather like to get at your blood basically by any means possible
/mob/living/basic/frog/crazy
name = "trash frog"
desc = "They seem a little mad."
icon_state = "frog_trash"
icon_living = "frog_trash"
icon_dead = "frog_trash_dead"
health = 25
maxHealth = 25
melee_damage_lower = 6
melee_damage_upper = 15
obj_damage = 20
minion_type = null
gold_core_spawnable = HOSTILE_SPAWN
ai_controller = /datum/ai_controller/basic_controller/frog/trash
/mob/living/basic/frog/crazy/rare
name = "crazy frog"
desc = "They look hopping mad."
icon_state = "rare_frog_trash"
icon_living = "rare_frog_trash"
icon_dead = "rare_frog_trash_dead"
minion_type = null
gold_core_spawnable = NO_SPAWN
butcher_results = list(/obj/item/food/nugget = 5)
poison_type = /datum/reagent/drug/mushroomhallucinogen
/mob/living/basic/frog/proc/on_entered(datum/source, AM as mob|obj)
SIGNAL_HANDLER
if(!stat && isliving(AM))
var/mob/living/L = AM
if(L.mob_size > MOB_SIZE_TINY)
playsound(src, stepped_sound, 50, TRUE)
/// The cold doesn't bother him
/mob/living/basic/frog/icemoon_facility
name = "Peter Jr."
desc = "They seem a little cold."
@@ -86,11 +118,17 @@
habitable_atmos = null
gold_core_spawnable = NO_SPAWN
/mob/living/basic/frog/icemoon_facility/make_rare()
. = ..()
name = "Peter Sr." //make him senior.
/mob/living/basic/frog/icemoon_facility/crazy
name = "Crazy Pete"
desc = "The cold is really getting to him."
icon_state = "frog_trash"
icon_living = "frog_trash"
icon_dead = "frog_trash_dead"
ai_controller = /datum/ai_controller/basic_controller/frog/trash
/mob/living/basic/frog/frog_suicide
/// Frog spawned by leapers which explodes on attack
/mob/living/basic/frog/suicide
name = "suicide frog"
desc = "Driven by sheer will."
icon_state = "frog_trash"
@@ -99,10 +137,11 @@
maxHealth = 5
health = 5
ai_controller = /datum/ai_controller/basic_controller/frog/suicide_frog
minion_type = null
///how long do we exist for
var/existence_period = 15 SECONDS
/mob/living/basic/frog/frog_suicide/Initialize(mapload)
/mob/living/basic/frog/suicide/Initialize(mapload)
. = ..()
AddComponent(/datum/component/explode_on_attack, mob_type_dont_bomb = typecacheof(list(/mob/living/basic/frog, /mob/living/basic/leaper)))
addtimer(CALLBACK(src, PROC_REF(death)), existence_period)
@@ -37,7 +37,7 @@
var/static/list/pet_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/follow,
/datum/pet_command/follow/start_active,
/datum/pet_command/perform_trick_sequence,
)
@@ -40,7 +40,7 @@
var/static/list/pet_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/follow,
/datum/pet_command/follow/start_active,
/datum/pet_command/perform_trick_sequence,
)
@@ -86,6 +86,8 @@
var/static/list/food_types = list(/obj/item/food/cheese)
AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 100)
AddElement(/datum/element/regal_rat_minion, converted_path = /mob/living/basic/mouse/rat, pet_commands = GLOB.regal_rat_minion_commands)
/mob/living/basic/mouse/Destroy()
SSmobs.cheeserats -= src
return ..()
@@ -300,7 +302,7 @@
name = "rat"
desc = "They're a nasty, ugly, evil, disease-ridden rodent with anger issues."
gold_core_spawnable = NO_SPAWN
gold_core_spawnable = HOSTILE_SPAWN
melee_damage_lower = 3
melee_damage_upper = 5
obj_damage = 5
@@ -14,7 +14,7 @@
if(istext(new_type))
new_type = text2path(new_type)
if( !ispath(new_type) )
if(!ispath(new_type) )
to_chat(usr, "Invalid type path (new_type = [new_type]) in change_mob_type(). Contact a coder.")
return
+14 -3
View File
@@ -143,21 +143,32 @@
king.visible_message(span_warning("[king] starts rummaging through [src]."),span_notice("You rummage through [src]..."))
if (!do_after(king, 2 SECONDS, src, interaction_key = "regalrat"))
return
var/cheese = FALSE
var/loot = rand(1,100)
switch(loot)
if(1 to 5)
to_chat(king, span_notice("You find some leftover coins. More for the royal treasury!"))
var/pickedcoin = pick(GLOB.ratking_coins)
for(var/i = 1 to rand(1,3))
new pickedcoin(get_turf(king))
new pickedcoin(king.drop_location())
if(6 to 33)
cheese = TRUE
king.say(pick("Treasure!","Our precious!","Cheese!"), ignore_spam = TRUE, forced = "regal rat rummaging")
to_chat(king, span_notice("Score! You find some cheese!"))
new /obj/item/food/cheese/wedge(get_turf(king))
new /obj/item/food/cheese/wedge(king.drop_location())
else
var/pickedtrash = pick(GLOB.ratking_trash)
to_chat(king, span_notice("You just find more garbage and dirt. Lovely, but beneath you now."))
new pickedtrash(get_turf(king))
new pickedtrash(king.drop_location())
if (cheese)
return // We don't want them to eat your reward
var/rat_cap = CONFIG_GET(number/ratcap)
if (LAZYLEN(SSmobs.cheeserats) < rat_cap && prob(33))
var/mob/living/basic/mouse/new_subject = new(king.drop_location())
playsound(new_subject, 'sound/mobs/non-humanoids/mouse/mousesqueek.ogg', 100)
visible_message(span_warning("[new_subject] climbs out of [src]!"))
/// Moves an item into the diposal bin
/obj/machinery/disposal/proc/place_item_in_disposal(obj/item/I, mob/user)
@@ -578,7 +578,7 @@
/datum/reagent/toxin = -1)
virus_suspectibility = 0.5
resulting_atom = /mob/living/basic/frog
resulting_atom = /obj/effect/spawner/random/frog
/datum/micro_organism/cell_line/axolotl
desc = "caudata amphibian cells"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 20 KiB

+3 -1
View File
@@ -1734,6 +1734,7 @@
#include "code\datums\elements\radiation_protected_clothing.dm"
#include "code\datums\elements\radioactive.dm"
#include "code\datums\elements\ranged_armour.dm"
#include "code\datums\elements\regal_rat_minion.dm"
#include "code\datums\elements\relay_attackers.dm"
#include "code\datums\elements\ridable.dm"
#include "code\datums\elements\rust.dm"
@@ -5434,12 +5435,13 @@
#include "code\modules\mob\living\basic\turtle\turtle_ai.dm"
#include "code\modules\mob\living\basic\vermin\axolotl.dm"
#include "code\modules\mob\living\basic\vermin\butterfly.dm"
#include "code\modules\mob\living\basic\vermin\cockroach.dm"
#include "code\modules\mob\living\basic\vermin\crab.dm"
#include "code\modules\mob\living\basic\vermin\frog.dm"
#include "code\modules\mob\living\basic\vermin\lizard.dm"
#include "code\modules\mob\living\basic\vermin\mouse.dm"
#include "code\modules\mob\living\basic\vermin\space_bat.dm"
#include "code\modules\mob\living\basic\vermin\cockroach\cockroach.dm"
#include "code\modules\mob\living\basic\vermin\cockroach\cockroach_ai.dm"
#include "code\modules\mob\living\basic\vermin\mothroach\mothroach.dm"
#include "code\modules\mob\living\basic\vermin\mothroach\mothroach_ai.dm"
#include "code\modules\mob\living\brain\brain.dm"