Merge remote-tracking branch 'origin/master' into fake_blood
This commit is contained in:
@@ -13,6 +13,10 @@
|
||||
|
||||
/obj/effect/dummy/phased_mob/slaughter/ex_act()
|
||||
return
|
||||
|
||||
/obj/effect/dummy/phased_mob/slaughter/wave_ex_act(power, datum/wave_explosion/explosion, dir)
|
||||
return power
|
||||
|
||||
/obj/effect/dummy/phased_mob/slaughter/bullet_act()
|
||||
return BULLET_ACT_FORCE_PIERCE
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@
|
||||
/mob/living/brain/ex_act() //you cant blow up brainmobs because it makes transfer_to() freak out when borgs blow up.
|
||||
return
|
||||
|
||||
/mob/living/brain/wave_ex_act(power, datum/wave_explosion/explosion, dir)
|
||||
return power
|
||||
|
||||
/mob/living/brain/blob_act(obj/structure/blob/B)
|
||||
return
|
||||
|
||||
|
||||
@@ -297,6 +297,11 @@
|
||||
icon = 'icons/obj/surgery.dmi'
|
||||
icon_state = "posibrain-ipc"
|
||||
|
||||
/obj/item/organ/brain/slime
|
||||
name = "slime nucleus"
|
||||
desc = "A slimey membranous mass from a slimeperson."
|
||||
icon_state = "brain-s"
|
||||
|
||||
|
||||
////////////////////////////////////TRAUMAS////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -16,20 +16,20 @@
|
||||
var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.return_temperature())/BREATH_VOLUME
|
||||
|
||||
//Partial pressure of the toxins in our breath
|
||||
var/Toxins_pp = (breath.get_moles(/datum/gas/plasma)/breath.total_moles())*breath_pressure
|
||||
var/Toxins_pp = (breath.get_moles(GAS_PLASMA)/breath.total_moles())*breath_pressure
|
||||
|
||||
if(Toxins_pp > tox_detect_threshold) // Detect toxins in air
|
||||
adjustPlasma(breath.get_moles(/datum/gas/plasma)*250)
|
||||
adjustPlasma(breath.get_moles(GAS_PLASMA)*250)
|
||||
throw_alert("alien_tox", /obj/screen/alert/alien_tox)
|
||||
|
||||
toxins_used = breath.get_moles(/datum/gas/plasma)
|
||||
toxins_used = breath.get_moles(GAS_PLASMA)
|
||||
|
||||
else
|
||||
clear_alert("alien_tox")
|
||||
|
||||
//Breathe in toxins and out oxygen
|
||||
breath.adjust_moles(/datum/gas/plasma, -toxins_used)
|
||||
breath.adjust_moles(/datum/gas/oxygen, toxins_used)
|
||||
breath.adjust_moles(GAS_PLASMA, -toxins_used)
|
||||
breath.adjust_moles(GAS_O2, toxins_used)
|
||||
|
||||
//BREATH TEMPERATURE
|
||||
handle_breath_temperature(breath)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//The effects include: stun, knockdown, unconscious, sleeping, resting, jitteriness, dizziness, ear damage,
|
||||
// eye damage, eye_blind, eye_blurry, druggy, TRAIT_BLIND trait, and TRAIT_NEARSIGHT trait.
|
||||
|
||||
/mob/living/carbon/alien/DefaultCombatKnockdown(amount, updating = TRUE, ignore_canknockdown = FALSE, override_hardstun, override_stamdmg)
|
||||
/mob/living/carbon/alien/DefaultCombatKnockdown(amount, updating = TRUE, ignore_canknockdown = FALSE, override_hardstun, override_stamdmg, knocktofloor)
|
||||
return //no
|
||||
|
||||
/////////////////////////////////// STUN ////////////////////////////////////
|
||||
|
||||
@@ -358,10 +358,11 @@
|
||||
return
|
||||
I.item_flags |= BEING_REMOVED
|
||||
breakouttime = I.breakouttime
|
||||
var/datum/cuffbreak_checker/cuffbreak_checker = new(get_turf(src), istype(I, /obj/item/restraints)? I : null)
|
||||
if(!cuff_break)
|
||||
visible_message("<span class='warning'>[src] attempts to remove [I]!</span>")
|
||||
to_chat(src, "<span class='notice'>You attempt to remove [I]... (This will take around [DisplayTimeText(breakouttime)] and you need to stand still.)</span>")
|
||||
if(do_after(src, breakouttime, 0, target = src, required_mobility_flags = MOBILITY_RESIST))
|
||||
if(do_after_advanced(src, breakouttime, src, NONE, CALLBACK(cuffbreak_checker, /datum/cuffbreak_checker.proc/check_movement), required_mobility_flags = MOBILITY_RESIST))
|
||||
clear_cuffs(I, cuff_break)
|
||||
else
|
||||
to_chat(src, "<span class='warning'>You fail to remove [I]!</span>")
|
||||
@@ -370,15 +371,36 @@
|
||||
breakouttime = 50
|
||||
visible_message("<span class='warning'>[src] is trying to break [I]!</span>")
|
||||
to_chat(src, "<span class='notice'>You attempt to break [I]... (This will take around 5 seconds and you need to stand still.)</span>")
|
||||
if(do_after(src, breakouttime, 0, target = src))
|
||||
if(do_after_advanced(src, breakouttime, src, NONE, CALLBACK(cuffbreak_checker, /datum/cuffbreak_checker.proc/check_movement), required_mobility_flags = MOBILITY_RESIST))
|
||||
clear_cuffs(I, cuff_break)
|
||||
else
|
||||
to_chat(src, "<span class='warning'>You fail to break [I]!</span>")
|
||||
|
||||
else if(cuff_break == INSTANT_CUFFBREAK)
|
||||
clear_cuffs(I, cuff_break)
|
||||
|
||||
QDEL_NULL(cuffbreak_checker)
|
||||
I.item_flags &= ~BEING_REMOVED
|
||||
|
||||
/datum/cuffbreak_checker
|
||||
var/turf/last
|
||||
var/obj/item/restraints/cuffs
|
||||
|
||||
/datum/cuffbreak_checker/New(turf/initial_turf, obj/item/restraints/R)
|
||||
last = initial_turf
|
||||
if(R)
|
||||
cuffs = R
|
||||
|
||||
/datum/cuffbreak_checker/proc/check_movement(atom/user, delay, atom/target, time_left, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initially_held_item, tool, list/passed_in)
|
||||
if(get_turf(user) != last)
|
||||
last = get_turf(user)
|
||||
passed_in[1] = 0.5
|
||||
if(cuffs && !cuffs.allow_breakout_movement)
|
||||
return DO_AFTER_STOP
|
||||
else
|
||||
passed_in[1] = 1
|
||||
return DO_AFTER_CONTINUE
|
||||
|
||||
/mob/living/carbon/proc/uncuff()
|
||||
if (handcuffed)
|
||||
var/obj/item/W = handcuffed
|
||||
@@ -459,10 +481,6 @@
|
||||
if(HAS_TRAIT(src, TRAIT_CLUMSY))
|
||||
modifier -= 40 //Clumsy people are more likely to hit themselves -Honk!
|
||||
|
||||
//CIT CHANGES START HERE
|
||||
else if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
||||
modifier -= 50
|
||||
|
||||
if(modifier < 100)
|
||||
dropItemToGround(I)
|
||||
//END OF CIT CHANGES
|
||||
@@ -502,17 +520,17 @@
|
||||
return 0
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/proc/vomit(lost_nutrition = 10, blood = FALSE, stun = TRUE, distance = 1, message = TRUE, toxic = FALSE)
|
||||
if(HAS_TRAIT(src, TRAIT_NOHUNGER))
|
||||
return 1
|
||||
/mob/living/carbon/proc/vomit(lost_nutrition = 10, blood = FALSE, stun = TRUE, distance = 1, message = TRUE, vomit_type = VOMIT_TOXIC, harm = TRUE, force = FALSE, purge_ratio = 0.1)
|
||||
if(HAS_TRAIT(src, TRAIT_NOHUNGER) && !force)
|
||||
return TRUE
|
||||
|
||||
if(nutrition < 100 && !blood)
|
||||
if(nutrition < 100 && !blood && !force)
|
||||
if(message)
|
||||
visible_message("<span class='warning'>[src] dry heaves!</span>", \
|
||||
"<span class='userdanger'>You try to throw up, but there's nothing in your stomach!</span>")
|
||||
if(stun)
|
||||
DefaultCombatKnockdown(200)
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
if(is_mouth_covered()) //make this add a blood/vomit overlay later it'll be hilarious
|
||||
if(message)
|
||||
@@ -525,30 +543,29 @@
|
||||
visible_message("<span class='danger'>[src] throws up!</span>", "<span class='userdanger'>You throw up!</span>")
|
||||
if(!isflyperson(src))
|
||||
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "vomit", /datum/mood_event/vomit)
|
||||
|
||||
if(stun)
|
||||
Stun(80)
|
||||
|
||||
playsound(get_turf(src), 'sound/effects/splat.ogg', 50, 1)
|
||||
playsound(get_turf(src), 'sound/effects/splat.ogg', 50, TRUE)
|
||||
var/turf/T = get_turf(src)
|
||||
if(!blood)
|
||||
adjust_nutrition(-lost_nutrition)
|
||||
adjustToxLoss(-3)
|
||||
|
||||
for(var/i=0 to distance)
|
||||
if(blood)
|
||||
if(T)
|
||||
add_splatter_floor(T)
|
||||
if(stun)
|
||||
if(harm)
|
||||
adjustBruteLoss(3)
|
||||
else if(src.reagents.has_reagent(/datum/reagent/consumable/ethanol/blazaam))
|
||||
if(T)
|
||||
T.add_vomit_floor(src, VOMIT_PURPLE)
|
||||
else
|
||||
if(T)
|
||||
T.add_vomit_floor(src, VOMIT_TOXIC)//toxic barf looks different
|
||||
T.add_vomit_floor(src, vomit_type, purge_ratio) //toxic barf looks different || call purge when doing detoxicfication to pump more chems out of the stomach.
|
||||
T = get_step(T, dir)
|
||||
if (is_blocked_turf(T))
|
||||
break
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/proc/spew_organ(power = 5, amt = 1)
|
||||
var/list/spillable_organs = list()
|
||||
@@ -950,7 +967,9 @@
|
||||
/mob/living/carbon/ExtinguishMob()
|
||||
for(var/X in get_equipped_items())
|
||||
var/obj/item/I = X
|
||||
I.acid_level = 0 //washes off the acid on our clothes
|
||||
var/datum/component/acid/acid = I.GetComponent(/datum/component/acid)
|
||||
if(acid)
|
||||
acid.level = 0
|
||||
I.extinguish() //extinguishes our clothes
|
||||
..()
|
||||
|
||||
@@ -1237,7 +1256,7 @@
|
||||
if(user.incapacitated() || !user.Adjacent(src))
|
||||
return FALSE
|
||||
if(W && user.a_intent == INTENT_HELP && W.can_give())
|
||||
user.give()
|
||||
user.give(src)
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/verb/give_verb()
|
||||
@@ -1253,4 +1272,4 @@
|
||||
var/obj/item/I = usr.get_active_held_item()
|
||||
var/mob/living/carbon/C = usr
|
||||
if(I.can_give())
|
||||
C.give()
|
||||
C.give(src)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
|
||||
/mob/living/carbon/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE)
|
||||
SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone)
|
||||
SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone)
|
||||
var/hit_percent = (100-blocked)/100
|
||||
if(!forced && hit_percent <= 0)
|
||||
return 0
|
||||
|
||||
@@ -24,7 +24,12 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
|
||||
/mob/living/carbon/human/dummy/proc/wipe_state()
|
||||
delete_equipment()
|
||||
icon_render_key = null
|
||||
cut_overlays()
|
||||
cut_overlays(TRUE)
|
||||
|
||||
/mob/living/carbon/human/dummy/setup_human_dna()
|
||||
create_dna(src)
|
||||
randomize_human(src)
|
||||
dna.initialize_dna(skip_index = TRUE) //Skip stuff that requires full round init.
|
||||
|
||||
//Inefficient pooling/caching way.
|
||||
GLOBAL_LIST_EMPTY(human_dummy_list)
|
||||
@@ -42,13 +47,48 @@ GLOBAL_LIST_EMPTY(dummy_mob_list)
|
||||
D = new
|
||||
GLOB.human_dummy_list[slotkey] = D
|
||||
GLOB.dummy_mob_list += D
|
||||
else
|
||||
D.regenerate_icons() //they were cut in wipe_state()
|
||||
D.in_use = TRUE
|
||||
return D
|
||||
|
||||
/proc/unset_busy_human_dummy(slotnumber)
|
||||
if(!slotnumber)
|
||||
/proc/generate_dummy_lookalike(slotkey, mob/target)
|
||||
if(!istype(target))
|
||||
return generate_or_wait_for_human_dummy(slotkey)
|
||||
|
||||
var/mob/living/carbon/human/dummy/copycat = generate_or_wait_for_human_dummy(slotkey)
|
||||
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/carbon_target = target
|
||||
carbon_target.dna.transfer_identity(copycat, transfer_SE = TRUE)
|
||||
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/human_target = target
|
||||
human_target.copy_clothing_prefs(copycat)
|
||||
|
||||
copycat.updateappearance(icon_update=TRUE, mutcolor_update=TRUE, mutations_overlay_update=TRUE)
|
||||
else
|
||||
//even if target isn't a carbon, if they have a client we can make the
|
||||
//dummy look like what their human would look like based on their prefs
|
||||
target?.client?.prefs?.copy_to(copycat, icon_updates=TRUE, roundstart_checks=FALSE)
|
||||
|
||||
return copycat
|
||||
|
||||
/proc/unset_busy_human_dummy(slotkey)
|
||||
if(!slotkey)
|
||||
return
|
||||
var/mob/living/carbon/human/dummy/D = GLOB.human_dummy_list[slotnumber]
|
||||
var/mob/living/carbon/human/dummy/D = GLOB.human_dummy_list[slotkey]
|
||||
if(istype(D))
|
||||
D.wipe_state()
|
||||
D.in_use = FALSE
|
||||
|
||||
/proc/clear_human_dummy(slotkey)
|
||||
if(!slotkey)
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/dummy/dummy = GLOB.human_dummy_list[slotkey]
|
||||
|
||||
GLOB.human_dummy_list -= slotkey
|
||||
if(istype(dummy))
|
||||
GLOB.dummy_mob_list -= dummy
|
||||
qdel(dummy)
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
if(skipface || get_visible_name() == "Unknown")
|
||||
. += "You can't make out what species they are."
|
||||
else
|
||||
. += "[t_He] [t_is] a [dna.custom_species ? dna.custom_species : dna.species.name]!"
|
||||
. += "[t_He] [t_is] a [spec_trait_examine_font()][dna.custom_species ? dna.custom_species : dna.species.name]</font>!"
|
||||
|
||||
//uniform
|
||||
if(w_uniform && !(SLOT_W_UNIFORM in obscured))
|
||||
@@ -41,10 +41,10 @@
|
||||
|
||||
. += "[t_He] [t_is] wearing [w_uniform.get_examine_string(user)][accessory_msg]."
|
||||
//head
|
||||
if(head)
|
||||
if(head && !(head.obj_flags & EXAMINE_SKIP))
|
||||
. += "[t_He] [t_is] wearing [head.get_examine_string(user)] on [t_his] head."
|
||||
//suit/armor
|
||||
if(wear_suit)
|
||||
if(wear_suit && !(wear_suit.obj_flags & EXAMINE_SKIP))
|
||||
. += "[t_He] [t_is] wearing [wear_suit.get_examine_string(user)]."
|
||||
//suit/armor storage
|
||||
if(s_store && !(SLOT_S_STORE in obscured))
|
||||
|
||||
@@ -14,10 +14,7 @@
|
||||
//initialize limbs first
|
||||
create_bodyparts()
|
||||
|
||||
//initialize dna. for spawned humans; overwritten by other code
|
||||
create_dna(src)
|
||||
randomize_human(src)
|
||||
dna.initialize_dna()
|
||||
setup_human_dna()
|
||||
|
||||
if(dna.species)
|
||||
set_species(dna.species.type)
|
||||
@@ -36,6 +33,11 @@
|
||||
RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, /atom.proc/clean_blood)
|
||||
GLOB.human_list += src
|
||||
|
||||
/mob/living/carbon/human/proc/setup_human_dna()
|
||||
//initialize dna. for spawned humans; overwritten by other code
|
||||
create_dna(src)
|
||||
randomize_human(src)
|
||||
dna.initialize_dna()
|
||||
|
||||
/mob/living/carbon/human/ComponentInitialize()
|
||||
. = ..()
|
||||
@@ -839,7 +841,7 @@
|
||||
override = dna.species.override_float
|
||||
..()
|
||||
|
||||
/mob/living/carbon/human/vomit(lost_nutrition = 10, blood = 0, stun = 1, distance = 0, message = 1, toxic = 0)
|
||||
/mob/living/carbon/human/vomit(lost_nutrition = 10, blood = FALSE, stun = TRUE, distance = 1, message = TRUE, vomit_type = VOMIT_TOXIC, harm = TRUE, force = FALSE, purge_ratio = 0.1)
|
||||
if(blood && dna?.species && (NOBLOOD in dna.species.species_traits))
|
||||
if(message)
|
||||
visible_message("<span class='warning'>[src] dry heaves!</span>", \
|
||||
@@ -1054,10 +1056,10 @@
|
||||
return
|
||||
if(!HAS_TRAIT(src, TRAIT_IGNOREDAMAGESLOWDOWN)) //if we want to ignore slowdown from damage, but not from equipment
|
||||
var/scaling = maxHealth / 100
|
||||
var/health_deficiency = ((maxHealth / scaling) - (health / scaling) + (getStaminaLoss()*0.75))//CIT CHANGE - reduces the impact of staminaloss and makes stamina buffer influence it
|
||||
var/health_deficiency = max(((maxHealth / scaling) - (health / scaling)), (getStaminaLoss()*0.75))
|
||||
if(health_deficiency >= 40)
|
||||
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown, TRUE, (health_deficiency-39) / 75)
|
||||
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying, TRUE, (health_deficiency-39) / 25)
|
||||
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown, TRUE, (health_deficiency - 15) / 75)
|
||||
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying, TRUE, (health_deficiency - 15) / 25)
|
||||
else
|
||||
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown)
|
||||
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying)
|
||||
@@ -1082,6 +1084,41 @@
|
||||
. = ..()
|
||||
set_species(race)
|
||||
|
||||
/**
|
||||
* # `spec_trait_examine_font()`
|
||||
*
|
||||
* This gets a humanoid's special examine font, which is used to color their species name during examine / health analyzing.
|
||||
* The first of these that applies is returned.
|
||||
* Returns:
|
||||
* * Metallic font if robotic
|
||||
* * Cyan if a toxinlover
|
||||
* * Purple if plasmaperson
|
||||
* * Rock / Brownish if a golem
|
||||
* * Green if none of the others apply (aka, generic organic)
|
||||
*/
|
||||
/mob/living/carbon/human/proc/spec_trait_examine_font()
|
||||
if(HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM))
|
||||
return "<font color='#aaa9ad'>"
|
||||
if(HAS_TRAIT(src, TRAIT_TOXINLOVER))
|
||||
return "<font color='#00ffff'>"
|
||||
if(isplasmaman(src))
|
||||
return "<font color='#800080'>"
|
||||
if(isgolem(src))
|
||||
return "<font color='#8b4513'>"
|
||||
return "<font color='#18d855'>"
|
||||
|
||||
|
||||
/mob/living/carbon/human/get_tooltip_data()
|
||||
var/t_He = p_they(TRUE)
|
||||
var/t_is = p_are()
|
||||
. = list()
|
||||
var/skipface = (wear_mask && (wear_mask.flags_inv & HIDEFACE)) || (head && (head.flags_inv & HIDEFACE))
|
||||
if(skipface || get_visible_name() == "Unknown")
|
||||
. += "You can't make out what species they are."
|
||||
else
|
||||
. += "[t_He] [t_is] a [spec_trait_examine_font()][dna.custom_species ? dna.custom_species : dna.species.name]</font>"
|
||||
SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, usr, .)
|
||||
|
||||
/mob/living/carbon/human/species/abductor
|
||||
race = /datum/species/abductor
|
||||
|
||||
|
||||
@@ -409,7 +409,7 @@
|
||||
return
|
||||
var/informed = FALSE
|
||||
if(isrobotic(src))
|
||||
apply_status_effect(/datum/status_effect/no_combat_mode/robotic_emp, severity / 20)
|
||||
apply_status_effect(/datum/status_effect/robotic_emp, severity / 20)
|
||||
severity *= 0.5
|
||||
var/do_not_stun = FALSE
|
||||
if(HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM))
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
block_parry_data = /datum/block_parry_data/unarmed/human
|
||||
default_block_parry_data = /datum/block_parry_data/unarmed/human
|
||||
causes_dirt_buildup_on_floor = TRUE
|
||||
|
||||
//Hair colour and style
|
||||
var/hair_color = "000"
|
||||
@@ -52,6 +51,9 @@
|
||||
var/obj/item/l_store = null
|
||||
var/obj/item/s_store = null
|
||||
|
||||
/// When an braindead player has their equipment fiddled with, we log that info here for when they come back so they know who took their ID while they were DC'd for 30 seconds
|
||||
var/list/afk_thefts
|
||||
|
||||
var/special_voice = "" // For changing our voice. Used by a symptom.
|
||||
|
||||
var/bleedsuppress = 0 //for stopping bloodloss, eventually this will be limb-based like bleeding
|
||||
@@ -77,6 +79,8 @@
|
||||
var/last_fire_update
|
||||
var/hardcore_survival_score = 0
|
||||
|
||||
tooltips = TRUE
|
||||
|
||||
/// Unarmed parry data for human
|
||||
/datum/block_parry_data/unarmed/human
|
||||
parry_respect_clickdelay = TRUE
|
||||
@@ -141,7 +145,7 @@
|
||||
)
|
||||
|
||||
parry_efficiency_considered_successful = 0.01
|
||||
parry_efficiency_to_counterattack = 0.01
|
||||
parry_efficiency_to_counterattack = INFINITY // no counterattacks
|
||||
parry_max_attacks = INFINITY
|
||||
parry_failed_cooldown_duration = 1.5 SECONDS
|
||||
parry_failed_stagger_duration = 1 SECONDS
|
||||
|
||||
@@ -115,13 +115,13 @@
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(HAS_TRAIT(src, TRAIT_NOGUNS))
|
||||
to_chat(src, "<span class='warning'>You can't bring yourself to use a ranged weapon!</span>")
|
||||
return FALSE
|
||||
if(G.trigger_guard == TRIGGER_GUARD_NORMAL)
|
||||
if(HAS_TRAIT(src, TRAIT_CHUNKYFINGERS))
|
||||
to_chat(src, "<span class='warning'>Your meaty finger is much too large for the trigger guard!</span>")
|
||||
return FALSE
|
||||
if(HAS_TRAIT(src, TRAIT_NOGUNS))
|
||||
to_chat(src, "<span class='warning'>Your fingers don't fit in the trigger guard!</span>")
|
||||
return FALSE
|
||||
|
||||
/mob/living/carbon/human/proc/get_bank_account()
|
||||
RETURN_TYPE(/datum/bank_account)
|
||||
@@ -176,3 +176,9 @@
|
||||
|
||||
/mob/living/carbon/human/get_biological_state()
|
||||
return dna.species.get_biological_state()
|
||||
|
||||
///copies over clothing preferences like underwear to another human
|
||||
/mob/living/carbon/human/proc/copy_clothing_prefs(mob/living/carbon/human/destination)
|
||||
destination.underwear = underwear
|
||||
destination.undershirt = undershirt
|
||||
destination.socks = socks
|
||||
|
||||
@@ -90,13 +90,31 @@
|
||||
//End bloody footprints
|
||||
|
||||
S.step_action()
|
||||
if(movement_type & GROUND)
|
||||
dirt_buildup()
|
||||
|
||||
/mob/living/carbon/human/Process_Spacemove(movement_dir = 0) //Temporary laziness thing. Will change to handles by species reee.
|
||||
if(dna.species.space_move(src))
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/human/dirt_buildup(strength)
|
||||
/mob/living/carbon/human/proc/dirt_buildup(strength = 1)
|
||||
if(!shoes || !(shoes.body_parts_covered & FEET))
|
||||
return // barefoot advantage
|
||||
return ..()
|
||||
var/turf/open/T = loc
|
||||
if(!istype(T) || !T.dirt_buildup_allowed)
|
||||
return
|
||||
var/area/A = T.loc
|
||||
if(!A.dirt_buildup_allowed)
|
||||
return
|
||||
var/multiplier = CONFIG_GET(number/turf_dirty_multiplier)
|
||||
strength *= multiplier
|
||||
var/obj/effect/decal/cleanable/dirt/D = locate() in T
|
||||
if(D)
|
||||
D.dirty(strength)
|
||||
else
|
||||
T.dirtyness += strength
|
||||
if(T.dirtyness >= (isnull(T.dirt_spawn_threshold)? CONFIG_GET(number/turf_dirt_threshold) : T.dirt_spawn_threshold))
|
||||
D = new /obj/effect/decal/cleanable/dirt(T)
|
||||
D.dirty(T.dirt_spawn_threshold - T.dirtyness)
|
||||
T.dirtyness = 0 // reset.
|
||||
|
||||
@@ -37,16 +37,17 @@
|
||||
to_chat(owner, "There's something stuck to your hand, stopping you from transforming!")
|
||||
return
|
||||
if(IsAvailable())
|
||||
transforming = TRUE
|
||||
UpdateButtonIcon()
|
||||
var/mutcolor = owner.get_ability_property(INNATE_ABILITY_SLIME_BLOBFORM, PROPERTY_BLOBFORM_COLOR) || ("#" + H.dna.features["mcolor"])
|
||||
if(!is_puddle)
|
||||
if(CHECK_MOBILITY(H, MOBILITY_USE)) //if we can use items, we can turn into a puddle
|
||||
transforming = TRUE
|
||||
is_puddle = TRUE //so we know which transformation to use when its used
|
||||
ADD_TRAIT(H, TRAIT_HUMAN_NO_RENDER, SLIMEPUDDLE_TRAIT)
|
||||
owner.cut_overlays() //we dont show our normal sprite, we show a puddle sprite
|
||||
var/obj/effect/puddle_effect = new puddle_into_effect(get_turf(owner), owner.dir)
|
||||
puddle_effect.color = mutcolor
|
||||
puddle_effect.transform = H.transform //copy mob size for consistent meltdown appearance
|
||||
H.Stun(in_transformation_duration, ignore_canstun = TRUE) //cant move while transforming
|
||||
|
||||
//series of traits that make up the puddle behaviour
|
||||
@@ -55,7 +56,6 @@
|
||||
ADD_TRAIT(H, TRAIT_MOBILITY_NOPICKUP, SLIMEPUDDLE_TRAIT)
|
||||
ADD_TRAIT(H, TRAIT_MOBILITY_NOUSE, SLIMEPUDDLE_TRAIT)
|
||||
ADD_TRAIT(H, TRAIT_SPRINT_LOCKED, SLIMEPUDDLE_TRAIT)
|
||||
ADD_TRAIT(H, TRAIT_COMBAT_MODE_LOCKED, SLIMEPUDDLE_TRAIT)
|
||||
ADD_TRAIT(H, TRAIT_MOBILITY_NOREST, SLIMEPUDDLE_TRAIT)
|
||||
ADD_TRAIT(H, TRAIT_ARMOR_BROKEN, SLIMEPUDDLE_TRAIT)
|
||||
H.update_disabled_bodyparts(silent = TRUE) //silently update arms to be paralysed
|
||||
@@ -75,6 +75,7 @@
|
||||
puddle_overlay.color = mutcolor
|
||||
tracked_overlay = puddle_overlay
|
||||
owner.add_overlay(puddle_overlay)
|
||||
owner.update_antag_overlays()
|
||||
|
||||
transforming = FALSE
|
||||
UpdateButtonIcon()
|
||||
@@ -89,6 +90,7 @@
|
||||
H.cut_overlay(tracked_overlay)
|
||||
var/obj/effect/puddle_effect = new puddle_from_effect(get_turf(owner), owner.dir)
|
||||
puddle_effect.color = tracked_overlay.color
|
||||
puddle_effect.transform = H.transform //copy mob size for consistent transform size
|
||||
H.Stun(out_transformation_duration, ignore_canstun = TRUE)
|
||||
sleep(out_transformation_duration)
|
||||
REMOVE_TRAIT(H, TRAIT_PARALYSIS_L_ARM, SLIMEPUDDLE_TRAIT)
|
||||
@@ -96,7 +98,6 @@
|
||||
REMOVE_TRAIT(H, TRAIT_MOBILITY_NOPICKUP, SLIMEPUDDLE_TRAIT)
|
||||
REMOVE_TRAIT(H, TRAIT_MOBILITY_NOUSE, SLIMEPUDDLE_TRAIT)
|
||||
REMOVE_TRAIT(H, TRAIT_SPRINT_LOCKED, SLIMEPUDDLE_TRAIT)
|
||||
REMOVE_TRAIT(H, TRAIT_COMBAT_MODE_LOCKED, SLIMEPUDDLE_TRAIT)
|
||||
REMOVE_TRAIT(H, TRAIT_MOBILITY_NOREST, SLIMEPUDDLE_TRAIT)
|
||||
REMOVE_TRAIT(H, TRAIT_ARMOR_BROKEN, SLIMEPUDDLE_TRAIT)
|
||||
REMOVE_TRAIT(H, TRAIT_HUMAN_NO_RENDER, SLIMEPUDDLE_TRAIT)
|
||||
@@ -106,5 +107,6 @@
|
||||
is_puddle = FALSE
|
||||
if(squeak)
|
||||
squeak.RemoveComponent()
|
||||
H.regenerate_icons()
|
||||
transforming = FALSE
|
||||
UpdateButtonIcon()
|
||||
|
||||
@@ -273,7 +273,7 @@
|
||||
if(getToxLoss() >= 45 && nutrition > 20 && !HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM))
|
||||
lastpuke += prob(50)
|
||||
if(lastpuke >= 50) // about 25 second delay I guess
|
||||
vomit(20, toxic = TRUE)
|
||||
vomit(20)
|
||||
lastpuke = 0
|
||||
|
||||
|
||||
|
||||
@@ -2,3 +2,33 @@
|
||||
..()
|
||||
if(dna?.species?.has_field_of_vision && CONFIG_GET(flag/use_field_of_vision))
|
||||
LoadComponent(/datum/component/field_of_vision, field_of_vision_type)
|
||||
|
||||
if(!LAZYLEN(afk_thefts))
|
||||
return
|
||||
|
||||
var/list/print_msg = list()
|
||||
print_msg += "<span class='info'>*---------*</span>"
|
||||
print_msg += "<span class='userdanger'>As you snap back to consciousness, you recall people messing with your stuff...</span>"
|
||||
|
||||
afk_thefts = reverseRange(afk_thefts)
|
||||
|
||||
for(var/list/iter_theft as anything in afk_thefts)
|
||||
if(!islist(iter_theft) || LAZYLEN(iter_theft) != AFK_THEFT_TIME)
|
||||
stack_trace("[src] ([ckey]) returned to their body and had a null/malformed afk_theft entry. Contents: [json_encode(iter_theft)]")
|
||||
continue
|
||||
|
||||
var/thief_name = iter_theft[AFK_THEFT_NAME]
|
||||
var/theft_message = iter_theft[AFK_THEFT_MESSAGE]
|
||||
var/time_since = world.time - iter_theft[AFK_THEFT_TIME]
|
||||
|
||||
if(time_since > AFK_THEFT_FORGET_DETAILS_TIME)
|
||||
print_msg += "\t<span class='danger'><b>Someone [theft_message], but it was at least [DisplayTimeText(AFK_THEFT_FORGET_DETAILS_TIME)] ago.</b></span>"
|
||||
else
|
||||
print_msg += "\t<span class='danger'><b>[thief_name] [theft_message] roughly [DisplayTimeText(time_since, 10)] ago.</b></span>"
|
||||
|
||||
if(LAZYLEN(afk_thefts) >= AFK_THEFT_MAX_MESSAGES)
|
||||
print_msg += "<span class='warning'>There may have been more, but that's all you can remember...</span>"
|
||||
print_msg += "<span class='info'>*---------*</span>"
|
||||
|
||||
to_chat(src, print_msg.Join("\n"))
|
||||
LAZYNULL(afk_thefts)
|
||||
|
||||
@@ -149,6 +149,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
///
|
||||
var/sound/attack_sound = 'sound/weapons/punch1.ogg'
|
||||
var/sound/miss_sound = 'sound/weapons/punchmiss.ogg'
|
||||
var/attack_sound_override = null
|
||||
|
||||
var/list/mob/living/ignored_by = list() // list of mobs that will ignore this species
|
||||
//Breathing!
|
||||
@@ -569,12 +570,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
/datum/species/proc/remove_blacklisted_quirks(mob/living/carbon/C)
|
||||
var/mob/living/L = C.mind?.current
|
||||
if(istype(L))
|
||||
var/list/my_quirks = L.client?.prefs.all_quirks.Copy()
|
||||
SSquirks.filter_quirks(my_quirks, blacklisted_quirks)
|
||||
for(var/q in L.roundstart_quirks)
|
||||
var/datum/quirk/Q = q
|
||||
if(!(SSquirks.quirk_name_by_path(Q.type) in my_quirks))
|
||||
L.remove_quirk(Q.type)
|
||||
if(Q.type in blacklisted_quirks)
|
||||
qdel(Q)
|
||||
removed_quirks += Q.type
|
||||
|
||||
// restore any quirks that we removed
|
||||
@@ -1590,12 +1589,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
var/punchedbrute = target.getBruteLoss()
|
||||
|
||||
//CITADEL CHANGES - makes resting and disabled combat mode reduce punch damage, makes being out of combat mode result in you taking more damage
|
||||
if(!SEND_SIGNAL(target, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
||||
damage *= 1.2
|
||||
if(!CHECK_MOBILITY(user, MOBILITY_STAND))
|
||||
damage *= 0.65
|
||||
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
||||
damage *= 0.8
|
||||
//END OF CITADEL CHANGES
|
||||
|
||||
var/obj/item/bodypart/affecting = target.get_bodypart(ran_zone(user.zone_selected))
|
||||
@@ -1615,13 +1610,12 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
|
||||
|
||||
var/armor_block = target.run_armor_check(affecting, "melee")
|
||||
playsound(target.loc, user.dna.species.attack_sound, 25, 1, -1)
|
||||
playsound(target.loc, user.dna.species.attack_sound_override || attack_sound, 25, 1, -1)
|
||||
target.visible_message("<span class='danger'>[user] [atk_verb]ed [target]!</span>", \
|
||||
"<span class='userdanger'>[user] [atk_verb]ed you!</span>", null, COMBAT_MESSAGE_RANGE, null, \
|
||||
user, "<span class='danger'>You [atk_verb]ed [target]!</span>")
|
||||
|
||||
target.lastattacker = user.real_name
|
||||
target.lastattackerckey = user.ckey
|
||||
target.set_last_attacker(user)
|
||||
user.dna.species.spec_unarmedattacked(user, target)
|
||||
|
||||
if(user.limb_destroyer)
|
||||
@@ -1720,12 +1714,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
else
|
||||
user.do_attack_animation(target, ATTACK_EFFECT_DISARM)
|
||||
|
||||
if(HAS_TRAIT(user, TRAIT_PUGILIST))//CITADEL CHANGE - makes disarmspam cause staminaloss, pugilists can do it almost effortlessly
|
||||
if(!user.UseStaminaBuffer(1, warn = TRUE))
|
||||
return
|
||||
else
|
||||
if(!user.UseStaminaBuffer(1, warn = TRUE))
|
||||
return
|
||||
if(!user.UseStaminaBuffer(1, warn = TRUE))
|
||||
return
|
||||
|
||||
if(attacker_style && attacker_style.disarm_act(user,target))
|
||||
return TRUE
|
||||
@@ -1743,12 +1733,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
log_combat(user, target, "disarmed out of grab from")
|
||||
return
|
||||
var/randn = rand(1, 100)
|
||||
if(SEND_SIGNAL(target, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) // CITADEL CHANGE
|
||||
randn += -10 //CITADEL CHANGE - being out of combat mode makes it easier for you to get disarmed
|
||||
if(!CHECK_MOBILITY(user, MOBILITY_STAND)) //CITADEL CHANGE
|
||||
randn += 100 //CITADEL CHANGE - No kosher disarming if you're resting
|
||||
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) //CITADEL CHANGE
|
||||
randn += 25 //CITADEL CHANGE - Makes it harder to disarm outside of combat mode
|
||||
if(user.pulling == target)
|
||||
randn -= 20 //If you have the time to get someone in a grab, you should have a greater chance at snatching the thing in their hand. Will be made completely obsolete by the grab rework but i've got a poor track record for releasing big projects on time so w/e i guess
|
||||
if(HAS_TRAIT(user, TRAIT_PUGILIST))
|
||||
@@ -1953,9 +1939,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
if(IS_STAMCRIT(user))
|
||||
to_chat(user, "<span class='warning'>You're too exhausted for that.</span>")
|
||||
return
|
||||
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
||||
to_chat(user, "<span class='warning'>You need combat mode to be active to that!</span>")
|
||||
return
|
||||
if(user.IsKnockdown() || user.IsParalyzed() || user.IsStun())
|
||||
to_chat(user, "<span class='warning'>You can't seem to force yourself up right now!</span>")
|
||||
return
|
||||
@@ -1995,6 +1978,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
|
||||
if(CHECK_MOBILITY(target, MOBILITY_STAND))
|
||||
target.adjustStaminaLoss(5)
|
||||
else
|
||||
target.adjustStaminaLoss(target.getStaminaLoss() > 75? 5 : 75)
|
||||
|
||||
if(target.is_shove_knockdown_blocked())
|
||||
return
|
||||
@@ -2004,6 +1989,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
var/turf/target_shove_turf = get_step(target.loc, shove_dir)
|
||||
var/mob/living/carbon/human/target_collateral_human
|
||||
var/shove_blocked = FALSE //Used to check if a shove is blocked so that if it is knockdown logic can be applied
|
||||
var/targetatrest = !CHECK_MOBILITY(target, MOBILITY_STAND)
|
||||
|
||||
//Thank you based whoneedsspace
|
||||
target_collateral_human = locate(/mob/living/carbon/human) in target_shove_turf.contents
|
||||
@@ -2018,7 +2004,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
var/append_message = ""
|
||||
if(shove_blocked && !target.buckled)
|
||||
var/directional_blocked = !target.Adjacent(target_shove_turf)
|
||||
var/targetatrest = !CHECK_MOBILITY(target, MOBILITY_STAND)
|
||||
if((directional_blocked || !(target_collateral_human || target_shove_turf.shove_act(target, user))) && !targetatrest)
|
||||
target.DefaultCombatKnockdown(SHOVE_KNOCKDOWN_SOLID)
|
||||
target.visible_message("<span class='danger'>[user.name] shoves [target.name], knocking them down!</span>",
|
||||
@@ -2037,10 +2022,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
target.visible_message("<span class='danger'>[user.name] shoves [target.name]!</span>",
|
||||
"<span class='danger'>[user.name] shoves you!</span>", null, COMBAT_MESSAGE_RANGE, null,
|
||||
user, "<span class='danger'>You shove [target.name]!</span>")
|
||||
target.Stagger(SHOVE_STAGGER_DURATION)
|
||||
var/obj/item/target_held_item = target.get_active_held_item()
|
||||
if(!is_type_in_typecache(target_held_item, GLOB.shove_disarming_types))
|
||||
target_held_item = null
|
||||
if(!target.has_status_effect(STATUS_EFFECT_OFF_BALANCE))
|
||||
if(target_held_item)
|
||||
if(!HAS_TRAIT(target_held_item, TRAIT_NODROP))
|
||||
@@ -2049,16 +2031,20 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
append_message += ", loosening their grip on [target_held_item]"
|
||||
else
|
||||
append_message += ", but couldn't loose their grip on [target_held_item]"
|
||||
else if(target_held_item)
|
||||
if(target.dropItemToGround(target_held_item))
|
||||
target.visible_message("<span class='danger'>[target.name] drops \the [target_held_item]!!</span>",
|
||||
"<span class='danger'>You drop \the [target_held_item]!!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
append_message += ", causing them to drop [target_held_item]"
|
||||
else if(target.has_status_effect(STATUS_EFFECT_OFF_BALANCE))
|
||||
if(target_held_item)
|
||||
if(shove_blocked)
|
||||
if (target.buckled)
|
||||
return
|
||||
else if(target.dropItemToGround(target_held_item))
|
||||
target.visible_message("<span class='danger'>[target.name] drops \the [target_held_item]!!</span>",
|
||||
"<span class='danger'>You drop \the [target_held_item]!!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
append_message += ", causing them to drop [target_held_item]"
|
||||
target.ShoveOffBalance(SHOVE_OFFBALANCE_DURATION)
|
||||
log_combat(user, target, "shoved", append_message)
|
||||
|
||||
/datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE)
|
||||
SEND_SIGNAL(H, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness) // make sure putting wound_bonus here doesn't screw up other signals or uses for this signal
|
||||
SEND_SIGNAL(H, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness) // make sure putting wound_bonus here doesn't screw up other signals or uses for this signal
|
||||
var/hit_percent = (100-(blocked+armor))/100
|
||||
hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100
|
||||
if(!forced && hit_percent <= 0)
|
||||
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
/datum/species/mammal
|
||||
name = "Anthromorph"
|
||||
name = "Anthropomorph"
|
||||
id = SPECIES_MAMMAL
|
||||
default_color = "4B4B4B"
|
||||
species_traits = list(MUTCOLORS,EYECOLOR,LIPS,HAIR,HORNCOLOR,WINGCOLOR,HAS_FLESH,HAS_BONE)
|
||||
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BEAST
|
||||
mutant_bodyparts = list("mcolor" = "FFFFFF","mcolor2" = "FFFFFF","mcolor3" = "FFFFFF", "mam_snouts" = "Husky", "mam_tail" = "Husky", "mam_ears" = "Husky", "deco_wings" = "None",
|
||||
"mam_body_markings" = "Husky", "taur" = "None", "horns" = "None", "legs" = "Plantigrade", "meat_type" = "Mammalian")
|
||||
"mam_body_markings" = list(), "taur" = "None", "horns" = "None", "legs" = "Plantigrade", "meat_type" = "Mammalian")
|
||||
attack_verb = "claw"
|
||||
attack_sound = 'sound/weapons/slash.ogg'
|
||||
miss_sound = 'sound/weapons/slashmiss.ogg'
|
||||
@@ -20,7 +20,7 @@
|
||||
allowed_limb_ids = list("mammal","aquatic","avian")
|
||||
|
||||
/datum/species/mammal/synthetic
|
||||
name = "Synthetic Anthromorph"
|
||||
name = "Synthetic Anthropomorph"
|
||||
id = SPECIES_MAMMAL_SYNTHETIC
|
||||
|
||||
species_traits = list(MUTCOLORS,NOTRANSSTING,EYECOLOR,LIPS,HAIR,ROBOTIC_LIMBS,HAS_FLESH,HAS_BONE,WINGCOLOR,HORNCOLOR)
|
||||
@@ -1,5 +1,5 @@
|
||||
/datum/species/insect
|
||||
name = "Anthromorphic Insect"
|
||||
name = "Anthropomorphic Insect"
|
||||
id = SPECIES_INSECT
|
||||
say_mod = "chitters"
|
||||
default_color = "00FF00"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/datum/species/fly
|
||||
name = "Anthromorphic Fly"
|
||||
name = "Anthropomorphic Fly"
|
||||
id = SPECIES_FLY
|
||||
say_mod = "buzzes"
|
||||
species_traits = list(NOEYES,HAS_FLESH,HAS_BONE)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
default_color = "FFFFFF"
|
||||
|
||||
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,MUTCOLORS_PARTSONLY,WINGCOLOR,HAS_FLESH,HAS_BONE)
|
||||
mutant_bodyparts = list("mcolor" = "FFFFFF", "mcolor2" = "FFFFFF","mcolor3" = "FFFFFF","tail_human" = "None", "ears" = "None", "taur" = "None", "deco_wings" = "None")
|
||||
mutant_bodyparts = list("mcolor" = "FFFFFF", "mcolor2" = "FFFFFF","mcolor3" = "FFFFFF","tail_human" = "None", "ears" = "None", "taur" = "None", "deco_wings" = "None", "legs" = "Plantigrade")
|
||||
use_skintones = USE_SKINTONES_GRAYSCALE_CUSTOM
|
||||
skinned_type = /obj/item/stack/sheet/animalhide/human
|
||||
disliked_food = GROSS | RAW
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,WINGCOLOR,HAS_FLESH)
|
||||
mutantlungs = /obj/item/organ/lungs/slime
|
||||
mutant_heart = /obj/item/organ/heart/slime
|
||||
mutantstomach = /obj/item/organ/stomach/slime
|
||||
mutantliver = /obj/item/organ/liver/slime
|
||||
mutant_brain = /obj/item/organ/brain/slime
|
||||
mutant_bodyparts = list("mcolor" = "FFFFFF", "mam_tail" = "None", "mam_ears" = "None", "mam_snouts" = "None", "taur" = "None", "deco_wings" = "None", "legs" = "Plantigrade")
|
||||
inherent_traits = list(TRAIT_TOXINLOVER)
|
||||
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime
|
||||
@@ -22,18 +25,12 @@
|
||||
heatmod = 0.5 // = 1/4x heat damage
|
||||
burnmod = 0.5 // = 1/2x generic burn damage
|
||||
species_language_holder = /datum/language_holder/jelly
|
||||
mutant_brain = /obj/item/organ/brain/jelly
|
||||
|
||||
tail_type = "mam_tail"
|
||||
wagging_type = "mam_waggingtail"
|
||||
species_category = SPECIES_CATEGORY_JELLY
|
||||
ass_image = 'icons/ass/assslime.png'
|
||||
|
||||
/obj/item/organ/brain/jelly
|
||||
name = "slime nucleus"
|
||||
desc = "A slimey membranous mass from a slime person"
|
||||
icon_state = "brain-slime"
|
||||
|
||||
/datum/species/jelly/on_species_loss(mob/living/carbon/C)
|
||||
C.faction -= "slime"
|
||||
if(ishuman(C))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/datum/species/lizard
|
||||
// Reptilian humanoids with scaled skin and tails.
|
||||
name = "Anthromorphic Lizard"
|
||||
name = "Anthropomorphic Lizard"
|
||||
id = SPECIES_LIZARD
|
||||
say_mod = "hisses"
|
||||
default_color = "00FF00"
|
||||
@@ -11,7 +11,7 @@
|
||||
coldmod = 1.5
|
||||
heatmod = 0.67
|
||||
mutant_bodyparts = list("mcolor" = "0F0", "mcolor2" = "0F0", "mcolor3" = "0F0", "tail_lizard" = "Smooth", "mam_snouts" = "Round",
|
||||
"horns" = "None", "frills" = "None", "spines" = "None", "body_markings" = "None",
|
||||
"horns" = "None", "frills" = "None", "spines" = "None", "mam_body_markings" = list(),
|
||||
"legs" = "Digitigrade", "taur" = "None", "deco_wings" = "None")
|
||||
attack_verb = "slash"
|
||||
attack_sound = 'sound/weapons/slash.ogg'
|
||||
@@ -62,9 +62,12 @@
|
||||
if((C.dna.features["spines"] != "None" ) && (C.dna.features["tail_lizard"] == "None")) //tbh, it's kinda ugly for them not to have a tail yet have floating spines
|
||||
C.dna.features["tail_lizard"] = "Smooth"
|
||||
C.update_body()
|
||||
if(C.dna.features["legs"] != "digitigrade")
|
||||
C.dna.features["legs"] = "digitigrade"
|
||||
if(C.dna.features["legs"] != "Digitigrade")
|
||||
C.dna.features["legs"] = "Digitigrade"
|
||||
for(var/obj/item/bodypart/leggie in C.bodyparts)
|
||||
if(leggie.body_zone == BODY_ZONE_L_LEG || leggie.body_zone == BODY_ZONE_R_LEG)
|
||||
leggie.update_limb(FALSE, C)
|
||||
if(C.dna.features["mam_snouts"] != "Sharp")
|
||||
C.dna.features["mam_snouts"] = "Sharp"
|
||||
C.update_body()
|
||||
return ..()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/datum/species/mush //mush mush codecuck
|
||||
name = "Anthromorphic Mushroom"
|
||||
name = "Anthropomorphic Mushroom"
|
||||
id = SPECIES_MUSHROOM
|
||||
mutant_bodyparts = list("caps" = "Round")
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
if((!istype(H.w_uniform, /obj/item/clothing/under/plasmaman) || !istype(H.head, /obj/item/clothing/head/helmet/space/plasmaman)) && !atmos_sealed)
|
||||
if(environment)
|
||||
if(environment.total_moles())
|
||||
if(environment.get_moles(/datum/gas/oxygen) >= 1) //Same threshhold that extinguishes fire
|
||||
if(environment.get_moles(GAS_O2) >= 1) //Same threshhold that extinguishes fire
|
||||
H.adjust_fire_stacks(0.5)
|
||||
if(!H.on_fire && H.fire_stacks > 0)
|
||||
H.visible_message("<span class='danger'>[H]'s body reacts with the atmosphere and bursts into flames!</span>","<span class='userdanger'>Your body reacts with the atmosphere and bursts into flame!</span>")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/datum/species/pod
|
||||
// A mutation caused by a human being ressurected in a revival pod. These regain health in light, and begin to wither in darkness.
|
||||
name = "Anthromorphic Plant"
|
||||
name = "Anthropomorphic Plant"
|
||||
id = SPECIES_POD
|
||||
default_color = "59CE00"
|
||||
species_traits = list(MUTCOLORS,EYECOLOR,CAN_SCAR,HAS_FLESH,HAS_BONE)
|
||||
@@ -71,10 +71,10 @@
|
||||
H.emote("spin")
|
||||
|
||||
/datum/species/pod/pseudo_weak
|
||||
name = "Anthromorphic Plant"
|
||||
name = "Anthropomorphic Plant"
|
||||
id = SPECIES_POD_WEAK
|
||||
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,MUTCOLORS,CAN_SCAR,HAS_FLESH,HAS_BONE)
|
||||
mutant_bodyparts = list("mcolor" = "FFFFFF","mcolor2" = "FFFFFF","mcolor3" = "FFFFFF", "mam_snouts" = "Husky", "mam_tail" = "Husky", "mam_ears" = "Husky", "mam_body_markings" = "Husky", "taur" = "None", "legs" = "Normal Legs")
|
||||
mutant_bodyparts = list("mcolor" = "FFFFFF","mcolor2" = "FFFFFF","mcolor3" = "FFFFFF", "mam_snouts" = "Husky", "mam_tail" = "Husky", "mam_ears" = "Husky", "mam_body_markings" = list(), "taur" = "None", "legs" = "Normal Legs")
|
||||
limbs_id = SPECIES_POD
|
||||
light_nutrition_gain_factor = 3
|
||||
light_bruteheal = -0.2
|
||||
|
||||
@@ -181,6 +181,10 @@
|
||||
if(T.light_range && !isspaceturf(T)) //no fairy grass or light tile can escape the fury of the darkness.
|
||||
to_chat(user, "<span class='notice'>You scrape away [T] with your [name] and snuff out its lights.</span>")
|
||||
T.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
else if(is_cleanable(AM))
|
||||
var/obj/effect/E = AM
|
||||
if(E.light_range && E.light_power)
|
||||
disintegrate(E)
|
||||
else if(isliving(AM))
|
||||
var/mob/living/L = AM
|
||||
if(isethereal(AM))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
id = SPECIES_VAMPIRE
|
||||
default_color = "FFFFFF"
|
||||
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,DRINKSBLOOD,HAS_FLESH,HAS_BONE)
|
||||
inherent_traits = list(TRAIT_NOHUNGER,TRAIT_NOBREATH)
|
||||
inherent_traits = list(TRAIT_NOHUNGER,TRAIT_NOBREATH,TRAIT_NOMARROW)
|
||||
inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID
|
||||
mutant_bodyparts = list("mcolor" = "FFFFFF", "tail_human" = "None", "ears" = "None", "deco_wings" = "None")
|
||||
exotic_bloodtype = "U"
|
||||
@@ -53,12 +53,13 @@
|
||||
C.adjustOxyLoss(-4)
|
||||
C.adjustCloneLoss(-4)
|
||||
return
|
||||
C.blood_volume -= 0.75 //Will take roughly 19.5 minutes to die from standard blood volume, roughly 83 minutes to die from max blood volume.
|
||||
if(C.blood_volume <= (BLOOD_VOLUME_SURVIVE*C.blood_ratio))
|
||||
to_chat(C, "<span class='danger'>You ran out of blood!</span>")
|
||||
C.dust()
|
||||
if(C.blood_volume > 0.5)
|
||||
C.blood_volume -= 0.5 //Will take roughly 19.5 minutes to die from standard blood volume, roughly 83 minutes to die from max blood volume.
|
||||
else
|
||||
C.dust(FALSE, TRUE)
|
||||
|
||||
var/area/A = get_area(C)
|
||||
if(istype(A, /area/chapel) && C.mind?.assigned_role != "Chaplain")
|
||||
if(istype(A, /area/service/chapel) && C.mind?.assigned_role != "Chaplain")
|
||||
to_chat(C, "<span class='danger'>You don't belong here!</span>")
|
||||
C.adjustFireLoss(5)
|
||||
C.adjust_fire_stacks(6)
|
||||
@@ -125,7 +126,7 @@
|
||||
. = ..()
|
||||
var/obj/item/organ/heart/vampire/darkheart = getorgan(/obj/item/organ/heart/vampire)
|
||||
if(darkheart)
|
||||
. += "<span class='notice'>Current blood level: [blood_volume]/[BLOOD_VOLUME_MAXIMUM].</span>"
|
||||
. += "Current blood level: [blood_volume]/[BLOOD_VOLUME_MAXIMUM]."
|
||||
|
||||
|
||||
/obj/item/organ/heart/vampire
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
say_mod = "hisses"
|
||||
default_color = "00FF00"
|
||||
species_traits = list(MUTCOLORS,EYECOLOR,LIPS,CAN_SCAR,HAS_FLESH,HAS_BONE)
|
||||
mutant_bodyparts = list("xenotail"="Xenomorph Tail","xenohead"="Standard","xenodorsal"="Standard", "mam_body_markings" = "Xeno","mcolor" = "0F0","mcolor2" = "0F0","mcolor3" = "0F0","taur" = "None", "legs" = "Digitigrade","deco_wings"= "None")
|
||||
mutant_bodyparts = list("xenotail"="Xenomorph Tail","xenohead"="Standard","xenodorsal"="Standard", "mam_body_markings" = list(),"mcolor" = "0F0","mcolor2" = "0F0","mcolor3" = "0F0","taur" = "None", "legs" = "Digitigrade","deco_wings"= "None")
|
||||
attack_verb = "slash"
|
||||
attack_sound = 'sound/weapons/slash.ogg'
|
||||
miss_sound = 'sound/weapons/slashmiss.ogg'
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
amount = dna.species.spec_stun(src,amount)
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/human/DefaultCombatKnockdown(amount, updating = TRUE, ignore_canknockdown = FALSE, override_hardstun, override_stamdmg)
|
||||
/mob/living/carbon/human/DefaultCombatKnockdown(amount, updating = TRUE, ignore_canknockdown = FALSE, override_hardstun, override_stamdmg, knocktofloor)
|
||||
amount = dna.species.spec_stun(src,amount)
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -100,10 +100,29 @@ There are several things that need to be remembered:
|
||||
update_mutations_overlay()
|
||||
//damage overlays
|
||||
update_damage_overlays()
|
||||
//antagonism
|
||||
update_antag_overlays()
|
||||
|
||||
/* --------------------------------------- */
|
||||
//vvvvvv UPDATE_INV PROCS vvvvvv
|
||||
|
||||
|
||||
/mob/living/carbon/human/update_antag_overlays()
|
||||
remove_overlay(ANTAG_LAYER)
|
||||
var/datum/antagonist/cult/D = src?.mind?.has_antag_datum(/datum/antagonist/cult) //check for cultism
|
||||
if(D && D.cult_team?.cult_ascendent == TRUE)
|
||||
var/istate = pick("halo1","halo2","halo3","halo4","halo5","halo6")
|
||||
var/mutable_appearance/new_cult_overlay = mutable_appearance('icons/effects/32x64.dmi', istate, -ANTAG_LAYER)
|
||||
overlays_standing[ANTAG_LAYER] = new_cult_overlay
|
||||
var/datum/antagonist/clockcult/C = src?.mind?.has_antag_datum(/datum/antagonist/clockcult) //check for clockcultism - surely one can't be both cult and clockie, right?
|
||||
if(C)
|
||||
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
|
||||
if(G && G.active && ishuman(src))
|
||||
var/mutable_appearance/new_cult_overlay = mutable_appearance('icons/effects/genetics.dmi', "servitude", -ANTAG_LAYER)
|
||||
overlays_standing[ANTAG_LAYER] = new_cult_overlay
|
||||
apply_overlay(ANTAG_LAYER)
|
||||
|
||||
|
||||
/mob/living/carbon/human/update_inv_w_uniform()
|
||||
if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
|
||||
remove_overlay(UNIFORM_LAYER)
|
||||
@@ -710,11 +729,8 @@ use_mob_overlay_icon: if FALSE, it will always use the default_icon_file even if
|
||||
. += "-[BP.digitigrade_type]"
|
||||
if(BP.dmg_overlay_type)
|
||||
. += "-[BP.dmg_overlay_type]"
|
||||
if(BP.body_markings)
|
||||
. += "-[BP.body_markings]"
|
||||
if(length(BP.markings_color) && length(BP.markings_color[1]))
|
||||
for(var/color in BP.markings_color[1])
|
||||
. += "-[color]"
|
||||
if(BP.body_markings_list)
|
||||
. += "-[safe_json_encode(BP.body_markings_list)]"
|
||||
if(BP.icon)
|
||||
. += "-[BP.icon]"
|
||||
else
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/mob/living/carbon/BiologicalLife(seconds, times_fired)
|
||||
//Reagent processing needs to come before breathing, to prevent edge cases.
|
||||
handle_organs()
|
||||
handle_organs(seconds, times_fired)
|
||||
. = ..() // if . is false, we are dead.
|
||||
if(stat == DEAD)
|
||||
stop_sound_channel(CHANNEL_HEARTBEAT)
|
||||
@@ -23,7 +23,7 @@
|
||||
handle_brain_damage()
|
||||
|
||||
if(stat != DEAD)
|
||||
handle_liver()
|
||||
handle_liver(seconds, times_fired)
|
||||
|
||||
if(stat != DEAD)
|
||||
handle_corruption()
|
||||
@@ -115,16 +115,18 @@
|
||||
breath = loc_as_obj.handle_internal_lifeform(src, BREATH_VOLUME)
|
||||
|
||||
else if(isturf(loc)) //Breathe from loc as turf
|
||||
var/breath_moles = 0
|
||||
var/breath_ratio = 0
|
||||
if(environment)
|
||||
breath_moles = environment.total_moles()*BREATH_PERCENTAGE
|
||||
breath_ratio = BREATH_VOLUME/environment.return_volume()
|
||||
|
||||
breath = loc.remove_air(breath_moles)
|
||||
breath = loc.remove_air_ratio(breath_ratio)
|
||||
else //Breathe from loc as obj again
|
||||
if(istype(loc, /obj/))
|
||||
var/obj/loc_as_obj = loc
|
||||
loc_as_obj.handle_internal_lifeform(src,0)
|
||||
|
||||
if(breath)
|
||||
breath.set_volume(BREATH_VOLUME)
|
||||
check_breath(breath)
|
||||
|
||||
if(breath)
|
||||
@@ -163,11 +165,11 @@
|
||||
var/SA_para_min = 1
|
||||
var/SA_sleep_min = 5
|
||||
var/oxygen_used = 0
|
||||
var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.return_temperature())/BREATH_VOLUME
|
||||
|
||||
var/O2_partialpressure = (breath.get_moles(/datum/gas/oxygen)/breath.total_moles())*breath_pressure
|
||||
var/Toxins_partialpressure = (breath.get_moles(/datum/gas/plasma)/breath.total_moles())*breath_pressure
|
||||
var/CO2_partialpressure = (breath.get_moles(/datum/gas/carbon_dioxide)/breath.total_moles())*breath_pressure
|
||||
var/moles = breath.total_moles()
|
||||
var/breath_pressure = (moles*R_IDEAL_GAS_EQUATION*breath.return_temperature())/BREATH_VOLUME
|
||||
var/O2_partialpressure = ((breath.get_moles(GAS_O2)/moles)*breath_pressure) + (((breath.get_moles(GAS_PLUOXIUM)*8)/moles)*breath_pressure)
|
||||
var/Toxins_partialpressure = (breath.get_moles(GAS_PLASMA)/moles)*breath_pressure
|
||||
var/CO2_partialpressure = (breath.get_moles(GAS_CO2)/moles)*breath_pressure
|
||||
|
||||
|
||||
//OXYGEN
|
||||
@@ -191,7 +193,7 @@
|
||||
var/ratio = 1 - O2_partialpressure/safe_oxy_min
|
||||
adjustOxyLoss(min(5*ratio, 3))
|
||||
failed_last_breath = 1
|
||||
oxygen_used = breath.get_moles(/datum/gas/oxygen)*ratio
|
||||
oxygen_used = breath.get_moles(GAS_O2)*ratio
|
||||
else
|
||||
adjustOxyLoss(3)
|
||||
failed_last_breath = 1
|
||||
@@ -203,12 +205,12 @@
|
||||
o2overloadtime = 0 //reset our counter for this too
|
||||
if(health >= crit_threshold)
|
||||
adjustOxyLoss(-5)
|
||||
oxygen_used = breath.get_moles(/datum/gas/oxygen)
|
||||
oxygen_used = breath.get_moles(GAS_O2)
|
||||
clear_alert("not_enough_oxy")
|
||||
SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "suffocation")
|
||||
|
||||
breath.adjust_moles(/datum/gas/oxygen, -oxygen_used)
|
||||
breath.adjust_moles(/datum/gas/carbon_dioxide, oxygen_used)
|
||||
breath.adjust_moles(GAS_O2, -oxygen_used)
|
||||
breath.adjust_moles(GAS_CO2, oxygen_used)
|
||||
|
||||
//CARBON DIOXIDE
|
||||
if(CO2_partialpressure > safe_co2_max)
|
||||
@@ -227,15 +229,15 @@
|
||||
|
||||
//TOXINS/PLASMA
|
||||
if(Toxins_partialpressure > safe_tox_max)
|
||||
var/ratio = (breath.get_moles(/datum/gas/plasma)/safe_tox_max) * 10
|
||||
var/ratio = (breath.get_moles(GAS_PLASMA)/safe_tox_max) * 10
|
||||
adjustToxLoss(clamp(ratio, MIN_TOXIC_GAS_DAMAGE, MAX_TOXIC_GAS_DAMAGE))
|
||||
throw_alert("too_much_tox", /obj/screen/alert/too_much_tox)
|
||||
else
|
||||
clear_alert("too_much_tox")
|
||||
|
||||
//NITROUS OXIDE
|
||||
if(breath.get_moles(/datum/gas/nitrous_oxide))
|
||||
var/SA_partialpressure = (breath.get_moles(/datum/gas/nitrous_oxide)/breath.total_moles())*breath_pressure
|
||||
if(breath.get_moles(GAS_NITROUS))
|
||||
var/SA_partialpressure = (breath.get_moles(GAS_NITROUS)/breath.total_moles())*breath_pressure
|
||||
if(SA_partialpressure > SA_para_min)
|
||||
Unconscious(60)
|
||||
if(SA_partialpressure > SA_sleep_min)
|
||||
@@ -248,26 +250,26 @@
|
||||
SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "chemical_euphoria")
|
||||
|
||||
//BZ (Facepunch port of their Agent B)
|
||||
if(breath.get_moles(/datum/gas/bz))
|
||||
var/bz_partialpressure = (breath.get_moles(/datum/gas/bz)/breath.total_moles())*breath_pressure
|
||||
if(breath.get_moles(GAS_BZ))
|
||||
var/bz_partialpressure = (breath.get_moles(GAS_BZ)/breath.total_moles())*breath_pressure
|
||||
if(bz_partialpressure > 1)
|
||||
hallucination += 10
|
||||
else if(bz_partialpressure > 0.01)
|
||||
hallucination += 5
|
||||
|
||||
//TRITIUM
|
||||
if(breath.get_moles(/datum/gas/tritium))
|
||||
var/tritium_partialpressure = (breath.get_moles(/datum/gas/tritium)/breath.total_moles())*breath_pressure
|
||||
if(breath.get_moles(GAS_TRITIUM))
|
||||
var/tritium_partialpressure = (breath.get_moles(GAS_TRITIUM)/breath.total_moles())*breath_pressure
|
||||
radiation += tritium_partialpressure/10
|
||||
|
||||
//NITRYL
|
||||
if(breath.get_moles(/datum/gas/nitryl))
|
||||
var/nitryl_partialpressure = (breath.get_moles(/datum/gas/nitryl)/breath.total_moles())*breath_pressure
|
||||
if(breath.get_moles(GAS_NITRYL))
|
||||
var/nitryl_partialpressure = (breath.get_moles(GAS_NITRYL)/breath.total_moles())*breath_pressure
|
||||
adjustFireLoss(nitryl_partialpressure/4)
|
||||
|
||||
//MIASMA
|
||||
if(breath.get_moles(/datum/gas/miasma))
|
||||
var/miasma_partialpressure = (breath.get_moles(/datum/gas/miasma)/breath.total_moles())*breath_pressure
|
||||
if(breath.get_moles(GAS_MIASMA))
|
||||
var/miasma_partialpressure = (breath.get_moles(GAS_MIASMA)/breath.total_moles())*breath_pressure
|
||||
if(miasma_partialpressure > MINIMUM_MOLES_DELTA_TO_MOVE)
|
||||
|
||||
if(prob(0.05 * miasma_partialpressure))
|
||||
@@ -365,7 +367,7 @@
|
||||
|
||||
var/datum/gas_mixture/stank = new
|
||||
|
||||
stank.set_moles(/datum/gas/miasma,0.1)
|
||||
stank.set_moles(GAS_MIASMA,0.1)
|
||||
|
||||
stank.set_temperature(BODYTEMP_NORMAL)
|
||||
|
||||
@@ -376,25 +378,25 @@
|
||||
/mob/living/carbon/proc/handle_blood()
|
||||
return
|
||||
|
||||
/mob/living/carbon/proc/handle_bodyparts()
|
||||
/mob/living/carbon/proc/handle_bodyparts(seconds, times_fired)
|
||||
for(var/I in bodyparts)
|
||||
var/obj/item/bodypart/BP = I
|
||||
if(BP.needs_processing)
|
||||
. |= BP.on_life()
|
||||
. |= BP.on_life(seconds, times_fired)
|
||||
|
||||
/mob/living/carbon/proc/handle_organs()
|
||||
/mob/living/carbon/proc/handle_organs(seconds, times_fired)
|
||||
if(stat != DEAD)
|
||||
for(var/V in internal_organs)
|
||||
var/obj/item/organ/O = V
|
||||
if(O)
|
||||
O.on_life()
|
||||
O.on_life(seconds, times_fired)
|
||||
else
|
||||
if(reagents.has_reagent(/datum/reagent/toxin/formaldehyde, 1) || reagents.has_reagent(/datum/reagent/preservahyde, 1)) // No organ decay if the body contains formaldehyde. Or preservahyde.
|
||||
return
|
||||
for(var/V in internal_organs)
|
||||
var/obj/item/organ/O = V
|
||||
if(O)
|
||||
O.on_death() //Needed so organs decay while inside the body.
|
||||
O.on_death(seconds, times_fired) //Needed so organs decay while inside the body.
|
||||
|
||||
/mob/living/carbon/handle_diseases()
|
||||
for(var/thing in diseases)
|
||||
@@ -510,9 +512,8 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
|
||||
//this updates all special effects: stun, sleeping, knockdown, druggy, stuttering, etc..
|
||||
/mob/living/carbon/handle_status_effects()
|
||||
..()
|
||||
var/combat_mode = SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)
|
||||
if(getStaminaLoss() && !HAS_TRAIT(src, TRAIT_NO_STAMINA_REGENERATION))
|
||||
adjustStaminaLoss((!CHECK_MOBILITY(src, MOBILITY_STAND) ? ((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) ? STAM_RECOVERY_STAM_CRIT : STAM_RECOVERY_RESTING) : STAM_RECOVERY_NORMAL) * (combat_mode? 0.25 : 1))
|
||||
adjustStaminaLoss((!CHECK_MOBILITY(src, MOBILITY_STAND) ? ((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) ? STAM_RECOVERY_STAM_CRIT : STAM_RECOVERY_RESTING) : STAM_RECOVERY_NORMAL))
|
||||
|
||||
if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && incomingstammult != 1)
|
||||
incomingstammult = max(0.01, incomingstammult)
|
||||
@@ -686,16 +687,16 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
|
||||
//LIVER//
|
||||
/////////
|
||||
|
||||
/mob/living/carbon/proc/handle_liver()
|
||||
/mob/living/carbon/proc/handle_liver(seconds, times_fired)
|
||||
var/obj/item/organ/liver/liver = getorganslot(ORGAN_SLOT_LIVER)
|
||||
if((!dna && !liver) || (NOLIVER in dna.species.species_traits))
|
||||
return
|
||||
if(!liver || liver.organ_flags & ORGAN_FAILING)
|
||||
liver_failure()
|
||||
liver_failure(seconds, times_fired)
|
||||
|
||||
/mob/living/carbon/proc/liver_failure()
|
||||
/mob/living/carbon/proc/liver_failure(seconds, times_fired)
|
||||
reagents.end_metabolization(src, keep_liverless = TRUE) //Stops trait-based effects on reagents, to prevent permanent buffs
|
||||
reagents.metabolize(src, can_overdose=FALSE, liverless = TRUE)
|
||||
reagents.metabolize(src, seconds, times_fired, can_overdose=FALSE, liverless = TRUE)
|
||||
if(HAS_TRAIT(src, TRAIT_STABLELIVER))
|
||||
return
|
||||
adjustToxLoss(4, TRUE, TRUE)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/**
|
||||
* Applies damage to this mob
|
||||
*
|
||||
* Sends [COMSIG_MOB_APPLY_DAMGE]
|
||||
* Sends [COMSIG_MOB_APPLY_DAMAGE]
|
||||
*
|
||||
* Arguuments:
|
||||
* * damage - amount of damage
|
||||
|
||||
@@ -55,6 +55,8 @@
|
||||
|
||||
|
||||
/mob/living/death(gibbed)
|
||||
SEND_SIGNAL(src, COMSIG_LIVING_PREDEATH, gibbed)
|
||||
|
||||
stat = DEAD
|
||||
unset_machine()
|
||||
timeofdeath = world.time
|
||||
@@ -87,7 +89,7 @@
|
||||
addtimer(CALLBACK(src, .proc/med_hud_set_status), (DEFIB_TIME_LIMIT * 10) + 1)
|
||||
stop_pulling()
|
||||
|
||||
var/signal = SEND_SIGNAL(src, COMSIG_MOB_DEATH, gibbed)
|
||||
var/signal = SEND_SIGNAL(src, COMSIG_MOB_DEATH, gibbed) | SEND_GLOBAL_SIGNAL(COMSIG_GLOB_MOB_DEATH, src, gibbed)
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
if(mind && mind.name && mind.active && !istype(T.loc, /area/ctf) && !(signal & COMPONENT_BLOCK_DEATH_BROADCAST))
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
ExtinguishMob()
|
||||
return
|
||||
var/datum/gas_mixture/G = loc.return_air() // Check if we're standing in an oxygenless environment
|
||||
if(!G.get_moles(/datum/gas/oxygen, 1))
|
||||
if(!G.get_moles(GAS_O2, 1))
|
||||
ExtinguishMob() //If there's no oxygen in the tile we're on, put out the fire
|
||||
return
|
||||
var/turf/location = get_turf(src)
|
||||
|
||||
@@ -542,8 +542,48 @@
|
||||
update_stat()
|
||||
med_hud_set_health()
|
||||
med_hud_set_status()
|
||||
update_health_hud()
|
||||
|
||||
//proc used to ressuscitate a mob
|
||||
/mob/living/update_health_hud()
|
||||
var/severity = 0
|
||||
var/healthpercent = (health/maxHealth) * 100
|
||||
if(hud_used?.healthdoll) //to really put you in the boots of a simplemob
|
||||
var/obj/screen/healthdoll/living/livingdoll = hud_used.healthdoll
|
||||
switch(healthpercent)
|
||||
if(100 to INFINITY)
|
||||
livingdoll.icon_state = "living0"
|
||||
if(80 to 100)
|
||||
livingdoll.icon_state = "living1"
|
||||
severity = 1
|
||||
if(60 to 80)
|
||||
livingdoll.icon_state = "living2"
|
||||
severity = 2
|
||||
if(40 to 60)
|
||||
livingdoll.icon_state = "living3"
|
||||
severity = 3
|
||||
if(20 to 40)
|
||||
livingdoll.icon_state = "living4"
|
||||
severity = 4
|
||||
if(1 to 20)
|
||||
livingdoll.icon_state = "living5"
|
||||
severity = 5
|
||||
else
|
||||
livingdoll.icon_state = "living6"
|
||||
severity = 6
|
||||
if(!livingdoll.filtered)
|
||||
livingdoll.filtered = TRUE
|
||||
var/icon/mob_mask = icon(icon, icon_state)
|
||||
if(mob_mask.Height() > world.icon_size || mob_mask.Width() > world.icon_size)
|
||||
var/health_doll_icon_state = health_doll_icon ? health_doll_icon : "megasprite"
|
||||
mob_mask = icon('icons/mob/screen_gen.dmi', health_doll_icon_state) //swap to something generic if they have no special doll
|
||||
UNLINT(livingdoll.filters += filter(type="alpha", icon = mob_mask))
|
||||
livingdoll.filters += filter(type="drop_shadow", size = -1)
|
||||
if(severity > 0)
|
||||
overlay_fullscreen("brute", /obj/screen/fullscreen/brute, severity)
|
||||
else
|
||||
clear_fullscreen("brute")
|
||||
|
||||
//Proc used to resuscitate a mob, for full_heal see fully_heal()
|
||||
/mob/living/proc/revive(full_heal = FALSE, admin_revive = FALSE)
|
||||
SEND_SIGNAL(src, COMSIG_LIVING_REVIVE, full_heal, admin_revive)
|
||||
if(full_heal)
|
||||
@@ -871,6 +911,12 @@
|
||||
"<span class='userdanger'>[src] tries to remove your [what.name].</span>", target = src,
|
||||
target_message = "<span class='danger'>You try to remove [who]'s [what.name].</span>")
|
||||
what.add_fingerprint(src)
|
||||
if(ishuman(who))
|
||||
var/mob/living/carbon/human/victim_human = who
|
||||
if(victim_human.key && !victim_human.client) // AKA braindead
|
||||
if(victim_human.stat <= SOFT_CRIT && LAZYLEN(victim_human.afk_thefts) <= AFK_THEFT_MAX_MESSAGES)
|
||||
var/list/new_entry = list(list(src.name, "tried unequipping your [what]", world.time))
|
||||
LAZYADD(victim_human.afk_thefts, new_entry)
|
||||
else
|
||||
to_chat(src,"<span class='notice'>You try to remove [who]'s [what.name].</span>")
|
||||
what.add_fingerprint(src)
|
||||
@@ -917,6 +963,13 @@
|
||||
to_chat(src, "<span class='warning'>\The [what.name] doesn't fit in that place!</span>")
|
||||
return
|
||||
|
||||
if(ishuman(who))
|
||||
var/mob/living/carbon/human/victim_human = who
|
||||
if(victim_human.key && !victim_human.client) // AKA braindead
|
||||
if(victim_human.stat <= SOFT_CRIT && LAZYLEN(victim_human.afk_thefts) <= AFK_THEFT_MAX_MESSAGES)
|
||||
var/list/new_entry = list(list(src.name, "tried equipping you with [what]", world.time))
|
||||
LAZYADD(victim_human.afk_thefts, new_entry)
|
||||
|
||||
who.visible_message("<span class='notice'>[src] tries to put [what] on [who].</span>",
|
||||
"<span class='notice'>[src] tries to put [what] on you.</span>", target = src,
|
||||
target_message = "<span class='notice'>You try to put [what] on [who].</span>")
|
||||
@@ -955,7 +1008,7 @@
|
||||
loc_temp = obj_temp
|
||||
else if(isspaceturf(get_turf(src)))
|
||||
var/turf/heat_turf = get_turf(src)
|
||||
loc_temp = heat_turf.temperature
|
||||
loc_temp = heat_turf.return_temperature()
|
||||
return loc_temp
|
||||
|
||||
/mob/living/proc/get_standard_pixel_x_offset(lying = 0)
|
||||
|
||||
@@ -48,25 +48,23 @@
|
||||
animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN)
|
||||
|
||||
/mob/living/proc/continue_starting_active_block()
|
||||
if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
||||
return DO_AFTER_STOP
|
||||
return (combat_flags & COMBAT_FLAG_ACTIVE_BLOCK_STARTING)? DO_AFTER_CONTINUE : DO_AFTER_STOP
|
||||
|
||||
/mob/living/get_standard_pixel_x_offset()
|
||||
. = ..()
|
||||
if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
|
||||
if(dir & EAST)
|
||||
. += 8
|
||||
. += 4
|
||||
if(dir & WEST)
|
||||
. -= 8
|
||||
. -= 4
|
||||
|
||||
/mob/living/get_standard_pixel_y_offset()
|
||||
. = ..()
|
||||
if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
|
||||
if(dir & NORTH)
|
||||
. += 8
|
||||
. += 4
|
||||
if(dir & SOUTH)
|
||||
. -= 8
|
||||
. -= 4
|
||||
|
||||
/**
|
||||
* Proc called by keybindings to toggle active blocking.
|
||||
@@ -100,11 +98,6 @@
|
||||
if(!I.can_active_block())
|
||||
to_chat(src, "<span class='warning'>[I] is either not capable of being used to actively block, or is not currently in a state that can! (Try wielding it if it's twohanded, for example.)</span>")
|
||||
return
|
||||
// QOL: Attempt to toggle on combat mode if it isn't already
|
||||
SEND_SIGNAL(src, COMSIG_ENABLE_COMBAT_MODE)
|
||||
if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
||||
to_chat(src, "<span class='warning'>You must be in combat mode to actively block!</span>")
|
||||
return FALSE
|
||||
var/datum/block_parry_data/data = I.get_block_parry_data()
|
||||
var/delay = data.block_start_delay
|
||||
combat_flags |= COMBAT_FLAG_ACTIVE_BLOCK_STARTING
|
||||
@@ -147,7 +140,7 @@
|
||||
/**
|
||||
* Calculates FINAL ATTACK DAMAGE after mitigation
|
||||
*/
|
||||
/obj/item/proc/active_block_calculate_final_damage(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
/obj/item/proc/active_block_calculate_final_damage(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, passive = FALSE)
|
||||
var/datum/block_parry_data/data = get_block_parry_data()
|
||||
var/absorption = data.attack_type_list_scan(data.block_damage_absorption_override, attack_type)
|
||||
var/efficiency = data.attack_type_list_scan(data.block_damage_multiplier_override, attack_type)
|
||||
@@ -156,7 +149,7 @@
|
||||
if(isnull(absorption))
|
||||
absorption = data.block_damage_absorption
|
||||
if(isnull(efficiency))
|
||||
efficiency = data.block_damage_multiplier
|
||||
efficiency = data.block_damage_multiplier * (passive? (1 / data.block_automatic_mitigation_multiplier) : 1)
|
||||
if(isnull(limit))
|
||||
limit = data.block_damage_limit
|
||||
// now we calculate damage to reduce.
|
||||
@@ -172,7 +165,7 @@
|
||||
return final_damage
|
||||
|
||||
/// Amount of stamina from damage blocked. Note that the damage argument is damage_blocked.
|
||||
/obj/item/proc/active_block_stamina_cost(mob/living/owner, atom/object, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
/obj/item/proc/active_block_stamina_cost(mob/living/owner, atom/object, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, passive = FALSE)
|
||||
var/datum/block_parry_data/data = get_block_parry_data()
|
||||
var/efficiency = data.attack_type_list_scan(data.block_stamina_efficiency_override, attack_type)
|
||||
if(isnull(efficiency))
|
||||
@@ -182,7 +175,7 @@
|
||||
multiplier = data.attack_type_list_scan(data.block_resting_stamina_penalty_multiplier_override, attack_type)
|
||||
if(isnull(multiplier))
|
||||
multiplier = data.block_resting_stamina_penalty_multiplier
|
||||
return (damage_blocked / efficiency) * multiplier
|
||||
return (damage_blocked / efficiency) * multiplier * (passive? data.block_automatic_stamina_multiplier : 1)
|
||||
|
||||
/// Apply the stamina damage to our user, notice how damage argument is stamina_amount.
|
||||
/obj/item/proc/active_block_do_stamina_damage(mob/living/owner, atom/object, stamina_amount, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
@@ -214,6 +207,18 @@
|
||||
return
|
||||
|
||||
/obj/item/proc/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction)
|
||||
return directional_block(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return, override_direction)
|
||||
|
||||
/obj/item/proc/can_passive_block()
|
||||
if(!block_parry_data || !(item_flags & ITEM_CAN_BLOCK))
|
||||
return FALSE
|
||||
var/datum/block_parry_data/data = return_block_parry_datum(block_parry_data)
|
||||
return data.block_automatic_enabled
|
||||
|
||||
/obj/item/proc/passive_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction)
|
||||
return directional_block(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return, override_direction, TRUE)
|
||||
|
||||
/obj/item/proc/directional_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction, passive = FALSE)
|
||||
if(!can_active_block())
|
||||
return BLOCK_NONE
|
||||
var/datum/block_parry_data/data = get_block_parry_data()
|
||||
@@ -228,12 +233,12 @@
|
||||
incoming_direction = get_dir(get_turf(attacker) || get_turf(object), src)
|
||||
if(!CHECK_MOBILITY(owner, MOBILITY_STAND) && !(data.block_resting_attack_types_anydir & attack_type) && (!(data.block_resting_attack_types_directional & attack_type) || !can_block_direction(owner.dir, incoming_direction)))
|
||||
return BLOCK_NONE
|
||||
else if(!can_block_direction(owner.dir, incoming_direction))
|
||||
else if(!can_block_direction(owner.dir, incoming_direction, passive))
|
||||
return BLOCK_NONE
|
||||
block_return[BLOCK_RETURN_ACTIVE_BLOCK] = TRUE
|
||||
var/final_damage = active_block_calculate_final_damage(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return)
|
||||
var/final_damage = active_block_calculate_final_damage(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return, passive)
|
||||
var/damage_blocked = damage - final_damage
|
||||
var/stamina_cost = active_block_stamina_cost(owner, object, damage_blocked, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return)
|
||||
var/stamina_cost = active_block_stamina_cost(owner, object, damage_blocked, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return, passive)
|
||||
active_block_do_stamina_damage(owner, object, stamina_cost, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return)
|
||||
block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED] = damage - final_damage
|
||||
block_return[BLOCK_RETURN_SET_DAMAGE_TO] = final_damage
|
||||
@@ -261,9 +266,9 @@
|
||||
/**
|
||||
* Gets the block direction bitflags of what we can block.
|
||||
*/
|
||||
/obj/item/proc/blockable_directions()
|
||||
/obj/item/proc/blockable_directions(passive = FALSE)
|
||||
var/datum/block_parry_data/data = get_block_parry_data()
|
||||
return data.can_block_directions
|
||||
return (!isnull(data.block_automatic_directions) && passive)? data.block_automatic_directions : data.can_block_directions
|
||||
|
||||
/**
|
||||
* Checks if we can block from a specific direction from our direction.
|
||||
@@ -272,14 +277,14 @@
|
||||
* * our_dir - our direction.
|
||||
* * their_dir - their direction. Must be a single direction, or NONE for an attack from the same tile. This is incoming direction.
|
||||
*/
|
||||
/obj/item/proc/can_block_direction(our_dir, their_dir)
|
||||
/obj/item/proc/can_block_direction(our_dir, their_dir, passive = FALSE)
|
||||
their_dir = turn(their_dir, 180)
|
||||
if(our_dir != NORTH)
|
||||
var/turn_angle = dir2angle(our_dir)
|
||||
// dir2angle(), ss13 proc is clockwise so dir2angle(EAST) == 90
|
||||
// turn(), byond proc is counterclockwise so turn(NORTH, 90) == WEST
|
||||
their_dir = turn(their_dir, turn_angle)
|
||||
return (DIR2BLOCKDIR(their_dir) & blockable_directions())
|
||||
return (DIR2BLOCKDIR(their_dir) & blockable_directions(passive))
|
||||
|
||||
/**
|
||||
* can_block_direction but for "compound" directions to check all of them and return the number of directions that were blocked.
|
||||
|
||||
@@ -14,18 +14,58 @@
|
||||
/**
|
||||
* Initiates a parrying sequence.
|
||||
*/
|
||||
/mob/living/proc/initiate_parry_sequence()
|
||||
/mob/living/proc/initiate_parry_sequence(silent = FALSE, list/override_method_data)
|
||||
if(parrying)
|
||||
return // already parrying
|
||||
return FALSE // already parrying
|
||||
if(!(mobility_flags & MOBILITY_USE))
|
||||
to_chat(src, "<span class='warning'>You can't move your arms!</span>")
|
||||
return
|
||||
if(!silent)
|
||||
to_chat(src, "<span class='warning'>You can't move your arms!</span>")
|
||||
return FALSE
|
||||
if(!(combat_flags & COMBAT_FLAG_PARRY_CAPABLE))
|
||||
to_chat(src, "<span class='warning'>You are not something that can parry attacks.</span>")
|
||||
return
|
||||
if(!silent)
|
||||
to_chat(src, "<span class='warning'>You are not something that can parry attacks.</span>")
|
||||
return FALSE
|
||||
if(!(mobility_flags & MOBILITY_STAND))
|
||||
to_chat(src, "<span class='warning'>You aren't able to parry without solid footing!</span>")
|
||||
return
|
||||
if(!silent)
|
||||
to_chat(src, "<span class='warning'>You aren't able to parry without solid footing!</span>")
|
||||
return FALSE
|
||||
var/datum/block_parry_data/data
|
||||
var/list/determined = override_method_data || determine_parry_method(FALSE, FALSE)
|
||||
if(!islist(determined))
|
||||
return FALSE
|
||||
var/method = determined[1]
|
||||
data = return_block_parry_datum(determined[2])
|
||||
var/datum/tool = determined[3]
|
||||
var/full_parry_duration = data.parry_time_windup + data.parry_time_active + data.parry_time_spindown
|
||||
// no system in place to "fallback" if out of the 3 the top priority one can't parry due to constraints but something else can.
|
||||
// can always implement it later, whatever.
|
||||
if((data.parry_respect_clickdelay && !CheckActionCooldown()) || ((parry_end_time_last + data.parry_cooldown) > world.time))
|
||||
if(!silent)
|
||||
to_chat(src, "<span class='warning'>You are not ready to parry (again)!</span>")
|
||||
return FALSE
|
||||
// Point of no return, make sure everything is set.
|
||||
parrying = method
|
||||
if(method == ITEM_PARRY)
|
||||
active_parry_item = tool
|
||||
if(!UseStaminaBuffer(data.parry_stamina_cost, TRUE))
|
||||
return FALSE
|
||||
parry_start_time = world.time
|
||||
successful_parries = list()
|
||||
successful_parry_counterattacks = list()
|
||||
addtimer(CALLBACK(src, .proc/end_parry_sequence), full_parry_duration)
|
||||
if(data.parry_flags & PARRY_LOCK_ATTACKING)
|
||||
ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_PARRY_TRAIT)
|
||||
if(data.parry_flags & PARRY_LOCK_SPRINTING)
|
||||
ADD_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_PARRY_TRAIT)
|
||||
handle_parry_starting_effects(data)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Massive snowflake proc for getting something to parry with.
|
||||
*
|
||||
* @return list of [method, data, tool], where method is the parry method define, data is the block_parry_data var that must be run through return_block_parry_data, and tool is the concept/object/martial art/etc used.
|
||||
*/
|
||||
/mob/living/proc/determine_parry_method(silent = TRUE, autoparry = FALSE)
|
||||
// Prioritize item, then martial art, then unarmed.
|
||||
// yanderedev else if time
|
||||
var/obj/item/using_item = get_active_held_item()
|
||||
@@ -55,7 +95,8 @@
|
||||
var/list/other_items = list()
|
||||
var/list/override = list()
|
||||
if(SEND_SIGNAL(src, COMSIG_LIVING_ACTIVE_PARRY_START, method, tool, other_items, override) & COMPONENT_PREVENT_PARRY_START)
|
||||
to_chat(src, "<span class='warning'>Something is preventing you from parrying!</span>")
|
||||
if(!silent)
|
||||
to_chat(src, "<span class='warning'>Something is preventing you from parrying!</span>")
|
||||
return
|
||||
if(length(override))
|
||||
var/datum/thing = override[1]
|
||||
@@ -72,35 +113,10 @@
|
||||
method = ITEM_PARRY
|
||||
data = using_item.block_parry_data
|
||||
if(!method)
|
||||
to_chat(src, "<span class='warning'>You have nothing to parry with!</span>")
|
||||
if(!silent)
|
||||
to_chat(src, "<span class='warning'>You have nothing to parry with!</span>")
|
||||
return FALSE
|
||||
//QOL: Try to enable combat mode if it isn't already
|
||||
SEND_SIGNAL(src, COMSIG_ENABLE_COMBAT_MODE)
|
||||
if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
||||
to_chat(src, "<span class='warning'>You must be in combat mode to parry!</span>")
|
||||
return FALSE
|
||||
data = return_block_parry_datum(data)
|
||||
var/full_parry_duration = data.parry_time_windup + data.parry_time_active + data.parry_time_spindown
|
||||
// no system in place to "fallback" if out of the 3 the top priority one can't parry due to constraints but something else can.
|
||||
// can always implement it later, whatever.
|
||||
if((data.parry_respect_clickdelay && !CheckActionCooldown()) || ((parry_end_time_last + data.parry_cooldown) > world.time))
|
||||
to_chat(src, "<span class='warning'>You are not ready to parry (again)!</span>")
|
||||
return FALSE
|
||||
// Point of no return, make sure everything is set.
|
||||
parrying = method
|
||||
if(method == ITEM_PARRY)
|
||||
active_parry_item = using_item
|
||||
if(!UseStaminaBuffer(data.parry_stamina_cost, TRUE))
|
||||
return FALSE
|
||||
parry_start_time = world.time
|
||||
successful_parries = list()
|
||||
addtimer(CALLBACK(src, .proc/end_parry_sequence), full_parry_duration)
|
||||
if(data.parry_flags & PARRY_LOCK_ATTACKING)
|
||||
ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_PARRY_TRAIT)
|
||||
if(data.parry_flags & PARRY_LOCK_SPRINTING)
|
||||
ADD_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_PARRY_TRAIT)
|
||||
handle_parry_starting_effects(data)
|
||||
return TRUE
|
||||
return list(method, data, tool)
|
||||
|
||||
/**
|
||||
* Tries to find a backup parry item.
|
||||
@@ -146,6 +162,7 @@
|
||||
parry_start_time = 0
|
||||
parry_end_time_last = world.time + (successful? 0 : data.parry_failed_cooldown_duration)
|
||||
successful_parries = null
|
||||
successful_parry_counterattacks = null
|
||||
|
||||
/**
|
||||
* Handles starting effects for parrying.
|
||||
@@ -178,17 +195,17 @@
|
||||
/**
|
||||
* Called when an attack is parried using this, whether or not the parry was successful.
|
||||
*/
|
||||
/obj/item/proc/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/block_return, parry_efficiency, parry_time)
|
||||
/obj/item/proc/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/block_return, parry_efficiency, parry_time, autoparry = FALSE)
|
||||
|
||||
/**
|
||||
* Called when an attack is parried innately, whether or not the parry was successful.
|
||||
*/
|
||||
/mob/living/proc/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/block_return, parry_efficiency, parry_time)
|
||||
/mob/living/proc/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/block_return, parry_efficiency, parry_time, autoparry = FALSE)
|
||||
|
||||
/**
|
||||
* Called when an attack is parried using this, whether or not the parry was successful.
|
||||
*/
|
||||
/datum/martial_art/proc/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/block_return, parry_efficiency, parry_time)
|
||||
/datum/martial_art/proc/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/block_return, parry_efficiency, parry_time, autoparry = FALSE)
|
||||
|
||||
/**
|
||||
* Called when an attack is parried and block_parra_data indicates to use a proc to handle counterattack.
|
||||
@@ -205,6 +222,94 @@
|
||||
*/
|
||||
/datum/martial_art/proc/active_parry_reflex_counter(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list, parry_efficiency, list/effect_text)
|
||||
|
||||
/**
|
||||
* Attempts to automatically parry an attacker.
|
||||
*/
|
||||
/mob/living/proc/attempt_auto_parry(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list())
|
||||
// determine how we'll parry
|
||||
var/list/determined = determine_parry_method(TRUE, TRUE)
|
||||
if(!islist(determined))
|
||||
return BLOCK_NONE
|
||||
var/datum/block_parry_data/data = return_block_parry_datum(determined[2])
|
||||
if(!data.parry_automatic_enabled || (last_autoparry > (world.time - data.autoparry_cooldown_absolute)))
|
||||
return BLOCK_NONE
|
||||
if(attack_type && !(attack_type & data.parry_attack_types))
|
||||
return BLOCK_NONE
|
||||
// before doing anything, check if the user moused over them properly
|
||||
if(!client)
|
||||
return BLOCK_NONE
|
||||
var/found = attacker == client.mouseObject
|
||||
if(!found)
|
||||
for(var/i in client.moused_over_objects)
|
||||
if(i == object)
|
||||
if((client.moused_over_objects[i] + (data.autoparry_mouse_delay_maximum)) >= world.time)
|
||||
found = TRUE
|
||||
break
|
||||
if(!found)
|
||||
return BLOCK_NONE
|
||||
|
||||
// if that works, try to start parry
|
||||
// first, check cooldowns
|
||||
|
||||
// now, depending on if we're doing a single simulation or a full sequence
|
||||
last_autoparry = world.time
|
||||
if(data.autoparry_sequence_simulation)
|
||||
// for full sequence simulation
|
||||
initiate_parry_sequence(TRUE, determined)
|
||||
if(data.autoparry_sequence_start_time == -1)
|
||||
parry_start_time = world.time - data.parry_time_windup
|
||||
else
|
||||
parry_start_time = world.time - data.autoparry_sequence_start_time
|
||||
// recurse back to original
|
||||
return run_parry(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, FALSE)
|
||||
else
|
||||
// yes, this is mostly a copypaste of run_parry.
|
||||
var/efficiency = data.attack_type_list_scan(data.autoparry_single_efficiency_override, attack_type)
|
||||
if(isnull(efficiency))
|
||||
efficiency = data.autoparry_single_efficiency
|
||||
var/method = determined[1]
|
||||
switch(method)
|
||||
if(ITEM_PARRY)
|
||||
var/obj/item/I = determined[3]
|
||||
. = I.on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, null, TRUE)
|
||||
if(UNARMED_PARRY)
|
||||
. = on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, null, TRUE)
|
||||
if(MARTIAL_PARRY)
|
||||
. = mind.martial_art.on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, null, TRUE)
|
||||
if(!isnull(return_list[BLOCK_RETURN_OVERRIDE_PARRY_EFFICIENCY])) // one of our procs overrode
|
||||
efficiency = return_list[BLOCK_RETURN_OVERRIDE_PARRY_EFFICIENCY]
|
||||
if(efficiency <= 0) // Do not allow automatically handled/standardized parries that increase damage for now.
|
||||
return
|
||||
. |= BLOCK_SHOULD_PARTIAL_MITIGATE
|
||||
if(efficiency >= data.parry_efficiency_perfect)
|
||||
. |= data.perfect_parry_block_return_flags
|
||||
if(data.perfect_parry_block_return_list)
|
||||
return_list |= data.perfect_parry_block_return_list
|
||||
else if(efficiency >= data.parry_efficiency_considered_successful)
|
||||
. |= data.imperfect_parry_block_return_flags
|
||||
if(data.imperfect_parry_block_return_list)
|
||||
return_list |= data.imperfect_parry_block_return_list
|
||||
else
|
||||
. |= data.failed_parry_block_return_flags
|
||||
if(data.failed_parry_block_return_list)
|
||||
return_list |= data.failed_parry_block_return_list
|
||||
if(isnull(return_list[BLOCK_RETURN_MITIGATION_PERCENT])) // if one of the on_active_parry procs overrode. We don't have to worry about interference since parries are the first thing checked in the [do_run_block()] sequence.
|
||||
return_list[BLOCK_RETURN_MITIGATION_PERCENT] = clamp(efficiency, 0, 100) // do not allow > 100% or < 0% for now.
|
||||
if((return_list[BLOCK_RETURN_MITIGATION_PERCENT] >= 100) || (damage <= 0))
|
||||
. |= BLOCK_SUCCESS
|
||||
var/list/effect_text
|
||||
var/pacifist_counter_check = TRUE
|
||||
if(HAS_TRAIT(src, TRAIT_PACIFISM))
|
||||
switch(parrying)
|
||||
if(ITEM_PARRY)
|
||||
pacifist_counter_check = (!active_parry_item.force || active_parry_item.damtype == STAMINA)
|
||||
else
|
||||
pacifist_counter_check = FALSE //Both martial and unarmed counter attacks generally are harmful, so no need to have the same line twice.
|
||||
if(efficiency >= data.parry_efficiency_to_counterattack && pacifist_counter_check && !return_list[BLOCK_RETURN_FORCE_NO_PARRY_COUNTERATTACK] && (!(attacker in successful_parry_counterattacks) && !data.parry_allow_repeated_counterattacks))
|
||||
effect_text = run_parry_countereffects(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, data)
|
||||
if(data.parry_flags & PARRY_DEFAULT_HANDLE_FEEDBACK)
|
||||
handle_parry_feedback(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, effect_text, data)
|
||||
|
||||
/**
|
||||
* Gets the stage of our parry sequence we're currently in.
|
||||
*/
|
||||
@@ -235,12 +340,20 @@
|
||||
return world.time - parry_start_time
|
||||
|
||||
/// same return values as normal blocking, called with absolute highest priority in the block "chain".
|
||||
/mob/living/proc/run_parry(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list())
|
||||
/mob/living/proc/run_parry(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), allow_auto = TRUE)
|
||||
var/stage = get_parry_stage()
|
||||
if(attack_type & ATTACK_TYPE_PARRY_COUNTERATTACK)
|
||||
return BLOCK_NONE // don't infinite loop
|
||||
if(stage != PARRY_ACTIVE)
|
||||
return BLOCK_NONE
|
||||
// If they're not currently parrying, attempt auto parry
|
||||
if(stage == NOT_PARRYING)
|
||||
if(!allow_auto || SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
||||
return BLOCK_NONE
|
||||
return attempt_auto_parry(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list)
|
||||
else
|
||||
return BLOCK_NONE
|
||||
var/datum/block_parry_data/data = get_parry_data()
|
||||
if(attack_type && (!(attack_type & data.parry_attack_types) || (attack_type & ATTACK_TYPE_PARRY_COUNTERATTACK))) // if this attack is from a parry do not parry it lest we infinite loop.
|
||||
if(attack_type && !(attack_type & data.parry_attack_types))
|
||||
return BLOCK_NONE
|
||||
var/efficiency = data.get_parry_efficiency(attack_type, get_parry_time())
|
||||
switch(parrying)
|
||||
@@ -281,7 +394,7 @@
|
||||
pacifist_counter_check = (!active_parry_item.force || active_parry_item.damtype == STAMINA)
|
||||
else
|
||||
pacifist_counter_check = FALSE //Both martial and unarmed counter attacks generally are harmful, so no need to have the same line twice.
|
||||
if(efficiency >= data.parry_efficiency_to_counterattack && pacifist_counter_check)
|
||||
if(efficiency >= data.parry_efficiency_to_counterattack && pacifist_counter_check && !return_list[BLOCK_RETURN_FORCE_NO_PARRY_COUNTERATTACK] && (!(attacker in successful_parry_counterattacks) && !data.parry_allow_repeated_counterattacks))
|
||||
effect_text = run_parry_countereffects(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency)
|
||||
if(data.parry_flags & PARRY_DEFAULT_HANDLE_FEEDBACK)
|
||||
handle_parry_feedback(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, effect_text)
|
||||
@@ -289,8 +402,9 @@
|
||||
if(length(successful_parries) >= data.parry_max_attacks)
|
||||
end_parry_sequence()
|
||||
|
||||
/mob/living/proc/handle_parry_feedback(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), parry_efficiency, list/effect_text)
|
||||
var/datum/block_parry_data/data = get_parry_data()
|
||||
/mob/living/proc/handle_parry_feedback(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), parry_efficiency, list/effect_text, datum/block_parry_data/data)
|
||||
if(!data)
|
||||
data = get_parry_data()
|
||||
var/knockdown_check = FALSE
|
||||
if(data.parry_data[PARRY_KNOCKDOWN_ATTACKER] && parry_efficiency >= data.parry_efficiency_to_counterattack)
|
||||
knockdown_check = TRUE
|
||||
@@ -299,12 +413,14 @@
|
||||
visible_message("<span class='danger'>[src] parries [attack_text][length(effect_text)? ", [english_list(effect_text)] [attacker]" : ""][length(effect_text) && knockdown_check? " and" : ""][knockdown_check? " knocking them to the ground" : ""]!</span>")
|
||||
|
||||
/// Run counterattack if any
|
||||
/mob/living/proc/run_parry_countereffects(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), parry_efficiency)
|
||||
/mob/living/proc/run_parry_countereffects(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), parry_efficiency, datum/block_parry_data/data)
|
||||
if(!isliving(attacker))
|
||||
return
|
||||
var/mob/living/L = attacker
|
||||
var/datum/block_parry_data/data = get_parry_data()
|
||||
if(!data)
|
||||
data = get_parry_data()
|
||||
var/list/effect_text = list()
|
||||
successful_parry_counterattacks |= attacker
|
||||
// Always proc so items can override behavior easily
|
||||
switch(parrying)
|
||||
if(ITEM_PARRY)
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
var/results
|
||||
if(I == active_block_item)
|
||||
results = I.active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list, attack_direction)
|
||||
else if(I.can_passive_block() && !SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
||||
results = I.passive_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list, attack_direction)
|
||||
else
|
||||
results = I.run_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list)
|
||||
. |= results
|
||||
|
||||
@@ -75,7 +75,7 @@ GLOBAL_LIST_EMPTY(block_parry_data)
|
||||
var/block_stamina_buffer_ratio = 1
|
||||
|
||||
/// Stamina dealt directly via UseStaminaBuffer() per SECOND of block.
|
||||
var/block_stamina_cost_per_second = 1.5
|
||||
var/block_stamina_cost_per_second = 1
|
||||
/// Prevent stamina buffer regeneration while block?
|
||||
var/block_no_stambuffer_regeneration = TRUE
|
||||
/// Prevent stamina regeneration while block?
|
||||
@@ -93,20 +93,31 @@ GLOBAL_LIST_EMPTY(block_parry_data)
|
||||
/// Sounds for blocking
|
||||
var/list/block_sounds = list('sound/block_parry/block_metal1.ogg' = 1, 'sound/block_parry/block_metal1.ogg' = 1)
|
||||
|
||||
// Autoblock
|
||||
// Other than for overrides, this mostly just reads from the above vars
|
||||
/// Can this item automatically block?
|
||||
var/block_automatic_enabled = TRUE
|
||||
/// Directions that you can autoblock in. Null to default to normal directions.
|
||||
var/block_automatic_directions = null
|
||||
/// Effectiveness multiplier for automated block. Only applies to efficiency, absorption and limits stay the same!
|
||||
var/block_automatic_mitigation_multiplier = 0.33
|
||||
/// Stamina cost multiplier for automated block
|
||||
var/block_automatic_stamina_multiplier = 1
|
||||
|
||||
/////////// PARRYING ////////////
|
||||
/// Prioriry for [mob/do_run_block()] while we're being used to parry.
|
||||
/// Priority for [mob/do_run_block()] while we're being used to parry.
|
||||
// None - Parry is always highest priority!
|
||||
/// Parry doesn't work if you aren't able to otherwise attack due to clickdelay
|
||||
var/parry_respect_clickdelay = TRUE
|
||||
var/parry_respect_clickdelay = FALSE
|
||||
/// Parry stamina cost
|
||||
var/parry_stamina_cost = 5
|
||||
/// Attack types we can block
|
||||
var/parry_attack_types = ALL
|
||||
/// Parry flags
|
||||
var/parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK | PARRY_LOCK_ATTACKING
|
||||
var/parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK
|
||||
|
||||
/// Parry windup duration in deciseconds. 0 to this is windup, afterwards is main stage.
|
||||
var/parry_time_windup = 2
|
||||
var/parry_time_windup = 0
|
||||
/// Parry spindown duration in deciseconds. main stage end to this is the spindown stage, afterwards the parry fully ends.
|
||||
var/parry_time_spindown = 3
|
||||
/// Main parry window in deciseconds. This is between [parry_time_windup] and [parry_time_spindown]
|
||||
@@ -139,7 +150,7 @@ GLOBAL_LIST_EMPTY(block_parry_data)
|
||||
/// Efficiency must be at least this to be considered successful
|
||||
var/parry_efficiency_considered_successful = 0.1
|
||||
/// Efficiency must be at least this to run automatic counterattack
|
||||
var/parry_efficiency_to_counterattack = 0.1
|
||||
var/parry_efficiency_to_counterattack = INFINITY
|
||||
/// Maximum attacks to parry successfully or unsuccessfully (but not efficiency < 0) during active period, hitting this immediately ends the sequence.
|
||||
var/parry_max_attacks = INFINITY
|
||||
/// Visual icon state override for parrying
|
||||
@@ -153,7 +164,7 @@ GLOBAL_LIST_EMPTY(block_parry_data)
|
||||
/// Stagger duration post-parry if you fail to parry an attack
|
||||
var/parry_failed_stagger_duration = 3.5 SECONDS
|
||||
/// Clickdelay duration post-parry if you fail to parry an attack
|
||||
var/parry_failed_clickcd_duration = 2 SECONDS
|
||||
var/parry_failed_clickcd_duration = 0 SECONDS
|
||||
/// Parry cooldown post-parry if failed. This is ADDED to parry_cooldown!!!
|
||||
var/parry_failed_cooldown_duration = 0 SECONDS
|
||||
|
||||
@@ -166,6 +177,29 @@ GLOBAL_LIST_EMPTY(block_parry_data)
|
||||
var/perfect_parry_block_return_list
|
||||
var/imperfect_parry_block_return_list
|
||||
var/failed_parry_block_return_list
|
||||
/// Allow multiple counterattacks per parry sequence. Bad idea.
|
||||
var/parry_allow_repeated_counterattacks = FALSE
|
||||
|
||||
// Auto parry
|
||||
// Anything not specified like cooldowns/clickdelay respecting is pulled from above.
|
||||
/// Can this data automatically parry? This is off by default because this is something that requires thought to balance.
|
||||
var/parry_automatic_enabled = FALSE
|
||||
/// Hard autoparry cooldown
|
||||
var/autoparry_cooldown_absolute = 7.5 SECONDS
|
||||
/// Autoparry : Simulate a parry sequence starting at a certain tick, or simply simulate a single attack parry?
|
||||
var/autoparry_sequence_simulation = FALSE
|
||||
// Single attack simulation:
|
||||
/// Single attack autoparry - efficiency
|
||||
var/autoparry_single_efficiency = 75
|
||||
/// Single attack autoparry - efficiency overrides by attack type, see above
|
||||
var/list/autoparry_single_efficiency_override
|
||||
// Parry sequence simulation:
|
||||
/// Decisecond of sequence to start on. -1 to start to 0th tick of active parry window.
|
||||
var/autoparry_sequence_start_time = -1
|
||||
// Clickdelay/cooldown settings not included, as well as whether or not to lock attack/sprinting/etc. They will be pulled from the above.
|
||||
|
||||
/// ADVANCED - Autoparry requirement for time since last moused over for a specific object
|
||||
var/autoparry_mouse_delay_maximum = 0.35 SECONDS
|
||||
|
||||
/**
|
||||
* Quirky proc to get average of flags in list that are in attack_type because why is attack_type a flag.
|
||||
@@ -308,6 +342,7 @@ GLOBAL_LIST_EMPTY(block_parry_data)
|
||||
RENDER_VARIABLE_SIMPLE(parry_cooldown, "Deciseconds it has to be since the last time a parry sequence <b>ended</b> for you before you can parry again.")
|
||||
RENDER_VARIABLE_SIMPLE(parry_failed_stagger_duration, "Deciseconds you are staggered for at the of the parry sequence if you do not successfully parry anything.")
|
||||
RENDER_VARIABLE_SIMPLE(parry_failed_clickcd_duration, "Deciseconds you are put on attack cooldown at the end of the parry sequence if you do not successfully parry anything.")
|
||||
dat += ""
|
||||
dat += "</div></table>"
|
||||
return dat.Join("")
|
||||
#undef RENDER_VARIABLE_SIMPLE
|
||||
|
||||
@@ -274,7 +274,7 @@
|
||||
|
||||
/mob/living/on_attack_hand(mob/user, act_intent = user.a_intent, attackchain_flags)
|
||||
..() //Ignoring parent return value here.
|
||||
SEND_SIGNAL(src, COMSIG_MOB_ATTACK_HAND, user)
|
||||
SEND_SIGNAL(src, COMSIG_MOB_ATTACK_HAND, user, act_intent)
|
||||
if((user != src) && act_intent != INTENT_HELP && (mob_run_block(user, 0, user.name, ATTACK_TYPE_UNARMED | ATTACK_TYPE_MELEE | ((attackchain_flags & ATTACK_IS_PARRY_COUNTERATTACK)? ATTACK_TYPE_PARRY_COUNTERATTACK : NONE), null, user, check_zone(user.zone_selected), null) & BLOCK_SUCCESS))
|
||||
log_combat(user, src, "attempted to touch")
|
||||
visible_message("<span class='warning'>[user] attempted to touch [src]!</span>",
|
||||
@@ -438,6 +438,12 @@
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/wave_ex_act(power, datum/wave_explosion/explosion, dir)
|
||||
if(power > EXPLOSION_POWER_NORMAL_MOB_GIB)
|
||||
gib()
|
||||
adjustBruteLoss(EXPLOSION_POWER_STANDARD_SCALE_MOB_DAMAGE(power, explosion.mob_damage_mod))
|
||||
return power
|
||||
|
||||
//Looking for irradiate()? It's been moved to radiation.dm under the rad_act() for mobs.
|
||||
|
||||
/mob/living/acid_act(acidpwr, acid_volume)
|
||||
@@ -561,3 +567,9 @@
|
||||
|
||||
/mob/living/proc/getFireLoss_nonProsthetic()
|
||||
return getFireLoss()
|
||||
|
||||
/mob/living/proc/set_last_attacker(mob/attacker)
|
||||
lastattacker = attacker.real_name
|
||||
lastattackerckey = attacker.ckey
|
||||
SEND_SIGNAL(src, COMSIG_LIVING_ATTACKER_SET, attacker)
|
||||
SEND_SIGNAL(attacker, COMSIG_LIVING_SET_AS_ATTACKER, src)
|
||||
|
||||
@@ -50,8 +50,12 @@
|
||||
var/obj/effect/abstract/parry/parry_visual_effect
|
||||
/// world.time of last parry end
|
||||
var/parry_end_time_last = 0
|
||||
/// Last autoparry
|
||||
var/last_autoparry = 0
|
||||
/// Successful parries within the current parry cycle. It's a list of efficiency percentages.
|
||||
var/list/successful_parries
|
||||
/// Current parry counterattacks. Makes sure we can only counterattack someone once per parry.
|
||||
var/list/successful_parry_counterattacks
|
||||
|
||||
var/confused = 0 //Makes the mob move in random directions.
|
||||
|
||||
@@ -63,8 +67,6 @@
|
||||
//Allows mobs to move through dense areas without restriction. For instance, in space or out of holder objects.
|
||||
var/incorporeal_move = FALSE //FALSE is off, INCORPOREAL_MOVE_BASIC is normal, INCORPOREAL_MOVE_SHADOW is for ninjas
|
||||
//and INCORPOREAL_MOVE_JAUNT is blocked by holy water/salt
|
||||
/// Do we make floors dirty as we move?
|
||||
var/causes_dirt_buildup_on_floor = FALSE
|
||||
|
||||
var/list/roundstart_quirks = list()
|
||||
|
||||
@@ -94,6 +96,7 @@
|
||||
var/smoke_delay = 0 //used to prevent spam with smoke reagent reaction on mob.
|
||||
|
||||
var/bubble_icon = "default" //what icon the mob uses for speechbubbles
|
||||
var/health_doll_icon //if this exists AND the normal sprite is bigger than 32x32, this is the replacement icon state (because health doll size limitations). the icon will always be screen_gen.dmi
|
||||
|
||||
var/last_bumped = 0
|
||||
var/unique_name = 0 //if a mob's name should be appended with an id when created e.g. Mob (666)
|
||||
|
||||
@@ -102,31 +102,6 @@
|
||||
if(lying && !buckled && prob(getBruteLoss()*200/maxHealth))
|
||||
makeTrail(newloc, T, old_direction)
|
||||
|
||||
if(causes_dirt_buildup_on_floor && (movement_type & GROUND))
|
||||
dirt_buildup()
|
||||
|
||||
/**
|
||||
* Attempts to make the floor dirty.
|
||||
*/
|
||||
/mob/living/proc/dirt_buildup(strength = 1)
|
||||
var/turf/open/T = loc
|
||||
if(!istype(T) || !T.dirt_buildup_allowed)
|
||||
return
|
||||
var/area/A = T.loc
|
||||
if(!A.dirt_buildup_allowed)
|
||||
return
|
||||
var/multiplier = CONFIG_GET(number/turf_dirty_multiplier)
|
||||
strength *= multiplier
|
||||
var/obj/effect/decal/cleanable/dirt/D = locate() in T
|
||||
if(D)
|
||||
D.dirty(strength)
|
||||
else
|
||||
T.dirtyness += strength
|
||||
if(T.dirtyness >= (isnull(T.dirt_spawn_threshold)? CONFIG_GET(number/turf_dirt_threshold) : T.dirt_spawn_threshold))
|
||||
D = new /obj/effect/decal/cleanable/dirt(T)
|
||||
D.dirty(T.dirt_spawn_threshold - T.dirtyness)
|
||||
T.dirtyness = 0 // reset.
|
||||
|
||||
/mob/living/Move_Pulled(atom/A)
|
||||
. = ..()
|
||||
if(!. || !isliving(A))
|
||||
|
||||
@@ -90,11 +90,11 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
|
||||
var/static/list/one_character_prefix = list(MODE_HEADSET = TRUE, MODE_ROBOT = TRUE, MODE_WHISPER = TRUE)
|
||||
|
||||
var/ic_blocked = FALSE
|
||||
/*
|
||||
if(client && !forced && config.ic_filter_regex && findtext(message, config.ic_filter_regex))
|
||||
|
||||
if(client && !forced && CHAT_FILTER_CHECK(message))
|
||||
//The filter doesn't act on the sanitized message, but the raw message.
|
||||
ic_blocked = TRUE
|
||||
*/
|
||||
|
||||
if(sanitize)
|
||||
message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
|
||||
if(!message || message == "")
|
||||
@@ -103,6 +103,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
|
||||
if(ic_blocked)
|
||||
//The filter warning message shows the sanitized message though.
|
||||
to_chat(src, "<span class='warning'>That message contained a word prohibited in IC chat! Consider reviewing the server rules.\n<span replaceRegex='show_filtered_ic_chat'>\"[message]\"</span></span>")
|
||||
SSblackbox.record_feedback("tally", "ic_blocked_words", 1, lowertext(config.ic_filter_regex.match))
|
||||
return
|
||||
|
||||
var/datum/saymode/saymode = SSradio.saymodes[talk_key]
|
||||
@@ -266,16 +267,10 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
|
||||
eavesdrop_range = EAVESDROP_EXTRA_RANGE
|
||||
var/list/listening = get_hearers_in_view(message_range+eavesdrop_range, source)
|
||||
var/list/the_dead = list()
|
||||
var/list/yellareas //CIT CHANGE - adds the ability for yelling to penetrate walls and echo throughout areas
|
||||
if(!eavesdrop_range && say_test(message) == "2") //CIT CHANGE - ditto
|
||||
yellareas = get_areas_in_range(message_range*0.5, source) //CIT CHANGE - ditto
|
||||
|
||||
for(var/_M in GLOB.player_list)
|
||||
var/mob/M = _M
|
||||
if(M.stat != DEAD) //not dead, not important
|
||||
if(yellareas) //CIT CHANGE - see above. makes yelling penetrate walls
|
||||
var/area/A = get_area(M) //CIT CHANGE - ditto
|
||||
if(istype(A) && A.ambientsounds != SPACE && (A in yellareas)) //CIT CHANGE - ditto
|
||||
listening |= M //CIT CHANGE - ditto
|
||||
continue
|
||||
if(!M.client || !client) //client is so that ghosts don't have to listen to mice
|
||||
continue
|
||||
@@ -302,6 +297,9 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
|
||||
AM.Hear(rendered, src, message_language, message, null, spans, message_mode, source)
|
||||
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_LIVING_SAY_SPECIAL, src, message)
|
||||
|
||||
if(!eavesdrop_range && say_test(message) == "2") // Yell hook
|
||||
process_yelling(listening, rendered, src, message_language, message, spans, message_mode, source)
|
||||
|
||||
//speech bubble
|
||||
var/list/speech_bubble_recipients = list()
|
||||
for(var/mob/M in listening)
|
||||
@@ -311,6 +309,30 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
|
||||
I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
|
||||
INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, speech_bubble_recipients, 30)
|
||||
|
||||
/atom/movable/proc/process_yelling(list/already_heard, rendered, atom/movable/speaker, datum/language/message_language, message, list/spans, message_mode, obj/source)
|
||||
if(last_yell > (world.time - 10))
|
||||
to_chat(src, "<span class='warning'>Your voice doesn't project as far as you try to yell in such quick succession.") // yeah no, no spamming an expensive floodfill.
|
||||
return
|
||||
last_yell = world.time
|
||||
var/list/overhearing = list()
|
||||
var/list/overhearing_text = list()
|
||||
overhearing = yelling_wavefill(src, yell_power)
|
||||
if(!overhearing.len)
|
||||
overhearing_text = "none"
|
||||
else
|
||||
for(var/mob/M as anything in overhearing)
|
||||
overhearing_text += key_name(M)
|
||||
overhearing_text = english_list(overhearing_text)
|
||||
log_say("YELL: [ismob(src)? key_name(src) : src] yelled [message] with overhearing mobs [overhearing_text]")
|
||||
// overhearing = get_hearers_in_view(35, src) | get_hearers_in_range(5, src)
|
||||
overhearing -= already_heard
|
||||
if(!overhearing.len)
|
||||
return
|
||||
// to_chat(world, "DEBUG: overhearing [english_list(overhearing)]")
|
||||
for(var/_AM in overhearing)
|
||||
var/atom/movable/AM = _AM
|
||||
AM.Hear(rendered, speaker, message_language, message, null, spans, message_mode, source)
|
||||
|
||||
/mob/proc/binarycheck()
|
||||
return FALSE
|
||||
|
||||
@@ -333,7 +355,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
|
||||
var/obj/item/bodypart/rightarm = get_bodypart(BODY_ZONE_R_ARM)
|
||||
if(HAS_TRAIT(src, TRAIT_MUTE) && get_selected_language() != /datum/language/signlanguage)
|
||||
return 0
|
||||
|
||||
|
||||
if (get_selected_language() == /datum/language/signlanguage)
|
||||
var/left_disabled = FALSE
|
||||
var/right_disabled = FALSE
|
||||
|
||||
@@ -182,6 +182,7 @@
|
||||
. = ..()
|
||||
|
||||
/mob/living/silicon/ai/proc/set_core_display_icon(input, client/C)
|
||||
set waitfor = FALSE
|
||||
if(client && !C)
|
||||
C = client
|
||||
if(!input && !C?.prefs?.preferred_ai_core_display)
|
||||
|
||||
@@ -92,12 +92,9 @@
|
||||
name = "ai_multicam_room"
|
||||
icon_state = "ai_camera_room"
|
||||
dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
|
||||
valid_territory = FALSE
|
||||
ambientsounds = list()
|
||||
blob_allowed = FALSE
|
||||
noteleport = TRUE
|
||||
hidden = TRUE
|
||||
safe = TRUE
|
||||
area_flags = NOTELEPORT | HIDDEN_AREA | UNIQUE_AREA
|
||||
ambientsounds = null
|
||||
flags_1 = NONE
|
||||
|
||||
GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room)
|
||||
|
||||
|
||||
@@ -575,7 +575,7 @@
|
||||
for(var/id in environment.get_gases())
|
||||
var/gas_level = environment.get_moles(id)/total_moles
|
||||
if(gas_level > 0.01)
|
||||
dat += "[GLOB.meta_gas_names[id]]: [round(gas_level*100)]%<br>"
|
||||
dat += "[GLOB.gas_data.names[id]]: [round(gas_level*100)]%<br>"
|
||||
dat += "Temperature: [round(environment.return_temperature()-T0C)]°C<br>"
|
||||
dat += "<a href='byond://?src=[REF(src)];software=atmosensor;sub=0'>Refresh Reading</a> <br>"
|
||||
dat += "<br>"
|
||||
|
||||
@@ -48,6 +48,9 @@
|
||||
. += "<span class='warning'>It doesn't seem to be responding.</span>"
|
||||
if(DEAD)
|
||||
. += "<span class='deadsay'>It looks like its system is corrupted and requires a reset.</span>"
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, usr, .)
|
||||
|
||||
. += "*---------*</span>"
|
||||
|
||||
. += ..()
|
||||
|
||||
@@ -966,7 +966,8 @@
|
||||
|
||||
upgrades.Cut()
|
||||
|
||||
speed = 0
|
||||
vtec = 0
|
||||
vtec_disabled = FALSE
|
||||
ionpulse = FALSE
|
||||
revert_shell()
|
||||
|
||||
@@ -1326,3 +1327,18 @@
|
||||
var/datum/computer_file/program/robotact/program = modularInterface.get_robotact()
|
||||
if(program)
|
||||
program.force_full_update()
|
||||
|
||||
/mob/living/silicon/robot/get_tooltip_data()
|
||||
var/t_He = p_they(TRUE)
|
||||
var/t_is = p_are()
|
||||
. = list()
|
||||
var/borg_type = module ? module : "Default"
|
||||
//This isn't even used normally, but if that ever changes, just uncomment this
|
||||
/* var/obj/item/borg_chameleon/chameleon = locate() in src
|
||||
if(!chameleon)
|
||||
chameleon = locate() in src.module
|
||||
if(chameleon?.active)
|
||||
borg_type = "Engineering"
|
||||
*/
|
||||
. += "[t_He] [t_is] a [borg_type] unit"
|
||||
SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, usr, .)
|
||||
|
||||
@@ -82,6 +82,18 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
|
||||
if(!opened)
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/robot/disarm_shove(mob/living/carbon/human/H)
|
||||
visible_message(span_danger("[src]'s motors grind as they are shoved by [H]!"))
|
||||
vtec_disable(10 SECONDS)
|
||||
|
||||
/mob/living/silicon/robot/proc/vtec_disable(time)
|
||||
var/datum/status_effect/vtec_disabled/V = has_status_effect(/datum/status_effect/vtec_disabled)
|
||||
if(V)
|
||||
V.duration = max(V.duration, world.time + time)
|
||||
else
|
||||
apply_status_effect(/datum/status_effect/vtec_disabled, time)
|
||||
update_movespeed()
|
||||
|
||||
/mob/living/silicon/robot/fire_act()
|
||||
if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them
|
||||
IgniteMob()
|
||||
|
||||
@@ -61,7 +61,9 @@
|
||||
|
||||
var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list(), "Burglar"=list())
|
||||
|
||||
var/speed = 0 // VTEC speed boost.
|
||||
var/vtec = 0 // VTEC speed boost.
|
||||
/// vtec shorted out
|
||||
var/vtec_disabled = FALSE
|
||||
var/magpulse = FALSE // Magboot-like effect.
|
||||
var/ionpulse = FALSE // Jetpack-like effect.
|
||||
var/ionpulse_on = FALSE // Jetpack-like effect.
|
||||
@@ -125,3 +127,5 @@
|
||||
var/sleeper_g
|
||||
var/sleeper_r
|
||||
var/sleeper_nv
|
||||
|
||||
tooltips = TRUE
|
||||
|
||||
@@ -231,8 +231,11 @@
|
||||
R.update_module_innate()
|
||||
RM.rebuild_modules()
|
||||
INVOKE_ASYNC(RM, .proc/do_transform_animation)
|
||||
if(RM.dogborg)
|
||||
if(RM.dogborg || R.dogborg)
|
||||
RM.dogborg_equip()
|
||||
R.typing_indicator_state = /obj/effect/overlay/typing_indicator/machine/dogborg
|
||||
else
|
||||
R.typing_indicator_state = /obj/effect/overlay/typing_indicator/machine
|
||||
R.maxHealth = borghealth
|
||||
R.health = min(borghealth, R.health)
|
||||
qdel(src)
|
||||
|
||||
@@ -23,4 +23,4 @@
|
||||
. = ..()
|
||||
if(!resting && !(combat_flags & COMBAT_FLAG_SPRINT_ACTIVE))
|
||||
. += 1
|
||||
. += speed
|
||||
. += vtec_disabled? 0 : vtec
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
var/hack_software = FALSE //Will be able to use hacking actions
|
||||
var/interaction_range = 7 //wireless control range
|
||||
|
||||
typing_indicator_state = /obj/effect/overlay/typing_indicator/machine
|
||||
|
||||
/mob/living/silicon/Initialize()
|
||||
. = ..()
|
||||
GLOB.silicon_mobs += src
|
||||
@@ -59,8 +61,8 @@
|
||||
|
||||
/mob/living/silicon/ComponentInitialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/flavor_text, _name = "Silicon Flavor Text", _save_key = "silicon_flavor_text")
|
||||
AddElement(/datum/element/flavor_text, "", "Temporary Flavor Text", "This should be used only for things pertaining to the current round!")
|
||||
AddElement(/datum/element/flavor_text, _name = "Silicon Flavor Text", _always_show = TRUE, _save_key = "silicon_flavor_text")
|
||||
AddElement(/datum/element/flavor_text, "", "Temporary Flavor Text", "This should be used only for things pertaining to the current round!", _always_show = TRUE)
|
||||
AddElement(/datum/element/flavor_text, _name = "OOC Notes", _addendum = "Put information on ERP/vore/lewd-related preferences here. THIS SHOULD NOT CONTAIN REGULAR FLAVORTEXT!!", _always_show = TRUE, _save_key = "ooc_notes", _examine_no_preview = TRUE)
|
||||
|
||||
/mob/living/silicon/med_hud_set_health()
|
||||
|
||||
@@ -79,6 +79,8 @@
|
||||
M.visible_message("[M] pets [src].", \
|
||||
"<span class='notice'>You pet [src].</span>", target = src,
|
||||
target_message = "[M] pets you.")
|
||||
if(INTENT_DISARM)
|
||||
disarm_shove(M)
|
||||
if(INTENT_GRAB)
|
||||
grabbedby(M)
|
||||
else
|
||||
@@ -88,6 +90,9 @@
|
||||
"<span class='warning'>[M] punches you, but doesn't leave a dent.</span>", null, COMBAT_MESSAGE_RANGE, null, M,
|
||||
"<span class='danger'>You punch [src], but don't leave a dent.</span>")
|
||||
|
||||
/mob/living/silicon/proc/disarm_shove(mob/living/carbon/human/H)
|
||||
visible_message(span_danger("[H] shoves [src], but doesn't manage to make much of an effect."))
|
||||
|
||||
/mob/living/silicon/attack_drone(mob/living/simple_animal/drone/M)
|
||||
if(M.a_intent == INTENT_HARM)
|
||||
return
|
||||
|
||||
@@ -1059,5 +1059,5 @@ Pass a positive integer as an argument to override a bot's default speed.
|
||||
I.icon_state = null
|
||||
path.Cut(1, 2)
|
||||
|
||||
/mob/living/silicon/rust_heretic_act()
|
||||
/mob/living/simple_animal/bot/rust_heretic_act()
|
||||
adjustBruteLoss(500)
|
||||
|
||||
@@ -753,6 +753,12 @@
|
||||
else
|
||||
return null
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/remove_air_ratio(ratio)
|
||||
if(loc)
|
||||
return loc.remove_air_ratio(ratio)
|
||||
else
|
||||
return null
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/do_resist()
|
||||
. = ..()
|
||||
if(load)
|
||||
|
||||
@@ -192,6 +192,11 @@
|
||||
back.contract_next_chain_into_single_tile()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy/proc/get_length()
|
||||
. += 1
|
||||
if(back)
|
||||
. += back.get_length()
|
||||
|
||||
///Updates the next mob in the chain to move to our last location, fixed the worm if somehow broken.
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy/proc/update_chain_links()
|
||||
gib_trail()
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
verb_yell = "buzzes intensely"
|
||||
emote_see = list("buzzes.", "makes a loud buzz.", "rolls several times.", "buzzes happily.")
|
||||
speak_chance = 1
|
||||
unique_name = TRUE
|
||||
unique_name = FALSE
|
||||
|
||||
/mob/living/simple_animal/pet/bumbles/Initialize()
|
||||
. = ..()
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//Drone hands
|
||||
|
||||
|
||||
/mob/living/simple_animal/drone/doUnEquip(obj/item/I, force, silent = FALSE)
|
||||
/mob/living/simple_animal/drone/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
|
||||
if(..())
|
||||
update_inv_hands()
|
||||
if(I == head)
|
||||
|
||||
@@ -280,6 +280,9 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
if(3)
|
||||
adjustBruteLoss(30)
|
||||
|
||||
/mob/living/simple_animal/hostile/guardian/wave_ex_act(power, datum/wave_explosion/explosion, dir)
|
||||
adjustBruteLoss(EXPLOSION_POWER_STANDARD_SCALE_MOB_DAMAGE(power, explosion.mob_damage_mod * 0.33))
|
||||
|
||||
/mob/living/simple_animal/hostile/guardian/gib()
|
||||
if(summoner)
|
||||
to_chat(summoner, "<span class='danger'><B>Your [src] was blown up!</span></B>")
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
melee_damage_upper = 25
|
||||
attack_verb_continuous = "slashes"
|
||||
attack_verb_simple = "slash"
|
||||
gold_core_spawnable = HOSTILE_SPAWN
|
||||
speak_emote = list("hisses")
|
||||
bubble_icon = "alien"
|
||||
a_intent = INTENT_HARM
|
||||
@@ -36,7 +37,6 @@
|
||||
see_in_dark = 8
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
|
||||
unique_name = 1
|
||||
gold_core_spawnable = NO_SPAWN
|
||||
death_sound = 'sound/voice/hiss6.ogg'
|
||||
deathmessage = "lets out a waning guttural screech, green blood bubbling from its maw..."
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
/obj/item/stack/sheet/animalhide/xeno = 1)
|
||||
projectiletype = /obj/item/projectile/neurotox
|
||||
projectilesound = 'sound/weapons/pierce.ogg'
|
||||
gold_core_spawnable = NO_SPAWN
|
||||
status_flags = 0
|
||||
unique_name = 0
|
||||
var/sterile = 1
|
||||
@@ -148,6 +149,7 @@
|
||||
icon_state = "alienq"
|
||||
icon_living = "alienq"
|
||||
icon_dead = "alienq_dead"
|
||||
health_doll_icon = "alienq"
|
||||
bubble_icon = "alienroyal"
|
||||
move_to_delay = 4
|
||||
maxHealth = 400
|
||||
|
||||
@@ -58,12 +58,17 @@
|
||||
var/icon_base = "bee"
|
||||
var/static/beehometypecache = typecacheof(/obj/structure/beebox)
|
||||
var/static/hydroponicstypecache = typecacheof(/obj/machinery/hydroponics)
|
||||
var/held_icon = "" // bees are small and have no held icon (aka the coder doesn't know how to sprite it)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/bees/Initialize()
|
||||
. = ..()
|
||||
generate_bee_visuals()
|
||||
AddComponent(/datum/component/swarming)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/bees/ComponentInitialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/mob_holder, held_icon, escape_on_find = TRUE)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/bees/Destroy()
|
||||
if(beehome)
|
||||
beehome.bees -= src
|
||||
|
||||
@@ -133,6 +133,10 @@
|
||||
chance_to_hold_onto_points = highest_cost*0.5
|
||||
if(points != max_points && prob(chance_to_hold_onto_points))
|
||||
return //Let's save our points for a better ability (unless we're at max points, in which case we can't save anymore!)
|
||||
do_ability()
|
||||
|
||||
/datum/boss_active_timed_battle/proc/do_ability()
|
||||
set waitfor = FALSE
|
||||
if(!boss.client)
|
||||
abilities = shuffle(abilities)
|
||||
for(var/ab in abilities)
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
icon_living = "megacarp"
|
||||
icon_dead = "megacarp_dead"
|
||||
icon_gib = "megacarp_gib"
|
||||
health_doll_icon = "megacarp"
|
||||
regen_amount = 6
|
||||
|
||||
maxHealth = 30
|
||||
|
||||
@@ -6,37 +6,108 @@
|
||||
icon_living = "cat_butcher"
|
||||
icon_dead = "syndicate_dead"
|
||||
icon_gib = "syndicate_gib"
|
||||
projectiletype = /obj/item/projectile/bullet/dart/catranq
|
||||
projectilesound = 'sound/items/syringeproj.ogg'
|
||||
ranged = 1
|
||||
ranged_message = "fires the syringe gun at"
|
||||
ranged_cooldown_time = 30
|
||||
speak_chance = 0
|
||||
turns_per_move = 5
|
||||
response_help_continuous = "pokes"
|
||||
response_harm_continuous = "hits"
|
||||
speed = 0
|
||||
stat_attack = UNCONSCIOUS
|
||||
robust_searching = 1
|
||||
maxHealth = 100
|
||||
health = 100
|
||||
harm_intent_damage = 5
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 15
|
||||
attack_verb_continuous = "slashes at"
|
||||
attack_verb_simple = "slash at"
|
||||
attack_sound = 'sound/weapons/circsawhit.ogg'
|
||||
a_intent = INTENT_HARM
|
||||
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
|
||||
loot = list(/obj/effect/mob_spawn/human/corpse/cat_butcher, /obj/item/circular_saw)
|
||||
mob_biotypes = list(MOB_ORGANIC, MOB_HUMANOID)
|
||||
loot = list(/obj/effect/mob_spawn/human/corpse/cat_butcher, /obj/item/circular_saw, /obj/item/gun/syringe)
|
||||
atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0)
|
||||
unsuitable_atmos_damage = 15
|
||||
faction = list("hostile")
|
||||
check_friendly_fire = 1
|
||||
status_flags = CANPUSH
|
||||
del_on_death = 1
|
||||
del_on_death = TRUE
|
||||
var/impatience = 0
|
||||
rapid_melee = 2 //this lets him attack nearly as fast as a normal player, instead of half as fast as one. Without this, due to mood differences, a greytider in melee can actually facetank and beat him to death with only fists
|
||||
dodging = TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/cat_butcherer/Life()
|
||||
. = ..()
|
||||
if(prob(10) && health <= maxHealth && !target) //heal himself when not in combat
|
||||
var/healamount = min(maxHealth-health, 20)
|
||||
visible_message("[src] medicates themself.", "<span class='notice'>You medicate yourself.</span>")
|
||||
health += healamount
|
||||
|
||||
/mob/living/simple_animal/hostile/cat_butcherer/CanAttack(atom/the_target)
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/human/C = target
|
||||
if(C.getorgan(/obj/item/organ/ears/cat) && C.getorgan(/obj/item/organ/tail/cat) && C.has_trauma_type(/datum/brain_trauma/severe/pacifism))//he wont attack his creations
|
||||
if(C.stat && !istype(C.dna.species, /datum/species/ipc))//unless they need healing
|
||||
return ..()
|
||||
else
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/cat_butcherer/AttackingTarget()
|
||||
. = ..()
|
||||
if(. && prob(35) && iscarbon(target))
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/human/L = target
|
||||
var/obj/item/organ/tail/cat/tail = L.getorgan(/obj/item/organ/tail/cat)
|
||||
if(!QDELETED(tail))
|
||||
visible_message("[src] severs [L]'s tail in one swift swipe!", "<span class='notice'>You sever [L]'s tail in one swift swipe.</span>")
|
||||
tail.Remove()
|
||||
var/obj/item/organ/tail/cat/dropped_tail = new(target.drop_location())
|
||||
dropped_tail.color = L.hair_color
|
||||
return 1
|
||||
if(!L.getorgan(/obj/item/organ/ears/cat) && L.stat) //target doesnt have cat ears
|
||||
if(L.getorgan(/obj/item/organ/ears)) //slice off the old ears
|
||||
var/obj/item/organ/ears/ears = L.getorgan(/obj/item/organ/ears)
|
||||
visible_message("[src] slices off [L]'s ears!", "<span class='notice'>You slice [L]'s ears off.</span>")
|
||||
ears.Remove(L)
|
||||
ears.forceMove(get_turf(L))
|
||||
else //implant new ears
|
||||
visible_message("[src] attaches a pair of cat ears to [L]!", "<span class='notice'>You attach a pair of cat ears to [L].</span>")
|
||||
var/obj/item/organ/ears/cat/newears = new
|
||||
newears.Insert(L, drop_if_replaced = FALSE)
|
||||
return
|
||||
else if(!L.getorgan(/obj/item/organ/tail/cat) && L.stat)
|
||||
if(L.getorgan(/obj/item/organ/tail)) //cut off the tail if they have one already
|
||||
var/obj/item/organ/tail/tail = L.getorgan(/obj/item/organ/tail)
|
||||
visible_message("[src] severs [L]'s tail in one swift swipe!", "<span class='notice'>You sever [L]'s tail in one swift swipe.</span>")
|
||||
tail.Remove(L)
|
||||
tail.forceMove(get_turf(L))
|
||||
else //put a cat tail on
|
||||
visible_message("[src] attaches a cat tail to [L]!", "<span class='notice'>You attach a tail to [L].</span>")
|
||||
var/obj/item/organ/tail/cat/newtail = new
|
||||
newtail.Insert(L, drop_if_replaced = FALSE)
|
||||
return
|
||||
else if(!L.has_trauma_type(/datum/brain_trauma/severe/pacifism) && L.getorgan(/obj/item/organ/ears/cat) && L.getorgan(/obj/item/organ/tail/cat)) //still does damage. This also lacks a Stat check- felinids beware.
|
||||
visible_message("[src] drills a hole in [L]'s skull!", "<span class='notice'>You pacify [L]. Another successful creation.</span>")
|
||||
if(L.stat == CONSCIOUS)
|
||||
L.emote("scream")
|
||||
L.gain_trauma(/datum/brain_trauma/severe/pacifism, TRAUMA_RESILIENCE_BASIC)
|
||||
say("Such a GOOD KITTY!!")
|
||||
if(L.mind && maxHealth <= 300) //if he robusts a tider, he becomes stronger
|
||||
maxHealth += 20
|
||||
adjustHealth(-(maxHealth)) //he heals whenever he finishes
|
||||
else if(L.stat) //quickly heal them up and move on to our next target!
|
||||
visible_message("[src] injects [L] with an unknown medicine!", "<span class='notice'>You inject [L] with medicine.</span>")
|
||||
L.SetSleeping(0, FALSE)
|
||||
L.SetUnconscious(0, FALSE)
|
||||
L.adjustOxyLoss(-50)// do CPR first
|
||||
if(L.blood_volume <= 500) //bandage them up and give em some blood if they're bleeding
|
||||
L.blood_volume += 30
|
||||
L.bleedsuppress = 0
|
||||
if(L.getBruteLoss() >= 50)// first, did we beat them into crit? if so, heal that
|
||||
var/healing = min(L.getBruteLoss(), 120)
|
||||
L.adjustBruteLoss(-healing)
|
||||
L.bleedsuppress = 0 //bandage their ass
|
||||
return
|
||||
else if(L.getFireLoss() >= 50) // are they still down from other damage? fix it, but not as fast as the burns
|
||||
var/healing = min(L.getFireLoss(), 50)
|
||||
L.adjustFireLoss(-healing)
|
||||
impatience += 50
|
||||
if(prob(impatience))
|
||||
FindTarget()//so we don't focus on some unconscious dude when we could get our eyes on the prize
|
||||
impatience = 0
|
||||
say("Bah!!")
|
||||
return
|
||||
return ..()
|
||||
|
||||
@@ -0,0 +1,457 @@
|
||||
GLOBAL_VAR_INIT(floor_cluwnes, 0)
|
||||
|
||||
#define STAGE_HAUNT 1
|
||||
#define STAGE_SPOOK 2
|
||||
#define STAGE_TORMENT 3
|
||||
#define STAGE_ATTACK 4
|
||||
#define MANIFEST_DELAY 9
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne
|
||||
name = "???"
|
||||
desc = "...."
|
||||
icon = 'icons/obj/clothing/masks.dmi'
|
||||
icon_state = "cluwne"
|
||||
icon_living = "cluwne"
|
||||
icon_gib = "clown_gib"
|
||||
maxHealth = 250
|
||||
health = 250
|
||||
speed = -1
|
||||
attack_verb_continuous = "attacks"
|
||||
attack_verb_simple = "attack"
|
||||
attack_sound = 'sound/items/bikehorn.ogg'
|
||||
del_on_death = TRUE
|
||||
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB | LETPASSTHROW | PASSGLASS | PASSBLOB//it's practically a ghost when unmanifested (under the floor)
|
||||
loot = list(/obj/item/clothing/mask/gas/cluwne)
|
||||
wander = FALSE
|
||||
minimum_distance = 2
|
||||
move_to_delay = 1
|
||||
movement_type = FLYING
|
||||
environment_smash = FALSE
|
||||
lose_patience_timeout = FALSE
|
||||
pixel_y = 8
|
||||
pressure_resistance = 200
|
||||
minbodytemp = 0
|
||||
maxbodytemp = 1500
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
var/mob/living/carbon/human/current_victim
|
||||
var/manifested = FALSE
|
||||
var/switch_stage = 60
|
||||
var/stage = STAGE_HAUNT
|
||||
var/interest = 0
|
||||
var/target_area
|
||||
var/invalid_area_typecache = list(/area/space, /area/lavaland, /area/centcom, /area/reebe, /area/shuttle/syndicate)
|
||||
var/eating = FALSE
|
||||
var/dontkill = FALSE //for if we just wanna curse a fucker
|
||||
var/obj/effect/dummy/floorcluwne_orbit/poi
|
||||
var/obj/effect/temp_visual/fcluwne_manifest/cluwnehole
|
||||
move_resist = INFINITY
|
||||
hud_type = /datum/hud/ghost
|
||||
hud_possible = list(ANTAG_HUD)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/Initialize()
|
||||
. = ..()
|
||||
access_card = new /obj/item/card/id(src)
|
||||
var/datum/job/captain/C = new /datum/job/captain
|
||||
access_card.access = C.get_access()
|
||||
invalid_area_typecache = typecacheof(invalid_area_typecache)
|
||||
Manifest()
|
||||
if(!current_victim)
|
||||
Acquire_Victim()
|
||||
poi = new(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/med_hud_set_health()
|
||||
return //we use a different hud
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/med_hud_set_status()
|
||||
return //we use a different hud
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/Destroy()
|
||||
QDEL_NULL(poi)
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/attack_hand(mob/living/carbon/human/M)
|
||||
..()
|
||||
playsound(src.loc, 'sound/items/bikehorn.ogg', 50, 1)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/CanPass(atom/A, turf/target)
|
||||
return TRUE
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/Life()
|
||||
do_jitter_animation(1000)
|
||||
pixel_y = 8
|
||||
|
||||
var/area/A = get_area(src.loc)
|
||||
if(is_type_in_typecache(A, invalid_area_typecache) || !is_station_level(z))
|
||||
var/area = pick(GLOB.teleportlocs)
|
||||
var/area/tp = GLOB.teleportlocs[area]
|
||||
forceMove(pick(get_area_turfs(tp.type)))
|
||||
|
||||
if(!current_victim)
|
||||
Acquire_Victim()
|
||||
|
||||
if(stage && !manifested)
|
||||
INVOKE_ASYNC(src, .proc/On_Stage)
|
||||
|
||||
if(stage == STAGE_ATTACK)
|
||||
playsound(src, 'sound/misc/cluwne_breathing.ogg', 75, 1)
|
||||
|
||||
if(eating)
|
||||
return
|
||||
|
||||
var/turf/T = get_turf(current_victim)
|
||||
A = get_area(T)
|
||||
if(prob(5))//checks roughly every 20 ticks
|
||||
if(current_victim.stat == DEAD || current_victim.dna.check_mutation(CLUWNEMUT) || is_type_in_typecache(A, invalid_area_typecache) || !is_station_level(current_victim.z))
|
||||
if(!Found_You())
|
||||
Acquire_Victim()
|
||||
|
||||
if(get_dist(src, current_victim) > 9 && !manifested && !is_type_in_typecache(A, invalid_area_typecache))//if cluwne gets stuck he just teleports
|
||||
do_teleport(src, T)
|
||||
|
||||
interest++
|
||||
if(interest >= switch_stage * 4 && !dontkill)
|
||||
stage = STAGE_ATTACK
|
||||
|
||||
else if(interest >= switch_stage * 2)
|
||||
stage = STAGE_TORMENT
|
||||
|
||||
else if(interest >= switch_stage)
|
||||
stage = STAGE_SPOOK
|
||||
|
||||
else if(interest < switch_stage)
|
||||
stage = STAGE_HAUNT
|
||||
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/Goto(target, delay, minimum_distance)
|
||||
var/area/A = get_area(current_victim.loc)
|
||||
if(!manifested && !is_type_in_typecache(A, invalid_area_typecache) && is_station_level(current_victim.z))
|
||||
walk_to(src, target, minimum_distance, delay)
|
||||
else
|
||||
walk_to(src,0)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/FindTarget()
|
||||
return current_victim
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/CanAttack(atom/the_target)//you will not escape
|
||||
return TRUE
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/AttackingTarget()
|
||||
return
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/LoseTarget()
|
||||
return
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)//prevents runtimes with machine fuckery
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/proc/Found_You()
|
||||
for(var/obj/structure/closet/hiding_spot in orange(7,src))
|
||||
if(current_victim.loc == hiding_spot)
|
||||
hiding_spot.bust_open()
|
||||
current_victim.Paralyze(40)
|
||||
to_chat(current_victim, "<span class='warning'>...edih t'nac uoY</span>")
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/proc/Acquire_Victim(specific)
|
||||
for(var/I in GLOB.player_list)//better than a potential recursive loop
|
||||
var/mob/living/carbon/human/H = pick(GLOB.player_list)//so the check is fair
|
||||
var/area/A
|
||||
|
||||
if(specific)
|
||||
H = specific
|
||||
A = get_area(H.loc)
|
||||
if(H.stat != DEAD && H.has_dna() && !H.dna.check_mutation(CLUWNEMUT) && !is_type_in_typecache(A, invalid_area_typecache) && is_station_level(H.z))
|
||||
return target = current_victim
|
||||
|
||||
A = get_area(H.loc)
|
||||
if(H && ishuman(H) && H.stat != DEAD && H != current_victim && H.has_dna() && !H.dna.check_mutation(CLUWNEMUT) && !is_type_in_typecache(A, invalid_area_typecache) && is_station_level(H.z))
|
||||
current_victim = H
|
||||
interest = 0
|
||||
stage = STAGE_HAUNT
|
||||
return target = current_victim
|
||||
|
||||
message_admins("Floor Cluwne was deleted due to a lack of valid targets, if this was a manually targeted instance please re-evaluate your choice.")
|
||||
qdel(src)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/proc/Manifest()//handles disappearing and appearance anim
|
||||
if(manifested)
|
||||
mobility_flags &= ~MOBILITY_MOVE
|
||||
update_mobility()
|
||||
cluwnehole = new(src.loc)
|
||||
addtimer(CALLBACK(src, /mob/living/simple_animal/hostile/floor_cluwne/.proc/Appear), MANIFEST_DELAY)
|
||||
else
|
||||
layer = GAME_PLANE
|
||||
invisibility = INVISIBILITY_OBSERVER
|
||||
density = FALSE
|
||||
mobility_flags |= MOBILITY_MOVE
|
||||
update_mobility()
|
||||
if(cluwnehole)
|
||||
qdel(cluwnehole)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/proc/Appear()//handled in a seperate proc so floor cluwne doesn't appear before the animation finishes
|
||||
layer = LYING_MOB_LAYER
|
||||
invisibility = FALSE
|
||||
density = TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/proc/Reset_View(screens, colour, mob/living/carbon/human/H)
|
||||
if(screens)
|
||||
for(var/whole_screen in screens)
|
||||
animate(whole_screen, transform = matrix(), time = 5, easing = QUAD_EASING)
|
||||
if(colour && H)
|
||||
H.client.color = colour
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/proc/On_Stage()
|
||||
var/mob/living/carbon/human/H = current_victim
|
||||
switch(stage)
|
||||
|
||||
if(STAGE_HAUNT)
|
||||
|
||||
if(prob(5))
|
||||
H.blur_eyes(1)
|
||||
|
||||
if(prob(5))
|
||||
H.playsound_local(src,'sound/voice/cluwnelaugh2_reversed.ogg', 1)
|
||||
|
||||
if(prob(5))
|
||||
H.playsound_local(src,'sound/misc/bikehorn_creepy.ogg', 5)
|
||||
|
||||
if(prob(3))
|
||||
var/obj/item/I = locate() in orange(H, 8)
|
||||
if(I && !I.anchored)
|
||||
I.throw_at(H, 4, 3)
|
||||
to_chat(H, "<span class='warning'>What threw that?</span>")
|
||||
|
||||
if(STAGE_SPOOK)
|
||||
|
||||
if(prob(4))
|
||||
var/turf/T = get_turf(H)
|
||||
T.handle_slip(H, 20)
|
||||
to_chat(H, "<span class='warning'>The floor shifts underneath you!</span>")
|
||||
|
||||
if(prob(5))
|
||||
H.playsound_local(src,'sound/voice/cluwnelaugh2.ogg', 2)
|
||||
|
||||
if(prob(5))
|
||||
H.playsound_local(src,'sound/voice/cluwnelaugh2_reversed.ogg', 2)
|
||||
|
||||
if(prob(5))
|
||||
H.playsound_local(src,'sound/misc/bikehorn_creepy.ogg', 10)
|
||||
to_chat(H, "<i>knoh</i>")
|
||||
|
||||
if(prob(5))
|
||||
var/obj/item/I = locate() in orange(H, 8)
|
||||
if(I && !I.anchored)
|
||||
I.throw_at(H, 4, 3)
|
||||
to_chat(H, "<span class='warning'>What threw that?</span>")
|
||||
|
||||
if(prob(2))
|
||||
to_chat(H, "<i>yalp ot tnaw I</i>")
|
||||
Appear()
|
||||
manifested = FALSE
|
||||
addtimer(CALLBACK(src, /mob/living/simple_animal/hostile/floor_cluwne/.proc/Manifest), 1)
|
||||
|
||||
if(STAGE_TORMENT)
|
||||
|
||||
if(prob(5))
|
||||
var/turf/T = get_turf(H)
|
||||
T.handle_slip(H, 20)
|
||||
to_chat(H, "<span class='warning'>The floor shifts underneath you!</span>")
|
||||
|
||||
if(prob(3))
|
||||
playsound(src,pick('sound/spookoween/scary_horn.ogg', 'sound/spookoween/scary_horn2.ogg', 'sound/spookoween/scary_horn3.ogg'), 30, 1)
|
||||
|
||||
if(prob(3))
|
||||
playsound(src,'sound/voice/cluwnelaugh1.ogg', 30, 1)
|
||||
|
||||
if(prob(3))
|
||||
playsound(src,'sound/voice/cluwnelaugh2_reversed.ogg', 30, 1)
|
||||
|
||||
if(prob(5))
|
||||
playsound(src,'sound/misc/bikehorn_creepy.ogg', 30, 1)
|
||||
|
||||
if(prob(4))
|
||||
for(var/obj/item/I in orange(8, H))
|
||||
if(!I.anchored)
|
||||
I.throw_at(H, 4, 3)
|
||||
to_chat(H, "<span class='warning'>What the hell?!</span>")
|
||||
|
||||
if(prob(2))
|
||||
to_chat(H, "<span class='warning'>Something feels very wrong...</span>")
|
||||
H.playsound_local(src,'sound/hallucinations/behind_you1.ogg', 25)
|
||||
H.flash_act()
|
||||
|
||||
if(prob(2))
|
||||
to_chat(H, "<i>!?REHTOMKNOH eht esiarp uoy oD</i>")
|
||||
to_chat(H, "<span class='warning'>Something grabs your foot!</span>")
|
||||
H.playsound_local(src,'sound/hallucinations/i_see_you1.ogg', 25)
|
||||
H.Stun(20)
|
||||
|
||||
if(prob(3))
|
||||
to_chat(H, "<i>KNOH ?od nottub siht seod tahW</i>")
|
||||
for(var/turf/open/O in RANGE_TURFS(6, src))
|
||||
O.MakeSlippery(TURF_WET_WATER, 10)
|
||||
playsound(src, 'sound/effects/meteorimpact.ogg', 30, 1)
|
||||
|
||||
if(prob(1))
|
||||
to_chat(H, "<span class='userdanger'>WHAT THE FUCK IS THAT?!</span>")
|
||||
to_chat(H, "<i>.KNOH !nuf hcum os si uoy htiw gniyalP .KNOH KNOH KNOH</i>")
|
||||
H.playsound_local(src,'sound/hallucinations/im_here1.ogg', 25)
|
||||
H.reagents.add_reagent(/datum/reagent/toxin/mindbreaker, 3)
|
||||
H.reagents.add_reagent(/datum/reagent/consumable/laughter, 5)
|
||||
H.reagents.add_reagent(/datum/reagent/mercury, 3)
|
||||
Appear()
|
||||
manifested = FALSE
|
||||
addtimer(CALLBACK(src, /mob/living/simple_animal/hostile/floor_cluwne/.proc/Manifest), 2)
|
||||
for(var/obj/machinery/light/L in range(8, H))
|
||||
L.flicker()
|
||||
|
||||
|
||||
if(STAGE_ATTACK)
|
||||
if(dontkill)
|
||||
stage = STAGE_TORMENT
|
||||
return
|
||||
if(!eating)
|
||||
Found_You()
|
||||
for(var/I in getline(src,H))
|
||||
var/turf/T = I
|
||||
if(T.density)
|
||||
forceMove(H.loc)
|
||||
for(var/obj/structure/O in T)
|
||||
if(O.density || istype(O, /obj/machinery/door/airlock))
|
||||
forceMove(H.loc)
|
||||
to_chat(H, "<span class='userdanger'>You feel the floor closing in on your feet!</span>")
|
||||
H.Paralyze(300)
|
||||
INVOKE_ASYNC(H, /mob.proc/emote, "scream")
|
||||
H.adjustBruteLoss(10)
|
||||
manifested = TRUE
|
||||
Manifest()
|
||||
if(!eating)
|
||||
addtimer(CALLBACK(src, /mob/living/simple_animal/hostile/floor_cluwne/.proc/Grab, H), 50, TIMER_OVERRIDE|TIMER_UNIQUE)
|
||||
for(var/turf/open/O in RANGE_TURFS(6, src))
|
||||
O.MakeSlippery(TURF_WET_LUBE, 20)
|
||||
playsound(src, 'sound/effects/meteorimpact.ogg', 30, 1)
|
||||
eating = TRUE
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/proc/Grab(mob/living/carbon/human/H)
|
||||
if (H != current_victim)
|
||||
message_admins("Cluwne tried to grab someone who's not the target. Returning to life stage.")
|
||||
return
|
||||
to_chat(H, "<span class='userdanger'>You feel a cold, gloved hand clamp down on your ankle!</span>")
|
||||
for(var/I in 1 to get_dist(src, H))
|
||||
if(do_after(src, 5, target = H))
|
||||
step_towards(H, src)
|
||||
playsound(H, pick('sound/effects/bodyscrape-01.ogg', 'sound/effects/bodyscrape-02.ogg'), 20, 1, -4)
|
||||
if(prob(40))
|
||||
H.emote("scream")
|
||||
else if(prob(25))
|
||||
H.say(pick("HELP ME!!","IT'S GOT ME!!","DON'T LET IT TAKE ME!!",";SOMETHING'S KILLING ME!!","HOLY FUCK!!"))
|
||||
playsound(src, pick('sound/voice/cluwnelaugh1.ogg', 'sound/voice/cluwnelaugh2.ogg', 'sound/voice/cluwnelaugh3.ogg'), 50, 1)
|
||||
|
||||
if(get_dist(src,H) <= 1)
|
||||
visible_message("<span class='danger'>[src] begins dragging [H] under the floor!</span>")
|
||||
if(do_after(src, 50, target = H) && eating)
|
||||
H.become_blind()
|
||||
H.layer = GAME_PLANE
|
||||
H.invisibility = INVISIBILITY_OBSERVER
|
||||
H.density = FALSE
|
||||
H.anchored = TRUE
|
||||
addtimer(CALLBACK(src, /mob/living/simple_animal/hostile/floor_cluwne/.proc/Kill, H), 100, TIMER_OVERRIDE|TIMER_UNIQUE)
|
||||
visible_message("<span class='danger'>[src] pulls [H] under!</span>")
|
||||
to_chat(H, "<span class='userdanger'>[src] drags you underneath the floor!</span>")
|
||||
else
|
||||
eating = FALSE
|
||||
else
|
||||
eating = FALSE
|
||||
manifested = FALSE
|
||||
Manifest()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/floor_cluwne/proc/Kill(mob/living/carbon/human/H)
|
||||
if (H != current_victim)
|
||||
message_admins("Cluwne tried to kill someone who's not the target. Returning to life stage.")
|
||||
H.invisibility = initial(H.invisibility)
|
||||
return
|
||||
if(!istype(H) || !H.client)
|
||||
H.invisibility = initial(H.invisibility)
|
||||
Acquire_Victim()
|
||||
return
|
||||
playsound(H, 'sound/effects/cluwne_feast.ogg', 100, 0, -4)
|
||||
var/old_color = H.client.color
|
||||
var/red_splash = list(1,0,0,0.8,0.2,0, 0.8,0,0.2,0.1,0,0)
|
||||
var/pure_red = list(0,0,0,0,0,0,0,0,0,1,0,0)
|
||||
H.client.color = pure_red
|
||||
animate(H.client,color = red_splash, time = 10, easing = SINE_EASING|EASE_OUT)
|
||||
for(var/turf/T in orange(H, 4))
|
||||
H.add_splatter_floor(T)
|
||||
if(do_after(src, 50, target = H))
|
||||
H.unequip_everything()//more runtime prevention
|
||||
if(prob(75))
|
||||
H.gib(FALSE)
|
||||
else
|
||||
H.cluwneify()
|
||||
H.adjustBruteLoss(30)
|
||||
H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 100, 100)
|
||||
H.cure_blind()
|
||||
H.layer = initial(H.layer)
|
||||
H.invisibility = initial(H.invisibility)
|
||||
H.density = initial(H.density)
|
||||
H.anchored = initial(H.anchored)
|
||||
H.blur_eyes(10)
|
||||
animate(H.client,color = old_color, time = 20)
|
||||
|
||||
eating = FALSE
|
||||
switch_stage = switch_stage * 0.75 //he gets faster after each feast
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
M.playsound_local(get_turf(M), 'sound/misc/honk_echo_distant.ogg', 50, 1, pressure_affected = FALSE)
|
||||
|
||||
interest = 0
|
||||
stage = STAGE_HAUNT
|
||||
Acquire_Victim()
|
||||
|
||||
//manifestation animation
|
||||
/obj/effect/temp_visual/fcluwne_manifest
|
||||
icon = 'icons/turf/floors.dmi'
|
||||
icon_state = "fcluwne_open"
|
||||
layer = TURF_LAYER
|
||||
duration = 600
|
||||
randomdir = FALSE
|
||||
|
||||
/obj/effect/temp_visual/fcluwne_manifest/Initialize()
|
||||
. = ..()
|
||||
playsound(src, 'sound/misc/floor_cluwne_emerge.ogg', 100, 1)
|
||||
flick("fcluwne_manifest",src)
|
||||
|
||||
/obj/effect/dummy/floorcluwne_orbit
|
||||
name = "floor cluwne"
|
||||
desc = "If you have this, tell a coder or admin!"
|
||||
|
||||
/obj/effect/dummy/floorcluwne_orbit/Initialize()
|
||||
. = ..()
|
||||
GLOB.floor_cluwnes++
|
||||
name += " ([GLOB.floor_cluwnes])"
|
||||
GLOB.poi_list += src
|
||||
|
||||
/obj/effect/dummy/floorcluwne_orbit/Destroy()
|
||||
. = ..()
|
||||
GLOB.poi_list -= src
|
||||
|
||||
#undef STAGE_HAUNT
|
||||
#undef STAGE_SPOOK
|
||||
#undef STAGE_TORMENT
|
||||
#undef STAGE_ATTACK
|
||||
#undef MANIFEST_DELAY
|
||||
@@ -9,6 +9,7 @@
|
||||
icon_state = "crawling"
|
||||
icon_living = "crawling"
|
||||
icon_dead = "dead"
|
||||
health_doll_icon = "crawling"
|
||||
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
|
||||
speak_chance = 80
|
||||
maxHealth = 220
|
||||
|
||||
@@ -364,11 +364,10 @@
|
||||
/mob/living/simple_animal/hostile/proc/AttackingTarget()
|
||||
SEND_SIGNAL(src, COMSIG_HOSTILE_ATTACKINGTARGET, target)
|
||||
in_melee = TRUE
|
||||
/* sorry for the simplemob vore fans
|
||||
if(vore_active)
|
||||
if(isliving(target))
|
||||
var/mob/living/L = target
|
||||
if(!client && L.Adjacent(src) && CHECK_BITFIELD(L.vore_flags,DEVOURABLE)) // aggressive check to ensure vore attacks can be made
|
||||
if(!client && L.Adjacent(src) && CHECK_BITFIELD(L.vore_flags, DEVOURABLE) && CHECK_BITFIELD(L.vore_flags, MOBVORE)) // aggressive check to ensure vore attacks can be made
|
||||
if(prob(voracious_chance))
|
||||
vore_attack(src,L,src)
|
||||
else
|
||||
@@ -379,7 +378,6 @@
|
||||
return target.attack_animal(src)
|
||||
else
|
||||
return target.attack_animal(src)
|
||||
*/
|
||||
return target.attack_animal(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/Aggro()
|
||||
|
||||
@@ -28,6 +28,7 @@ Difficulty: Medium
|
||||
icon_state = "miner"
|
||||
icon_living = "miner"
|
||||
icon = 'icons/mob/broadMobs.dmi'
|
||||
health_doll_icon = "miner"
|
||||
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
|
||||
light_color = "#E4C7C5"
|
||||
movement_type = GROUND
|
||||
@@ -39,8 +40,10 @@ Difficulty: Medium
|
||||
ranged = 1
|
||||
ranged_cooldown_time = 16
|
||||
pixel_x = -16
|
||||
crusher_loot = list(/obj/item/melee/transforming/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator/premiumka, /obj/item/crusher_trophy/miner_eye)
|
||||
loot = list(/obj/item/melee/transforming/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator/premiumka)
|
||||
crusher_loot = list(/obj/item/melee/transforming/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator/premiumka, /obj/item/crusher_trophy/miner_eye, /obj/item/disk/design_disk/modkit_disc/mob_and_turf_aoe,
|
||||
/obj/item/disk/design_disk/modkit_disc/bounty,/obj/item/disk/design_disk/modkit_disc/resonator_blast,/obj/item/disk/design_disk/modkit_disc/rapid_repeater)
|
||||
loot = list(/obj/item/melee/transforming/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator/premiumka,/obj/item/disk/design_disk/modkit_disc/mob_and_turf_aoe,
|
||||
/obj/item/disk/design_disk/modkit_disc/bounty,/obj/item/disk/design_disk/modkit_disc/resonator_blast,/obj/item/disk/design_disk/modkit_disc/rapid_repeater)
|
||||
wander = FALSE
|
||||
del_on_death = TRUE
|
||||
blood_volume = BLOOD_VOLUME_NORMAL
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#define MEDAL_PREFIX "Bubblegum"
|
||||
|
||||
/*
|
||||
|
||||
BUBBLEGUM
|
||||
@@ -7,24 +5,25 @@ BUBBLEGUM
|
||||
Bubblegum spawns randomly wherever a lavaland creature is able to spawn. It is the most powerful slaughter demon in existence.
|
||||
Bubblegum's footsteps are heralded by shaking booms, proving its tremendous size.
|
||||
|
||||
It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power that increase as it takes damage.
|
||||
It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power
|
||||
|
||||
It often charges, dealing massive damage to anything unfortunate enough to be standing where it's aiming.
|
||||
Whenever it isn't chasing something down, it will sink into nearby blood pools (if possible) and springs out of the closest one to its target.
|
||||
To make this possible, it sprays streams of blood at random.
|
||||
From these blood pools Bubblegum may summon slaughterlings - weak, low-damage minions designed to impede the target's progress.
|
||||
It leaves blood trails behind wherever it goes, its clones do as well.
|
||||
It tries to strike at its target through any bloodpools under them; if it fails to do that.
|
||||
If it does warp it will enter an enraged state, becoming immune to all projectiles, becoming much faster, and dealing damage and knockback to anything that gets in the cloud around it.
|
||||
It may summon clones charging from all sides, one of these charges being bubblegum himself.
|
||||
It can charge at its target, and also heavily damaging anything directly hit in the charge.
|
||||
If at half health it will start to charge from all sides with clones.
|
||||
|
||||
When Bubblegum dies, it leaves behind a H.E.C.K. suit+helmet as well as a chest that can contain three things:
|
||||
1. A spellblade that can slice off limbs at range
|
||||
2. A bottle that, when activated, drives everyone nearby into a frenzy
|
||||
3. A super double-barrel shotgun that shoots both shells at the same time.
|
||||
When Bubblegum dies, it leaves behind a H.E.C.K. mining suit as well as a chest that can contain three things:
|
||||
1. A bottle that, when activated, drives everyone nearby into a frenzy
|
||||
2. A scroll that teaches the reader a martial art that sacrifices health for raw demonic power with their bare hands.
|
||||
|
||||
Difficulty: Hard
|
||||
|
||||
*/
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum
|
||||
name = "bubblegum"
|
||||
name = "Bubblegum"
|
||||
desc = "In what passes for a hierarchy among slaughter demons, this one is king."
|
||||
health = 2500
|
||||
maxHealth = 2500
|
||||
@@ -34,6 +33,7 @@ Difficulty: Hard
|
||||
icon_state = "bubblegum"
|
||||
icon_living = "bubblegum"
|
||||
icon_dead = ""
|
||||
health_doll_icon = "bubblegum"
|
||||
friendly_verb_continuous = "stares down"
|
||||
friendly_verb_simple = "stare down"
|
||||
icon = 'icons/mob/lavaland/96x96megafauna.dmi'
|
||||
@@ -42,15 +42,20 @@ Difficulty: Hard
|
||||
melee_damage_lower = 40
|
||||
melee_damage_upper = 40
|
||||
speed = 1
|
||||
move_to_delay = 10
|
||||
ranged_cooldown_time = 10
|
||||
move_to_delay = 5
|
||||
retreat_distance = 5
|
||||
minimum_distance = 5
|
||||
rapid_melee = 8 // every 1/4 second
|
||||
melee_queue_distance = 20 // as far as possible really, need this because of blood warp
|
||||
ranged = 1
|
||||
pixel_x = -32
|
||||
gender = MALE
|
||||
del_on_death = 1
|
||||
crusher_loot = list(/obj/structure/closet/crate/necropolis/bubblegum/crusher)
|
||||
loot = list(/obj/structure/closet/crate/necropolis/bubblegum)
|
||||
blood_volume = BLOOD_VOLUME_MAXIMUM //BLEED FOR ME
|
||||
var/charging = 0
|
||||
var/enrage_till = null
|
||||
|
||||
achievement_type = /datum/award/achievement/boss/bubblegum_kill
|
||||
crusher_achievement_type = /datum/award/achievement/boss/bubblegum_crusher
|
||||
@@ -67,36 +72,60 @@ Difficulty: Hard
|
||||
desc = "You're not quite sure how a signal can be bloody."
|
||||
invisibility = 100
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/BiologicalLife(seconds, times_fired)
|
||||
if(!(. = ..()))
|
||||
return
|
||||
move_to_delay = clamp(round((health/maxHealth) * 10), 3, 10)
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE, only_robotic = FALSE, only_organic = TRUE)
|
||||
. = ..()
|
||||
if(. > 0 && prob(25))
|
||||
var/obj/effect/decal/cleanable/blood/gibs/bubblegum/B = new /obj/effect/decal/cleanable/blood/gibs/bubblegum(loc)
|
||||
if(prob(40))
|
||||
step(B, pick(GLOB.cardinals))
|
||||
else
|
||||
B.setDir(pick(GLOB.cardinals))
|
||||
|
||||
/obj/effect/decal/cleanable/blood/gibs/bubblegum
|
||||
name = "thick blood"
|
||||
desc = "Thick, splattered blood."
|
||||
random_icon_states = list("gib3", "gib5", "gib6")
|
||||
bloodiness = 20
|
||||
|
||||
/obj/effect/decal/cleanable/blood/gibs/bubblegum/can_bloodcrawl_in()
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/OpenFire()
|
||||
anger_modifier = clamp(((maxHealth - health)/50),0,20)
|
||||
anger_modifier = clamp(((maxHealth - health)/60),0,20)
|
||||
if(charging)
|
||||
return
|
||||
ranged_cooldown = world.time + ranged_cooldown_time
|
||||
ranged_cooldown = world.time + 50
|
||||
if(!try_bloodattack())
|
||||
blood_warp()
|
||||
|
||||
blood_warp()
|
||||
|
||||
if(prob(25))
|
||||
INVOKE_ASYNC(src, .proc/blood_spray)
|
||||
|
||||
else if(prob(5+anger_modifier/2))
|
||||
slaughterlings()
|
||||
else
|
||||
if(health > maxHealth/2 && !client)
|
||||
INVOKE_ASYNC(src, .proc/charge)
|
||||
if(health > maxHealth * 0.5)
|
||||
if(prob(50 + anger_modifier))
|
||||
charge(delay = 6)
|
||||
charge(delay = 4) // The FitnessGram Pacer Test is a multistage aerobic capacity test that progressively gets more difficult as it continues.
|
||||
charge(delay = 2)
|
||||
SetRecoveryTime(15)
|
||||
else
|
||||
INVOKE_ASYNC(src, .proc/triple_charge)
|
||||
hallucination_charge_around(times = 6, delay = 10 - anger_modifier / 5)
|
||||
SetRecoveryTime(10)
|
||||
else
|
||||
if(prob(50 - anger_modifier))
|
||||
hallucination_charge_around(times = 4, delay = 9)
|
||||
hallucination_charge_around(times = 4, delay = 8)
|
||||
hallucination_charge_around(times = 4, delay = 7)
|
||||
SetRecoveryTime(15)
|
||||
else
|
||||
for(var/i = 1 to 5)
|
||||
INVOKE_ASYNC(src, .proc/hallucination_charge_around, 2, 10, 2, 0)
|
||||
sleep(5)
|
||||
SetRecoveryTime(10)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/Initialize()
|
||||
. = ..()
|
||||
for(var/mob/living/simple_animal/hostile/megafauna/bubblegum/B in GLOB.mob_list)
|
||||
if(B != src)
|
||||
return INITIALIZE_HINT_QDEL //There can be only one
|
||||
if(istype(src, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination))
|
||||
return
|
||||
for(var/mob/living/simple_animal/hostile/megafauna/bubblegum/B in GLOB.mob_living_list) if(B != src)
|
||||
return INITIALIZE_HINT_QDEL //There can be only one
|
||||
var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new
|
||||
AddSpell(bloodspell)
|
||||
if(istype(loc, /obj/effect/dummy/phased_mob/slaughter))
|
||||
@@ -106,55 +135,81 @@ Difficulty: Hard
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/grant_achievement(medaltype,scoretype)
|
||||
. = ..()
|
||||
if(.)
|
||||
SSshuttle.shuttle_purchase_requirements_met |= "bubblegum"
|
||||
SSshuttle.shuttle_purchase_requirements_met["bubblegum"] = TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
|
||||
if(charging)
|
||||
return
|
||||
..()
|
||||
if(!charging)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/AttackingTarget()
|
||||
if(charging)
|
||||
if(!charging)
|
||||
. = ..()
|
||||
if(.)
|
||||
recovery_time = world.time + 20 // can only attack melee once every 2 seconds but rapid_melee gives higher priority
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/bullet_act(obj/item/projectile/P)
|
||||
if(is_enraged())
|
||||
visible_message("<span class='danger'>[src] deflects the projectile; [p_they()] can't be hit with ranged weapons while enraged!</span>", "<span class='userdanger'>You deflect the projectile!</span>")
|
||||
playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 300, 1)
|
||||
return 0
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/ex_act(severity, target)
|
||||
if(severity >= EXPLODE_LIGHT)
|
||||
return
|
||||
..()
|
||||
severity = EXPLODE_LIGHT // puny mortals
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/CanPass(atom/movable/mover, turf/target)
|
||||
if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination))
|
||||
return 1
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/Goto(target, delay, minimum_distance)
|
||||
if(charging)
|
||||
return
|
||||
..()
|
||||
if(!charging)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/MoveToTarget(list/possible_targets)
|
||||
if(!charging)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/Move()
|
||||
if(!stat)
|
||||
playsound(src.loc, 'sound/effects/meteorimpact.ogg', 200, 1, 2, 1)
|
||||
if(charging)
|
||||
new/obj/effect/temp_visual/decoy/fading(loc,src)
|
||||
new /obj/effect/temp_visual/decoy/fading(loc,src)
|
||||
DestroySurroundings()
|
||||
. = ..()
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/Moved()
|
||||
new /obj/effect/decal/cleanable/blood(src.loc)
|
||||
if(charging)
|
||||
DestroySurroundings()
|
||||
playsound(src, 'sound/effects/meteorimpact.ogg', 200, 1, 2, 1)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/triple_charge()
|
||||
charge()
|
||||
sleep(10)
|
||||
charge()
|
||||
sleep(10)
|
||||
charge()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge()
|
||||
var/turf/T = get_turf(target)
|
||||
if(!T || T == loc)
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge(atom/chargeat = target, delay = 3, chargepast = 2)
|
||||
if(!chargeat)
|
||||
return
|
||||
new /obj/effect/temp_visual/dragon_swoop(T)
|
||||
var/chargeturf = get_turf(chargeat)
|
||||
if(!chargeturf)
|
||||
return
|
||||
var/dir = get_dir(src, chargeturf)
|
||||
var/turf/T = get_ranged_target_turf(chargeturf, dir, chargepast)
|
||||
if(!T)
|
||||
return
|
||||
new /obj/effect/temp_visual/dragon_swoop/bubblegum(T)
|
||||
charging = 1
|
||||
DestroySurroundings()
|
||||
walk(src, 0)
|
||||
setDir(get_dir(src, T))
|
||||
setDir(dir)
|
||||
var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc,src)
|
||||
animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 5)
|
||||
sleep(5)
|
||||
throw_at(T, get_dist(src, T), 1, src, 0)
|
||||
animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 3)
|
||||
sleep(delay)
|
||||
var/movespeed = 0.7
|
||||
walk_towards(src, T, movespeed)
|
||||
sleep(get_dist(src, T) * movespeed)
|
||||
walk(src, 0) // cancel the movement
|
||||
try_bloodattack()
|
||||
charging = 0
|
||||
Goto(target, move_to_delay, minimum_distance)
|
||||
|
||||
/**
|
||||
* Attack by override for bubblegum
|
||||
@@ -176,36 +231,147 @@ Difficulty: Hard
|
||||
if(isturf(A) || isobj(A) && A.density)
|
||||
A.ex_act(EXPLODE_HEAVY)
|
||||
DestroySurroundings()
|
||||
if(isliving(A))
|
||||
var/mob/living/L = A
|
||||
L.visible_message("<span class='danger'>[src] slams into [L]!</span>", "<span class='userdanger'>[src] tramples you into the ground!</span>")
|
||||
src.forceMove(get_turf(L))
|
||||
L.apply_damage(istype(src, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination) ? 15 : 30, BRUTE)
|
||||
playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, 1)
|
||||
shake_camera(L, 4, 3)
|
||||
shake_camera(src, 2, 3)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
|
||||
if(!charging)
|
||||
return ..()
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_mobs_on_blood()
|
||||
var/list/targets = ListTargets()
|
||||
. = list()
|
||||
for(var/mob/living/L in targets)
|
||||
var/list/bloodpool = get_pools(get_turf(L), 0)
|
||||
if(bloodpool.len && (!faction_check_mob(L) || L.stat == DEAD))
|
||||
. += L
|
||||
|
||||
else if(isliving(hit_atom))
|
||||
var/mob/living/L = hit_atom
|
||||
L.visible_message("<span class='danger'>[src] slams into [L]!</span>", "<span class='userdanger'>[src] slams into you!</span>")
|
||||
L.apply_damage(40, BRUTE)
|
||||
playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, 1)
|
||||
shake_camera(L, 4, 3)
|
||||
shake_camera(src, 2, 3)
|
||||
var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(L, src)))
|
||||
L.throw_at(throwtarget, 3)
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/try_bloodattack()
|
||||
var/list/targets = get_mobs_on_blood()
|
||||
if(targets.len)
|
||||
INVOKE_ASYNC(src, .proc/bloodattack, targets, prob(50))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
charging = 0
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodattack(list/targets, handedness)
|
||||
var/mob/living/target_one = pick_n_take(targets)
|
||||
var/turf/target_one_turf = get_turf(target_one)
|
||||
var/mob/living/target_two
|
||||
if(targets.len)
|
||||
target_two = pick_n_take(targets)
|
||||
var/turf/target_two_turf = get_turf(target_two)
|
||||
if(target_two.stat != CONSCIOUS || prob(10))
|
||||
bloodgrab(target_two_turf, handedness)
|
||||
else
|
||||
bloodsmack(target_two_turf, handedness)
|
||||
|
||||
if(target_one)
|
||||
var/list/pools = get_pools(get_turf(target_one), 0)
|
||||
if(pools.len)
|
||||
target_one_turf = get_turf(target_one)
|
||||
if(target_one_turf)
|
||||
if(target_one.stat != CONSCIOUS || prob(10))
|
||||
bloodgrab(target_one_turf, !handedness)
|
||||
else
|
||||
bloodsmack(target_one_turf, !handedness)
|
||||
|
||||
if(!target_two && target_one)
|
||||
var/list/poolstwo = get_pools(get_turf(target_one), 0)
|
||||
if(poolstwo.len)
|
||||
target_one_turf = get_turf(target_one)
|
||||
if(target_one_turf)
|
||||
if(target_one.stat != CONSCIOUS || prob(10))
|
||||
bloodgrab(target_one_turf, handedness)
|
||||
else
|
||||
bloodsmack(target_one_turf, handedness)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodsmack(turf/T, handedness)
|
||||
if(handedness)
|
||||
new /obj/effect/temp_visual/bubblegum_hands/rightsmack(T)
|
||||
else
|
||||
new /obj/effect/temp_visual/bubblegum_hands/leftsmack(T)
|
||||
sleep(4)
|
||||
for(var/mob/living/L in T)
|
||||
if(!faction_check_mob(L))
|
||||
to_chat(L, "<span class='userdanger'>[src] rends you!</span>")
|
||||
playsound(T, attack_sound, 100, 1, -1)
|
||||
var/limb_to_hit = L.get_bodypart(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
|
||||
L.apply_damage(10, BRUTE, limb_to_hit, L.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration))
|
||||
sleep(3)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodgrab(turf/T, handedness)
|
||||
if(handedness)
|
||||
new /obj/effect/temp_visual/bubblegum_hands/rightpaw(T)
|
||||
new /obj/effect/temp_visual/bubblegum_hands/rightthumb(T)
|
||||
else
|
||||
new /obj/effect/temp_visual/bubblegum_hands/leftpaw(T)
|
||||
new /obj/effect/temp_visual/bubblegum_hands/leftthumb(T)
|
||||
sleep(6)
|
||||
for(var/mob/living/L in T)
|
||||
if(!faction_check_mob(L))
|
||||
if(L.stat != CONSCIOUS)
|
||||
to_chat(L, "<span class='userdanger'>[src] drags you through the blood!</span>")
|
||||
playsound(T, 'sound/magic/enter_blood.ogg', 100, 1, -1)
|
||||
var/turf/targetturf = get_step(src, dir)
|
||||
L.forceMove(targetturf)
|
||||
playsound(targetturf, 'sound/magic/exit_blood.ogg', 100, 1, -1)
|
||||
addtimer(CALLBACK(src, .proc/devour, L), 2)
|
||||
sleep(1)
|
||||
|
||||
/obj/effect/temp_visual/dragon_swoop/bubblegum
|
||||
duration = 10
|
||||
|
||||
/obj/effect/temp_visual/bubblegum_hands
|
||||
icon = 'icons/effects/bubblegum.dmi'
|
||||
duration = 9
|
||||
|
||||
/obj/effect/temp_visual/bubblegum_hands/rightthumb
|
||||
icon_state = "rightthumbgrab"
|
||||
|
||||
/obj/effect/temp_visual/bubblegum_hands/leftthumb
|
||||
icon_state = "leftthumbgrab"
|
||||
|
||||
/obj/effect/temp_visual/bubblegum_hands/rightpaw
|
||||
icon_state = "rightpawgrab"
|
||||
layer = BELOW_MOB_LAYER
|
||||
|
||||
/obj/effect/temp_visual/bubblegum_hands/leftpaw
|
||||
icon_state = "leftpawgrab"
|
||||
layer = BELOW_MOB_LAYER
|
||||
|
||||
/obj/effect/temp_visual/bubblegum_hands/rightsmack
|
||||
icon_state = "rightsmack"
|
||||
|
||||
/obj/effect/temp_visual/bubblegum_hands/leftsmack
|
||||
icon_state = "leftsmack"
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_warp()
|
||||
if(Adjacent(target) || (enrage_till + 30 > world.time))
|
||||
return FALSE
|
||||
var/list/can_jaunt = get_pools(get_turf(src), 1)
|
||||
if(!can_jaunt.len)
|
||||
return FALSE
|
||||
|
||||
var/list/pools = get_pools(get_turf(target), 2)
|
||||
var/list/pools_to_remove = get_pools(get_turf(target), 1)
|
||||
pools -= pools_to_remove
|
||||
if(!pools.len)
|
||||
return FALSE
|
||||
|
||||
var/obj/effect/temp_visual/decoy/DA = new /obj/effect/temp_visual/decoy(loc,src)
|
||||
DA.color = "#FF0000"
|
||||
var/oldtransform = DA.transform
|
||||
DA.transform = matrix()*2
|
||||
animate(DA, alpha = 255, color = initial(DA.color), transform = oldtransform, time = 3)
|
||||
sleep(3)
|
||||
qdel(DA)
|
||||
var/obj/effect/decal/cleanable/blood/found_bloodpool
|
||||
var/list/pools = list()
|
||||
var/can_jaunt = FALSE
|
||||
for(var/obj/effect/decal/cleanable/blood/nearby in view(src,2))
|
||||
can_jaunt = TRUE
|
||||
break
|
||||
if(!can_jaunt)
|
||||
return
|
||||
for(var/obj/effect/decal/cleanable/blood/nearby in view(get_turf(target),2))
|
||||
pools += nearby
|
||||
pools = get_pools(get_turf(target), 2)
|
||||
pools_to_remove = get_pools(get_turf(target), 1)
|
||||
pools -= pools_to_remove
|
||||
if(pools.len)
|
||||
shuffle_inplace(pools)
|
||||
found_bloodpool = pick(pools)
|
||||
@@ -215,48 +381,110 @@ Difficulty: Hard
|
||||
forceMove(get_turf(found_bloodpool))
|
||||
playsound(get_turf(src), 'sound/magic/exit_blood.ogg', 100, 1, -1)
|
||||
visible_message("<span class='danger'>And springs back out!</span>")
|
||||
blood_enrage()
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_enrage(var/boost_time = 30)
|
||||
enrage_till = world.time + boost_time
|
||||
retreat_distance = null
|
||||
minimum_distance = 1
|
||||
change_move_delay(3.75)
|
||||
var/newcolor = rgb(149, 10, 10)
|
||||
add_atom_colour(newcolor, TEMPORARY_COLOUR_PRIORITY)
|
||||
var/datum/callback/cb = CALLBACK(src, .proc/blood_enrage_end)
|
||||
addtimer(cb, boost_time)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_spray()
|
||||
visible_message("<span class='danger'>[src] sprays a stream of gore!</span>")
|
||||
var/turf/E = get_edge_target_turf(src, src.dir)
|
||||
var/range = 10
|
||||
var/turf/previousturf = get_turf(src)
|
||||
for(var/turf/J in getline(src,E))
|
||||
if(!range)
|
||||
break
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(previousturf, get_dir(previousturf, J))
|
||||
if(!previousturf.CanAtmosPass(J))
|
||||
break
|
||||
playsound(J,'sound/effects/splat.ogg', 100, 1, -1)
|
||||
new /obj/effect/decal/cleanable/blood(J)
|
||||
range--
|
||||
previousturf = J
|
||||
sleep(1)
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_enrage_end(var/newcolor = rgb(149, 10, 10))
|
||||
retreat_distance = initial(retreat_distance)
|
||||
minimum_distance = initial(minimum_distance)
|
||||
change_move_delay()
|
||||
remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, newcolor)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/slaughterlings()
|
||||
visible_message("<span class='danger'>[src] summons a shoal of slaughterlings!</span>")
|
||||
for(var/obj/effect/decal/cleanable/blood/H in range(src, 10))
|
||||
if(prob(25))
|
||||
new /mob/living/simple_animal/hostile/asteroid/hivelordbrood/slaughter(H.loc)
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/is_enraged()
|
||||
return (enrage_till > world.time)
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/slaughter
|
||||
name = "slaughterling"
|
||||
desc = "Though not yet strong enough to create a true physical form, it's nonetheless determined to murder you."
|
||||
icon_state = "bloodbrood"
|
||||
icon_living = "bloodbrood"
|
||||
icon_aggro = "bloodbrood"
|
||||
attack_verb_continuous = "pierces"
|
||||
attack_verb_simple = "pierce"
|
||||
color = "#C80000"
|
||||
density = FALSE
|
||||
faction = list("mining", "boss")
|
||||
weather_immunities = list("lava","ash")
|
||||
has_field_of_vision = FALSE
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/change_move_delay(var/newmove = initial(move_to_delay))
|
||||
move_to_delay = newmove
|
||||
handle_automated_action() // need to recheck movement otherwise move_to_delay won't update until the next checking aka will be wrong speed for a bit
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/slaughter/CanPass(atom/movable/mover, turf/target)
|
||||
if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum))
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_pools(turf/T, range)
|
||||
. = list()
|
||||
for(var/obj/effect/decal/cleanable/nearby in view(T, range))
|
||||
if(nearby.can_bloodcrawl_in())
|
||||
. += nearby
|
||||
|
||||
/obj/effect/decal/cleanable/blood/bubblegum
|
||||
bloodiness = 0
|
||||
|
||||
/obj/effect/decal/cleanable/blood/bubblegum/can_bloodcrawl_in()
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/hallucination_charge_around(var/times = 4, var/delay = 6, var/chargepast = 0, var/useoriginal = 1)
|
||||
var/startingangle = rand(1, 360)
|
||||
if(!target)
|
||||
return
|
||||
var/turf/chargeat = get_turf(target)
|
||||
var/srcplaced = 0
|
||||
for(var/i = 1 to times)
|
||||
var/ang = (startingangle + 360/times * i)
|
||||
if(!chargeat)
|
||||
return
|
||||
var/turf/place = locate(chargeat.x + cos(ang) * times, chargeat.y + sin(ang) * times, chargeat.z)
|
||||
if(!place)
|
||||
continue
|
||||
if(!srcplaced && useoriginal)
|
||||
forceMove(place)
|
||||
srcplaced = 1
|
||||
continue
|
||||
var/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/B = new /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination(src.loc)
|
||||
B.forceMove(place)
|
||||
INVOKE_ASYNC(B, .proc/charge, chargeat, delay, chargepast)
|
||||
if(useoriginal)
|
||||
charge(chargeat, delay, chargepast)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination
|
||||
name = "Bubblegum's hallucination"
|
||||
desc = "Is that really just a hallucination?"
|
||||
health = 1
|
||||
maxHealth = 1
|
||||
alpha = 127.5
|
||||
crusher_loot = null
|
||||
loot = null
|
||||
deathmessage = "Explodes into a pool of blood!"
|
||||
deathsound = 'sound/effects/splat.ogg'
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Initialize()
|
||||
..()
|
||||
toggle_ai(AI_OFF)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/charge(atom/chargeat = target, delay = 3, chargepast = 2)
|
||||
..()
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Destroy()
|
||||
new /obj/effect/decal/cleanable/blood(get_turf(src))
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/CanPass(atom/movable/mover, turf/target)
|
||||
if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum)) // hallucinations should not be stopping bubblegum or eachother
|
||||
return 1
|
||||
return 0
|
||||
return ..()
|
||||
|
||||
#undef MEDAL_PREFIX
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Life()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE, only_robotic = FALSE, only_organic = TRUE)
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/OpenFire()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/AttackingTarget()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/try_bloodattack()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/grant_achievement(medaltype,scoretype)
|
||||
return
|
||||
|
||||
@@ -32,6 +32,7 @@ Difficulty: Very Hard
|
||||
icon_state = "eva"
|
||||
icon_living = "eva"
|
||||
icon_dead = "dragon_dead"
|
||||
health_doll_icon = "eva"
|
||||
friendly_verb_continuous = "stares down"
|
||||
friendly_verb_simple = "stare down"
|
||||
icon = 'icons/mob/lavaland/96x96megafauna.dmi'
|
||||
|
||||
@@ -26,8 +26,10 @@ Difficulty: Extremely Hard
|
||||
speed = 20
|
||||
move_to_delay = 20
|
||||
ranged = TRUE
|
||||
crusher_loot = list(/obj/effect/decal/remains/plasma, /obj/item/crusher_trophy/ice_block_talisman)
|
||||
loot = list(/obj/effect/decal/remains/plasma)
|
||||
crusher_loot = list(/obj/effect/decal/remains/plasma, /obj/item/crusher_trophy/ice_block_talisman, ,/obj/item/disk/design_disk/modkit_disc/mob_and_turf_aoe,
|
||||
/obj/item/disk/design_disk/modkit_disc/bounty,/obj/item/disk/design_disk/modkit_disc/resonator_blast,/obj/item/disk/design_disk/modkit_disc/rapid_repeater)
|
||||
loot = list(/obj/effect/decal/remains/plasma, ,/obj/item/disk/design_disk/modkit_disc/mob_and_turf_aoe,
|
||||
/obj/item/disk/design_disk/modkit_disc/bounty,/obj/item/disk/design_disk/modkit_disc/resonator_blast,/obj/item/disk/design_disk/modkit_disc/rapid_repeater)
|
||||
wander = FALSE
|
||||
del_on_death = TRUE
|
||||
blood_volume = BLOOD_VOLUME_NORMAL
|
||||
|
||||
@@ -48,6 +48,7 @@ Difficulty: Medium
|
||||
icon_state = "dragon"
|
||||
icon_living = "dragon"
|
||||
icon_dead = "dragon_dead"
|
||||
health_doll_icon = "dragon"
|
||||
friendly_verb_continuous = "stares down"
|
||||
friendly_verb_simple = "stare down"
|
||||
speak_emote = list("roars")
|
||||
@@ -60,10 +61,10 @@ Difficulty: Medium
|
||||
pixel_x = -16
|
||||
crusher_loot = list(/obj/structure/closet/crate/necropolis/dragon/crusher)
|
||||
loot = list(/obj/structure/closet/crate/necropolis/dragon)
|
||||
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/bone = 30)
|
||||
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/bone = 30, /obj/item/reagent_containers/food/snacks/meat/slab/dragon = 5)
|
||||
guaranteed_butcher_results = list(/obj/item/stack/sheet/animalhide/ashdrake = 10)
|
||||
var/swooping = NONE
|
||||
var/swoop_cooldown = 0
|
||||
var/player_cooldown = 0
|
||||
achievement_type = /datum/award/achievement/boss/drake_kill
|
||||
crusher_achievement_type = /datum/award/achievement/boss/drake_crusher
|
||||
score_achievement_type = /datum/award/score/drake_score
|
||||
@@ -116,66 +117,167 @@ Difficulty: Medium
|
||||
ranged_cooldown = world.time + ranged_cooldown_time
|
||||
|
||||
if(prob(15 + anger_modifier) && !client)
|
||||
if(health < maxHealth/2)
|
||||
INVOKE_ASYNC(src, .proc/swoop_attack, TRUE, null, 50)
|
||||
if(health < maxHealth*0.5)
|
||||
swoop_attack(lava_arena = TRUE)
|
||||
else
|
||||
fire_rain()
|
||||
lava_swoop()
|
||||
|
||||
else if(prob(10+anger_modifier) && !client)
|
||||
if(health > maxHealth/2)
|
||||
INVOKE_ASYNC(src, .proc/swoop_attack)
|
||||
if(health < maxHealth*0.5)
|
||||
mass_fire()
|
||||
else
|
||||
INVOKE_ASYNC(src, .proc/triple_swoop)
|
||||
fire_cone()
|
||||
else
|
||||
fire_walls()
|
||||
if(prob(50) && !client)
|
||||
INVOKE_ASYNC(src, .proc/lava_pools, 10, 2)
|
||||
fire_cone()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_rain()
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/lava_pools(var/amount, var/delay = 0.8)
|
||||
if(!target)
|
||||
return
|
||||
target.visible_message("<span class='boldwarning'>Fire rains from the sky!</span>")
|
||||
for(var/turf/turf in range(9,get_turf(target)))
|
||||
if(prob(11))
|
||||
new /obj/effect/temp_visual/target(turf)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_walls()
|
||||
playsound(get_turf(src),'sound/magic/fireball.ogg', 200, 1)
|
||||
|
||||
for(var/d in GLOB.cardinals)
|
||||
INVOKE_ASYNC(src, .proc/fire_wall, d)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_wall(dir)
|
||||
var/list/hit_things = list(src)
|
||||
var/turf/E = get_edge_target_turf(src, dir)
|
||||
var/range = 10
|
||||
var/turf/previousturf = get_turf(src)
|
||||
for(var/turf/J in getline(src,E))
|
||||
if(!range || (J != previousturf && (!previousturf.atmos_adjacent_turfs || !previousturf.atmos_adjacent_turfs[J])))
|
||||
target.visible_message("<span class='boldwarning'>Lava starts to pool up around you!</span>")
|
||||
while(amount > 0)
|
||||
if(!target)
|
||||
break
|
||||
range--
|
||||
new /obj/effect/hotspot(J)
|
||||
J.hotspot_expose(DRAKE_FIRE_TEMP, DRAKE_FIRE_EXPOSURE, 1)
|
||||
for(var/mob/living/L in J.contents - hit_things)
|
||||
if(istype(L, /mob/living/simple_animal/hostile/megafauna/dragon))
|
||||
var/turf/T = pick(RANGE_TURFS(1, target))
|
||||
new /obj/effect/temp_visual/lava_warning(T, 60) // longer reset time for the lava
|
||||
amount--
|
||||
sleep(delay)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/lava_swoop(var/amount = 30)
|
||||
INVOKE_ASYNC(src, .proc/lava_pools, amount)
|
||||
swoop_attack(FALSE, target, 1000) // longer cooldown until it gets reset below
|
||||
fire_cone()
|
||||
if(health < maxHealth*0.5)
|
||||
sleep(10)
|
||||
fire_cone()
|
||||
sleep(10)
|
||||
fire_cone()
|
||||
SetRecoveryTime(40)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/mass_fire(var/spiral_count = 12, var/range = 15, var/times = 3)
|
||||
for(var/i = 1 to times)
|
||||
SetRecoveryTime(50)
|
||||
playsound(get_turf(src),'sound/magic/fireball.ogg', 200, 1)
|
||||
var/increment = 360 / spiral_count
|
||||
for(var/j = 1 to spiral_count)
|
||||
var/list/turfs = line_target(j * increment + i * increment / 2, range, src)
|
||||
INVOKE_ASYNC(src, .proc/fire_line, turfs)
|
||||
sleep(25)
|
||||
SetRecoveryTime(30)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/lava_arena()
|
||||
if(!target)
|
||||
return
|
||||
target.visible_message("<span class='boldwarning'>[src] encases you in an arena of fire!</span>")
|
||||
var/amount = 3
|
||||
var/turf/center = get_turf(target)
|
||||
var/list/walled = RANGE_TURFS(3, center) - RANGE_TURFS(2, center)
|
||||
var/list/drakewalls = list()
|
||||
for(var/turf/T in walled)
|
||||
drakewalls += new /obj/effect/temp_visual/drakewall(T) // no people with lava immunity can just run away from the attack for free
|
||||
var/list/indestructible_turfs = list()
|
||||
for(var/turf/T in RANGE_TURFS(2, center))
|
||||
if(istype(T, /turf/open/indestructible))
|
||||
continue
|
||||
if(!istype(T, /turf/closed/indestructible))
|
||||
T.ChangeTurf(/turf/open/floor/plating/asteroid/basalt/lava_land_surface)
|
||||
else
|
||||
indestructible_turfs += T
|
||||
sleep(10) // give them a bit of time to realize what attack is actually happening
|
||||
|
||||
var/list/turfs = RANGE_TURFS(2, center)
|
||||
while(amount > 0)
|
||||
var/list/empty = indestructible_turfs.Copy() // can't place safe turfs on turfs that weren't changed to be open
|
||||
var/any_attack = 0
|
||||
for(var/turf/T in turfs)
|
||||
for(var/mob/living/L in T.contents)
|
||||
if(L.client)
|
||||
empty += pick(((RANGE_TURFS(2, L) - RANGE_TURFS(1, L)) & turfs) - empty) // picks a turf within 2 of the creature not outside or in the shield
|
||||
any_attack = 1
|
||||
for(var/obj/mecha/M in T.contents)
|
||||
empty += pick(((RANGE_TURFS(2, M) - RANGE_TURFS(1, M)) & turfs) - empty)
|
||||
any_attack = 1
|
||||
if(!any_attack)
|
||||
for(var/obj/effect/temp_visual/drakewall/D in drakewalls)
|
||||
qdel(D)
|
||||
return 0 // nothing to attack in the arena time for enraged attack if we still have a target
|
||||
for(var/turf/T in turfs)
|
||||
if(!(T in empty))
|
||||
new /obj/effect/temp_visual/lava_warning(T)
|
||||
else if(!istype(T, /turf/closed/indestructible))
|
||||
new /obj/effect/temp_visual/lava_safe(T)
|
||||
amount--
|
||||
sleep(24)
|
||||
return 1 // attack finished completely
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/arena_escape_enrage() // you ran somehow / teleported away from my arena attack now i'm mad fucker
|
||||
SetRecoveryTime(80)
|
||||
visible_message("<span class='boldwarning'>[src] starts to glow vibrantly as its wounds close up!</span>")
|
||||
adjustBruteLoss(-250) // yeah you're gonna pay for that, don't run nerd
|
||||
add_atom_colour(rgb(255, 255, 0), TEMPORARY_COLOUR_PRIORITY)
|
||||
move_to_delay = move_to_delay / 2
|
||||
light_range = 10
|
||||
sleep(10) // run.
|
||||
mass_fire(20, 15, 3)
|
||||
remove_atom_colour(TEMPORARY_COLOUR_PRIORITY)
|
||||
move_to_delay = initial(move_to_delay)
|
||||
light_range = initial(light_range)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_cone(var/atom/at = target)
|
||||
playsound(get_turf(src),'sound/magic/fireball.ogg', 200, 1)
|
||||
if(QDELETED(src) || stat == DEAD) // we dead no fire
|
||||
return
|
||||
var/range = 15
|
||||
var/list/turfs = list()
|
||||
turfs = line_target(-40, range, at)
|
||||
INVOKE_ASYNC(src, .proc/fire_line, turfs)
|
||||
turfs = line_target(0, range, at)
|
||||
INVOKE_ASYNC(src, .proc/fire_line, turfs)
|
||||
turfs = line_target(40, range, at)
|
||||
INVOKE_ASYNC(src, .proc/fire_line, turfs)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/line_target(var/offset, var/range, var/atom/at = target)
|
||||
if(!at)
|
||||
return
|
||||
var/angle = ATAN2(at.x - src.x, at.y - src.y) + offset
|
||||
var/turf/T = get_turf(src)
|
||||
for(var/i in 1 to range)
|
||||
var/turf/check = locate(src.x + cos(angle) * i, src.y + sin(angle) * i, src.z)
|
||||
if(!check)
|
||||
break
|
||||
T = check
|
||||
return (getline(src, T) - get_turf(src))
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_line(var/list/turfs)
|
||||
var/list/hit_list = list()
|
||||
for(var/turf/T in turfs)
|
||||
if(istype(T, /turf/closed))
|
||||
break
|
||||
new /obj/effect/hotspot(T)
|
||||
T.hotspot_expose(700,50,1)
|
||||
for(var/mob/living/L in T.contents)
|
||||
if(L in hit_list || L == src)
|
||||
continue
|
||||
hit_list += L
|
||||
L.adjustFireLoss(20)
|
||||
to_chat(L, "<span class='userdanger'>You're hit by the drake's fire breath!</span>")
|
||||
hit_things += L
|
||||
previousturf = J
|
||||
sleep(1)
|
||||
to_chat(L, "<span class='userdanger'>You're hit by [src]'s fire breath!</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/triple_swoop()
|
||||
swoop_attack(swoop_duration = 30)
|
||||
swoop_attack(swoop_duration = 30)
|
||||
swoop_attack(swoop_duration = 30)
|
||||
// deals damage to mechs
|
||||
for(var/obj/mecha/M in T.contents)
|
||||
if(M in hit_list)
|
||||
continue
|
||||
hit_list += M
|
||||
M.take_damage(45, BRUTE, "melee", 1)
|
||||
sleep(1.5)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/swoop_attack(fire_rain, atom/movable/manual_target, swoop_duration = 40)
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/swoop_attack(lava_arena = FALSE, atom/movable/manual_target, var/swoop_cooldown = 30)
|
||||
if(stat || swooping)
|
||||
return
|
||||
if(manual_target)
|
||||
target = manual_target
|
||||
if(!target)
|
||||
return
|
||||
swoop_cooldown = world.time + 200
|
||||
stop_automated_movement = TRUE
|
||||
swooping |= SWOOP_DAMAGEABLE
|
||||
density = FALSE
|
||||
@@ -209,32 +311,16 @@ Difficulty: Medium
|
||||
swooping |= SWOOP_INVULNERABLE
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
sleep(7)
|
||||
var/list/flame_hit = list()
|
||||
while(swoop_duration > 0)
|
||||
if(!target && !FindTarget())
|
||||
break //we lost our target while chasing it down and couldn't get a new one
|
||||
if(swoop_duration < 7)
|
||||
fire_rain = FALSE //stop raining fire near the end of the swoop
|
||||
if(loc == get_turf(target))
|
||||
if(!fire_rain)
|
||||
break //we're not spewing fire at our target, slam they
|
||||
if(isliving(target))
|
||||
var/mob/living/L = target
|
||||
if(L.stat == DEAD)
|
||||
break //target is dead and we're on em, slam they
|
||||
if(fire_rain)
|
||||
new /obj/effect/temp_visual/target(loc, flame_hit)
|
||||
|
||||
while(target && loc != get_turf(target))
|
||||
forceMove(get_step(src, get_dir(src, target)))
|
||||
if(loc == get_turf(target))
|
||||
if(!fire_rain)
|
||||
break
|
||||
if(isliving(target))
|
||||
var/mob/living/L = target
|
||||
if(L.stat == DEAD)
|
||||
break
|
||||
var/swoop_speed = 1.5
|
||||
swoop_duration -= swoop_speed
|
||||
sleep(swoop_speed)
|
||||
sleep(0.5)
|
||||
|
||||
// Ash drake flies onto its target and rains fire down upon them
|
||||
var/descentTime = 10;
|
||||
var/lava_success = 1
|
||||
if(lava_arena)
|
||||
lava_success = lava_arena()
|
||||
|
||||
//ensure swoop direction continuity.
|
||||
if(negative)
|
||||
@@ -245,8 +331,8 @@ Difficulty: Medium
|
||||
negative = TRUE
|
||||
new /obj/effect/temp_visual/dragon_flight/end(loc, negative)
|
||||
new /obj/effect/temp_visual/dragon_swoop(loc)
|
||||
animate(src, alpha = 255, transform = oldtransform, time = 5)
|
||||
sleep(5)
|
||||
animate(src, alpha = 255, transform = oldtransform, descentTime)
|
||||
sleep(descentTime)
|
||||
swooping &= ~SWOOP_INVULNERABLE
|
||||
mouse_opacity = initial(mouse_opacity)
|
||||
icon_state = "dragon"
|
||||
@@ -264,6 +350,8 @@ Difficulty: Medium
|
||||
var/throwtarget = get_edge_target_turf(src, throw_dir)
|
||||
L.throw_at(throwtarget, 3)
|
||||
visible_message("<span class='warning'>[L] is thrown clear of [src]!</span>")
|
||||
for(var/obj/mecha/M in orange(1, src))
|
||||
M.take_damage(75, BRUTE, "melee", 1)
|
||||
|
||||
for(var/mob/M in range(7, src))
|
||||
shake_camera(M, 15, 1)
|
||||
@@ -271,16 +359,20 @@ Difficulty: Medium
|
||||
density = TRUE
|
||||
sleep(1)
|
||||
swooping &= ~SWOOP_DAMAGEABLE
|
||||
SetRecoveryTime(MEGAFAUNA_DEFAULT_RECOVERY_TIME)
|
||||
SetRecoveryTime(swoop_cooldown)
|
||||
if(!lava_success)
|
||||
arena_escape_enrage()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/AltClickOn(atom/movable/A)
|
||||
if(!istype(A))
|
||||
AltClickNoInteract(src, A)
|
||||
return
|
||||
if(swoop_cooldown >= world.time)
|
||||
to_chat(src, "<span class='warning'>You need to wait 20 seconds between swoop attacks!</span>")
|
||||
if(player_cooldown >= world.time)
|
||||
to_chat(src, "<span class='warning'>You need to wait [(player_cooldown - world.time) / 10] seconds before swooping again!</span>")
|
||||
return
|
||||
swoop_attack(TRUE, A, 25)
|
||||
swoop_attack(FALSE, A)
|
||||
lava_pools(10, 2) // less pools but longer delay before spawns
|
||||
player_cooldown = world.time + 200 // needs seperate cooldown or cant use fire attacks
|
||||
|
||||
/obj/item/gps/internal/dragon
|
||||
icon_state = null
|
||||
@@ -289,54 +381,63 @@ Difficulty: Medium
|
||||
invisibility = 100
|
||||
|
||||
|
||||
/obj/effect/temp_visual/fireball
|
||||
icon = 'icons/obj/wizard.dmi'
|
||||
icon_state = "fireball"
|
||||
name = "fireball"
|
||||
desc = "Get out of the way!"
|
||||
layer = FLY_LAYER
|
||||
randomdir = FALSE
|
||||
duration = 9
|
||||
pixel_z = DRAKE_SWOOP_HEIGHT
|
||||
|
||||
/obj/effect/temp_visual/fireball/Initialize()
|
||||
. = ..()
|
||||
animate(src, pixel_z = 0, time = duration)
|
||||
|
||||
/obj/effect/temp_visual/target
|
||||
icon = 'icons/mob/actions/actions_items.dmi'
|
||||
icon_state = "sniper_zoom"
|
||||
/obj/effect/temp_visual/lava_warning
|
||||
icon_state = "lavastaff_warn"
|
||||
layer = BELOW_MOB_LAYER
|
||||
light_range = 2
|
||||
duration = 9
|
||||
duration = 13
|
||||
|
||||
/obj/effect/temp_visual/target/ex_act()
|
||||
/obj/effect/temp_visual/lava_warning/ex_act()
|
||||
return
|
||||
|
||||
/obj/effect/temp_visual/target/Initialize(mapload, list/flame_hit)
|
||||
/obj/effect/temp_visual/lava_warning/Initialize(mapload, var/reset_time = 10)
|
||||
. = ..()
|
||||
INVOKE_ASYNC(src, .proc/fall, flame_hit)
|
||||
INVOKE_ASYNC(src, .proc/fall, reset_time)
|
||||
src.alpha = 63.75
|
||||
animate(src, alpha = 255, time = duration)
|
||||
|
||||
/obj/effect/temp_visual/target/proc/fall(list/flame_hit)
|
||||
/obj/effect/temp_visual/lava_warning/proc/fall(var/reset_time)
|
||||
var/turf/T = get_turf(src)
|
||||
playsound(T,'sound/magic/fleshtostone.ogg', 80, 1)
|
||||
new /obj/effect/temp_visual/fireball(T)
|
||||
sleep(duration)
|
||||
if(ismineralturf(T))
|
||||
var/turf/closed/mineral/M = T
|
||||
M.gets_drilled()
|
||||
playsound(T, "explosion", 80, 1)
|
||||
new /obj/effect/hotspot(T)
|
||||
T.hotspot_expose(700, 50, 1)
|
||||
playsound(T,'sound/magic/fireball.ogg', 200, 1)
|
||||
|
||||
for(var/mob/living/L in T.contents)
|
||||
if(istype(L, /mob/living/simple_animal/hostile/megafauna/dragon))
|
||||
continue
|
||||
if(islist(flame_hit) && !flame_hit[L])
|
||||
L.adjustFireLoss(40)
|
||||
to_chat(L, "<span class='userdanger'>You're hit by the drake's fire breath!</span>")
|
||||
flame_hit[L] = TRUE
|
||||
else
|
||||
L.adjustFireLoss(10) //if we've already hit them, do way less damage
|
||||
L.adjustFireLoss(10)
|
||||
to_chat(L, "<span class='userdanger'>You fall directly into the pool of lava!</span>")
|
||||
|
||||
// deals damage to mechs
|
||||
for(var/obj/mecha/M in T.contents)
|
||||
M.take_damage(45, BRUTE, "melee", 1)
|
||||
|
||||
// changes turf to lava temporarily
|
||||
if(!istype(T, /turf/closed) && !istype(T, /turf/open/lava))
|
||||
var/lava_turf = /turf/open/lava/smooth
|
||||
var/reset_turf = T.type
|
||||
T.ChangeTurf(lava_turf)
|
||||
sleep(reset_time)
|
||||
T.ChangeTurf(reset_turf)
|
||||
|
||||
/obj/effect/temp_visual/drakewall
|
||||
desc = "An ash drakes true flame."
|
||||
name = "Fire Barrier"
|
||||
icon = 'icons/effects/fire.dmi'
|
||||
icon_state = "1"
|
||||
anchored = TRUE
|
||||
opacity = 0
|
||||
density = TRUE
|
||||
CanAtmosPass = ATMOS_PASS_DENSITY
|
||||
duration = 82
|
||||
color = COLOR_DARK_ORANGE
|
||||
|
||||
/obj/effect/temp_visual/lava_safe
|
||||
icon = 'icons/obj/hand_of_god_structures.dmi'
|
||||
icon_state = "trap-earth"
|
||||
layer = BELOW_MOB_LAYER
|
||||
light_range = 2
|
||||
duration = 13
|
||||
|
||||
/obj/effect/temp_visual/dragon_swoop
|
||||
name = "certain death"
|
||||
@@ -347,7 +448,7 @@ Difficulty: Medium
|
||||
pixel_x = -32
|
||||
pixel_y = -32
|
||||
color = "#FF0000"
|
||||
duration = 5
|
||||
duration = 10
|
||||
|
||||
/obj/effect/temp_visual/dragon_flight
|
||||
icon = 'icons/mob/lavaland/64x64megafauna.dmi'
|
||||
@@ -376,7 +477,7 @@ Difficulty: Medium
|
||||
/obj/effect/temp_visual/dragon_flight/end
|
||||
pixel_x = DRAKE_SWOOP_HEIGHT
|
||||
pixel_z = DRAKE_SWOOP_HEIGHT
|
||||
duration = 5
|
||||
duration = 10
|
||||
|
||||
/obj/effect/temp_visual/dragon_flight/end/flight(negative)
|
||||
if(negative)
|
||||
@@ -399,6 +500,16 @@ Difficulty: Medium
|
||||
crusher_loot = list()
|
||||
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/bone = 30)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/lesser/AltClickOn(atom/movable/A)
|
||||
if(!istype(A))
|
||||
return
|
||||
if(player_cooldown >= world.time)
|
||||
to_chat(src, "<span class='warning'>You need to wait [(player_cooldown - world.time) / 10] seconds before swooping again!</span>")
|
||||
return
|
||||
swoop_attack(FALSE, A)
|
||||
lava_pools(10, 2) // less pools but longer delay before spawns
|
||||
player_cooldown = world.time + 200 // needs seperate cooldown or cant use fire attacks
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/lesser/transformed //ash drake balanced around player control
|
||||
name = "transformed ash drake"
|
||||
desc = "A sentient being transformed into an ash drake"
|
||||
|
||||
@@ -44,6 +44,7 @@ Difficulty: Normal
|
||||
attack_sound = 'sound/weapons/sonic_jackhammer.ogg'
|
||||
icon_state = "hierophant"
|
||||
icon_living = "hierophant"
|
||||
health_doll_icon = "hierophant"
|
||||
friendly_verb_continuous = "stares down"
|
||||
friendly_verb_simple = "stare down"
|
||||
icon = 'icons/mob/lavaland/hierophant_new.dmi'
|
||||
|
||||
@@ -26,6 +26,7 @@ SHITCODE AHEAD. BE ADVISED. Also comment extravaganza
|
||||
spacewalk = TRUE
|
||||
icon_state = "mega_legion"
|
||||
icon_living = "mega_legion"
|
||||
health_doll_icon = "mega_legion"
|
||||
desc = "One of many."
|
||||
icon = 'icons/mob/lavaland/96x96megafauna.dmi'
|
||||
attack_verb_continuous = "chomps"
|
||||
|
||||
@@ -149,6 +149,9 @@
|
||||
if(EXPLODE_LIGHT)
|
||||
adjustBruteLoss(50)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/wave_ex_act(power, datum/wave_explosion/explosion, dir)
|
||||
adjustBruteLoss(EXPLOSION_POWER_STANDARD_SCALE_MOB_DAMAGE(power, explosion.mob_damage_mod) / 2)
|
||||
|
||||
/// Sets the next time the megafauna can use a melee or ranged attack, in deciseconds
|
||||
/mob/living/simple_animal/hostile/megafauna/proc/SetRecoveryTime(buffer_time, ranged_buffer_time)
|
||||
recovery_time = world.time + buffer_time
|
||||
|
||||
@@ -59,6 +59,13 @@
|
||||
if(3)
|
||||
adjustBruteLoss(110)
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/wave_ex_act(power, datum/wave_explosion/explosion, dir)
|
||||
. = ..()
|
||||
if(power > EXPLOSION_POWER_NORMAL_MOB_GIB)
|
||||
gib()
|
||||
else
|
||||
adjustBruteLoss(EXPLOSION_POWER_STANDARD_SCALE_MOB_DAMAGE(power, explosion.mob_damage_mod))
|
||||
|
||||
//Watcher
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher
|
||||
name = "watcher"
|
||||
@@ -68,6 +75,7 @@
|
||||
icon_living = "watcher"
|
||||
icon_aggro = "watcher"
|
||||
icon_dead = "watcher_dead"
|
||||
health_doll_icon = "watcher"
|
||||
pixel_x = -10
|
||||
throw_message = "bounces harmlessly off of"
|
||||
melee_damage_lower = 15
|
||||
@@ -94,23 +102,13 @@
|
||||
consume_bait()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/proc/consume_bait()
|
||||
var/list/L = list()
|
||||
for(var/obj/O in view(src, 9))
|
||||
L += O
|
||||
var/obj/item/stack/ore/diamond/diamonds = locate(/obj/item/stack/ore/diamond) in L
|
||||
if(diamonds)
|
||||
var/distanced = 0
|
||||
distanced = get_dist(loc,diamonds.loc)
|
||||
if(distanced <= 1 && diamonds)
|
||||
qdel(diamonds)
|
||||
src.visible_message("<span class='notice'>[src] consumes [diamonds], and it disappears! ...At least, you think.</span>")
|
||||
var/obj/item/pen/survival/bait = locate(/obj/item/pen/survival) in L
|
||||
if(bait)
|
||||
var/distanceb = 0
|
||||
distanceb = get_dist(loc,bait.loc)
|
||||
if(distanceb <= 1 && bait)
|
||||
qdel(bait)
|
||||
visible_message("<span class='notice'>[src] examines [bait] closer, and telekinetically shatters the pen.</span>")
|
||||
for(var/obj/O in view(1, src))
|
||||
if(istype(O, /obj/item/stack/ore/diamond))
|
||||
qdel(O)
|
||||
src.visible_message("<span class='notice'>[src] consumes [O], and it disappears! ...At least, you think.</span>")
|
||||
else if(istype(O, /obj/item/pen/survival))
|
||||
qdel(O)
|
||||
src.visible_message("<span class='notice'>[src] examines [O] closer, and telekinetically shatters the pen.</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/random/Initialize()
|
||||
. = ..()
|
||||
|
||||
+1
@@ -25,6 +25,7 @@
|
||||
icon_aggro = "broodmother"
|
||||
icon_dead = "egg_sac"
|
||||
icon_gib = "syndicate_gib"
|
||||
health_doll_icon = "broodmother"
|
||||
maxHealth = 800
|
||||
health = 800
|
||||
melee_damage_lower = 30
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
icon_aggro = "herald"
|
||||
icon_dead = "herald_dying"
|
||||
icon_gib = "syndicate_gib"
|
||||
health_doll_icon = "herald"
|
||||
maxHealth = 800
|
||||
health = 800
|
||||
melee_damage_lower = 20
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
icon_aggro = "legionnaire"
|
||||
icon_dead = "legionnaire_dead"
|
||||
icon_gib = "syndicate_gib"
|
||||
health_doll_icon = "legionnaire"
|
||||
maxHealth = 800
|
||||
health = 800
|
||||
melee_damage_lower = 30
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
icon_aggro = "pandora"
|
||||
icon_dead = "pandora_dead"
|
||||
icon_gib = "syndicate_gib"
|
||||
health_doll_icon = "pandora"
|
||||
maxHealth = 800
|
||||
health = 800
|
||||
melee_damage_lower = 15
|
||||
|
||||
@@ -253,6 +253,7 @@
|
||||
icon_state = "legion"
|
||||
icon_living = "legion"
|
||||
icon_dead = "legion"
|
||||
health_doll_icon = "legion"
|
||||
health = 450
|
||||
maxHealth = 450
|
||||
melee_damage_lower = 20
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
icon_living = "clown"
|
||||
icon_dead = "clown_dead"
|
||||
icon_gib = "clown_gib"
|
||||
health_doll_icon = "clown" //if >32x32, it will use this generic. for all the huge clown mobs that subtype from this
|
||||
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
|
||||
turns_per_move = 5
|
||||
response_disarm_continuous = "gently pushes aside"
|
||||
|
||||
@@ -0,0 +1,690 @@
|
||||
/// The carp rift is currently charging.
|
||||
#define CHARGE_ONGOING 0
|
||||
/// The carp rift is currently charging and has output a final warning.
|
||||
#define CHARGE_FINALWARNING 1
|
||||
/// The carp rift is now fully charged.
|
||||
#define CHARGE_COMPLETED 2
|
||||
/// The darkness threshold for space dragon when choosing a color
|
||||
#define DARKNESS_THRESHOLD 50
|
||||
|
||||
/**
|
||||
* # Space Dragon
|
||||
*
|
||||
* A space-faring leviathan-esque monster which breathes fire and summons carp. Spawned during its respective midround antagonist event.
|
||||
*
|
||||
* A space-faring monstrosity who has the ability to breathe dangerous fire breath and uses its powerful wings to knock foes away.
|
||||
* Normally spawned as an antagonist during the Space Dragon event, Space Dragon's main goal is to open three rifts from which to pull a great tide of carp onto the station.
|
||||
* Space Dragon can summon only one rift at a time, and can do so anywhere a blob is allowed to spawn. In order to trigger his victory condition, Space Dragon must summon and defend three rifts while they charge.
|
||||
* Space Dragon, when spawned, has five minutes to summon the first rift. Failing to do so will cause Space Dragon to return from whence he came.
|
||||
* When the rift spawns, ghosts can interact with it to spawn in as space carp to help complete the mission. One carp is granted when the rift is first summoned, with an extra one every 30 seconds.
|
||||
* Once the victory condition is met, all current rifts become invulnerable to damage, are allowed to spawn infinite sentient space carp, and Space Dragon gets unlimited rage.
|
||||
* Alternatively, if the shuttle arrives while Space Dragon is still active, their victory condition will automatically be met and all the rifts will immediately become fully charged.
|
||||
* If a charging rift is destroyed, Space Dragon will be incredibly slowed, and the endlag on his gust attack is greatly increased on each use.
|
||||
* Space Dragon has the following abilities to assist him with his objective:
|
||||
* - Can shoot fire in straight line, dealing 30 burn damage and setting those suseptible on fire.
|
||||
* - Can use his wings to temporarily stun and knock back any nearby mobs. This attack has no cooldown, but instead has endlag after the attack where Space Dragon cannot act. This endlag's time decreases over time, but is added to every time he uses the move.
|
||||
* - Can swallow mob corpses to heal for half their max health. Any corpses swallowed are stored within him, and will be regurgitated on death.
|
||||
* - Can tear through any type of wall. This takes 4 seconds for most walls, and 12 seconds for reinforced walls.
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon
|
||||
name = "Space Dragon"
|
||||
desc = "A vile, leviathan-esque creature that flies in the most unnatural way. Looks slightly similar to a space carp."
|
||||
maxHealth = 400
|
||||
health = 400
|
||||
a_intent = INTENT_HARM
|
||||
speed = 0
|
||||
attack_verb_continuous = "chomps"
|
||||
attack_verb_simple = "chomp"
|
||||
attack_sound = 'sound/magic/demon_attack1.ogg'
|
||||
deathsound = 'sound/creatures/space_dragon_roar.ogg'
|
||||
icon = 'icons/mob/spacedragon.dmi'
|
||||
icon_state = "spacedragon"
|
||||
icon_living = "spacedragon"
|
||||
icon_dead = "spacedragon_dead"
|
||||
obj_damage = 50
|
||||
environment_smash = ENVIRONMENT_SMASH_NONE
|
||||
flags_1 = PREVENT_CONTENTS_EXPLOSION_1 | HEAR_1
|
||||
melee_damage_upper = 35
|
||||
melee_damage_lower = 35
|
||||
mob_size = MOB_SIZE_LARGE
|
||||
armour_penetration = 30
|
||||
pixel_x = -16
|
||||
turns_per_move = 5
|
||||
movement_type = FLYING
|
||||
health_doll_icon = "spacedragon"
|
||||
ranged = TRUE
|
||||
mouse_opacity = MOUSE_OPACITY_ICON
|
||||
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/bone = 30)
|
||||
deathmessage = "screeches as its wings turn to dust and it collapses on the floor, its life extinguished."
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
minbodytemp = 0
|
||||
maxbodytemp = 1500
|
||||
faction = list("carp")
|
||||
pressure_resistance = 200
|
||||
/// Current time since the the last rift was activated. If set to -1, does not increment.
|
||||
var/riftTimer = 0
|
||||
/// Maximum amount of time which can pass without a rift before Space Dragon despawns.
|
||||
var/maxRiftTimer = 300
|
||||
/// How much endlag using Wing Gust should apply. Each use of wing gust increments this, and it decreases over time.
|
||||
var/tiredness = 0
|
||||
/// A multiplier to how much each use of wing gust should add to the tiredness variable. Set to 5 if the current rift is destroyed.
|
||||
var/tiredness_mult = 1
|
||||
/// The distance Space Dragon's gust reaches
|
||||
var/gust_distance = 4
|
||||
/// The amount of tiredness to add to Space Dragon per use of gust
|
||||
var/gust_tiredness = 30
|
||||
/// Determines whether or not Space Dragon is in the middle of using wing gust. If set to true, prevents him from moving and doing certain actions.
|
||||
var/using_special = FALSE
|
||||
/// Determines whether or not Space Dragon is currently tearing through a wall.
|
||||
var/tearing_wall = FALSE
|
||||
/// A list of all of the rifts created by Space Dragon. Used for setting them all to infinite carp spawn when Space Dragon wins, and removing them when Space Dragon dies.
|
||||
var/list/obj/structure/carp_rift/rift_list = list()
|
||||
/// How many rifts have been successfully charged
|
||||
var/rifts_charged = 0
|
||||
/// Whether or not Space Dragon has completed their objective, and thus triggered the ending sequence.
|
||||
var/objective_complete = FALSE
|
||||
/// The innate ability to summon rifts
|
||||
var/datum/action/innate/summon_rift/rift
|
||||
/// The color of the space dragon.
|
||||
var/chosen_color
|
||||
|
||||
/mob/living/simple_animal/hostile/space_dragon/Initialize(mapload)
|
||||
. = ..()
|
||||
ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
|
||||
rift = new
|
||||
rift.Grant(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/space_dragon/Login()
|
||||
. = ..()
|
||||
if(!chosen_color)
|
||||
dragon_name()
|
||||
color_selection()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/space_dragon/Life()
|
||||
. = ..()
|
||||
tiredness = max(tiredness - 1, 0)
|
||||
for(var/mob/living/consumed_mob in src)
|
||||
if(consumed_mob.stat == DEAD)
|
||||
continue
|
||||
playsound(src, 'sound/effects/splat.ogg', 50, TRUE)
|
||||
visible_message("<span class='danger'>[src] vomits up [consumed_mob]!</span>")
|
||||
consumed_mob.forceMove(loc)
|
||||
consumed_mob.Paralyze(50)
|
||||
if((rifts_charged == 3 || (SSshuttle.emergency.mode == SHUTTLE_DOCKED && rifts_charged > 0)) && !objective_complete)
|
||||
victory()
|
||||
if(riftTimer == -1)
|
||||
return
|
||||
riftTimer = min(riftTimer + 1, maxRiftTimer + 1)
|
||||
if(riftTimer == (maxRiftTimer - 60))
|
||||
to_chat(src, "<span class='boldwarning'>You have a minute left to summon the rift! Get to it!</span>")
|
||||
return
|
||||
if(riftTimer >= maxRiftTimer)
|
||||
to_chat(src, "<span class='boldwarning'>You've failed to summon the rift in a timely manner! You're being pulled back from whence you came!</span>")
|
||||
destroy_rifts()
|
||||
playsound(src, 'sound/magic/demon_dies.ogg', 100, TRUE)
|
||||
QDEL_NULL(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/space_dragon/AttackingTarget()
|
||||
if(using_special)
|
||||
return
|
||||
if(target == src)
|
||||
to_chat(src, "<span class='warning'>You almost bite yourself, but then decide against it.</span>")
|
||||
return
|
||||
if(istype(target, /turf/closed/wall))
|
||||
if(tearing_wall)
|
||||
return
|
||||
tearing_wall = TRUE
|
||||
var/turf/closed/wall/thewall = target
|
||||
to_chat(src, "<span class='warning'>You begin tearing through the wall...</span>")
|
||||
playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, TRUE)
|
||||
var/timetotear = 40
|
||||
if(istype(target, /turf/closed/wall/r_wall))
|
||||
timetotear = 120
|
||||
if(do_after(src, timetotear, target = thewall))
|
||||
if(istype(thewall, /turf/open))
|
||||
return
|
||||
thewall.dismantle_wall(1)
|
||||
playsound(src, 'sound/effects/meteorimpact.ogg', 100, TRUE)
|
||||
tearing_wall = FALSE
|
||||
return
|
||||
if(isliving(target)) //Swallows corpses like a snake to regain health.
|
||||
var/mob/living/L = target
|
||||
if(L.stat == DEAD)
|
||||
to_chat(src, "<span class='warning'>You begin to swallow [L] whole...</span>")
|
||||
if(do_after(src, 30, target = L))
|
||||
if(eat(L))
|
||||
adjustHealth(-L.maxHealth * 0.5)
|
||||
return
|
||||
. = ..()
|
||||
if(istype(target, /obj/mecha))
|
||||
var/obj/mecha/M = target
|
||||
M.take_damage(50, BRUTE, MELEE, 1)
|
||||
|
||||
/mob/living/simple_animal/hostile/space_dragon/AltClickOn(atom/A)
|
||||
. = ..()
|
||||
if(using_special)
|
||||
return
|
||||
using_special = TRUE
|
||||
icon_state = "spacedragon_gust"
|
||||
add_dragon_overlay()
|
||||
useGust(0)
|
||||
|
||||
/mob/living/simple_animal/hostile/space_dragon/Move()
|
||||
if(!using_special)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/space_dragon/OpenFire()
|
||||
if(using_special)
|
||||
return
|
||||
ranged_cooldown = world.time + ranged_cooldown_time
|
||||
fire_stream()
|
||||
|
||||
/mob/living/simple_animal/hostile/space_dragon/death(gibbed)
|
||||
empty_contents()
|
||||
if(!objective_complete)
|
||||
destroy_rifts()
|
||||
..()
|
||||
add_dragon_overlay()
|
||||
|
||||
/mob/living/simple_animal/hostile/space_dragon/revive(full_heal, admin_revive)
|
||||
. = ..()
|
||||
add_dragon_overlay()
|
||||
|
||||
/mob/living/simple_animal/hostile/space_dragon/wabbajack_act(mob/living/new_mob)
|
||||
empty_contents()
|
||||
. = ..()
|
||||
|
||||
/**
|
||||
* Allows space dragon to choose its own name.
|
||||
*
|
||||
* Prompts the space dragon to choose a name, which it will then apply to itself.
|
||||
* If the name is invalid, will re-prompt the dragon until a proper name is chosen.
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/dragon_name()
|
||||
var/chosen_name = sanitize_name(reject_bad_text(stripped_input(src, "What would you like your name to be?", "Choose Your Name", real_name, MAX_NAME_LEN)))
|
||||
if(!chosen_name)
|
||||
to_chat(src, "<span class='warning'>Not a valid name, please try again.</span>")
|
||||
dragon_name()
|
||||
return
|
||||
visible_message("<span class='notice'>Your name is now <span class='name'>[chosen_name]</span>, the feared Space Dragon.</span>")
|
||||
fully_replace_character_name(null, chosen_name)
|
||||
|
||||
/**
|
||||
* Allows space dragon to choose a color for itself.
|
||||
*
|
||||
* Prompts the space dragon to choose a color, from which it will then apply to itself.
|
||||
* If an invalid color is given, will re-prompt the dragon until a proper color is chosen.
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/color_selection()
|
||||
chosen_color = input(src,"What would you like your color to be?","Choose Your Color", COLOR_WHITE) as color|null
|
||||
if(!chosen_color) //redo proc until we get a color
|
||||
to_chat(src, "<span class='warning'>Not a valid color, please try again.</span>")
|
||||
color_selection()
|
||||
return
|
||||
var/temp_hsv = RGBtoHSV(chosen_color)
|
||||
if(ReadHSV(temp_hsv)[3] < DARKNESS_THRESHOLD)
|
||||
to_chat(src, "<span class='danger'>Invalid color. Your color is not bright enough.</span>")
|
||||
color_selection()
|
||||
return
|
||||
add_atom_colour(chosen_color, FIXED_COLOUR_PRIORITY)
|
||||
add_dragon_overlay()
|
||||
|
||||
/**
|
||||
* Adds the proper overlay to the space dragon.
|
||||
*
|
||||
* Clears the current overlay on space dragon and adds a proper one for whatever animation he's in.
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/add_dragon_overlay()
|
||||
cut_overlays()
|
||||
if(stat == DEAD)
|
||||
var/mutable_appearance/overlay = mutable_appearance(icon, "overlay_dead")
|
||||
overlay.appearance_flags = RESET_COLOR
|
||||
add_overlay(overlay)
|
||||
return
|
||||
if(!using_special)
|
||||
var/mutable_appearance/overlay = mutable_appearance(icon, "overlay_base")
|
||||
overlay.appearance_flags = RESET_COLOR
|
||||
add_overlay(overlay)
|
||||
return
|
||||
if(using_special)
|
||||
var/mutable_appearance/overlay = mutable_appearance(icon, "overlay_gust")
|
||||
overlay.appearance_flags = RESET_COLOR
|
||||
add_overlay(overlay)
|
||||
|
||||
/**
|
||||
* Determines a line of turfs from sources's position to the target with length range.
|
||||
*
|
||||
* Determines a line of turfs from the source's position to the target with length range.
|
||||
* The line will extend on past the target if the range is large enough, and not reach the target if range is small enough.
|
||||
* Arguments:
|
||||
* * offset - whether or not to aim slightly to the left or right of the target
|
||||
* * range - how many turfs should we go out for
|
||||
* * atom/at - The target
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/line_target(offset, range, atom/at = target)
|
||||
if(!at)
|
||||
return
|
||||
var/angle = ATAN2(at.x - src.x, at.y - src.y) + offset
|
||||
var/turf/T = get_turf(src)
|
||||
for(var/i in 1 to range)
|
||||
var/turf/check = locate(src.x + cos(angle) * i, src.y + sin(angle) * i, src.z)
|
||||
if(!check)
|
||||
break
|
||||
T = check
|
||||
return (getline(src, T) - get_turf(src))
|
||||
|
||||
/**
|
||||
* Spawns fire at each position in a line from the source to the target.
|
||||
*
|
||||
* Spawns fire at each position in a line from the source to the target.
|
||||
* Stops if it comes into contact with a solid wall, a window, or a door.
|
||||
* Delays the spawning of each fire by 1.5 deciseconds.
|
||||
* Arguments:
|
||||
* * atom/at - The target
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/fire_stream(atom/at = target)
|
||||
playsound(get_turf(src),'sound/magic/fireball.ogg', 200, TRUE)
|
||||
var/range = 20
|
||||
var/list/turfs = list()
|
||||
turfs = line_target(0, range, at)
|
||||
var/delayFire = -1.0
|
||||
for(var/turf/T in turfs)
|
||||
if(istype(T, /turf/closed))
|
||||
return
|
||||
for(var/obj/structure/window/W in T.contents)
|
||||
return
|
||||
for(var/obj/machinery/door/D in T.contents)
|
||||
if(D.density)
|
||||
return
|
||||
delayFire += 1.0
|
||||
addtimer(CALLBACK(src, .proc/dragon_fire_line, T), delayFire)
|
||||
|
||||
/**
|
||||
* What occurs on each tile to actually create the fire.
|
||||
*
|
||||
* Creates a fire on the given turf.
|
||||
* It creates a hotspot on the given turf, damages any living mob with 30 burn damage, and damages mechs by 50.
|
||||
* It can only hit any given target once.
|
||||
* Arguments:
|
||||
* * turf/T - The turf to trigger the effects on.
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/dragon_fire_line(turf/T)
|
||||
var/list/hit_list = list()
|
||||
hit_list += src
|
||||
new /obj/effect/hotspot(T)
|
||||
T.hotspot_expose(700,50,1)
|
||||
for(var/mob/living/L in T.contents)
|
||||
if(L in hit_list)
|
||||
continue
|
||||
hit_list += L
|
||||
L.adjustFireLoss(30)
|
||||
to_chat(L, "<span class='userdanger'>You're hit by [src]'s fire breath!</span>")
|
||||
// deals damage to mechs
|
||||
for(var/obj/mecha/M in T.contents)
|
||||
if(M in hit_list)
|
||||
continue
|
||||
hit_list += M
|
||||
M.take_damage(50, BRUTE, MELEE, 1)
|
||||
|
||||
/**
|
||||
* Handles consuming and storing consumed things inside Space Dragon
|
||||
*
|
||||
* Plays a sound and then stores the consumed thing inside Space Dragon.
|
||||
* Used in AttackingTarget(), paired with a heal should it succeed.
|
||||
* Arguments:
|
||||
* * atom/movable/A - The thing being consumed
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/eat(atom/movable/A)
|
||||
if(A && A.loc != src)
|
||||
playsound(src, 'sound/magic/demon_attack1.ogg', 100, TRUE)
|
||||
visible_message("<span class='warning'>[src] swallows [A] whole!</span>")
|
||||
A.forceMove(src)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/**
|
||||
* Disperses the contents of the mob on the surrounding tiles.
|
||||
*
|
||||
* Randomly places the contents of the mob onto surrounding tiles.
|
||||
* Has a 10% chance to place on the same tile as the mob.
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/empty_contents()
|
||||
for(var/atom/movable/AM in src)
|
||||
AM.forceMove(loc)
|
||||
if(prob(90))
|
||||
step(AM, pick(GLOB.alldirs))
|
||||
|
||||
/**
|
||||
* Resets Space Dragon's status after using wing gust.
|
||||
*
|
||||
* Resets Space Dragon's status after using wing gust.
|
||||
* If it isn't dead by the time it calls this method, reset the sprite back to the normal living sprite.
|
||||
* Also sets the using_special variable to FALSE, allowing Space Dragon to move and attack freely again.
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/reset_status()
|
||||
if(stat != DEAD)
|
||||
icon_state = "spacedragon"
|
||||
using_special = FALSE
|
||||
add_dragon_overlay()
|
||||
|
||||
/**
|
||||
* Handles Space Dragon's temporary empowerment after boosting a rift.
|
||||
*
|
||||
* Empowers and depowers Space Dragon after a successful rift charge.
|
||||
* Empowered, Space Dragon regains all his health and becomes temporarily faster for 30 seconds, along with being tinted red.
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/rift_empower(is_permanent)
|
||||
fully_heal()
|
||||
add_filter("anger_glow", 3, list("type" = "outline", "color" = "#ff330030", "size" = 5))
|
||||
add_movespeed_modifier(/datum/movespeed_modifier/dragon_rage)
|
||||
addtimer(CALLBACK(src, .proc/rift_depower), 30 SECONDS)
|
||||
|
||||
/**
|
||||
* Gives Space Dragon their the rift speed buff permanantly.
|
||||
*
|
||||
* Gives Space Dragon the enraged speed buff from charging rifts permanantly.
|
||||
* Only happens in circumstances where Space Dragon completes their objective.
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/permanant_empower()
|
||||
fully_heal()
|
||||
add_filter("anger_glow", 3, list("type" = "outline", "color" = "#ff330030", "size" = 5))
|
||||
add_movespeed_modifier(/datum/movespeed_modifier/dragon_rage)
|
||||
|
||||
/**
|
||||
* Removes Space Dragon's rift speed buff.
|
||||
*
|
||||
* Removes Space Dragon's speed buff from charging a rift. This is only called
|
||||
* in rift_empower, which uses a timer to call this after 30 seconds. Also
|
||||
* removes the red glow from Space Dragon which is synonymous with the speed buff.
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/rift_depower()
|
||||
remove_filter("anger_glow")
|
||||
remove_movespeed_modifier(/datum/movespeed_modifier/dragon_rage)
|
||||
|
||||
/**
|
||||
* Destroys all of Space Dragon's current rifts.
|
||||
*
|
||||
* QDeletes all the current rifts after removing their references to other objects.
|
||||
* Currently, the only reference they have is to the Dragon which created them, so we clear that before deleting them.
|
||||
* Currently used when Space Dragon dies or one of his rifts is destroyed.
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/destroy_rifts()
|
||||
rifts_charged = 0
|
||||
add_movespeed_modifier(/datum/movespeed_modifier/dragon_depression)
|
||||
riftTimer = -1
|
||||
tiredness_mult = 5
|
||||
playsound(src, 'sound/vehicles/rocketlaunch.ogg', 100, TRUE)
|
||||
for(var/obj/structure/carp_rift/rift in rift_list)
|
||||
rift.dragon = null
|
||||
rift_list -= rift
|
||||
if(!QDELETED(rift))
|
||||
QDEL_NULL(rift)
|
||||
|
||||
/**
|
||||
* Handles wing gust from the windup all the way to the endlag at the end.
|
||||
*
|
||||
* Handles the wing gust attack from start to finish, based on the timer.
|
||||
* When intially triggered, starts at 0. Until the timer reaches 10, increase Space Dragon's y position by 2 and call back to the function in 1.5 deciseconds.
|
||||
* When the timer is at 10, trigger the attack. Change Space Dragon's sprite. reset his y position, and push all living creatures back in a 3 tile radius and stun them for 5 seconds.
|
||||
* Stay in the ending state for how much our tiredness dictates and add to our tiredness.
|
||||
* Arguments:
|
||||
* * timer - The timer used for the windup.
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/useGust(timer)
|
||||
if(timer != 10)
|
||||
pixel_y = pixel_y + 2;
|
||||
addtimer(CALLBACK(src, .proc/useGust, timer + 1), 1.5)
|
||||
return
|
||||
pixel_y = 0
|
||||
icon_state = "spacedragon_gust_2"
|
||||
cut_overlays()
|
||||
var/mutable_appearance/overlay = mutable_appearance(icon, "overlay_gust_2")
|
||||
overlay.appearance_flags = RESET_COLOR
|
||||
add_overlay(overlay)
|
||||
playsound(src, 'sound/effects/gravhit.ogg', 100, TRUE)
|
||||
var/gust_locs = spiral_range_turfs(gust_distance, get_turf(src))
|
||||
var/list/hit_things = list()
|
||||
for(var/turf/T in gust_locs)
|
||||
for(var/mob/living/L in T.contents)
|
||||
if(L == src)
|
||||
continue
|
||||
hit_things += L
|
||||
visible_message("<span class='boldwarning'>[L] is knocked back by the gust!</span>")
|
||||
to_chat(L, "<span class='userdanger'>You're knocked back by the gust!</span>")
|
||||
var/dir_to_target = get_dir(get_turf(src), get_turf(L))
|
||||
var/throwtarget = get_edge_target_turf(target, dir_to_target)
|
||||
L.safe_throw_at(throwtarget, 10, 1, src)
|
||||
L.Paralyze(50)
|
||||
addtimer(CALLBACK(src, .proc/reset_status), 4 + ((tiredness * tiredness_mult) / 10))
|
||||
tiredness = tiredness + (gust_tiredness * tiredness_mult)
|
||||
|
||||
/**
|
||||
* Sets up Space Dragon's victory for completing the objectives.
|
||||
*
|
||||
* Triggers when Space Dragon completes his objective.
|
||||
* Calls the shuttle with a coefficient of 3, making it impossible to recall.
|
||||
* Sets all of his rifts to allow for infinite sentient carp spawns
|
||||
* Also plays appropiate sounds and CENTCOM messages.
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/space_dragon/proc/victory()
|
||||
objective_complete = TRUE
|
||||
permanant_empower()
|
||||
var/datum/antagonist/space_dragon/S = mind.has_antag_datum(/datum/antagonist/space_dragon)
|
||||
if(S)
|
||||
var/datum/objective/summon_carp/main_objective = locate() in S.objectives
|
||||
if(main_objective)
|
||||
main_objective.completed = TRUE
|
||||
priority_announce("A large amount of lifeforms have been detected approaching [station_name()] at extreme speeds. Remaining crew are advised to evacuate as soon as possible.", "Central Command Wildlife Observations")
|
||||
sound_to_playing_players('sound/creatures/space_dragon_roar.ogg')
|
||||
for(var/obj/structure/carp_rift/rift in rift_list)
|
||||
rift.carp_stored = 999999
|
||||
rift.time_charged = rift.max_charge
|
||||
|
||||
/datum/action/innate/summon_rift
|
||||
name = "Summon Rift"
|
||||
desc = "Summon a rift to bring forth a horde of space carp."
|
||||
background_icon_state = "bg_default"
|
||||
icon_icon = 'icons/mob/actions/actions_space_dragon.dmi'
|
||||
button_icon_state = "carp_rift"
|
||||
|
||||
/datum/action/innate/summon_rift/Activate()
|
||||
var/mob/living/simple_animal/hostile/space_dragon/S = owner
|
||||
if(S.using_special)
|
||||
return
|
||||
if(S.riftTimer == -1)
|
||||
to_chat(S, "<span class='warning'>Your death has left you unable to summon rifts!</span>")
|
||||
return
|
||||
var/area/A = get_area(S)
|
||||
if(!(A.area_flags & (VALID_TERRITORY | BLOBS_ALLOWED)))
|
||||
to_chat(S, "<span class='warning'>You can't summon a rift here! Try summoning somewhere secure within the station!</span>")
|
||||
return
|
||||
for(var/obj/structure/carp_rift/rift in S.rift_list)
|
||||
var/area/RA = get_area(rift)
|
||||
if(RA == A)
|
||||
to_chat(S, "<span class='warning'>You've already summoned a rift in this area! You have to summon again somewhere else!</span>")
|
||||
return
|
||||
to_chat(S, "<span class='warning'>You begin to open a rift...</span>")
|
||||
if(do_after(S, 100, target = S))
|
||||
for(var/obj/structure/carp_rift/c in S.loc.contents)
|
||||
return
|
||||
var/obj/structure/carp_rift/CR = new /obj/structure/carp_rift(S.loc)
|
||||
playsound(S, 'sound/vehicles/rocketlaunch.ogg', 100, TRUE)
|
||||
S.riftTimer = -1
|
||||
CR.dragon = S
|
||||
S.rift_list += CR
|
||||
to_chat(S, "<span class='boldwarning'>The rift has been summoned. Prevent the crew from destroying it at all costs!</span>")
|
||||
notify_ghosts("The Space Dragon has opened a rift!", source = CR, action = NOTIFY_ORBIT, flashwindow = FALSE, header = "Carp Rift Opened")
|
||||
qdel(src)
|
||||
|
||||
/**
|
||||
* # Carp Rift
|
||||
*
|
||||
* The portals Space Dragon summons to bring carp onto the station.
|
||||
*
|
||||
* The portals Space Dragon summons to bring carp onto the station. His main objective is to summon 3 of them and protect them from being destroyed.
|
||||
* The portals can summon sentient space carp in limited amounts. The portal also changes color based on whether or not a carp spawn is available.
|
||||
* Once it is fully charged, it becomes indestructible, and intermitently spawns non-sentient carp. It is still destroyed if Space Dragon dies.
|
||||
*/
|
||||
/obj/structure/carp_rift
|
||||
name = "carp rift"
|
||||
desc = "A rift akin to the ones space carp use to travel long distances."
|
||||
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 100, BOMB = 50, BIO = 100, RAD = 100, FIRE = 100, ACID = 100)
|
||||
max_integrity = 300
|
||||
icon = 'icons/obj/carp_rift.dmi'
|
||||
icon_state = "carp_rift_carpspawn"
|
||||
light_color = LIGHT_COLOR_PURPLE
|
||||
light_range = 10
|
||||
anchored = TRUE
|
||||
density = FALSE
|
||||
layer = MASSIVE_OBJ_LAYER
|
||||
/// The amount of time the rift has charged for.
|
||||
var/time_charged = 0
|
||||
/// The maximum charge the rift can have.
|
||||
var/max_charge = 300
|
||||
/// How many carp spawns it has available.
|
||||
var/carp_stored = 1
|
||||
/// A reference to the Space Dragon that created it.
|
||||
var/mob/living/simple_animal/hostile/space_dragon/dragon
|
||||
/// Current charge state of the rift.
|
||||
var/charge_state = CHARGE_ONGOING
|
||||
/// The interval for adding additional space carp spawns to the rift.
|
||||
var/carp_interval = 30
|
||||
/// The time since an extra carp was added to the ghost role spawning pool.
|
||||
var/last_carp_inc = 0
|
||||
|
||||
/obj/structure/carp_rift/Initialize(mapload)
|
||||
. = ..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/structure/carp_rift/examine(mob/user)
|
||||
. = ..()
|
||||
if(time_charged < max_charge)
|
||||
. += "<span class='notice'>It seems to be [(time_charged / max_charge) * 100]% charged.</span>"
|
||||
else
|
||||
. += "<span class='warning'>This one is fully charged. In this state, it is poised to transport a much larger amount of carp than normal.</span>"
|
||||
|
||||
if(isobserver(user))
|
||||
. += "<span class='notice'>It has [carp_stored] carp available to spawn as.</span>"
|
||||
|
||||
/obj/structure/carp_rift/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
|
||||
playsound(src, 'sound/magic/lightningshock.ogg', 50, TRUE)
|
||||
|
||||
/obj/structure/carp_rift/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
if(time_charged != max_charge + 1)
|
||||
dragon?.destroy_rifts()
|
||||
if(dragon)
|
||||
to_chat(dragon, "<span class='boldwarning'>A rift has been destroyed! You have failed, and find yourself weakened.</span>")
|
||||
return ..()
|
||||
|
||||
/obj/structure/carp_rift/process(delta_time)
|
||||
// Heal carp on our loc.
|
||||
for(var/mob/living/simple_animal/hostile/hostilehere in loc)
|
||||
if("carp" in hostilehere.faction)
|
||||
hostilehere.adjustHealth(-5 * delta_time)
|
||||
var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(hostilehere))
|
||||
H.color = "#0000FF"
|
||||
|
||||
// If we're fully charged, just start mass spawning carp and move around.
|
||||
if(charge_state == CHARGE_COMPLETED)
|
||||
if(DT_PROB(1.25, delta_time))
|
||||
new /mob/living/simple_animal/hostile/carp(loc)
|
||||
if(DT_PROB(1.5, delta_time))
|
||||
var/rand_dir = pick(GLOB.cardinals)
|
||||
Move(get_step(src, rand_dir), rand_dir)
|
||||
return
|
||||
|
||||
// Increase time trackers and check for any updated states.
|
||||
time_charged = min(time_charged + delta_time, max_charge)
|
||||
last_carp_inc += delta_time
|
||||
update_check()
|
||||
|
||||
/obj/structure/carp_rift/attack_ghost(mob/user)
|
||||
. = ..()
|
||||
summon_carp(user)
|
||||
|
||||
/**
|
||||
* Does a series of checks based on the portal's status.
|
||||
*
|
||||
* Performs a number of checks based on the current charge of the portal, and triggers various effects accordingly.
|
||||
* If the current charge is a multiple of carp_interval, add an extra carp spawn.
|
||||
* If we're halfway charged, announce to the crew our location in a CENTCOM announcement.
|
||||
* If we're fully charged, tell the crew we are, change our color to yellow, become invulnerable, and give Space Dragon the ability to make another rift, if he hasn't summoned 3 total.
|
||||
*/
|
||||
/obj/structure/carp_rift/proc/update_check()
|
||||
// If the rift is fully charged, there's nothing to do here anymore.
|
||||
if(charge_state == CHARGE_COMPLETED)
|
||||
return
|
||||
|
||||
// Can we increase the carp spawn pool size?
|
||||
if(last_carp_inc >= carp_interval)
|
||||
carp_stored++
|
||||
icon_state = "carp_rift_carpspawn"
|
||||
if(light_color != LIGHT_COLOR_PURPLE)
|
||||
light_color = LIGHT_COLOR_PURPLE
|
||||
update_light()
|
||||
notify_ghosts("The carp rift can summon an additional carp!", source = src, action = NOTIFY_ORBIT, flashwindow = FALSE, header = "Carp Spawn Available")
|
||||
last_carp_inc -= carp_interval
|
||||
|
||||
// Is the rift now fully charged?
|
||||
if(time_charged >= max_charge)
|
||||
charge_state = CHARGE_COMPLETED
|
||||
var/area/A = get_area(src)
|
||||
priority_announce("Spatial object has reached peak energy charge in [initial(A.name)], please stand-by.", "Central Command Wildlife Observations")
|
||||
obj_integrity = INFINITY
|
||||
icon_state = "carp_rift_charged"
|
||||
light_color = LIGHT_COLOR_YELLOW
|
||||
update_light()
|
||||
armor = list(MELEE = 100, BULLET = 100, LASER = 100, ENERGY = 100, BOMB = 100, BIO = 100, RAD = 100, FIRE = 100, ACID = 100)
|
||||
resistance_flags = INDESTRUCTIBLE
|
||||
dragon.rifts_charged += 1
|
||||
if(dragon.rifts_charged != 3 && !dragon.objective_complete)
|
||||
dragon.rift = new
|
||||
dragon.rift.Grant(dragon)
|
||||
dragon.riftTimer = 0
|
||||
dragon.rift_empower()
|
||||
// Early return, nothing to do after this point.
|
||||
return
|
||||
|
||||
// Do we need to give a final warning to the station at the halfway mark?
|
||||
if(charge_state < CHARGE_FINALWARNING && time_charged >= (max_charge * 0.5))
|
||||
charge_state = CHARGE_FINALWARNING
|
||||
var/area/A = get_area(src)
|
||||
priority_announce("A rift is causing an unnaturally large energy flux in [initial(A.name)]. Stop it at all costs!", "Central Command Wildlife Observations", sound = 'sound/announcer/classic/spanomalies.ogg')
|
||||
|
||||
/**
|
||||
* Used to create carp controlled by ghosts when the option is available.
|
||||
*
|
||||
* Creates a carp for the ghost to control if we have a carp spawn available.
|
||||
* Gives them prompt to control a carp, and if our circumstances still allow if when they hit yes, spawn them in as a carp.
|
||||
* Also add them to the list of carps in Space Dragon's antgonist datum, so they'll be displayed as having assisted him on round end.
|
||||
* Arguments:
|
||||
* * mob/user - The ghost which will take control of the carp.
|
||||
*/
|
||||
/obj/structure/carp_rift/proc/summon_carp(mob/user)
|
||||
if(carp_stored <= 0)//Not enough carp points
|
||||
return FALSE
|
||||
var/carp_ask = alert("Become a carp?", "Help bring forth the horde?", "Yes", "No")
|
||||
if(carp_ask == "No" || !src || QDELETED(src) || QDELETED(user))
|
||||
return FALSE
|
||||
if(carp_stored <= 0)
|
||||
to_chat(user, "<span class='warning'>The rift already summoned enough carp!</span>")
|
||||
return FALSE
|
||||
var/mob/living/simple_animal/hostile/carp/newcarp = new /mob/living/simple_animal/hostile/carp(loc)
|
||||
newcarp.key = user.key
|
||||
var/datum/antagonist/space_dragon/S = dragon.mind.has_antag_datum(/datum/antagonist/space_dragon)
|
||||
if(S)
|
||||
S.carp += newcarp.mind
|
||||
to_chat(newcarp, "<span class='boldwarning'>You have arrived in order to assist the space dragon with securing the rifts. Do not jeopardize the mission, and protect the rifts at all costs!</span>")
|
||||
carp_stored--
|
||||
if(carp_stored <= 0 && charge_state < CHARGE_COMPLETED)
|
||||
icon_state = "carp_rift"
|
||||
light_color = LIGHT_COLOR_BLUE
|
||||
update_light()
|
||||
return TRUE
|
||||
|
||||
#undef CHARGE_ONGOING
|
||||
#undef CHARGE_FINALWARNING
|
||||
#undef CHARGE_COMPLETED
|
||||
#undef DARKNESS_THRESHOLD
|
||||
@@ -130,11 +130,11 @@
|
||||
QDEL_NULL(sord)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/syndicate/melee/bullet_act(obj/item/projectile/Proj)
|
||||
if(prob(25))
|
||||
return ..()
|
||||
visible_message("<span class='danger'>[src] blocks [Proj] with its shield!</span>")
|
||||
return BULLET_ACT_BLOCK
|
||||
/mob/living/simple_animal/hostile/syndicate/melee/sword/bullet_act(obj/item/projectile/Proj)
|
||||
if(prob(50))
|
||||
visible_message("<span class='danger'>[src] blocks [Proj] with its shield!</span>")
|
||||
return BULLET_ACT_BLOCK
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/syndicate/melee/sword/space
|
||||
icon_state = "syndicate_space_sword"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
icon_living = "pine_1"
|
||||
icon_dead = "pine_1"
|
||||
icon_gib = "pine_1"
|
||||
health_doll_icon = "pine_1"
|
||||
gender = NEUTER
|
||||
speak_chance = 0
|
||||
turns_per_move = 5
|
||||
@@ -49,12 +50,12 @@
|
||||
if(isopenturf(loc))
|
||||
var/turf/open/T = src.loc
|
||||
if(T.air)
|
||||
var/co2 = T.air.get_moles(/datum/gas/carbon_dioxide)
|
||||
var/co2 = T.air.get_moles(GAS_CO2)
|
||||
if(co2 > 0)
|
||||
if(prob(25))
|
||||
var/amt = min(co2, 9)
|
||||
T.air.adjust_moles(/datum/gas/carbon_dioxide, -amt)
|
||||
T.atmos_spawn_air("o2=[amt]")
|
||||
T.air.adjust_moles(GAS_CO2, -amt)
|
||||
T.atmos_spawn_air("o2=[amt];TEMP=293.15")
|
||||
|
||||
/mob/living/simple_animal/hostile/tree/AttackingTarget()
|
||||
. = ..()
|
||||
@@ -72,5 +73,6 @@
|
||||
icon_living = "festivus_pole"
|
||||
icon_dead = "festivus_pole"
|
||||
icon_gib = "festivus_pole"
|
||||
health_doll_icon = "festivus_pole"
|
||||
loot = list(/obj/item/stack/rods)
|
||||
speak_emote = list("polls")
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
icon_aggro = "Fugu0"
|
||||
icon_dead = "Fugu_dead"
|
||||
icon_gib = "syndicate_gib"
|
||||
health_doll_icon = "Fugu0"
|
||||
mob_biotypes = MOB_ORGANIC|MOB_BEAST
|
||||
mouse_opacity = MOUSE_OPACITY_ICON
|
||||
move_to_delay = 5
|
||||
|
||||
@@ -64,7 +64,6 @@
|
||||
friendly_verb_simple = "groom"
|
||||
mob_size = MOB_SIZE_SMALL
|
||||
movement_type = FLYING
|
||||
gold_core_spawnable = FRIENDLY_SPAWN
|
||||
|
||||
var/parrot_damage_upper = 10
|
||||
var/parrot_state = PARROT_WANDER //Hunt for a perch when created
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
///How much blud it has for bloodsucking
|
||||
blood_volume = 550
|
||||
rad_flags = RAD_NO_CONTAMINATE
|
||||
hud_type = /datum/hud/living/simple_animal
|
||||
|
||||
status_flags = CANPUSH
|
||||
|
||||
@@ -263,10 +264,10 @@
|
||||
var/turf/open/ST = src.loc
|
||||
if(ST.air)
|
||||
|
||||
var/tox = ST.air.get_moles(/datum/gas/plasma)
|
||||
var/oxy = ST.air.get_moles(/datum/gas/oxygen)
|
||||
var/n2 = ST.air.get_moles(/datum/gas/nitrogen)
|
||||
var/co2 = ST.air.get_moles(/datum/gas/carbon_dioxide)
|
||||
var/tox = ST.air.get_moles(GAS_PLASMA)
|
||||
var/oxy = ST.air.get_moles(GAS_O2)
|
||||
var/n2 = ST.air.get_moles(GAS_N2)
|
||||
var/co2 = ST.air.get_moles(GAS_CO2)
|
||||
|
||||
if(atmos_requirements["min_oxy"] && oxy < atmos_requirements["min_oxy"])
|
||||
. = FALSE
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
var/Discipline = 0 // if a slime has been hit with a freeze gun, or wrestled/attacked off a human, they become disciplined and don't attack anymore for a while
|
||||
var/SStun = 0 // stun variable
|
||||
|
||||
typing_indicator_state = /obj/effect/overlay/typing_indicator/slime
|
||||
|
||||
/mob/living/simple_animal/slime/BiologicalLife(seconds, times_fired)
|
||||
if(!(. = ..()))
|
||||
@@ -128,7 +129,7 @@
|
||||
Tempstun = 0
|
||||
|
||||
if(stat != DEAD)
|
||||
var/bz_percentage = environment.total_moles() ? (environment.get_moles(/datum/gas/bz) / environment.total_moles()) : 0
|
||||
var/bz_percentage = environment.total_moles() ? (environment.get_moles(GAS_BZ) / environment.total_moles()) : 0
|
||||
var/stasis = (bz_percentage >= 0.05 && bodytemperature < (T0C + 100)) || force_stasis
|
||||
|
||||
if(stat == CONSCIOUS && stasis)
|
||||
|
||||
@@ -35,11 +35,10 @@
|
||||
return
|
||||
CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/out_of_combat_timer, out_of_combat_timer)
|
||||
CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/base_regeneration, base_regeneration)
|
||||
CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/combat_regeneration, combat_regeneration)
|
||||
CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/percent_regeneration_out_of_combat, percent_regeneration_out_of_combat)
|
||||
CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/post_action_penalty_delay, post_action_penalty_delay)
|
||||
CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/post_action_penalty_factor, post_action_penalty_factor)
|
||||
var/base_regen = (SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))? base_regeneration : combat_regeneration
|
||||
var/base_regen = base_regeneration
|
||||
var/time_since_last_action = world.time - stamina_buffer_last_use
|
||||
var/action_penalty = ((time_since_last_action) < (post_action_penalty_delay * 10))? post_action_penalty_factor : 1
|
||||
var/out_of_combat_bonus = (time_since_last_action < (out_of_combat_timer * 10))? 0 : ((buffer_max * percent_regeneration_out_of_combat * 0.01))
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
// ignore_castun = same logic as Paralyze() in general
|
||||
// override_duration = If this is set, does Paralyze() for this duration.
|
||||
// override_stam = If this is set, does this amount of stamina damage.
|
||||
/mob/living/proc/DefaultCombatKnockdown(amount, updating = TRUE, ignore_canknockdown = FALSE, override_hardstun, override_stamdmg)
|
||||
// knocktofloor - whether to knock them to the ground
|
||||
/mob/living/proc/DefaultCombatKnockdown(amount, updating = TRUE, ignore_canknockdown = FALSE, override_hardstun, override_stamdmg, knocktofloor = TRUE)
|
||||
if(!iscarbon(src))
|
||||
return Paralyze(amount, updating, ignore_canknockdown)
|
||||
if(!ignore_canknockdown && !(status_flags & CANKNOCKDOWN))
|
||||
@@ -13,7 +14,8 @@
|
||||
buckled.unbuckle_mob(src)
|
||||
var/drop_items = amount > 80 //80 is cutoff for old item dropping behavior
|
||||
var/stamdmg = isnull(override_stamdmg)? (amount * 0.25) : override_stamdmg
|
||||
KnockToFloor(drop_items, TRUE, updating)
|
||||
if(knocktofloor)
|
||||
KnockToFloor(drop_items, TRUE, updating)
|
||||
adjustStaminaLoss(stamdmg)
|
||||
if(!isnull(override_hardstun))
|
||||
Paralyze(override_hardstun)
|
||||
|
||||
Reference in New Issue
Block a user