mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-10 23:54:14 +01:00
e5f29ab03c
## About The Pull Request Random slimes didn't have their type randomized because the check in `set_slime_type()` that causes randomization is checking for `null`, and the way this was supposed to be activating was passing `null` through the `/random` subtype's `Initialize()` arguments, but apparently even if you pass `null` in the arguments explicitly, default values get applied(in this case, grey slime). This has been resolved by switching out `null` for a non-null define. Also, slimes revived via aheals and probably other stuff would have their icon reset to grey slime baby, regardless of their actual state, because `icon_living` was not being set. `icon_living` is now being set along with `icon_dead`. ## Why It's Good For The Game fixes #95072 fixes fake grey slimes ## Changelog 🆑 fix: Sources of random slimes will now produce random slimes fix: Slimes revived via magic will now continue to look as they should /🆑
408 lines
14 KiB
Plaintext
408 lines
14 KiB
Plaintext
#define SLIME_EXTRA_SHOCK_COST 3
|
|
#define SLIME_EXTRA_SHOCK_THRESHOLD 8
|
|
#define SLIME_BASE_SHOCK_PERCENTAGE 10
|
|
#define SLIME_SHOCK_PERCENTAGE_PER_LEVEL 7
|
|
|
|
/mob/living/basic/slime
|
|
name = "grey baby slime (123)"
|
|
icon = 'icons/mob/simple/slimes.dmi'
|
|
icon_state = "grey-baby"
|
|
pass_flags = PASSTABLE | PASSGRILLE
|
|
gender = NEUTER
|
|
faction = list(FACTION_SLIME, FACTION_NEUTRAL)
|
|
|
|
icon_living = "grey-baby"
|
|
icon_dead = "grey-baby-dead"
|
|
|
|
attack_sound = 'sound/items/weapons/bite.ogg'
|
|
|
|
//Base physiology
|
|
|
|
maxHealth = 150
|
|
health = 150
|
|
mob_biotypes = MOB_SLIME
|
|
melee_damage_lower = 7
|
|
melee_damage_upper = 17
|
|
wound_bonus = -45
|
|
can_buckle_to = FALSE
|
|
|
|
damage_coeff = list(BRUTE = 1, BURN = -1, TOX = 1, STAMINA = 1, OXY = 1) //Healed by fire
|
|
unsuitable_cold_damage = 15
|
|
unsuitable_heat_damage = 0
|
|
maximum_survivable_temperature = INFINITY
|
|
habitable_atmos = null
|
|
|
|
//Messages
|
|
|
|
attack_verb_simple = "glomp"
|
|
attack_verb_continuous = "glomps"
|
|
|
|
response_help_continuous = "pets"
|
|
response_help_simple = "pet"
|
|
response_disarm_continuous = "shoos"
|
|
response_disarm_simple = "shoo"
|
|
response_harm_continuous = "stomps on"
|
|
response_harm_simple = "stomp on"
|
|
|
|
//Speech
|
|
|
|
speak_emote = list("blorbles")
|
|
bubble_icon = "slime"
|
|
initial_language_holder = /datum/language_holder/slime
|
|
|
|
verb_say = "blorbles"
|
|
verb_ask = "inquisitively blorbles"
|
|
verb_exclaim = "loudly blorbles"
|
|
verb_yell = "loudly blorbles"
|
|
|
|
//AI controller
|
|
|
|
ai_controller = /datum/ai_controller/basic_controller/slime
|
|
|
|
//Slime physiology
|
|
///What is our current lifestage?
|
|
var/life_stage = SLIME_LIFE_STAGE_BABY
|
|
|
|
///Our slime's current mood
|
|
var/current_mood = SLIME_MOOD_NONE
|
|
|
|
///If the slime is currently overcrowded and unable to reproduce.
|
|
var/overcrowded = FALSE
|
|
|
|
///The number of /obj/item/slime_extract's the slime has left inside
|
|
var/cores = 1
|
|
///Chance of mutating, should be between 25 and 35
|
|
var/mutation_chance = 30
|
|
///1-10 controls how much electricity they are generating
|
|
var/powerlevel = SLIME_MIN_POWER
|
|
///Controls how long the slime has been overfed, if 10, grows or reproduces
|
|
var/amount_grown = 0
|
|
/// No hunger
|
|
var/hunger_disabled = FALSE
|
|
|
|
///Has a mutator been used on the slime? Only one is allowed
|
|
var/mutator_used = FALSE
|
|
|
|
// The datum that handles the slime colour's core and possible mutations
|
|
var/datum/slime_type/slime_type = null
|
|
// After a slime is initialized, a list of all possible initialized datums of slime_types
|
|
var/static/list/possible_slime_types = null
|
|
|
|
//CORE-CROSSING CODE
|
|
|
|
///What cross core modification is being used.
|
|
var/crossbreed_modification
|
|
///How many extracts of the modtype have been applied.
|
|
var/applied_crossbreed_amount = 0
|
|
|
|
//AI related traits
|
|
|
|
/// Instructions you can give to slimes
|
|
var/static/list/pet_commands = list(
|
|
/datum/pet_command/idle,
|
|
/datum/pet_command/free,
|
|
/datum/pet_command/follow,
|
|
/datum/pet_command/attack/slime,
|
|
)
|
|
|
|
/// Our evolve action
|
|
var/datum/action/innate/slime/evolve/evolve_action
|
|
/// Our reproduction action
|
|
var/datum/action/innate/slime/reproduce/reproduce_action
|
|
|
|
/mob/living/basic/slime/Initialize(mapload, new_type = /datum/slime_type/grey, new_life_stage = SLIME_LIFE_STAGE_BABY)
|
|
|
|
. = ..()
|
|
|
|
evolve_action = new (src)
|
|
evolve_action.Grant(src)
|
|
|
|
reproduce_action = new (src)
|
|
reproduce_action.Grant(src)
|
|
|
|
if(isnull(possible_slime_types))
|
|
possible_slime_types = list()
|
|
for(var/datum/slime_type/slime_type as anything in subtypesof(/datum/slime_type))
|
|
possible_slime_types[slime_type] = new slime_type
|
|
|
|
set_life_stage(new_life_stage, TRUE)
|
|
set_slime_type(new_type)
|
|
set_nutrition(SLIME_STARTING_NUTRITION)
|
|
|
|
AddComponent(/datum/component/health_scaling_effects, min_health_slowdown = 2)
|
|
AddComponent(/datum/component/obeys_commands, pet_commands)
|
|
|
|
AddElement(/datum/element/ai_retaliate)
|
|
AddElement(/datum/element/basic_health_examine, light_damage_message = "It has some punctures in its flesh!", heavy_damage_message = span_bold("It has severe punctures and tears in its flesh!"), heavy_threshold = 0.4)
|
|
AddElement(/datum/element/footstep, footstep_type = FOOTSTEP_MOB_SLIME)
|
|
AddElement(/datum/element/soft_landing)
|
|
AddElement(/datum/element/swabable, CELL_LINE_TABLE_SLIME, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
|
|
|
|
add_traits(list(TRAIT_CANT_RIDE, TRAIT_CAN_MOUNT_HUMANS, TRAIT_VENTCRAWLER_ALWAYS), INNATE_TRAIT)
|
|
|
|
RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(on_slime_pre_attack))
|
|
RegisterSignal(src, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_attack_hand) )
|
|
|
|
ai_controller.set_blackboard_key(BB_SLIME_EVOLVE, evolve_action)
|
|
ai_controller.set_blackboard_key(BB_SLIME_REPRODUCE, reproduce_action)
|
|
|
|
/mob/living/basic/slime/Destroy()
|
|
QDEL_NULL(evolve_action)
|
|
QDEL_NULL(reproduce_action)
|
|
slime_type = null
|
|
|
|
return ..()
|
|
|
|
///Random slime subtype
|
|
/mob/living/basic/slime/random
|
|
|
|
/mob/living/basic/slime/random/Initialize(mapload, new_colour, new_life_stage)
|
|
return ..(mapload, SLIME_TYPE_RANDOM, prob(50) ? SLIME_LIFE_STAGE_ADULT : SLIME_LIFE_STAGE_BABY)
|
|
|
|
///Friendly docile subtype
|
|
/mob/living/basic/slime/pet
|
|
hunger_disabled = TRUE
|
|
|
|
/mob/living/basic/slime/pet/Initialize(mapload, new_colour, new_life_stage)
|
|
. = ..()
|
|
set_pacified_behaviour()
|
|
|
|
//Hilbert subtype
|
|
/mob/living/basic/slime/hilbert
|
|
|
|
/mob/living/basic/slime/hilbert/Initialize(mapload, new_colour, new_life_stage)
|
|
. = ..(mapload, /datum/slime_type/bluespace)
|
|
ai_controller?.set_blackboard_key(BB_SLIME_RABID, TRUE)
|
|
|
|
/mob/living/basic/slime/adjust_nutrition(change, forced)
|
|
. = ..()
|
|
nutrition = min(nutrition, SLIME_MAX_NUTRITION)
|
|
|
|
/mob/living/basic/slime/set_nutrition(set_to, forced = FALSE)
|
|
. = ..()
|
|
nutrition = min(nutrition, SLIME_MAX_NUTRITION)
|
|
|
|
|
|
/mob/living/basic/slime/update_name()
|
|
///Checks if the slime has a generic name, in the format of baby/adult slime (123)
|
|
var/static/regex/slime_name_regex = new("\\w+ (baby|adult) slime \\(\\d+\\)")
|
|
if(slime_name_regex.Find(name))
|
|
var/slime_id = rand(1, 1000)
|
|
name = "[slime_type.colour] [life_stage] slime ([slime_id])"
|
|
real_name = name
|
|
return ..()
|
|
|
|
/mob/living/basic/slime/regenerate_icons()
|
|
cut_overlays()
|
|
if(slime_type.transparent)
|
|
alpha = SLIME_TRANSPARENCY_ALPHA
|
|
|
|
icon_living = "[slime_type.colour]-[life_stage]"
|
|
icon_dead = !cores ? "[slime_type.colour]-cut" : "[slime_type.colour]-[life_stage]-dead"
|
|
|
|
if(stat != DEAD)
|
|
icon_state = icon_living
|
|
if(current_mood && current_mood != SLIME_MOOD_NONE && !stat)
|
|
add_overlay("aslime-[current_mood]")
|
|
else
|
|
icon_state = icon_dead
|
|
|
|
return ..()
|
|
|
|
/mob/living/basic/slime/get_status_tab_items()
|
|
. = ..()
|
|
if(!hunger_disabled)
|
|
. += "Nutrition: [nutrition]/[SLIME_MAX_NUTRITION]"
|
|
. += "Growth: [amount_grown]/[SLIME_EVOLUTION_THRESHOLD]"
|
|
. += "Power Level: [powerlevel]/[SLIME_MAX_POWER]"
|
|
|
|
/mob/living/basic/slime/mouse_drop_dragged(atom/target_atom, mob/user)
|
|
if(isliving(target_atom) && target_atom != src && user == src)
|
|
var/mob/living/food = target_atom
|
|
if(can_feed_on(food))
|
|
start_feeding(food)
|
|
|
|
///Slimes can hop off mobs they have latched onto
|
|
/mob/living/basic/slime/resist_buckle()
|
|
if(isliving(buckled))
|
|
buckled.unbuckle_mob(src,force=TRUE)
|
|
|
|
//slimes can not pull
|
|
/mob/living/basic/slime/start_pulling(atom/movable/moveable_atom, state, force = move_force, supress_message = FALSE)
|
|
return
|
|
|
|
/mob/living/basic/slime/examine(mob/user)
|
|
. = ..()
|
|
|
|
switch(powerlevel)
|
|
if(SLIME_MIN_POWER to SLIME_EXTRA_SHOCK_COST)
|
|
. += "It is flickering gently with harmless levels of electrical activity."
|
|
|
|
if(SLIME_EXTRA_SHOCK_COST to SLIME_MEDIUM_POWER)
|
|
. += "It is glowing brightly with medium levels electrical activity."
|
|
|
|
|
|
if(SLIME_MEDIUM_POWER to SLIME_MAX_POWER)
|
|
. += "It is glowing alarmingly with high levels of electrical activity."
|
|
|
|
if(SLIME_MAX_POWER)
|
|
. += span_boldwarning("It is radiating with massive levels of electrical activity!")
|
|
if(overcrowded)
|
|
. += span_warning("It seems too overcroweded to properly reproduce!")
|
|
|
|
///Changes the slime's current life state
|
|
/mob/living/basic/slime/proc/set_life_stage(new_life_stage = SLIME_LIFE_STAGE_BABY, initial = FALSE)
|
|
life_stage = new_life_stage
|
|
if(life_stage == SLIME_LIFE_STAGE_ADULT)
|
|
health /= 0.75
|
|
maxHealth /= 0.75
|
|
melee_damage_lower *= 2
|
|
melee_damage_upper *= 2
|
|
obj_damage = 15
|
|
wound_bonus = -90
|
|
|
|
else if(!initial)
|
|
health *= 0.75
|
|
maxHealth *= 0.75
|
|
melee_damage_lower *= 0.5
|
|
melee_damage_upper *= 0.5
|
|
obj_damage = initial(obj_damage)
|
|
wound_bonus = initial(wound_bonus)
|
|
|
|
ai_controller.set_blackboard_key(BB_SLIME_LIFE_STAGE, life_stage)
|
|
update_mob_action_buttons()
|
|
|
|
/// Sets the slime's type, name and its icons.
|
|
/// If not provided with a type it will instead be random
|
|
/mob/living/basic/slime/proc/set_slime_type(new_type = SLIME_TYPE_RANDOM)
|
|
if(new_type == SLIME_TYPE_RANDOM)
|
|
new_type = pick(subtypesof(/datum/slime_type))
|
|
|
|
slime_type = possible_slime_types[new_type]
|
|
update_name()
|
|
regenerate_icons()
|
|
|
|
///Handles slime attacking restrictions, and any extra effects that would trigger
|
|
/mob/living/basic/slime/proc/on_slime_pre_attack(mob/living/basic/slime/our_slime, atom/target, proximity, modifiers)
|
|
SIGNAL_HANDLER
|
|
|
|
if(LAZYACCESS(modifiers, RIGHT_CLICK) && isliving(target) && target != src)
|
|
if(our_slime.can_feed_on(target))
|
|
our_slime.start_feeding(target)
|
|
return COMPONENT_HOSTILE_NO_ATTACK
|
|
|
|
if(isAI(target)) //The aI is not tasty!
|
|
target.balloon_alert(our_slime, "not tasty!")
|
|
return COMPONENT_HOSTILE_NO_ATTACK
|
|
|
|
if(our_slime.buckled == target) //If you try to attack the creature you are latched on, you instead cancel feeding
|
|
our_slime.stop_feeding()
|
|
return COMPONENT_HOSTILE_NO_ATTACK
|
|
|
|
if(iscyborg(target))
|
|
var/mob/living/silicon/robot/borg_target = target
|
|
borg_target.flash_act()
|
|
do_sparks(5, TRUE, borg_target)
|
|
var/stunprob = our_slime.powerlevel * SLIME_SHOCK_PERCENTAGE_PER_LEVEL + SLIME_BASE_SHOCK_PERCENTAGE
|
|
if(prob(stunprob) && our_slime.powerlevel >= SLIME_EXTRA_SHOCK_COST)
|
|
our_slime.powerlevel = clamp(our_slime.powerlevel - SLIME_EXTRA_SHOCK_COST, SLIME_MIN_POWER, SLIME_MAX_POWER)
|
|
borg_target.apply_damage(our_slime.powerlevel * rand(6, 10), BRUTE, spread_damage = TRUE, wound_bonus = CANT_WOUND)
|
|
borg_target.visible_message(span_danger("\The [our_slime] shocks [borg_target]!"), span_userdanger("\The [our_slime] shocks you!"))
|
|
else
|
|
borg_target.visible_message(span_danger("\The [our_slime] fails to hurt [borg_target]!"), span_userdanger("\The [our_slime] failed to hurt you!"))
|
|
|
|
return COMPONENT_HOSTILE_NO_ATTACK
|
|
|
|
if(iscarbon(target) && our_slime.powerlevel > SLIME_MIN_POWER)
|
|
var/mob/living/carbon/carbon_target = target
|
|
var/stunprob = our_slime.powerlevel * SLIME_SHOCK_PERCENTAGE_PER_LEVEL + SLIME_BASE_SHOCK_PERCENTAGE // 17 at level 1, 80 at level 10
|
|
if(!prob(stunprob))
|
|
return NONE // normal attack
|
|
|
|
carbon_target.visible_message(span_danger("\The [our_slime] shocks [carbon_target]!"), span_userdanger("\The [our_slime] shocks you!"))
|
|
|
|
do_sparks(5, TRUE, carbon_target)
|
|
var/power = our_slime.powerlevel + rand(0,3)
|
|
carbon_target.Paralyze(2 SECONDS)
|
|
carbon_target.Knockdown(power * 2 SECONDS)
|
|
carbon_target.set_stutter_if_lower(power * 2 SECONDS)
|
|
if (prob(stunprob) && our_slime.powerlevel >= SLIME_EXTRA_SHOCK_COST)
|
|
our_slime.powerlevel = clamp(our_slime.powerlevel - SLIME_EXTRA_SHOCK_COST, SLIME_MIN_POWER, SLIME_MAX_POWER)
|
|
carbon_target.apply_damage(our_slime.powerlevel * rand(6, 10), BURN, spread_damage = TRUE, wound_bonus = CANT_WOUND)
|
|
|
|
if(isslime(target))
|
|
if(target == our_slime)
|
|
return COMPONENT_HOSTILE_NO_ATTACK
|
|
var/mob/living/basic/slime/target_slime = target
|
|
if(target_slime.buckled)
|
|
target_slime.stop_feeding(silent = TRUE)
|
|
visible_message(span_danger("[our_slime] pulls [target_slime] off!"), \
|
|
span_danger("You pull [target_slime] off!"))
|
|
return NONE // normal attack
|
|
|
|
var/is_adult_slime = our_slime.life_stage == SLIME_LIFE_STAGE_ADULT
|
|
if(target_slime.nutrition >= 100) //steal some nutrition. negval handled in life()
|
|
var/stolen_nutrition = min(is_adult_slime ? 90 : 50, target_slime.nutrition)
|
|
target_slime.adjust_nutrition(-stolen_nutrition)
|
|
our_slime.adjust_nutrition(stolen_nutrition)
|
|
if(target_slime.health > 0)
|
|
our_slime.adjust_brute_loss(is_adult_slime ? -20 : -10)
|
|
|
|
|
|
///Spawns a crossed slimecore item
|
|
/mob/living/basic/slime/proc/spawn_corecross()
|
|
var/static/list/crossbreeds = subtypesof(/obj/item/slimecross)
|
|
visible_message(span_danger("[src] shudders, its mutated core consuming the rest of its body!"))
|
|
playsound(src, 'sound/effects/magic/smoke.ogg', 50, TRUE)
|
|
var/selected_crossbreed_path
|
|
for(var/crossbreed_path in crossbreeds)
|
|
var/obj/item/slimecross/cross_item = crossbreed_path
|
|
if(initial(cross_item.colour) == slime_type.colour && initial(cross_item.effect) == crossbreed_modification)
|
|
selected_crossbreed_path = cross_item
|
|
break
|
|
if(selected_crossbreed_path)
|
|
new selected_crossbreed_path(loc)
|
|
else
|
|
visible_message(span_warning("The mutated core shudders, and collapses into a puddle, unable to maintain its form."))
|
|
qdel(src)
|
|
|
|
///Proc for slime core removal surgery, tries to remove cores from a dead slime.
|
|
/mob/living/basic/slime/proc/try_extract_cores(count = 1)
|
|
if(stat != DEAD)
|
|
return FALSE
|
|
if(count <= 0 || cores < count)
|
|
return FALSE
|
|
|
|
var/core_count = min(count, cores)
|
|
for(var/i in 1 to core_count)
|
|
new slime_type.core_type(loc)
|
|
cores--
|
|
|
|
regenerate_icons()
|
|
|
|
return TRUE
|
|
|
|
///Makes the slime peaceful and content
|
|
/mob/living/basic/slime/proc/set_pacified_behaviour()
|
|
hunger_disabled = TRUE
|
|
ai_controller?.set_blackboard_key(BB_SLIME_RABID, FALSE)
|
|
ai_controller?.set_blackboard_key(BB_SLIME_HUNGER_DISABLED, TRUE)
|
|
set_nutrition(SLIME_STARTING_NUTRITION)
|
|
|
|
///Makes the slime angry and hungry
|
|
/mob/living/basic/slime/proc/set_enraged_behaviour()
|
|
hunger_disabled = FALSE
|
|
ai_controller?.set_blackboard_key(BB_SLIME_HUNGER_DISABLED, FALSE)
|
|
ai_controller?.set_blackboard_key(BB_SLIME_RABID, TRUE)
|
|
|
|
///Makes the slime hungry but mostly friendly
|
|
/mob/living/basic/slime/proc/set_default_behaviour()
|
|
hunger_disabled = FALSE
|
|
ai_controller?.set_blackboard_key(BB_SLIME_HUNGER_DISABLED, FALSE)
|
|
ai_controller?.set_blackboard_key(BB_SLIME_RABID, FALSE)
|
|
|
|
#undef SLIME_EXTRA_SHOCK_COST
|
|
#undef SLIME_EXTRA_SHOCK_THRESHOLD
|
|
#undef SLIME_BASE_SHOCK_PERCENTAGE
|
|
#undef SLIME_SHOCK_PERCENTAGE_PER_LEVEL
|