mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-28 01:51:46 +00:00
<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may not be viewable. --> <!-- You can view Contributing.MD for a detailed description of the pull request process. --> ## About The Pull Request Does what it says on the tin. We don't have any "special" sources of clone damage left in the game, most of them are rather trivial so I bunched them together into this PR. Notable things removed: - Clonexadone, because its entire thing was centered around clone damage - Decloner gun, it's also centered around cloning damage, I couldn't think of a replacement mechanic and nobody uses it anyways - Everything else already dealt clone damage as a side (rainbow knife deals a random damage type for example), so these sources were removed <!-- Describe The Pull Request. Please be sure every change is documented or this can delay review and even discourage maintainers from merging your PR! --> ## Why It's Good For The Game Consider the four sources of normal damage that you can get: Brute, Burn, Toxins and Oxygen. These four horsemen of the apocalypse are very well put together and it's no surprise that they are in the game, as you can fit any way of damaging a mob into them. Getting beaten to death by a security officer? Brute damage. Running around on fire? Burn damage. Poisoned or irradiated? Toxin damage. Suffocating in space? Brute, burn and oxygen damage. Technically there's also stamina damage but that's its own ballpark and it also makes sense why we have a damage number for it. Picture this now: We have this cool mechanic called "clone pods" where you can magically revive dead people with absolute ease. We don't want it to be for free though, it comes at a cost. This cost is clone damage, and it serves to restrain people from abusing cloning. Fast forward time a bit and cloning is now removed from the game. What stays with us is a damage number that is intrinsically tied to the context of a removed feature. It was a good idea that we had it for that feature at the time, but now it just sits there. It's the odd one out from all the other damage types. You can easily explain why your blade dealt brute damage, but how are you going to fit clone damage into any context without also becoming extremely specific? My point is: **clone damage is conceptually a flawed mechanic because it is too specific**. That is the major issue why no one uses it, and why that makes it unworthy of being a damage stat. Don't take my word for it though, because a while ago we only had a handful of sources for this damage type in the game. And in most of the rounds where you saw this damage, it came from only one department. It's not worthwhile to keep it around as a damage number. People also didn't know what to do with this damage type, so we currently have two ways of healing clone damage: Cryotubes as a roundstart way of healing clone damage and Rezadone, which instantly sets your clone damage to 0 on the first tick. As a medical doctor, when was the last time you saw someone come in with clone damage and thought to yourself, "Oh, this person has clone damage, I cannot wait to heal them!" ? Now we have replacements for these clone damage sources. Slimes? Slime status effect that deals brute instead of clone. Cosmic heretics? Random organ damage, because their mechanics are already pretty fleshed out. Decloning virus? The virus operated as a "ticking timebomb" which used cloning damage as the timer, so it has been reworked to not use clone damage. What remains after all this is now a basically unused damage type. Every specific situation that used clone damage is now relying on another damage type. Now it's time to put clone damage to rest once and for all. Sure, you can technically add some form of cellular degradation in the future, but it shouldn't be a damage number. The idea of your cells being degraded is a cool concept, don't get me wrong, but make it a status effect or maybe even a wound for that matter. <!-- Argue for the merits of your changes and how they benefit the game, especially if they are controversial and/or far reaching. If you can't actually explain WHY what you are doing will improve the game, then it probably isn't good for the game in the first place. --> ## Changelog <!-- If your PR modifies aspects of the game that can be concretely observed by players or admins you should add a changelog. If your change does NOT meet this description, remove this section. Be sure to properly mark your PRs to prevent unnecessary GBP loss. You can read up on GBP and it's effects on PRs in the tgstation guides for contributors. Please note that maintainers freely reserve the right to remove and add tags should they deem it appropriate. You can attempt to finagle the system all you want, but it's best to shoot for clear communication right off the bat. --> 🆑 del: Removed clone damage. del: Removed the decloner gun. del: Removed clonexadone. /🆑 <!-- Both 🆑's are required for the changelog to work! You can put your name to the right of the first 🆑 if you want to overwrite your GitHub username as author ingame. --> <!-- You can use multiple of the same prefix (they're only used for the icon ingame) and delete the unneeded ones. Despite some of the tags, changelogs should generally represent how a player might be affected by the changes rather than a summary of the PR's contents. -->
308 lines
12 KiB
Plaintext
308 lines
12 KiB
Plaintext
///Simple animals 2.0, This time, let's really try to keep it simple. This basetype should purely be used as a base-level for implementing simplified behaviours for things such as damage and attacks. Everything else should be in components or AI behaviours.
|
|
/mob/living/basic
|
|
name = "basic mob"
|
|
icon = 'icons/mob/simple/animal.dmi'
|
|
health = 20
|
|
maxHealth = 20
|
|
gender = PLURAL
|
|
living_flags = MOVES_ON_ITS_OWN
|
|
status_flags = CANPUSH
|
|
fire_stack_decay_rate = -5 // Reasonably fast as NPCs will not usually actively extinguish themselves
|
|
|
|
var/basic_mob_flags = NONE
|
|
|
|
///Defines how fast the basic mob can move. This is not a multiplier
|
|
var/speed = 1
|
|
///How much stamina the mob recovers per second
|
|
var/stamina_recovery = 5
|
|
|
|
///how much damage this basic mob does to objects, if any.
|
|
var/obj_damage = 0
|
|
///How much armour they ignore, as a flat reduction from the targets armour value.
|
|
var/armour_penetration = 0
|
|
///Damage type of a simple mob's melee attack, should it do damage.
|
|
var/melee_damage_type = BRUTE
|
|
///How much wounding power it has
|
|
var/wound_bonus = CANT_WOUND
|
|
///How much bare wounding power it has
|
|
var/bare_wound_bonus = 0
|
|
///If the attacks from this are sharp
|
|
var/sharpness = NONE
|
|
|
|
/// Sound played when the critter attacks.
|
|
var/attack_sound
|
|
/// Override for the visual attack effect shown on 'do_attack_animation()'.
|
|
var/attack_vis_effect
|
|
///Played when someone punches the creature.
|
|
var/attacked_sound = SFX_PUNCH //This should be an element
|
|
/// How often can you melee attack?
|
|
var/melee_attack_cooldown = 2 SECONDS
|
|
|
|
/// Variable maintained for compatibility with attack_animal procs until simple animals can be refactored away. Use element instead of setting manually.
|
|
var/environment_smash = ENVIRONMENT_SMASH_STRUCTURES
|
|
|
|
/// 1 for full damage, 0 for none, -1 for 1:1 heal from that source.
|
|
var/list/damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, STAMINA = 0, OXY = 1)
|
|
|
|
///Verbs used for speaking e.g. "Says" or "Chitters". This can be elementized
|
|
var/list/speak_emote = list()
|
|
|
|
///When someone interacts with the simple animal.
|
|
///Help-intent verb in present continuous tense.
|
|
var/response_help_continuous = "pokes"
|
|
///Help-intent verb in present simple tense.
|
|
var/response_help_simple = "poke"
|
|
///Disarm-intent verb in present continuous tense.
|
|
var/response_disarm_continuous = "shoves"
|
|
///Disarm-intent verb in present simple tense.
|
|
var/response_disarm_simple = "shove"
|
|
///Harm-intent verb in present continuous tense.
|
|
var/response_harm_continuous = "hits"
|
|
///Harm-intent verb in present simple tense.
|
|
var/response_harm_simple = "hit"
|
|
|
|
///Basic mob's own attacks verbs,
|
|
///Attacking verb in present continuous tense.
|
|
var/attack_verb_continuous = "attacks"
|
|
///Attacking verb in present simple tense.
|
|
var/attack_verb_simple = "attack"
|
|
///Attacking, but without damage, verb in present continuous tense.
|
|
var/friendly_verb_continuous = "nuzzles"
|
|
///Attacking, but without damage, verb in present simple tense.
|
|
var/friendly_verb_simple = "nuzzle"
|
|
|
|
////////THIS SECTION COULD BE ITS OWN ELEMENT
|
|
///Icon to use
|
|
var/icon_living = ""
|
|
///Icon when the animal is dead. Don't use animated icons for this.
|
|
var/icon_dead = ""
|
|
///We only try to show a gibbing animation if this exists.
|
|
var/icon_gib = null
|
|
|
|
///If the mob can be spawned with a gold slime core. HOSTILE_SPAWN are spawned with plasma, FRIENDLY_SPAWN are spawned with blood.
|
|
var/gold_core_spawnable = NO_SPAWN
|
|
///Sentience type, for slime potions. SHOULD BE AN ELEMENT BUT I DONT CARE ABOUT IT FOR NOW
|
|
var/sentience_type = SENTIENCE_ORGANIC
|
|
|
|
///Leaving something at 0 means it's off - has no maximum.
|
|
var/list/habitable_atmos = list("min_oxy" = 5, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0)
|
|
///This damage is taken when atmos doesn't fit all the requirements above. Set to 0 to avoid adding the atmos_requirements element.
|
|
var/unsuitable_atmos_damage = 1
|
|
|
|
///Minimal body temperature without receiving damage
|
|
var/minimum_survivable_temperature = NPC_DEFAULT_MIN_TEMP
|
|
///Maximal body temperature without receiving damage
|
|
var/maximum_survivable_temperature = NPC_DEFAULT_MAX_TEMP
|
|
///This damage is taken when the body temp is too cold. Set both this and unsuitable_heat_damage to 0 to avoid adding the basic_body_temp_sensitive element.
|
|
var/unsuitable_cold_damage = 1
|
|
///This damage is taken when the body temp is too hot. Set both this and unsuitable_cold_damage to 0 to avoid adding the basic_body_temp_sensitive element.
|
|
var/unsuitable_heat_damage = 1
|
|
|
|
/mob/living/basic/Initialize(mapload)
|
|
. = ..()
|
|
|
|
if(gender == PLURAL)
|
|
gender = pick(MALE,FEMALE)
|
|
|
|
if(!real_name)
|
|
real_name = name
|
|
|
|
if(!loc)
|
|
stack_trace("Basic mob being instantiated in nullspace")
|
|
|
|
update_basic_mob_varspeed()
|
|
|
|
if(speak_emote)
|
|
speak_emote = string_list(speak_emote)
|
|
|
|
apply_atmos_requirements()
|
|
apply_temperature_requirements()
|
|
|
|
/// Ensures this mob can take atmospheric damage if it's supposed to
|
|
/mob/living/basic/proc/apply_atmos_requirements()
|
|
if(unsuitable_atmos_damage == 0)
|
|
return
|
|
//String assoc list returns a cached list, so this is like a static list to pass into the element below.
|
|
habitable_atmos = string_assoc_list(habitable_atmos)
|
|
AddElement(/datum/element/atmos_requirements, habitable_atmos, unsuitable_atmos_damage)
|
|
|
|
/// Ensures this mob can take temperature damage if it's supposed to
|
|
/mob/living/basic/proc/apply_temperature_requirements()
|
|
if(unsuitable_cold_damage == 0 && unsuitable_heat_damage == 0)
|
|
return
|
|
AddElement(/datum/element/basic_body_temp_sensitive, minimum_survivable_temperature, maximum_survivable_temperature, unsuitable_cold_damage, unsuitable_heat_damage)
|
|
|
|
|
|
/mob/living/basic/Life(seconds_per_tick = SSMOBS_DT, times_fired)
|
|
. = ..()
|
|
if(staminaloss > 0)
|
|
adjustStaminaLoss(-stamina_recovery * seconds_per_tick, forced = TRUE)
|
|
|
|
/mob/living/basic/say_mod(input, list/message_mods = list())
|
|
if(length(speak_emote))
|
|
verb_say = pick(speak_emote)
|
|
return ..()
|
|
|
|
/mob/living/basic/death(gibbed)
|
|
. = ..()
|
|
if(basic_mob_flags & DEL_ON_DEATH)
|
|
ghostize(can_reenter_corpse = FALSE)
|
|
qdel(src)
|
|
else
|
|
health = 0
|
|
look_dead()
|
|
|
|
/mob/living/basic/gib()
|
|
if(butcher_results || guaranteed_butcher_results)
|
|
var/list/butcher_loot = list()
|
|
if(butcher_results)
|
|
butcher_loot += butcher_results
|
|
if(guaranteed_butcher_results)
|
|
butcher_loot += guaranteed_butcher_results
|
|
var/atom/loot_destination = drop_location()
|
|
for(var/path in butcher_loot)
|
|
for(var/i in 1 to butcher_loot[path])
|
|
new path(loot_destination)
|
|
return ..()
|
|
|
|
/**
|
|
* Apply the appearance and properties this mob has when it dies
|
|
* This is called by the mob pretending to be dead too so don't put loot drops in here or something
|
|
*/
|
|
/mob/living/basic/proc/look_dead()
|
|
icon_state = icon_dead
|
|
if(basic_mob_flags & FLIP_ON_DEATH)
|
|
transform = transform.Turn(180)
|
|
if(!(basic_mob_flags & REMAIN_DENSE_WHILE_DEAD))
|
|
ADD_TRAIT(src, TRAIT_UNDENSE, BASIC_MOB_DEATH_TRAIT)
|
|
SEND_SIGNAL(src, COMSIG_BASICMOB_LOOK_DEAD)
|
|
|
|
/mob/living/basic/revive(full_heal_flags = NONE, excess_healing = 0, force_grab_ghost = FALSE)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
look_alive()
|
|
|
|
/// Apply the appearance and properties this mob has when it is alive
|
|
/mob/living/basic/proc/look_alive()
|
|
icon_state = icon_living
|
|
if(basic_mob_flags & FLIP_ON_DEATH)
|
|
transform = transform.Turn(180)
|
|
if(!(basic_mob_flags & REMAIN_DENSE_WHILE_DEAD))
|
|
REMOVE_TRAIT(src, TRAIT_UNDENSE, BASIC_MOB_DEATH_TRAIT)
|
|
SEND_SIGNAL(src, COMSIG_BASICMOB_LOOK_ALIVE)
|
|
|
|
/mob/living/basic/update_sight()
|
|
lighting_color_cutoffs = list(lighting_cutoff_red, lighting_cutoff_green, lighting_cutoff_blue)
|
|
return ..()
|
|
|
|
/mob/living/basic/examine(mob/user)
|
|
. = ..()
|
|
if(stat != DEAD)
|
|
return
|
|
. += span_deadsay("Upon closer examination, [p_they()] appear[p_s()] to be [HAS_MIND_TRAIT(user, TRAIT_NAIVE) ? "asleep" : "dead"].")
|
|
|
|
/mob/living/basic/proc/melee_attack(atom/target, list/modifiers, ignore_cooldown = FALSE)
|
|
face_atom(target)
|
|
if (!ignore_cooldown)
|
|
changeNext_move(melee_attack_cooldown)
|
|
if(SEND_SIGNAL(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, target, Adjacent(target), modifiers) & COMPONENT_HOSTILE_NO_ATTACK)
|
|
return FALSE //but more importantly return before attack_animal called
|
|
var/result = target.attack_basic_mob(src, modifiers)
|
|
SEND_SIGNAL(src, COMSIG_HOSTILE_POST_ATTACKINGTARGET, target, result)
|
|
return result
|
|
|
|
/mob/living/basic/resolve_unarmed_attack(atom/attack_target, list/modifiers)
|
|
melee_attack(attack_target, modifiers)
|
|
|
|
/mob/living/basic/vv_edit_var(vname, vval)
|
|
switch(vname)
|
|
if(NAMEOF(src, habitable_atmos), NAMEOF(src, unsuitable_atmos_damage))
|
|
RemoveElement(/datum/element/atmos_requirements, habitable_atmos, unsuitable_atmos_damage)
|
|
. = TRUE
|
|
if(NAMEOF(src, minimum_survivable_temperature), NAMEOF(src, maximum_survivable_temperature), NAMEOF(src, unsuitable_cold_damage), NAMEOF(src, unsuitable_heat_damage))
|
|
RemoveElement(/datum/element/basic_body_temp_sensitive, minimum_survivable_temperature, maximum_survivable_temperature, unsuitable_cold_damage, unsuitable_heat_damage)
|
|
. = TRUE
|
|
|
|
. = ..()
|
|
|
|
switch(vname)
|
|
if(NAMEOF(src, habitable_atmos), NAMEOF(src, unsuitable_atmos_damage))
|
|
apply_atmos_requirements()
|
|
if(NAMEOF(src, minimum_survivable_temperature), NAMEOF(src, maximum_survivable_temperature), NAMEOF(src, unsuitable_cold_damage), NAMEOF(src, unsuitable_heat_damage))
|
|
apply_temperature_requirements()
|
|
if(NAMEOF(src, speed))
|
|
datum_flags |= DF_VAR_EDITED
|
|
set_varspeed(vval)
|
|
|
|
/mob/living/basic/proc/set_varspeed(var_value)
|
|
speed = var_value
|
|
update_basic_mob_varspeed()
|
|
|
|
/mob/living/basic/proc/update_basic_mob_varspeed()
|
|
if(speed == 0)
|
|
remove_movespeed_modifier(/datum/movespeed_modifier/simplemob_varspeed)
|
|
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/simplemob_varspeed, multiplicative_slowdown = speed)
|
|
SEND_SIGNAL(src, POST_BASIC_MOB_UPDATE_VARSPEED)
|
|
|
|
/mob/living/basic/update_movespeed()
|
|
. = ..()
|
|
if (cached_multiplicative_slowdown > END_GLIDE_SPEED)
|
|
ADD_TRAIT(src, TRAIT_NO_GLIDE, SPEED_TRAIT)
|
|
else
|
|
REMOVE_TRAIT(src, TRAIT_NO_GLIDE, SPEED_TRAIT)
|
|
|
|
/mob/living/basic/relaymove(mob/living/user, direction)
|
|
if(user.incapacitated())
|
|
return
|
|
return relaydrive(user, direction)
|
|
|
|
/mob/living/basic/get_status_tab_items()
|
|
. = ..()
|
|
. += "Health: [round((health / maxHealth) * 100)]%"
|
|
. += "Combat Mode: [combat_mode ? "On" : "Off"]"
|
|
|
|
/mob/living/basic/compare_sentience_type(compare_type)
|
|
return sentience_type == compare_type
|
|
|
|
/// Updates movement speed based on stamina loss
|
|
/mob/living/basic/update_stamina()
|
|
set_varspeed(initial(speed) + (staminaloss * 0.06))
|
|
|
|
/mob/living/basic/on_fire_stack(seconds_per_tick, datum/status_effect/fire_handler/fire_stacks/fire_handler)
|
|
adjust_bodytemperature((maximum_survivable_temperature + (fire_handler.stacks * 12)) * 0.5 * seconds_per_tick)
|
|
|
|
/mob/living/basic/update_fire_overlay(stacks, on_fire, last_icon_state, suffix = "")
|
|
var/mutable_appearance/fire_overlay = mutable_appearance('icons/mob/effects/onfire.dmi', "generic_fire")
|
|
if(on_fire && isnull(last_icon_state))
|
|
add_overlay(fire_overlay)
|
|
return fire_overlay
|
|
else if(!on_fire && !isnull(last_icon_state))
|
|
cut_overlay(fire_overlay)
|
|
return null
|
|
else if(on_fire && !isnull(last_icon_state))
|
|
return last_icon_state
|
|
return null
|
|
|
|
/mob/living/basic/put_in_hands(obj/item/I, del_on_fail = FALSE, merge_stacks = TRUE, ignore_animation = TRUE)
|
|
. = ..()
|
|
if (.)
|
|
update_held_items()
|
|
|
|
/mob/living/basic/update_held_items()
|
|
. = ..()
|
|
if(isnull(client) || isnull(hud_used) || hud_used.hud_version == HUD_STYLE_NOHUD)
|
|
return
|
|
var/turf/our_turf = get_turf(src)
|
|
for(var/obj/item/held in held_items)
|
|
var/index = get_held_index_of_item(held)
|
|
SET_PLANE(held, ABOVE_HUD_PLANE, our_turf)
|
|
held.screen_loc = ui_hand_position(index)
|
|
client.screen |= held
|
|
|
|
/mob/living/basic/get_body_temp_heat_damage_limit()
|
|
return maximum_survivable_temperature
|
|
|
|
/mob/living/basic/get_body_temp_cold_damage_limit()
|
|
return minimum_survivable_temperature
|