This commit is contained in:
EmeraldSundisk
2020-07-04 21:33:29 -07:00
parent 99168e3ed0
commit c6b5bac0d8
871 changed files with 411113 additions and 96055 deletions
+3 -3
View File
@@ -68,7 +68,7 @@ INITIALIZE_IMMEDIATE(/mob/dead)
set category = "OOC"
set name = "Server Hop!"
set desc= "Jump to the other server"
if(mob_transforming)
if(notransform)
return
var/list/csa = CONFIG_GET(keyed_list/cross_server)
var/pick
@@ -93,9 +93,9 @@ INITIALIZE_IMMEDIATE(/mob/dead)
to_chat(C, "<span class='notice'>Sending you to [pick].</span>")
new /obj/screen/splash(C)
mob_transforming = TRUE
notransform = TRUE
sleep(29) //let the animation play
mob_transforming = FALSE
notransform = FALSE
if(!C)
return
@@ -176,6 +176,9 @@
SSticker.queue_delay = 4
qdel(src)
if(!ready && href_list["preference"])
if(client)
client.prefs.process_link(src, href_list)
else if(!href_list["late_join"])
new_player_panel()
@@ -412,8 +415,6 @@
give_guns(humanc)
if(GLOB.summon_magic_triggered)
give_magic(humanc)
if(GLOB.curse_of_madness_triggered)
give_madness(humanc, GLOB.curse_of_madness_triggered)
GLOB.joined_player_list += character.ckey
GLOB.latejoiners += character
@@ -581,12 +582,6 @@
qdel(src)
/mob/dead/new_player/proc/ViewManifest()
if(!client)
return
if(world.time < client.crew_manifest_delay)
return
client.crew_manifest_delay = world.time + (1 SECONDS)
var/dat = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'></head><body>"
dat += "<h4>Crew Manifest</h4>"
dat += GLOB.data_core.get_manifest(OOC = 1)
@@ -717,12 +717,6 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
set name = "View Crew Manifest"
set category = "Ghost"
if(!client)
return
if(world.time < client.crew_manifest_delay)
return
client.crew_manifest_delay = world.time + (1 SECONDS)
var/dat
dat += "<h4>Crew Manifest</h4>"
dat += GLOB.data_core.get_manifest()
+41 -55
View File
@@ -158,7 +158,7 @@
//Returns if a certain item can be equipped to a certain slot.
// Currently invalid for two-handed items - call obj/item/mob_can_equip() instead.
/mob/proc/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, clothing_check = FALSE, list/return_warning)
/mob/proc/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE)
return FALSE
/mob/proc/can_put_in_hand(I, hand_index)
@@ -333,68 +333,54 @@
I.moveToNullspace()
else
I.forceMove(newloc)
on_item_dropped(I)
if(I.dropped(src) == ITEM_RELOCATED_BY_DROPPED)
return FALSE
return TRUE
//This is a SAFE proc. Use this instead of equip_to_slot()!
//set qdel_on_fail to have it delete W if it fails to equip
//set disable_warning to disable the 'you are unable to equip that' warning.
//unset redraw_mob to prevent the mob from being redrawn at the end.
/mob/proc/equip_to_slot_if_possible(obj/item/W, slot, qdel_on_fail = FALSE, disable_warning = FALSE, redraw_mob = TRUE, bypass_equip_delay_self = FALSE, clothing_check = FALSE)
if(!istype(W))
return FALSE
var/list/warning = list("<span class='warning'>You are unable to equip that!</span>")
if(!W.mob_can_equip(src, null, slot, disable_warning, bypass_equip_delay_self, clothing_check, warning))
if(qdel_on_fail)
qdel(W)
else if(!disable_warning)
to_chat(src, warning[1])
return FALSE
equip_to_slot(W, slot, redraw_mob) //This proc should not ever fail.
return TRUE
//This is an UNSAFE proc. It merely handles the actual job of equipping. All the checks on whether you can or can't equip need to be done before! Use mob_can_equip() for that task.
//In most cases you will want to use equip_to_slot_if_possible()
/mob/proc/equip_to_slot(obj/item/W, slot)
return
//This is just a commonly used configuration for the equip_to_slot_if_possible() proc, used to equip people when the round starts and when events happen and such.
//Also bypasses equip delay checks, since the mob isn't actually putting it on.
/mob/proc/equip_to_slot_or_del(obj/item/W, slot)
return equip_to_slot_if_possible(W, slot, TRUE, TRUE, FALSE, TRUE)
//puts the item "W" into an appropriate slot in a human's inventory
//returns 0 if it cannot, 1 if successful
/mob/proc/equip_to_appropriate_slot(obj/item/W, clothing_check = FALSE)
if(!istype(W))
return 0
var/slot_priority = W.slot_equipment_priority
if(!slot_priority)
slot_priority = list( \
SLOT_BACK, SLOT_WEAR_ID,\
SLOT_W_UNIFORM, SLOT_WEAR_SUIT,\
SLOT_WEAR_MASK, SLOT_HEAD, SLOT_NECK,\
SLOT_SHOES, SLOT_GLOVES,\
SLOT_EARS, SLOT_GLASSES,\
SLOT_BELT, SLOT_S_STORE,\
SLOT_L_STORE, SLOT_R_STORE,\
SLOT_GENERC_DEXTROUS_STORAGE\
)
for(var/slot in slot_priority)
if(equip_to_slot_if_possible(W, slot, FALSE, TRUE, TRUE, FALSE, clothing_check)) //qdel_on_fail = 0; disable_warning = 1; redraw_mob = 1
return 1
return 0
//Outdated but still in use apparently. This should at least be a human proc.
//Daily reminder to murder this - Remie.
/mob/living/proc/get_equipped_items(include_pockets = FALSE)
return
/mob/living/carbon/get_equipped_items(include_pockets = FALSE)
var/list/items = list()
if(back)
items += back
if(head)
items += head
if(wear_mask)
items += wear_mask
if(wear_neck)
items += wear_neck
return items
/mob/living/carbon/human/get_equipped_items(include_pockets = FALSE)
var/list/items = ..()
if(belt)
items += belt
if(ears)
items += ears
if(glasses)
items += glasses
if(gloves)
items += gloves
if(shoes)
items += shoes
if(wear_id)
items += wear_id
if(wear_suit)
items += wear_suit
if(w_uniform)
items += w_uniform
if(include_pockets)
if(l_store)
items += l_store
if(r_store)
items += r_store
if(s_store)
items += s_store
return items
/mob/living/proc/unequip_everything()
var/list/items = list()
items |= get_equipped_items(TRUE)
@@ -407,7 +393,7 @@
to_chat(M, "<span class='warning'>You are not holding anything to equip!</span>")
return FALSE
if(M.equip_to_appropriate_slot(src, TRUE))
if(M.equip_to_appropriate_slot(src))
M.update_inv_hands()
return TRUE
else
+2 -4
View File
@@ -94,9 +94,7 @@
//We want an accurate reading of .len
listclearnulls(BP.embedded_objects)
for(var/obj/item/embeddies in BP.embedded_objects)
if(!embeddies.isEmbedHarmless())
temp_bleed += 0.5
temp_bleed += 0.5 * BP.embedded_objects.len
if(brutedamage >= 20)
temp_bleed += (brutedamage * 0.013)
@@ -188,7 +186,7 @@
blood_data["viruses"] += D.Copy()
blood_data["blood_DNA"] = dna.unique_enzymes
blood_data["bloodcolor"] = dna.species.exotic_blood_color
blood_data["bloodcolor"] = bloodtype_to_color(dna.blood_type)
if(disease_resistances && disease_resistances.len)
blood_data["resistances"] = disease_resistances.Copy()
var/list/temp_chem = list()
+3 -3
View File
@@ -37,10 +37,10 @@
C.put_in_hands(B1)
C.put_in_hands(B2)
C.regenerate_icons()
src.mob_transforming = TRUE
src.notransform = TRUE
spawn(0)
bloodpool_sink(B)
src.mob_transforming = FALSE
src.notransform = FALSE
return 1
/mob/living/proc/bloodpool_sink(obj/effect/decal/cleanable/B)
@@ -155,7 +155,7 @@
addtimer(CALLBACK(src, /atom/.proc/remove_atom_colour, TEMPORARY_COLOUR_PRIORITY, newcolor), 6 SECONDS)
/mob/living/proc/phasein(obj/effect/decal/cleanable/B)
if(src.mob_transforming)
if(src.notransform)
to_chat(src, "<span class='warning'>Finish eating first!</span>")
return 0
B.visible_message("<span class='warning'>[B] starts to bubble...</span>")
-6
View File
@@ -100,9 +100,3 @@
client.mouse_pointer_icon = M.mouse_pointer
if (client && ranged_ability && ranged_ability.ranged_mousepointer)
client.mouse_pointer_icon = ranged_ability.ranged_mousepointer
/mob/living/brain/proc/get_traumas()
. = list()
if(istype(loc, /obj/item/organ/brain))
var/obj/item/organ/brain/B = loc
. = B.traumas
@@ -76,6 +76,9 @@
REMOVE_SKILL_MODIFIER_BODY(/datum/skill_modifier/heavy_brain_damage, null, C)
C.update_hair()
/obj/item/organ/brain/prepare_eat()
return // Too important to eat.
/obj/item/organ/brain/proc/transfer_identity(mob/living/L)
name = "[L.name]'s brain"
if(brainmob)
+6 -2
View File
@@ -1,7 +1,11 @@
/mob/living/brain/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
/mob/living/brain/Life()
set invisibility = 0
if (notransform)
return
if(!loc)
return
. = ..()
handle_emp_damage()
/mob/living/brain/update_stat()
@@ -8,6 +8,9 @@
/mob/living/carbon/alien/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
return ..(AM, skipcatch = TRUE, hitpush = FALSE)
/mob/living/carbon/alien/can_embed(obj/item/I)
return FALSE
/*Code for aliens attacking aliens. Because aliens act on a hivemind, I don't see them as very aggressive with each other.
As such, they can either help or harm other aliens. Help works like the human help command while harm is a simple nibble.
In all, this is a lot like the monkey code. /N
@@ -51,8 +54,6 @@ In all, this is a lot like the monkey code. /N
return
switch(M.a_intent)
if(INTENT_HELP)
if(M == src && check_self_for_injuries())
return
help_shake_act(M)
if(INTENT_GRAB)
grabbedby(M)
@@ -77,18 +77,20 @@
return TRUE
/mob/living/carbon/alien/humanoid/get_standard_pixel_y_offset(lying = 0)
. = ..()
if(leaping)
. -= 32
if(custom_pixel_y_offset)
. += custom_pixel_y_offset
return -32
else if(custom_pixel_y_offset)
return custom_pixel_y_offset
else
return initial(pixel_y)
/mob/living/carbon/alien/humanoid/get_standard_pixel_x_offset(lying = 0)
. = ..()
if(leaping)
. -= 32
if(custom_pixel_x_offset)
. += custom_pixel_x_offset
return -32
else if(custom_pixel_x_offset)
return custom_pixel_x_offset
else
return initial(pixel_x)
/mob/living/carbon/alien/humanoid/get_permeability_protection(list/target_zones)
return 0.8
@@ -0,0 +1,5 @@
/mob/living/carbon/alien/humanoid/doUnEquip(obj/item/I)
. = ..()
if(!. || !I)
return
@@ -1,10 +1,14 @@
/mob/living/carbon/alien/larva/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
/mob/living/carbon/alien/larva/Life()
set invisibility = 0
if (notransform)
return
// GROW!
if(amount_grown < max_grown)
amount_grown++
update_icons()
if(..()) //not dead
// GROW!
if(amount_grown < max_grown)
amount_grown++
update_icons()
/mob/living/carbon/alien/larva/update_stat()
+2 -3
View File
@@ -1,7 +1,6 @@
/mob/living/carbon/alien/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/carbon/alien/Life()
findQueen()
return..()
/mob/living/carbon/alien/check_breath(datum/gas_mixture/breath)
if(status_flags & GODMODE)
+12 -3
View File
@@ -1,8 +1,7 @@
/obj/item/organ/alien
icon_state = "xgibmid2"
food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/toxin/acid = 10)
var/list/alien_powers = list()
organ_flags = ORGAN_NO_SPOIL|ORGAN_EDIBLE
organ_flags = ORGAN_NO_SPOIL
/obj/item/organ/alien/Initialize()
. = ..()
@@ -27,6 +26,12 @@
owner.RemoveAbility(P)
..()
/obj/item/organ/alien/prepare_eat()
var/obj/S = ..()
S.reagents.add_reagent(/datum/reagent/toxin/acid, 10)
return S
/obj/item/organ/alien/plasmavessel
name = "plasma vessel"
icon_state = "plasma"
@@ -34,13 +39,17 @@
zone = BODY_ZONE_CHEST
slot = "plasmavessel"
alien_powers = list(/obj/effect/proc_holder/alien/plant, /obj/effect/proc_holder/alien/transfer)
food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/toxin/plasma = 10)
var/storedPlasma = 100
var/max_plasma = 250
var/heal_rate = 5
var/plasma_rate = 10
/obj/item/organ/alien/plasmavessel/prepare_eat()
var/obj/S = ..()
S.reagents.add_reagent(/datum/reagent/toxin/plasma, storedPlasma/10)
return S
/obj/item/organ/alien/plasmavessel/large
name = "large plasma vessel"
icon_state = "plasma_large"
@@ -4,7 +4,6 @@
name = "alien embryo"
icon = 'icons/mob/alien.dmi'
icon_state = "larva0_dead"
food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/toxin/acid = 10)
var/stage = 0
var/bursting = FALSE
@@ -17,6 +16,11 @@
if(prob(10))
AttemptGrow(0)
/obj/item/organ/body_egg/alien_embryo/prepare_eat()
var/obj/S = ..()
S.reagents.add_reagent(/datum/reagent/toxin/acid, 10)
return S
/obj/item/organ/body_egg/alien_embryo/on_life()
. = ..()
if(!owner)
@@ -88,7 +92,7 @@
ghost.transfer_ckey(new_xeno, FALSE)
SEND_SOUND(new_xeno, sound('sound/voice/hiss5.ogg',0,0,0,100)) //To get the player's attention
new_xeno.Paralyze(6)
new_xeno.mob_transforming = TRUE
new_xeno.notransform = TRUE
new_xeno.invisibility = INVISIBILITY_MAXIMUM
sleep(6)
@@ -98,7 +102,7 @@
if(new_xeno)
new_xeno.SetParalyzed(0)
new_xeno.mob_transforming = FALSE
new_xeno.notransform = FALSE
new_xeno.invisibility = 0
var/mob/living/carbon/old_owner = owner
@@ -88,7 +88,6 @@
Die()
/obj/item/clothing/mask/facehugger/equipped(mob/M)
. = ..()
Attach(M)
/obj/item/clothing/mask/facehugger/Crossed(atom/target)
@@ -255,7 +254,7 @@
return FALSE
if(AmBloodsucker(M))
return FALSE
if(ismonkey(M))
return 1
+14 -31
View File
@@ -47,13 +47,16 @@
/mob/living/carbon/swap_hand(held_index)
. = ..()
if(!.)
var/obj/item/held_item = get_active_held_item()
to_chat(usr, "<span class='warning'>Your other hand is too busy holding [held_item].</span>")
return
if(!held_index)
held_index = (active_hand_index % held_items.len)+1
var/obj/item/item_in_hand = src.get_active_held_item()
if(item_in_hand) //this segment checks if the item in your hand is twohanded.
var/obj/item/twohanded/TH = item_in_hand
if(istype(TH))
if(TH.wielded == 1)
to_chat(usr, "<span class='warning'>Your other hand is too busy holding [TH]</span>")
return
var/oindex = active_hand_index
active_hand_index = held_index
if(hud_used)
@@ -179,7 +182,7 @@
to_chat(src, "<span class='notice'>You gently let go of [throwable_mob].</span>")
return
adjustStaminaLossBuffered(STAM_COST_THROW_MOB * ((throwable_mob.mob_size+1)**2))// throwing an entire person shall be very tiring
adjustStaminaLossBuffered(25)//CIT CHANGE - throwing an entire person shall be very tiring
var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors
var/turf/end_T = get_turf(target)
if(start_T && end_T)
@@ -244,7 +247,7 @@
/mob/living/carbon/Topic(href, href_list)
..()
//strip panel
if(usr.canUseTopic(src, BE_CLOSE))
if(usr.canUseTopic(src, BE_CLOSE, NO_DEXTERY))
if(href_list["internal"] && !HAS_TRAIT(src, TRAIT_NO_INTERNALS))
var/slot = text2num(href_list["internal"])
var/obj/item/ITEM = get_item_by_slot(slot)
@@ -264,15 +267,7 @@
visible_message("<span class='danger'>[usr] [internal ? "opens" : "closes"] the valve on [src]'s [ITEM.name].</span>", \
"<span class='userdanger'>[usr] [internal ? "opens" : "closes"] the valve on your [ITEM.name].</span>", \
target = usr, target_message = "<span class='danger'>You [internal ? "opens" : "closes"] the valve on [src]'s [ITEM.name].</span>")
if(href_list["embedded_object"] && usr.canUseTopic(src, BE_CLOSE))
var/obj/item/bodypart/L = locate(href_list["embedded_limb"]) in bodyparts
if(!L)
return
var/obj/item/I = locate(href_list["embedded_object"]) in L.embedded_objects
if(!I || I.loc != src) //no item, no limb, or item is not in limb or in the person anymore
return
SEND_SIGNAL(src, COMSIG_CARBON_EMBED_RIP, I, L)
return
/mob/living/carbon/fall(forced)
loc.handle_fall(src, forced)//it's loc so it doesn't call the mob's handle_fall which does nothing
@@ -437,9 +432,10 @@
return
/mob/living/carbon/get_standard_pixel_y_offset(lying = 0)
. = ..()
if(lying)
. -= 6
return -6
else
return initial(pixel_y)
/mob/living/carbon/proc/accident(obj/item/I)
if(!I || (I.item_flags & ABSTRACT) || HAS_TRAIT(I, TRAIT_NODROP))
@@ -1157,16 +1153,3 @@
dna.features["body_model"] = MALE
if(update_icon)
update_body()
/mob/living/carbon/check_obscured_slots()
if(head)
if(head.flags_inv & HIDEMASK)
LAZYOR(., SLOT_WEAR_MASK)
if(head.flags_inv & HIDEEYES)
LAZYOR(., SLOT_GLASSES)
if(head.flags_inv & HIDEEARS)
LAZYOR(., SLOT_EARS)
if(wear_mask)
if(wear_mask.flags_inv & HIDEEYES)
LAZYOR(., SLOT_GLASSES)
@@ -65,18 +65,27 @@
throw_mode_off()
return TRUE
/mob/living/carbon/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1)
var/totitemdamage = pre_attacked_by(I, user) * damage_multiplier
/mob/living/carbon/embed_item(obj/item/I)
throw_alert("embeddedobject", /obj/screen/alert/embeddedobject)
var/obj/item/bodypart/L = pick(bodyparts)
L.embedded_objects |= I
I.add_mob_blood(src)//it embedded itself in you, of course it's bloody!
I.forceMove(src)
I.embedded()
L.receive_damage(I.w_class*I.embedding.embedded_impact_pain_multiplier)
visible_message("<span class='danger'>[I] embeds itself in [src]'s [L.name]!</span>","<span class='userdanger'>[I] embeds itself in your [L.name]!</span>")
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "embedded", /datum/mood_event/embedded)
/mob/living/carbon/attacked_by(obj/item/I, mob/living/user)
var/totitemdamage = pre_attacked_by(I, user)
var/impacting_zone = (user == src)? check_zone(user.zone_selected) : ran_zone(user.zone_selected)
var/list/block_return = list()
if((user != src) && (mob_run_block(I, totitemdamage, "the [I]", ((attackchain_flags & ATTACKCHAIN_PARRY_COUNTERATTACK)? ATTACK_TYPE_PARRY_COUNTERATTACK : NONE) | ATTACK_TYPE_MELEE, I.armour_penetration, user, impacting_zone, block_return) & BLOCK_SUCCESS))
if((user != src) && (mob_run_block(I, totitemdamage, "the [I]", ATTACK_TYPE_MELEE, I.armour_penetration, user, impacting_zone, null) & BLOCK_SUCCESS))
return FALSE
totitemdamage = block_calculate_resultant_damage(totitemdamage, block_return)
var/obj/item/bodypart/affecting = get_bodypart(impacting_zone)
if(!affecting) //missing limb? we select the first bodypart (you can never have zero, because of chest)
affecting = bodyparts[1]
SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting)
send_item_attack_message(I, user, affecting.name, totitemdamage)
send_item_attack_message(I, user, affecting.name)
I.do_stagger_action(src, user, totitemdamage)
if(I.force)
apply_damage(totitemdamage, I.damtype, affecting) //CIT CHANGE - replaces I.force with totitemdamage
@@ -263,9 +272,6 @@
to_chat(M, "<span class='warning'>You can't put [p_them()] out with just your bare hands!</span>")
return
if(M == src && check_self_for_injuries())
return
if(health >= 0 && !(HAS_TRAIT(src, TRAIT_FAKEDEATH)))
var/friendly_check = FALSE
if(mob_run_block(M, 0, M.name, ATTACK_TYPE_UNARMED, 0, null, null, null))
@@ -333,26 +339,6 @@
update_mobility()
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
/// Check ourselves to see if we've got any shrapnel, return true if we do. This is a much simpler version of what humans do, we only indicate we're checking ourselves if there's actually shrapnel
/mob/living/carbon/proc/check_self_for_injuries()
if(stat == DEAD || stat == UNCONSCIOUS)
return
var/embeds = FALSE
for(var/X in bodyparts)
var/obj/item/bodypart/LB = X
for(var/obj/item/I in LB.embedded_objects)
if(!embeds)
embeds = TRUE
// this way, we only visibly try to examine ourselves if we have something embedded, otherwise we'll still hug ourselves :)
visible_message("<span class='notice'>[src] examines [p_them()]self.</span>", \
"<span class='notice'>You check yourself for shrapnel.</span>")
if(I.isEmbedHarmless())
to_chat(src, "\t <a href='?src=[REF(src)];embedded_object=[REF(I)];embedded_limb=[REF(LB)]' class='warning'>There is \a [I] stuck to your [LB.name]!</a>")
else
to_chat(src, "\t <a href='?src=[REF(src)];embedded_object=[REF(I)];embedded_limb=[REF(LB)]' class='warning'>There is \a [I] embedded in your [LB.name]!</a>")
return embeds
/mob/living/carbon/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0)
. = ..()
@@ -20,7 +20,7 @@
if(istype(J) && (movement_dir || J.stabilizers) && J.allow_thrust(0.01, src))
return 1
/mob/living/carbon/Moved()
/mob/living/carbon/Move(NewLoc, direct)
. = ..()
if(. && (movement_type & FLOATING)) //floating is easy
if(HAS_TRAIT(src, TRAIT_NOHUNGER))
+52 -10
View File
@@ -1,21 +1,20 @@
/mob/living/carbon/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE)
/mob/living/carbon/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE)
SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone)
var/hit_percent = (100-blocked)/100
if(!forced && hit_percent <= 0)
return 0
var/obj/item/bodypart/BP = null
if(!spread_damage)
if(isbodypart(def_zone)) //we specified a bodypart object
BP = def_zone
else
if(!def_zone)
def_zone = ran_zone(def_zone)
BP = get_bodypart(check_zone(def_zone))
if(!BP)
BP = bodyparts[1]
if(isbodypart(def_zone)) //we specified a bodypart object
BP = def_zone
else
if(!def_zone)
def_zone = ran_zone(def_zone)
BP = get_bodypart(check_zone(def_zone))
if(!BP)
BP = bodyparts[1]
var/damage_amount = forced ? damage : damage * hit_percent
switch(damagetype)
@@ -265,3 +264,46 @@
if(update)
update_damage_overlays()
update_stamina()
/* TO_REMOVE
/mob/living/carbon/getOrganLoss(ORGAN_SLOT_BRAIN)
. = 0
var/obj/item/organ/brain/B = getorganslot(ORGAN_SLOT_BRAIN)
if(B)
. = B.get_brain_damage()
//Some sources of brain damage shouldn't be deadly
/mob/living/carbon/adjustOrganLoss(ORGAN_SLOT_BRAIN, amount, maximum = BRAIN_DAMAGE_DEATH)
if(status_flags & GODMODE)
return FALSE
var/prev_brainloss = getOrganLoss(ORGAN_SLOT_BRAIN)
var/obj/item/organ/brain/B = getorganslot(ORGAN_SLOT_BRAIN)
if(!B)
return
B.adjust_brain_damage(amount, maximum)
if(amount <= 0) //cut this early
return
var/brainloss = getOrganLoss(ORGAN_SLOT_BRAIN)
if(brainloss > BRAIN_DAMAGE_MILD)
if(prob(amount * ((2 * (100 + brainloss - BRAIN_DAMAGE_MILD)) / 100))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 2%
gain_trauma_type(BRAIN_TRAUMA_MILD)
if(brainloss > BRAIN_DAMAGE_SEVERE)
if(prob(amount * ((2 * (100 + brainloss - BRAIN_DAMAGE_SEVERE)) / 100))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 2%
if(prob(20))
gain_trauma_type(BRAIN_TRAUMA_SPECIAL)
else
gain_trauma_type(BRAIN_TRAUMA_SEVERE)
if(prev_brainloss < BRAIN_DAMAGE_MILD && brainloss >= BRAIN_DAMAGE_MILD)
to_chat(src, "<span class='warning'>You feel lightheaded.</span>")
else if(prev_brainloss < BRAIN_DAMAGE_SEVERE && brainloss >= BRAIN_DAMAGE_SEVERE)
to_chat(src, "<span class='warning'>You feel less in control of your thoughts.</span>")
else if(prev_brainloss < (BRAIN_DAMAGE_DEATH - 20) && brainloss >= (BRAIN_DAMAGE_DEATH - 20))
to_chat(src, "<span class='warning'>You can feel your mind flickering on and off...</span>")
/mob/living/carbon/setBrainLoss(amount)
var/obj/item/organ/brain/B = getorganslot(ORGAN_SLOT_BRAIN)
if(B)
var/adjusted_amount = amount - B.get_brain_damage()
B.adjust_brain_damage(adjusted_amount, null)
*/
+4 -25
View File
@@ -31,35 +31,14 @@
else if(get_bodypart(BODY_ZONE_HEAD))
. += "<span class='deadsay'>It appears that [t_his] brain is missing...</span>"
var/list/msg = list("<span class='warning'>")
var/list/missing = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)
var/list/disabled = list()
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
if(BP.disabled)
disabled += BP
missing -= BP.body_zone
for(var/obj/item/I in BP.embedded_objects)
if(I.isEmbedHarmless())
msg += "<B>[t_He] [t_has] \a [icon2html(I, user)] [I] stuck to [t_his] [BP.name]!</B>\n"
else
msg += "<B>[t_He] [t_has] \a [icon2html(I, user)] [I] embedded in [t_his] [BP.name]!</B>\n"
for(var/X in disabled)
var/obj/item/bodypart/BP = X
var/damage_text
if(!(BP.get_damage(include_stamina = FALSE) >= BP.max_damage)) //Stamina is disabling the limb
damage_text = "limp and lifeless"
else
damage_text = (BP.brute_dam >= BP.burn_dam) ? BP.heavy_brute_msg : BP.heavy_burn_msg
msg += "<B>[capitalize(t_his)] [BP.name] is [damage_text]!</B>\n"
var/list/missing = get_missing_limbs()
for(var/t in missing)
if(t==BODY_ZONE_HEAD)
msg += "<span class='deadsay'><B>[t_His] [parse_zone(t)] is missing!</B></span>\n"
. += "<span class='deadsay'><B>[t_His] [parse_zone(t)] is missing!</B></span>"
continue
msg += "<span class='warning'><B>[t_His] [parse_zone(t)] is missing!</B></span>\n"
. += "<span class='warning'><B>[t_His] [parse_zone(t)] is missing!</B></span>"
var/list/msg = list()
var/temp = getBruteLoss()
if(!(user == src && src.hal_screwyhud == SCREWYHUD_HEALTHY)) //fake healthy
if(temp)
@@ -1,5 +1,5 @@
/mob/living/carbon/human/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage)
/mob/living/carbon/human/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE)
// depending on the species, it will run the corresponding apply_damage code there
return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, forced, spread_damage)
return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, forced)
@@ -159,10 +159,7 @@
disabled += BP
missing -= BP.body_zone
for(var/obj/item/I in BP.embedded_objects)
if(I.isEmbedHarmless())
msg += "<B>[t_He] [t_has] \a [icon2html(I, user)] [I] stuck to [t_his] [BP.name]!</B>\n"
else
msg += "<B>[t_He] [t_has] \a [icon2html(I, user)] [I] embedded in [t_his] [BP.name]!</B>\n"
msg += "<B>[t_He] [t_has] \a [icon2html(I, user)] [I] embedded in [t_his] [BP.name]!</B>\n"
for(var/X in disabled)
var/obj/item/bodypart/BP = X
+43 -29
View File
@@ -152,12 +152,8 @@
dat += "<tr><td>&nbsp;</td></tr>"
dat += "<tr><td><B>Exosuit:</B></td><td><A href='?src=[REF(src)];item=[SLOT_WEAR_SUIT]'>[(wear_suit && !(wear_suit.item_flags & ABSTRACT)) ? wear_suit : "<font color=grey>Empty</font>"]</A>"
dat += "<tr><td><B>Exosuit:</B></td><td><A href='?src=[REF(src)];item=[SLOT_WEAR_SUIT]'>[(wear_suit && !(wear_suit.item_flags & ABSTRACT)) ? wear_suit : "<font color=grey>Empty</font>"]</A></td></tr>"
if(wear_suit)
if(istype(wear_suit, /obj/item/clothing/suit/space/hardsuit))
var/hardsuit_head = head && istype(head, /obj/item/clothing/head/helmet/space/hardsuit)
dat += "&nbsp;<A href='?src=[REF(src)];toggle_helmet=[SLOT_WEAR_SUIT]'>[hardsuit_head ? "Retract Helmet" : "Extend Helmet"]</A>"
dat += "</td></tr>"
dat += "<tr><td>&nbsp;&#8627;<B>Suit Storage:</B></td><td><A href='?src=[REF(src)];item=[SLOT_S_STORE]'>[(s_store && !(s_store.item_flags & ABSTRACT)) ? s_store : "<font color=grey>Empty</font>"]</A>"
if(has_breathable_mask && istype(s_store, /obj/item/tank))
dat += "&nbsp;<A href='?src=[REF(src)];internal=[SLOT_S_STORE]'>[internal ? "Disable Internals" : "Set Internals"]</A>"
@@ -224,30 +220,30 @@
var/obj/item/I = locate(href_list["embedded_object"]) in L.embedded_objects
if(!I || I.loc != src) //no item, no limb, or item is not in limb or in the person anymore
return
SEND_SIGNAL(src, COMSIG_CARBON_EMBED_RIP, I, L)
return
if(href_list["toggle_helmet"])
if(!istype(head, /obj/item/clothing/head/helmet/space/hardsuit))
return
var/obj/item/clothing/head/helmet/space/hardsuit/hardsuit_head = head
visible_message("<span class='danger'>[usr] tries to [hardsuit_head ? "retract" : "extend"] [src]'s helmet.</span>", \
"<span class='userdanger'>[usr] tries to [hardsuit_head ? "retract" : "extend"] [src]'s helmet.</span>", \
target = usr, target_message = "<span class='danger'>You try to [hardsuit_head ? "retract" : "extend"] [src]'s helmet.</span>")
if(!do_mob(usr, src, hardsuit_head ? head.strip_delay : POCKET_STRIP_DELAY))
return
if(!istype(wear_suit, /obj/item/clothing/suit/space/hardsuit) || (hardsuit_head ? (!head || head != hardsuit_head) : head))
return
var/obj/item/clothing/suit/space/hardsuit/hardsuit = wear_suit //This should be an hardsuit given all our checks
if(hardsuit.ToggleHelmet(FALSE))
visible_message("<span class='danger'>[usr] [hardsuit_head ? "retract" : "extend"] [src]'s helmet</span>", \
"<span class='userdanger'>[usr] [hardsuit_head ? "retract" : "extend"] [src]'s helmet</span>", \
target = usr, target_message = "<span class='danger'>You [hardsuit_head ? "retract" : "extend"] [src]'s helmet.</span>")
var/time_taken = I.embedding.embedded_unsafe_removal_time/I.w_class //Citadel Change from * to /
usr.visible_message("<span class='warning'>[usr] attempts to remove [I] from [usr.p_their()] [L.name].</span>","<span class='notice'>You attempt to remove [I] from your [L.name]... (It will take [DisplayTimeText(time_taken)].)</span>")
if(do_after(usr, time_taken, needhand = 1, target = src))
remove_embedded_unsafe(L, I, usr)
/* CITADEL EDIT: remove_embedded_unsafe replaces this code
if(!I || !L || I.loc != src || !(I in L.embedded_objects))
return
L.embedded_objects -= I
L.receive_damage(I.embedding.embedded_unsafe_removal_pain_multiplier*I.w_class)//It hurts to rip it out, get surgery you dingus.
I.forceMove(get_turf(src))
usr.put_in_hands(I)
usr.emote("scream")
usr.visible_message("[usr] successfully rips [I] out of [usr.p_their()] [L.name]!","<span class='notice'>You successfully remove [I] from your [L.name].</span>")
if(!has_embedded_objects())
clear_alert("embeddedobject")
SEND_SIGNAL(usr, COMSIG_CLEAR_MOOD_EVENT, "embedded") */
return
if(href_list["item"])
var/slot = text2num(href_list["item"])
if(slot in check_obscured_slots())
to_chat(usr, "<span class='warning'>You can't reach that! Something is covering it.</span>")
return
if(href_list["pockets"])
var/strip_mod = 1
var/strip_silence = FALSE
@@ -530,15 +526,33 @@
// Might need re-wording.
to_chat(user, "<span class='alert'>There is no exposed flesh or thin material [above_neck(target_zone) ? "on [p_their()] head" : "on [p_their()] body"].</span>")
/mob/living/carbon/human/check_obscured_slots()
. = ..()
/mob/living/carbon/human/proc/check_obscured_slots()
var/list/obscured = list()
if(wear_suit)
if(wear_suit.flags_inv & HIDEGLOVES)
LAZYOR(., SLOT_GLOVES)
obscured |= SLOT_GLOVES
if(wear_suit.flags_inv & HIDEJUMPSUIT)
LAZYOR(., SLOT_W_UNIFORM)
obscured |= SLOT_W_UNIFORM
if(wear_suit.flags_inv & HIDESHOES)
LAZYOR(., SLOT_SHOES)
obscured |= SLOT_SHOES
if(head)
if(head.flags_inv & HIDEMASK)
obscured |= SLOT_WEAR_MASK
if(head.flags_inv & HIDEEYES)
obscured |= SLOT_GLASSES
if(head.flags_inv & HIDEEARS)
obscured |= SLOT_EARS
if(wear_mask)
if(wear_mask.flags_inv & HIDEEYES)
obscured |= SLOT_GLASSES
if(obscured.len)
return obscured
else
return null
/mob/living/carbon/human/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null)
if(judgement_criteria & JUDGE_EMAGGED)
@@ -983,7 +997,7 @@
if(target.incapacitated(FALSE, TRUE) || incapacitated(FALSE, TRUE))
target.visible_message("<span class='warning'>[target] can't hang onto [src]!</span>")
return
buckle_mob(target, TRUE, TRUE, FALSE, 1, 2, FALSE)
buckle_mob(target, TRUE, TRUE, FALSE, 0, 2, FALSE)
else
visible_message("<span class='warning'>[target] fails to climb onto [src]!</span>")
else
@@ -1,11 +1,8 @@
/mob/living/carbon/human/get_blocking_items()
. = ..()
if(wear_suit)
if(!.[wear_suit])
.[wear_suit] = wear_suit.block_priority
. |= wear_suit
if(w_uniform)
if(!.[w_uniform])
.[w_uniform] = w_uniform.block_priority
. |= w_uniform
if(wear_neck)
if(!.[wear_neck])
.[wear_neck] = wear_neck.block_priority
. |= wear_neck
@@ -52,6 +52,11 @@
return martial_art_result
return ..()
/mob/living/carbon/human/can_embed(obj/item/I)
if(I.get_sharpness() || is_pointed(I) || is_type_in_typecache(I, GLOB.can_embed_types))
return TRUE
return FALSE
/mob/living/carbon/human/proc/check_martial_melee_block()
if(mind)
if(mind.martial_art && prob(mind.martial_art.block_chance) && mind.martial_art.can_use(src) && in_throw_mode && !incapacitated(FALSE, TRUE))
@@ -73,7 +78,7 @@
..()
/mob/living/carbon/human/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1)
/mob/living/carbon/human/attacked_by(obj/item/I, mob/living/user)
if(!I || !user)
return 0
@@ -90,7 +95,7 @@
SSblackbox.record_feedback("tally", "zone_targeted", 1, target_area)
// the attacked_by code varies among species
return dna.species.spec_attacked_by(I, user, affecting, a_intent, src, attackchain_flags, damage_multiplier)
return dna.species.spec_attacked_by(I, user, affecting, a_intent, src)
/mob/living/carbon/human/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
if(user.a_intent == INTENT_HARM)
@@ -209,7 +214,7 @@
/mob/living/carbon/human/attack_animal(mob/living/simple_animal/M)
. = ..()
if(.)
var/damage = .
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
if(!dam_zone) //Dismemberment successful
return TRUE
@@ -615,10 +620,7 @@
to_send += "\t <span class='[no_damage ? "notice" : "warning"]'>Your [LB.name] [HAS_TRAIT(src, TRAIT_SELF_AWARE) ? "has" : "is"] [status].</span>\n"
for(var/obj/item/I in LB.embedded_objects)
if(I.isEmbedHarmless())
to_chat(src, "\t <a href='?src=[REF(src)];embedded_object=[REF(I)];embedded_limb=[REF(LB)]' class='warning'>There is \a [I] stuck to your [LB.name]!</a>")
else
to_chat(src, "\t <a href='?src=[REF(src)];embedded_object=[REF(I)];embedded_limb=[REF(LB)]' class='warning'>There is \a [I] embedded in your [LB.name]!</a>")
to_send += "\t <a href='?src=[REF(src)];embedded_object=[REF(I)];embedded_limb=[REF(LB)]' class='warning'>There is \a [I] embedded in your [LB.name]!</a>\n"
for(var/t in missing)
to_send += "<span class='boldannounce'>Your [parse_zone(t)] is missing!</span>\n"
@@ -77,7 +77,7 @@
var/turf/T = get_turf(src)
if(S.bloody_shoes && S.bloody_shoes[S.blood_state])
var/obj/effect/decal/cleanable/blood/footprints/oldFP = locate(/obj/effect/decal/cleanable/blood/footprints) in T
if(oldFP && (oldFP.blood_state == S.blood_state && oldFP.color == S.last_blood_color))
if(oldFP && (oldFP.blood_state == S.blood_state && oldFP.color == bloodtype_to_color(S.last_bloodtype)))
return
S.bloody_shoes[S.blood_state] = max(0, S.bloody_shoes[S.blood_state] - BLOOD_LOSS_PER_STEP)
var/obj/effect/decal/cleanable/blood/footprints/FP = new /obj/effect/decal/cleanable/blood/footprints(T)
@@ -85,11 +85,7 @@
FP.entered_dirs |= dir
FP.bloodiness = S.bloody_shoes[S.blood_state]
if(S.last_bloodtype)
FP.blood_DNA[S.last_blood_DNA] = S.last_bloodtype
if(!FP.blood_DNA["color"])
FP.blood_DNA["color"] = S.last_blood_color
else
FP.blood_DNA["color"] = BlendRGB(FP.blood_DNA["color"], S.last_blood_color)
FP.blood_DNA += list(S.last_blood_DNA = S.last_bloodtype)
FP.update_icon()
update_inv_shoes()
//End bloody footprints
@@ -1,32 +1,5 @@
/mob/living/carbon/human/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, clothing_check = FALSE, list/return_warning)
return dna.species.can_equip(I, slot, disable_warning, src, bypass_equip_delay_self, clothing_check, return_warning)
/mob/living/carbon/human/get_equipped_items(include_pockets = FALSE)
var/list/items = ..()
if(belt)
items += belt
if(ears)
items += ears
if(glasses)
items += glasses
if(gloves)
items += gloves
if(shoes)
items += shoes
if(wear_id)
items += wear_id
if(wear_suit)
items += wear_suit
if(w_uniform)
items += w_uniform
if(include_pockets)
if(l_store)
items += l_store
if(r_store)
items += r_store
if(s_store)
items += s_store
return items
/mob/living/carbon/human/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE)
return dna.species.can_equip(I, slot, disable_warning, src, bypass_equip_delay_self)
// Return the item currently in the slot ID
/mob/living/carbon/human/get_item_by_slot(slot_id)
+44 -10
View File
@@ -18,21 +18,36 @@
#define THERMAL_PROTECTION_HAND_LEFT 0.025
#define THERMAL_PROTECTION_HAND_RIGHT 0.025
/mob/living/carbon/human/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
/mob/living/carbon/human/Life(seconds, times_fired)
set invisibility = 0
if (notransform)
return
handle_active_genes()
//heart attack stuff
handle_heart()
dna.species.spec_life(src) // for mutantraces
return (stat != DEAD) && !QDELETED(src)
/mob/living/carbon/human/PhysicalLife(seconds, times_fired)
if(!(. = ..()))
return
. = ..()
if (QDELETED(src))
return 0
if(.) //not dead
handle_active_genes()
if(stat != DEAD)
//heart attack stuff
handle_heart()
if(stat != DEAD)
//Stuff jammed in your limbs hurts
handle_embedded_objects()
//Update our name based on whether our face is obscured/disfigured
name = get_visible_name()
dna.species.spec_life(src) // for mutantraces
if(stat != DEAD)
return 1
/mob/living/carbon/human/calculate_affecting_pressure(pressure)
var/headless = !get_bodypart(BODY_ZONE_HEAD) //should the mob be perennially headless (see dullahans), we only take the suit into account, so they can into space.
if (wear_suit && istype(wear_suit, /obj/item/clothing) && (headless || (head && istype(head, /obj/item/clothing))))
@@ -288,6 +303,25 @@
return TRUE
return ..()
/mob/living/carbon/human/proc/handle_embedded_objects()
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
for(var/obj/item/I in BP.embedded_objects)
if(prob(I.embedding.embedded_pain_chance))
BP.receive_damage(I.w_class*I.embedding.embedded_pain_multiplier)
to_chat(src, "<span class='userdanger'>[I] embedded in your [BP.name] hurts!</span>")
if(prob(I.embedding.embedded_fall_chance))
BP.receive_damage(I.w_class*I.embedding.embedded_fall_pain_multiplier)
BP.embedded_objects -= I
I.forceMove(drop_location())
I.unembedded()
visible_message("<span class='danger'>[I] falls out of [name]'s [BP.name]!</span>","<span class='userdanger'>[I] falls out of your [BP.name]!</span>")
if(!has_embedded_objects())
clear_alert("embeddedobject")
SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "embedded")
/mob/living/carbon/human/proc/handle_active_genes()
if(HAS_TRAIT(src, TRAIT_MUTATION_STASIS))
return
+36 -45
View File
@@ -39,7 +39,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/use_skintones = NO_SKINTONES // does it use skintones or not? (spoiler alert this is only used by humans)
var/exotic_blood = "" // If your race wants to bleed something other than bog standard blood, change this to reagent id.
var/exotic_bloodtype = "" //If your race uses a non standard bloodtype (A+, O-, AB-, etc)
var/exotic_blood_color = BLOOD_COLOR_HUMAN //assume human as the default blood colour, override this default by species subtypes
var/meat = /obj/item/reagent_containers/food/snacks/meat/slab/human //What the species drops on gibbing
var/list/gib_types = list(/obj/effect/gibspawner/human, /obj/effect/gibspawner/human/bodypartless)
var/skinned_type
@@ -1066,16 +1065,11 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
// handles the equipping of species-specific gear
return
/datum/species/proc/can_equip(obj/item/I, slot, disable_warning, mob/living/carbon/human/H, bypass_equip_delay_self = FALSE, clothing_check = FALSE, list/return_warning)
/datum/species/proc/can_equip(obj/item/I, slot, disable_warning, mob/living/carbon/human/H, bypass_equip_delay_self = FALSE)
if(slot in no_equip)
if(!I.species_exception || !is_type_in_list(src, I.species_exception))
return FALSE
if(clothing_check && (slot in H.check_obscured_slots()))
if(return_warning)
return_warning[1] = "<span class='warning'>You are unable to equip that with your current garments in the way!</span>"
return FALSE
var/num_arms = H.get_num_arms(FALSE)
var/num_legs = H.get_num_legs(FALSE)
@@ -1137,8 +1131,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(!CHECK_BITFIELD(I.item_flags, NO_UNIFORM_REQUIRED))
var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST)
if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
if(return_warning)
return_warning[1] = "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>"
if(!disable_warning)
to_chat(H, "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>")
return FALSE
if(!(I.slot_flags & ITEM_SLOT_BELT))
return
@@ -1179,8 +1173,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(!CHECK_BITFIELD(I.item_flags, NO_UNIFORM_REQUIRED))
var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST)
if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
if(return_warning)
return_warning[1] = "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>"
if(!disable_warning)
to_chat(H, "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>")
return FALSE
if( !(I.slot_flags & ITEM_SLOT_ID) )
return FALSE
@@ -1194,8 +1188,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_L_LEG)
if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
if(return_warning)
return_warning[1] = "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>"
if(!disable_warning)
to_chat(H, "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>")
return FALSE
if(I.slot_flags & ITEM_SLOT_DENYPOCKET)
return FALSE
@@ -1210,8 +1204,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_R_LEG)
if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
if(return_warning)
return_warning[1] = "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>"
if(!disable_warning)
to_chat(H, "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>")
return FALSE
if(I.slot_flags & ITEM_SLOT_DENYPOCKET)
return FALSE
@@ -1224,16 +1218,16 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(H.s_store)
return FALSE
if(!H.wear_suit)
if(return_warning)
return_warning[1] = "<span class='warning'>You need a suit before you can attach this [I.name]!</span>"
if(!disable_warning)
to_chat(H, "<span class='warning'>You need a suit before you can attach this [I.name]!</span>")
return FALSE
if(!H.wear_suit.allowed)
if(return_warning)
return_warning[1] = "You somehow have a suit with no defined allowed items for suit storage, stop that."
if(!disable_warning)
to_chat(H, "You somehow have a suit with no defined allowed items for suit storage, stop that.")
return FALSE
if(I.w_class > WEIGHT_CLASS_BULKY)
if(return_warning)
return_warning[1] = "The [I.name] is too big to attach."
if(!disable_warning)
to_chat(H, "The [I.name] is too big to attach.") //should be src?
return FALSE
if( istype(I, /obj/item/pda) || istype(I, /obj/item/pen) || is_type_in_list(I, H.wear_suit.allowed) )
return TRUE
@@ -1498,12 +1492,12 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/miss_chance = 100//calculate the odds that a punch misses entirely. considers stamina and brute damage of the puncher. punches miss by default to prevent weird cases
if(user.dna.species.punchdamagelow)
if(HAS_TRAIT(user, TRAIT_PUGILIST)) //pugilists have a flat 10% miss chance
miss_chance = 10
if(atk_verb == ATTACK_EFFECT_KICK) //kicks never miss (provided your species deals more than 0 damage)
miss_chance = 0
else if(HAS_TRAIT(user, TRAIT_PUGILIST)) //pugilists have a flat 10% miss chance
miss_chance = 10
else
miss_chance = min(10 + max(puncherstam * 0.5, puncherbrute * 0.5), 100) //probability of miss has a base of 10, and modified based on half brute total. Capped at max 100 to prevent weirdness in prob()
miss_chance = min(10 + ((puncherstam + puncherbrute)*0.5), 100) //probability of miss has a base of 10, and modified based on half brute total. Capped at max 100 to prevent weirdness in prob()
if(!damage || !affecting || prob(miss_chance))//future-proofing for species that have 0 damage/weird cases where no zone is targeted
playsound(target.loc, user.dna.species.miss_sound, 25, TRUE, -1)
@@ -1706,14 +1700,12 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if("disarm")
disarm(M, H, attacker_style)
/datum/species/proc/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, mob/living/carbon/human/H, attackchain_flags = NONE, damage_multiplier = 1)
var/totitemdamage = H.pre_attacked_by(I, user) * damage_multiplier
/datum/species/proc/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, mob/living/carbon/human/H)
var/totitemdamage = H.pre_attacked_by(I, user)
// Allows you to put in item-specific reactions based on species
if(user != H)
var/list/block_return = list()
if(H.mob_run_block(I, totitemdamage, "the [I.name]", ((attackchain_flags & ATTACKCHAIN_PARRY_COUNTERATTACK)? ATTACK_TYPE_PARRY_COUNTERATTACK : NONE) | ATTACK_TYPE_MELEE, I.armour_penetration, user, affecting.body_zone, block_return) & BLOCK_SUCCESS)
if(H.mob_run_block(I, totitemdamage, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user, affecting.body_zone, null) & BLOCK_SUCCESS)
return 0
totitemdamage = block_calculate_resultant_damage(totitemdamage, block_return)
if(H.check_martial_melee_block())
H.visible_message("<span class='warning'>[H] blocks [I]!</span>")
return 0
@@ -1728,11 +1720,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/armor_block = H.run_armor_check(affecting, "melee", "<span class='notice'>Your armor has protected your [hit_area].</span>", "<span class='notice'>Your armor has softened a hit to your [hit_area].</span>",I.armour_penetration)
armor_block = min(90,armor_block) //cap damage reduction at 90%
var/Iforce = I.force //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords)
var/weakness = H.check_weakness(I, user)
apply_damage(totitemdamage * weakness, I.damtype, def_zone, armor_block, H) //CIT CHANGE - replaces I.force with totitemdamage
H.send_item_attack_message(I, user, hit_area, totitemdamage)
H.send_item_attack_message(I, user, hit_area)
I.do_stagger_action(H, user, totitemdamage)
@@ -1945,7 +1936,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
append_message += ", causing them to drop [target_held_item]"
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)
/datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE)
SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone)
var/hit_percent = (100-(blocked+armor))/100
hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100
@@ -1953,20 +1944,20 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
return 0
var/obj/item/bodypart/BP = null
if(!spread_damage)
if(isbodypart(def_zone))
if(damagetype == STAMINA && istype(def_zone, /obj/item/bodypart/head))
BP = H.get_bodypart(check_zone(BODY_ZONE_CHEST))
else
BP = def_zone
if(isbodypart(def_zone))
if(damagetype == STAMINA && istype(def_zone, /obj/item/bodypart/head))
BP = H.get_bodypart(check_zone(BODY_ZONE_CHEST))
else
if(!def_zone)
def_zone = ran_zone(def_zone)
if(damagetype == STAMINA && def_zone == BODY_ZONE_HEAD)
def_zone = BODY_ZONE_CHEST
BP = H.get_bodypart(check_zone(def_zone))
if(!BP)
BP = H.bodyparts[1]
BP = def_zone
else
if(!def_zone)
def_zone = ran_zone(def_zone)
if(damagetype == STAMINA && def_zone == BODY_ZONE_HEAD)
def_zone = BODY_ZONE_CHEST
BP = H.get_bodypart(check_zone(def_zone))
if(!BP)
BP = H.bodyparts[1]
switch(damagetype)
if(BRUTE)
@@ -1,7 +1,6 @@
/datum/species/insect
name = "Anthromorphic Insect"
id = "insect"
say_mod = "chitters"
default_color = "00FF00"
species_traits = list(LIPS,EYECOLOR,HAIR,FACEHAIR,MUTCOLORS,HORNCOLOR,WINGCOLOR)
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BUG
@@ -14,8 +13,6 @@
liked_food = MEAT | FRUIT
disliked_food = TOXIC
icon_limbs = DEFAULT_BODYPART_ICON_CITADEL
exotic_bloodtype = "BUG"
exotic_blood_color = BLOOD_COLOR_BUG
/datum/species/insect/spec_death(gibbed, mob/living/carbon/human/H)
if(H)
@@ -45,4 +42,4 @@
H.update_body()
/datum/species/insect/qualifies_for_rank(rank, list/features)
return TRUE
return TRUE
@@ -89,7 +89,11 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) //
//These count in on_life ticks which should be 2 seconds per every increment of 1 in a perfect world.
var/dwarf_eth_ticker = 0 //Currently set =< 1, that means this will fire the proc around every 2 seconds
var/last_alcohol_spam
food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/consumable/ethanol = 10)
/obj/item/organ/dwarfgland/prepare_eat()
var/obj/S = ..()
S.reagents.add_reagent(/datum/reagent/consumable/ethanol, stored_alcohol/10)
return S
/obj/item/organ/dwarfgland/on_life() //Primary loop to hook into to start delayed loops for other loops..
. = ..()
@@ -11,7 +11,6 @@
disliked_food = null
liked_food = GROSS
exotic_bloodtype = "BUG"
exotic_blood_color = BLOOD_COLOR_BUG
/datum/species/fly/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H)
if(istype(chem, /datum/reagent/toxin/pestkiller))
@@ -20,7 +20,6 @@
mutanteyes = /obj/item/organ/eyes/ipc
exotic_bloodtype = "HF"
exotic_blood_color = BLOOD_COLOR_OIL
var/datum/action/innate/monitor_change/screen
@@ -13,7 +13,6 @@
gib_types = list(/obj/effect/gibspawner/slime, /obj/effect/gibspawner/slime/bodypartless)
exotic_blood = /datum/reagent/blood/jellyblood
exotic_bloodtype = "GEL"
exotic_blood_color = "BLOOD_COLOR_SLIME"
damage_overlay_type = ""
var/datum/action/innate/regenerate_limbs/regenerate_limbs
var/datum/action/innate/slime_change/slime_change //CIT CHANGE
@@ -42,11 +41,6 @@
slime_change.Grant(C) //CIT CHANGE
C.faction |= "slime"
/datum/species/jelly/handle_body(mob/living/carbon/human/H)
. = ..()
//update blood color to body color
exotic_blood_color = "#" + H.dna.features["mcolor"]
/datum/species/jelly/spec_life(mob/living/carbon/human/H)
if(H.stat == DEAD || HAS_TRAIT(H, TRAIT_NOMARROW)) //can't farm slime jelly from a dead slime/jelly person indefinitely, and no regeneration for blooduskers
return
@@ -245,7 +239,7 @@
"<span class='notice'>You focus intently on moving your body while \
standing perfectly still...</span>")
H.mob_transforming = TRUE
H.notransform = TRUE
if(do_after(owner, delay=60, needhand=FALSE, target=owner, progress=TRUE))
if(H.blood_volume >= BLOOD_VOLUME_SLIME_SPLIT)
@@ -255,7 +249,7 @@
else
to_chat(H, "<span class='warning'>...but fail to stand perfectly still!</span>")
H.mob_transforming = FALSE
H.notransform = FALSE
/datum/action/innate/split_body/proc/make_dupe()
var/mob/living/carbon/human/H = owner
@@ -273,7 +267,7 @@
spare.Move(get_step(H.loc, pick(NORTH,SOUTH,EAST,WEST)))
H.blood_volume *= 0.45
H.mob_transforming = 0
H.notransform = 0
var/datum/species/jelly/slime/origin_datum = H.dna.species
origin_datum.bodies |= spare
@@ -465,19 +459,8 @@
/datum/action/innate/slime_change/proc/change_form()
var/mob/living/carbon/human/H = owner
var/select_alteration = input(owner, "Select what part of your form to alter", "Form Alteration", "cancel") in list("Body Color","Hair Style", "Genitals", "Tail", "Snout", "Markings", "Ears", "Taur body", "Penis", "Vagina", "Penis Length", "Breast Size", "Breast Shape", "Cancel")
if(select_alteration == "Body Color")
var/new_color = input(owner, "Choose your skin color:", "Race change","#"+H.dna.features["mcolor"]) as color|null
if(new_color)
var/temp_hsv = RGBtoHSV(new_color)
if(ReadHSV(temp_hsv)[3] >= ReadHSV("#7F7F7F")[3]) // mutantcolors must be bright
H.dna.features["mcolor"] = sanitize_hexcolor(new_color, 6)
H.update_body()
H.update_hair()
else
to_chat(H, "<span class='notice'>Invalid color. Your color is not bright enough.</span>")
else if(select_alteration == "Hair Style")
var/select_alteration = input(owner, "Select what part of your form to alter", "Form Alteration", "cancel") in list("Hair Style", "Genitals", "Tail", "Snout", "Markings", "Ears", "Taur body", "Penis", "Vagina", "Penis Length", "Breast Size", "Breast Shape", "Cancel")
if(select_alteration == "Hair Style")
if(H.gender == MALE)
var/new_style = input(owner, "Select a facial hair style", "Hair Alterations") as null|anything in GLOB.facial_hair_styles_list
if(new_style)
@@ -540,7 +523,7 @@
H.update_body()
else if (select_alteration == "Markings")
var/list/snowflake_markings_list = list("None")
var/list/snowflake_markings_list = list()
for(var/path in GLOB.mam_body_markings_list)
var/datum/sprite_accessory/mam_body_markings/instance = GLOB.mam_body_markings_list[path]
if(istype(instance, /datum/sprite_accessory))
@@ -551,6 +534,8 @@
new_mam_body_markings = input(H, "Choose your character's body markings:", "Marking Alteration") as null|anything in snowflake_markings_list
if(new_mam_body_markings)
H.dna.features["mam_body_markings"] = new_mam_body_markings
if(new_mam_body_markings == "None")
H.dna.features["mam_body_markings"] = "Plain"
for(var/X in H.bodyparts) //propagates the markings changes
var/obj/item/bodypart/BP = X
BP.update_limb(FALSE, H)
@@ -21,7 +21,6 @@
gib_types = list(/obj/effect/gibspawner/lizard, /obj/effect/gibspawner/lizard/bodypartless)
skinned_type = /obj/item/stack/sheet/animalhide/lizard
exotic_bloodtype = "L"
exotic_blood_color = BLOOD_COLOR_LIZARD
disliked_food = GRAIN | DAIRY
liked_food = GROSS | MEAT
inert_mutation = FIREBREATH
@@ -1,28 +1,20 @@
/datum/species/skeleton
name = "Skeleton"
// 2spooky
name = "Spooky Scary Skeleton"
id = "skeleton"
say_mod = "rattles"
blacklisted = 0
sexes = 0
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/skeleton
species_traits = list(NOBLOOD,NOGENITALS,NOAROUSAL)
inherent_traits = list(TRAIT_NOBREATH,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_FAKEDEATH, TRAIT_CALCIUM_HEALER)
inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_FAKEDEATH, TRAIT_CALCIUM_HEALER)
inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID
mutanttongue = /obj/item/organ/tongue/bone
damage_overlay_type = ""//let's not show bloody wounds or burns over bones.
disliked_food = NONE
liked_food = GROSS | MEAT | RAW | DAIRY
brutemod = 1.25
burnmod = 1.25
/datum/species/skeleton/New()
if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) //skeletons are stronger during the spooky season!
inherent_traits |= list(TRAIT_RESISTHEAT,TRAIT_RESISTCOLD)
brutemod = 1
burnmod = 1
..()
/datum/species/skeleton/greater/check_roundstart_eligible()
/datum/species/skeleton/check_roundstart_eligible()
if(SSevents.holidays && SSevents.holidays[HALLOWEEN])
return TRUE
return ..()
@@ -35,4 +27,4 @@
inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT, TRAIT_FAKEDEATH, TRAIT_CALCIUM_HEALER)
/datum/species/skeleton/space/check_roundstart_eligible()
return FALSE
return FALSE
@@ -18,7 +18,7 @@
mutanteyes = /obj/item/organ/eyes/ipc
exotic_bloodtype = "S"
exotic_blood_color = BLOOD_COLOR_OIL
/datum/species/synthliz/qualifies_for_rank(rank, list/features)
return TRUE
@@ -117,18 +117,6 @@
if(!QDELETED(src))
update_inv_legcuffed()
/mob/living/carbon/get_equipped_items(include_pockets = FALSE)
var/list/items = list()
if(back)
items += back
if(head)
items += head
if(wear_mask)
items += wear_mask
if(wear_neck)
items += wear_neck
return items
//handle stuff to update when a mob equips/unequips a mask.
/mob/living/proc/wear_mask_update(obj/item/clothing/C, toggle_off = 1)
update_inv_wear_mask()
+26 -19
View File
@@ -1,23 +1,29 @@
/mob/living/carbon/BiologicalLife(seconds, times_fired)
/mob/living/carbon/Life()
set invisibility = 0
if(notransform)
return
if(damageoverlaytemp)
damageoverlaytemp = 0
update_damage_hud()
//Reagent processing needs to come before breathing, to prevent edge cases.
handle_organs()
. = ..() // if . is false, we are dead.
if(stat == DEAD)
stop_sound_channel(CHANNEL_HEARTBEAT)
handle_death()
rot()
. = FALSE
if(!.)
. = ..()
if (QDELETED(src))
return
handle_blood()
// handle_blood *could* kill us.
// we should probably have a better system for if we need to check for death or something in the future hmw
if(.) //not dead
handle_blood()
if(stat != DEAD)
var/bprv = handle_bodyparts()
if(bprv & BODYPART_LIFE_UPDATE_HEALTH)
updatehealth()
update_stamina()
doSprintBufferRegen()
if(stat != DEAD)
handle_brain_damage()
@@ -25,15 +31,16 @@
if(stat != DEAD)
handle_liver()
if(stat == DEAD)
stop_sound_channel(CHANNEL_HEARTBEAT)
handle_death()
rot()
//Updates the number of stored chemicals for powers
handle_changeling()
/mob/living/carbon/PhysicalLife(seconds, times_fired)
if(!(. = ..()))
return
if(damageoverlaytemp)
damageoverlaytemp = 0
update_damage_hud()
if(stat != DEAD)
return 1
//Procs called while dead
/mob/living/carbon/proc/handle_death()
@@ -517,7 +524,7 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
/mob/living/carbon/handle_status_effects()
..()
if(getStaminaLoss() && !SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) //CIT CHANGE - prevents stamina regen while combat mode is active
adjustStaminaLoss(!CHECK_MOBILITY(src, MOBILITY_STAND) ? ((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) ? STAM_RECOVERY_STAM_CRIT : STAM_RECOVERY_RESTING) : STAM_RECOVERY_NORMAL)
adjustStaminaLoss(!CHECK_MOBILITY(src, MOBILITY_STAND) ? ((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) ? -7.5 : -6) : -3)//CIT CHANGE - decreases adjuststaminaloss to stop stamina damage from being such a joke
if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && incomingstammult != 1)
incomingstammult = max(0.01, incomingstammult)
@@ -81,7 +81,7 @@
/mob/living/carbon/monkey/proc/pickup_and_wear(obj/item/I)
if(QDELETED(I) || I.loc != src)
return
equip_to_appropriate_slot(I, TRUE)
equip_to_appropriate_slot(I)
/mob/living/carbon/monkey/resist_restraints()
var/obj/item/I = null
@@ -1,9 +1,4 @@
/mob/living/carbon/monkey/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, clothing_check = FALSE, list/return_warning)
if(clothing_check && (slot in check_obscured_slots()))
if(return_warning)
return_warning[1] = "<span class='warning'>You are unable to equip that with your current garments in the way!</span>"
return FALSE
/mob/living/carbon/monkey/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE)
switch(slot)
if(SLOT_HANDS)
if(get_empty_held_indexes())
+22 -18
View File
@@ -3,26 +3,30 @@
/mob/living/carbon/monkey
/mob/living/carbon/monkey/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
/mob/living/carbon/monkey/Life()
set invisibility = 0
if (notransform)
return
if(client)
return
if(stat == CONSCIOUS)
if(on_fire || buckled || restrained() || (!CHECK_MOBILITY(src, MOBILITY_STAND) && CHECK_MOBILITY(src, MOBILITY_MOVE))) //CIT CHANGE - makes it so monkeys attempt to resist if they're resting)
if(!resisting && prob(MONKEY_RESIST_PROB))
resisting = TRUE
if(..())
if(!client)
if(stat == CONSCIOUS)
if(on_fire || buckled || restrained() || (!CHECK_MOBILITY(src, MOBILITY_STAND) && CHECK_MOBILITY(src, MOBILITY_MOVE))) //CIT CHANGE - makes it so monkeys attempt to resist if they're resting)
if(!resisting && prob(MONKEY_RESIST_PROB))
resisting = TRUE
walk_to(src,0)
resist()
else if(resisting)
resisting = FALSE
else if((mode == MONKEY_IDLE && !pickupTarget && !prob(MONKEY_SHENANIGAN_PROB)) || !handle_combat())
if(prob(25) && CHECK_MOBILITY(src, MOBILITY_MOVE) && isturf(loc) && !pulledby)
step(src, pick(GLOB.cardinals))
else if(prob(1))
emote(pick("scratch","jump","roll","tail"))
else
walk_to(src,0)
resist()
else if(resisting)
resisting = FALSE
else if((mode == MONKEY_IDLE && !pickupTarget && !prob(MONKEY_SHENANIGAN_PROB)) || !handle_combat())
if(prob(25) && CHECK_MOBILITY(src, MOBILITY_MOVE) && isturf(loc) && !pulledby)
step(src, pick(GLOB.cardinals))
else if(prob(1))
emote(pick("scratch","jump","roll","tail"))
else
walk_to(src,0)
/mob/living/carbon/monkey/handle_mutations_and_radiation()
if(radiation)
@@ -141,7 +141,7 @@
/mob/living/carbon/monkey/attack_animal(mob/living/simple_animal/M)
. = ..()
if(.)
var/damage = .
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
if(!dam_zone) //Dismemberment successful
return TRUE
@@ -31,12 +31,11 @@
if(relic_mask)
equip_to_slot_or_del(new relic_mask, SLOT_WEAR_MASK)
/mob/living/carbon/monkey/punpun/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/carbon/monkey/punpun/Life()
if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved)
Write_Memory(FALSE, FALSE)
memory_saved = TRUE
..()
/mob/living/carbon/monkey/punpun/death(gibbed)
if(!memory_saved)
@@ -12,7 +12,7 @@
overlays_standing[cache_index] = null
/mob/living/carbon/regenerate_icons()
if(mob_transforming)
if(notransform)
return 1
update_inv_hands()
update_inv_handcuffed()
@@ -68,7 +68,7 @@
var/dam_colors = "#E62525"
if(ishuman(src))
var/mob/living/carbon/human/H = src
dam_colors = H.dna.species.exotic_blood_color
dam_colors = bloodtype_to_color(H.dna.blood_type)
var/mutable_appearance/damage_overlay = mutable_appearance('icons/mob/dam_mob.dmi', "blank", -DAMAGE_LAYER, color = dam_colors)
overlays_standing[DAMAGE_LAYER] = damage_overlay
+13 -17
View File
@@ -1,20 +1,14 @@
/**
* Applies damage to this mob
*
* Sends [COMSIG_MOB_APPLY_DAMGE]
*
* Arguuments:
* * damage - amount of damage
* * damagetype - one of [BRUTE], [BURN], [TOX], [OXY], [CLONE], [STAMINA]
* * def_zone - zone that is being hit if any
* * blocked - armor value applied
* * forced - bypass hit percentage
* * spread_damage - used in overrides
*
* Returns TRUE if damage applied
*/
/mob/living/proc/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE)
/*
apply_damage(a,b,c)
args
a:damage - How much damage to take
b:damage_type - What type of damage to take, brute, burn
c:def_zone - Where to take the damage if its brute or burn
Returns
standard 0 if fail
*/
/mob/living/proc/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE)
var/hit_percent = (100-blocked)/100
if(!damage || (hit_percent <= 0))
return 0
@@ -84,6 +78,8 @@
apply_damage(brain, BRAIN, def_zone, blocked)
return 1
/mob/living/proc/apply_effect(effect = 0,effecttype = EFFECT_STUN, blocked = FALSE, knockdown_stamoverride, knockdown_stammax)
var/hit_percent = (100-blocked)/100
if(!effect || (hit_percent <= 0))
@@ -112,7 +108,7 @@
return 1
/mob/living/proc/apply_effects(stun = 0, knockdown = 0, unconscious = 0, irradiate = 0, slur = 0, stutter = 0, eyeblur = 0, drowsy = 0, blocked = 0, stamina = 0, jitter = 0, kd_stamoverride, kd_stammax)
/mob/living/proc/apply_effects(stun = 0, knockdown = 0, unconscious = 0, irradiate = 0, slur = 0, stutter = 0, eyeblur = 0, drowsy = 0, blocked = FALSE, stamina = 0, jitter = 0, kd_stamoverride, kd_stammax)
if(blocked >= 100)
return BULLET_ACT_BLOCK
if(stun)
-1
View File
@@ -457,7 +457,6 @@
message = params
if(type_override)
emote_type = type_override
message = user.say_emphasis(message)
. = ..()
message = null
emote_type = EMOTE_VISIBLE
+36 -60
View File
@@ -1,20 +1,12 @@
/**
* Called by SSmobs at (hopefully) an interval of 1 second.
* Splits off into PhysicalLife() and BiologicalLife(). Override those instead of this.
*/
/mob/living/proc/Life(seconds, times_fired)
set waitfor = FALSE // yeah hey we're kind of on a subsystem, no sleeping will be tolerated here!
if(mob_transforming)
return
set waitfor = FALSE
set invisibility = 0
. = SEND_SIGNAL(src, COMSIG_LIVING_LIFE, seconds, times_fired)
if(!(. & COMPONENT_INTERRUPT_LIFE_PHYSICAL))
PhysicalLife(seconds, times_fired)
if(!(. & COMPONENT_INTERRUPT_LIFE_BIOLOGICAL))
BiologicalLife(seconds, times_fired)
if(digitalinvis)
handle_diginvis() //AI becomes unable to see mob
// CODE BELOW SHOULD ONLY BE THINGS THAT SHOULD HAPPEN NO MATTER WHAT AND CAN NOT BE SUSPENDED!
// Otherwise, it goes into one of the two split Life procs!
if((movement_type & FLYING) && !(movement_type & FLOATING)) //TODO: Better floating
float(on = TRUE)
if (client)
var/turf/T = get_turf(src)
@@ -38,54 +30,28 @@
log_game("Z-TRACKING: [src] of type [src.type] has a Z-registration despite not having a client.")
update_z(null)
/**
* Handles biological life processes like chemical metabolism, breathing, etc
* Returns TRUE or FALSE based on if we were interrupted. This is used by overridden variants to check if they should stop.
*/
/mob/living/proc/BiologicalLife(seconds, times_fired)
if (notransform)
return
if(!loc)
return
var/datum/gas_mixture/environment = loc.return_air()
if(stat != DEAD)
//Mutations and radiation
handle_mutations_and_radiation()
if(stat != DEAD)
//Breathing, if applicable
handle_breathing(times_fired)
handle_diseases()// DEAD check is in the proc itself; we want it to spread even if the mob is dead, but to handle its disease-y properties only if you're not.
// Everything after this shouldn't process while dead (as of the time of writing)
if(stat == DEAD)
return FALSE
//Mutations and radiation
handle_mutations_and_radiation()
//Breathing, if applicable
handle_breathing(times_fired)
if (QDELETED(src)) // diseases can qdel the mob via transformations
return FALSE
return
//Random events (vomiting etc)
handle_random_events()
//stuff in the stomach
handle_stomach()
handle_block_parry(seconds)
// These two MIGHT need to be moved to base Life() if we get any in the future that's a "physical" effect that needs to fire even while in stasis.
handle_traits() // eye, ear, brain damages
handle_status_effects() //all special effects, stun, knockdown, jitteryness, hallucination, sleeping, etc
return TRUE
/**
* Handles physical life processes like being on fire. Don't ask why this is considered "Life".
* Returns TRUE or FALSE based on if we were interrupted. This is used by overridden variants to check if they should stop.
*/
/mob/living/proc/PhysicalLife(seconds, times_fired)
if(digitalinvis)
handle_diginvis() //AI becomes unable to see mob
if((movement_type & FLYING) && !(movement_type & FLOATING)) //TODO: Better floating
float(on = TRUE)
if(!loc)
return FALSE
var/datum/gas_mixture/environment = loc.return_air()
if(stat != DEAD)
//Random events (vomiting etc)
handle_random_events()
//Handle temperature/pressure differences between body and environment
if(environment)
@@ -93,11 +59,21 @@
handle_fire()
//stuff in the stomach
handle_stomach()
handle_gravity()
if(machine)
machine.check_eye(src)
return TRUE
if(stat != DEAD)
handle_traits() // eye, ear, brain damages
if(stat != DEAD)
handle_status_effects() //all special effects, stun, knockdown, jitteryness, hallucination, sleeping, etc
if(stat != DEAD)
return 1
/mob/living/proc/handle_breathing(times_fired)
return
@@ -190,4 +166,4 @@
/mob/living/proc/handle_high_gravity(gravity)
if(gravity >= GRAVITY_DAMAGE_TRESHOLD) //Aka gravity values of 3 or more
var/grav_stregth = gravity - GRAVITY_DAMAGE_TRESHOLD
adjustBruteLoss(min(grav_stregth,3))
adjustBruteLoss(min(grav_stregth,3))
-2
View File
@@ -19,8 +19,6 @@
med_hud_set_status()
/mob/living/Destroy()
end_parry_sequence()
stop_active_blocking()
if(LAZYLEN(status_effects))
for(var/s in status_effects)
var/datum/status_effect/S = s
@@ -1,282 +0,0 @@
// Active directional block system. Shared code is in [living_blocking_parrying.dm]
/mob/living/proc/stop_active_blocking(was_forced = FALSE)
if(!(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)))
return FALSE
var/obj/item/I = active_block_item
combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCKING | COMBAT_FLAG_ACTIVE_BLOCK_STARTING)
active_block_effect_end()
active_block_item = null
REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT)
REMOVE_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_BLOCK_TRAIT)
remove_movespeed_modifier(/datum/movespeed_modifier/active_block)
var/datum/block_parry_data/data = I.get_block_parry_data()
if(timeToNextMove() < data.block_end_click_cd_add)
changeNext_move(data.block_end_click_cd_add)
return TRUE
/mob/living/proc/ACTIVE_BLOCK_START(obj/item/I)
if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
return FALSE
if(!(I in held_items))
return FALSE
var/datum/block_parry_data/data = I.get_block_parry_data()
if(!istype(data)) //Typecheck because if an admin/coder screws up varediting or something we do not want someone being broken forever, the CRASH logs feedback so we know what happened.
CRASH("ACTIVE_BLOCK_START called with an item with no valid data: [I] --> [I.block_parry_data]!")
combat_flags |= COMBAT_FLAG_ACTIVE_BLOCKING
active_block_item = I
if(data.block_lock_attacking)
ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) //probably should be something else at some point
if(data.block_lock_sprinting)
ADD_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_BLOCK_TRAIT)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/active_block, multiplicative_slowdown = data.block_slowdown)
active_block_effect_start()
return TRUE
/// Visual effect setup for starting a directional block
/mob/living/proc/active_block_effect_start()
visible_message("<span class='warning'>[src] raises their [active_block_item], dropping into a defensive stance!</span>")
animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_OUT)
/// Visual effect cleanup for starting a directional block
/mob/living/proc/active_block_effect_end()
visible_message("<span class='warning'>[src] lowers their [active_block_item].</span>")
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
if(dir & WEST)
. -= 8
/mob/living/get_standard_pixel_y_offset()
. = ..()
if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
if(dir & NORTH)
. += 8
if(dir & SOUTH)
. -= 8
/**
* Proc called by keybindings to toggle active blocking.
*/
/mob/living/proc/keybind_toggle_active_blocking()
if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
return keybind_stop_active_blocking()
else
return keybind_start_active_blocking()
/**
* Proc called by keybindings to start active blocking.
*/
/mob/living/proc/keybind_start_active_blocking()
if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
return FALSE
if(!(combat_flags & COMBAT_FLAG_BLOCK_CAPABLE))
to_chat(src, "<span class='warning'>You're not something that can actively block.</span>")
return FALSE
// QOL: Instead of trying to just block with held item, grab first available item.
var/obj/item/I = find_active_block_item()
var/list/other_items = list()
if(SEND_SIGNAL(src, COMSIG_LIVING_ACTIVE_BLOCK_START, I, other_items) & COMPONENT_PREVENT_BLOCK_START)
to_chat(src, "<span class='warning'>Something is preventing you from blocking!</span>")
return
if(!I)
if(!length(other_items))
to_chat(src, "<span class='warning'>You can't block with your bare hands!</span>")
return
I = other_items[1]
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_ACTIVE))
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
animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = delay, FALSE, SINE_EASING | EASE_IN)
if(!do_after_advanced(src, delay, src, DO_AFTER_REQUIRES_USER_ON_TURF|DO_AFTER_NO_COEFFICIENT, CALLBACK(src, .proc/continue_starting_active_block), MOBILITY_USE, null, null, I))
to_chat(src, "<span class='warning'>You fail to raise [I].</span>")
combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING)
animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN, ANIMATION_END_NOW)
return
combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING)
ACTIVE_BLOCK_START(I)
/**
* Gets the first item we can that can block, but if that fails, default to active held item.COMSIG_ENABLE_COMBAT_MODE
*/
/mob/living/proc/find_active_block_item()
var/obj/item/held = get_active_held_item()
if(!held?.can_active_block())
for(var/obj/item/I in held_items - held)
if(I.can_active_block())
return I
else
return held
/**
* Proc called by keybindings to stop active blocking.
*/
/mob/living/proc/keybind_stop_active_blocking()
combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING)
if(combat_flags & COMBAT_FLAG_ACTIVE_BLOCKING)
stop_active_blocking(FALSE)
return TRUE
/**
* Returns if we can actively block.
*/
/obj/item/proc/can_active_block()
return block_parry_data && (item_flags & ITEM_CAN_BLOCK)
/**
* 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)
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)
var/limit = data.attack_type_list_scan(data.block_damage_limit_override, attack_type)
// must use isnulls to handle 0's.
if(isnull(absorption))
absorption = data.block_damage_absorption
if(isnull(efficiency))
efficiency = data.block_damage_multiplier
if(isnull(limit))
limit = data.block_damage_limit
// now we calculate damage to reduce.
var/final_damage = 0
// apply limit
if(damage > limit) //clamp and apply overrun
final_damage += (damage - limit)
damage = limit
// apply absorption
damage -= min(absorption, damage) //this way if damage is less than absorption it 0's properly.
// apply multiplier to remaining
final_damage += (damage * efficiency)
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)
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))
efficiency = data.block_stamina_efficiency
var/multiplier = 1
if(!CHECK_MOBILITY(owner, MOBILITY_STAND))
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
/// 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)
var/datum/block_parry_data/data = get_block_parry_data()
if(iscarbon(owner))
var/mob/living/carbon/C = owner
var/held_index = C.get_held_index_of_item(src)
var/obj/item/bodypart/BP = C.hand_bodyparts[held_index]
if(!BP?.body_zone)
return C.adjustStaminaLossBuffered(stamina_amount) //nah
var/zone = BP.body_zone
var/stamina_to_zone = data.block_stamina_limb_ratio * stamina_amount
var/stamina_to_chest = stamina_amount - stamina_to_zone
var/stamina_buffered = stamina_to_chest * data.block_stamina_buffer_ratio
stamina_to_chest -= stamina_buffered
C.apply_damage(stamina_to_zone, STAMINA, zone)
C.apply_damage(stamina_to_chest, STAMINA, BODY_ZONE_CHEST)
C.adjustStaminaLossBuffered(stamina_buffered)
else
owner.adjustStaminaLossBuffered(stamina_amount)
/obj/item/proc/on_active_block(mob/living/owner, atom/object, damage, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction)
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)
if(!can_active_block())
return BLOCK_NONE
var/datum/block_parry_data/data = get_block_parry_data()
if(attack_type && !(attack_type & data.can_block_attack_types))
return BLOCK_NONE
var/incoming_direction
if(isnull(override_direction))
if(istype(object, /obj/item/projectile))
var/obj/item/projectile/P = object
incoming_direction = angle2dir(P.Angle)
else
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))
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/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)
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
. = BLOCK_SHOULD_CHANGE_DAMAGE
if((final_damage <= 0) || (damage <= 0))
. |= BLOCK_SUCCESS //full block
owner.visible_message("<span class='warning'>[owner] blocks \the [attack_text] with [src]!</span>")
else
owner.visible_message("<span class='warning'>[owner] dampens \the [attack_text] with [src]!</span>")
block_return[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] = data.block_projectile_mitigation
if(length(data.block_sounds))
playsound(loc, pickweight(data.block_sounds), 75, TRUE)
on_active_block(owner, object, damage, damage_blocked, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return, override_direction)
/obj/item/proc/check_active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
if(!can_active_block())
return
var/incoming_direction = get_dir(get_turf(attacker) || get_turf(object), src)
if(!can_block_direction(owner.dir, incoming_direction))
return
block_return[BLOCK_RETURN_ACTIVE_BLOCK] = TRUE
block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED] = damage - active_block_calculate_final_damage(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return)
/**
* Gets the block direction bitflags of what we can block.
*/
/obj/item/proc/blockable_directions()
var/datum/block_parry_data/data = get_block_parry_data()
return data.can_block_directions
/**
* Checks if we can block from a specific direction from our direction.
*
* @params
* * 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)
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())
/**
* can_block_direction but for "compound" directions to check all of them and return the number of directions that were blocked.
*
* @params
* * our_dir - our direction.
* * their_dirs - list of their directions as we cannot use bitfields here.
*/
/obj/item/proc/can_block_directions_multiple(our_dir, list/their_dirs)
. = FALSE
for(var/i in their_dirs)
. |= can_block_direction(our_dir, i)
@@ -1,346 +0,0 @@
// Active parry system goes in here.
/**
* Determines if we can actively parry.
*/
/obj/item/proc/can_active_parry()
return block_parry_data && (item_flags & ITEM_CAN_PARRY)
/**
* Called from keybindings.
*/
/mob/living/proc/keybind_parry()
initiate_parry_sequence()
/**
* Initiates a parrying sequence.
*/
/mob/living/proc/initiate_parry_sequence()
if(parrying)
return // already parrying
if(!(combat_flags & COMBAT_FLAG_PARRY_CAPABLE))
to_chat(src, "<span class='warning'>You are not something that can parry attacks.</span>")
return
// Prioritize item, then martial art, then unarmed.
// yanderedev else if time
var/obj/item/using_item = get_active_held_item()
var/datum/block_parry_data/data
var/datum/tool
var/method
if(using_item?.can_active_parry())
data = using_item.block_parry_data
method = ITEM_PARRY
tool = using_item
else if(mind?.martial_art?.can_martial_parry)
data = mind.martial_art.block_parry_data
method = MARTIAL_PARRY
tool = mind.martial_art
else if(combat_flags & COMBAT_FLAG_UNARMED_PARRY)
data = block_parry_data
method = UNARMED_PARRY
tool = src
else
// QOL: If none of the above work, try to find another item.
var/obj/item/backup = find_backup_parry_item()
if(backup)
tool = backup
data = backup.block_parry_data
using_item = backup
method = ITEM_PARRY
var/list/other_items = list()
if(SEND_SIGNAL(src, COMSIG_LIVING_ACTIVE_PARRY_START, method, tool, other_items) & COMPONENT_PREVENT_PARRY_START)
to_chat(src, "<span class='warning'>Something is preventing you from parrying!</span>")
return
if(!using_item && !method && length(other_items))
using_item = other_items[1]
method = ITEM_PARRY
data = using_item.block_parry_data
if(!method)
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_ACTIVE))
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 && (next_move > world.time)) || ((parry_end_time_last + data.parry_cooldown) > world.time))
to_chat(src, "<span class='warning'>You are not ready to parry (again)!</span>")
return
// Point of no return, make sure everything is set.
parrying = method
if(method == ITEM_PARRY)
active_parry_item = using_item
adjustStaminaLossBuffered(data.parry_stamina_cost)
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
/**
* Tries to find a backup parry item.
* Does not look at active held item.
*/
/mob/living/proc/find_backup_parry_item()
for(var/obj/item/I in held_items - get_active_held_item())
if(I.can_active_parry())
return I
/**
* Called via timer when the parry sequence ends.
*/
/mob/living/proc/end_parry_sequence()
if(!parrying)
return
REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_PARRY_TRAIT)
REMOVE_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_PARRY_TRAIT)
if(parry_visual_effect)
QDEL_NULL(parry_visual_effect)
var/datum/block_parry_data/data = get_parry_data()
var/list/effect_text = list()
var/successful = FALSE
for(var/efficiency in successful_parries)
if(efficiency >= data.parry_efficiency_considered_successful)
successful = TRUE
break
if(!successful) // didn't parry anything successfully
if(data.parry_failed_stagger_duration)
Stagger(data.parry_failed_stagger_duration)
effect_text += "staggering themselves"
if(data.parry_failed_clickcd_duration)
changeNext_move(data.parry_failed_clickcd_duration)
effect_text += "throwing themselves off balance"
handle_parry_ending_effects(data, effect_text)
parrying = NOT_PARRYING
parry_start_time = 0
parry_end_time_last = world.time
successful_parries = null
/**
* Handles starting effects for parrying.
*/
/mob/living/proc/handle_parry_starting_effects(datum/block_parry_data/data)
playsound(src, data.parry_start_sound, 75, 1)
parry_visual_effect = new /obj/effect/abstract/parry/main(null, TRUE, src, data.parry_effect_icon_state, data.parry_time_windup_visual_override || data.parry_time_windup, data.parry_time_active_visual_override || data.parry_time_active, data.parry_time_spindown_visual_override || data.parry_time_spindown)
switch(parrying)
if(ITEM_PARRY)
visible_message("<span class='warning'>[src] swings [active_parry_item]!</span>")
else
visible_message("<span class='warning'>[src] rushes forwards!</span>")
/**
* Handles ending effects for parrying.
*/
/mob/living/proc/handle_parry_ending_effects(datum/block_parry_data/data, list/failed_effect_text)
if(length(successful_parries))
return
visible_message("<span class='warning'>[src] fails to connect their parry[failed_effect_text? ", [english_list(failed_effect_text)]" : ""]!")
/**
* Gets this item's datum/block_parry_data
*/
/obj/item/proc/get_block_parry_data()
return return_block_parry_datum(block_parry_data)
//Stubs.
/**
* 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, final_block_chance, list/block_return, parry_efficiency, parry_time)
/**
* 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, final_block_chance, list/block_return, parry_efficiency, parry_time)
/**
* 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, final_block_chance, list/block_return, parry_efficiency, parry_time)
/**
* Called when an attack is parried and block_parra_data indicates to use a proc to handle counterattack.
*/
/obj/item/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)
/**
* Called when an attack is parried and block_parra_data indicates to use a proc to handle counterattack.
*/
/mob/living/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)
/**
* Called when an attack is parried and block_parra_data indicates to use a proc to handle counterattack.
*/
/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)
/**
* Gets the stage of our parry sequence we're currently in.
*/
/mob/living/proc/get_parry_stage()
if(!parrying)
return NOT_PARRYING
var/datum/block_parry_data/data = get_parry_data()
var/windup_end = data.parry_time_windup
var/active_end = windup_end + data.parry_time_active
var/spindown_end = active_end + data.parry_time_spindown
var/current_time = get_parry_time()
// Not a switch statement because byond switch statements don't support floats at time of writing with "to" keyword.
if(current_time < 0)
return NOT_PARRYING
else if(current_time < windup_end)
return PARRY_WINDUP
else if(current_time <= active_end) // this uses <= on purpose, give a slight bit of advantage because time is rounded to world.tick_lag
return PARRY_ACTIVE
else if(current_time <= spindown_end)
return PARRY_SPINDOWN
else
return NOT_PARRYING
/**
* Gets the current decisecond "frame" of an active parry.
*/
/mob/living/proc/get_parry_time()
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())
var/stage = get_parry_stage()
if(stage != PARRY_ACTIVE)
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.
return BLOCK_NONE
var/efficiency = data.get_parry_efficiency(attack_type, get_parry_time())
switch(parrying)
if(ITEM_PARRY)
if(!active_parry_item.can_active_parry())
return BLOCK_NONE
. = active_parry_item.on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, get_parry_time())
if(UNARMED_PARRY)
. = on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, get_parry_time())
if(MARTIAL_PARRY)
. = mind.martial_art.on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, get_parry_time())
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(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
if(efficiency >= data.parry_efficiency_to_counterattack)
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)
successful_parries += efficiency
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()
if(data.parry_sounds)
playsound(src, pick(data.parry_sounds), 75)
visible_message("<span class='danger'>[src] parries \the [attack_text][length(effect_text)? ", [english_list(effect_text)] [attacker]" : ""]!</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)
if(!isliving(attacker))
return
var/mob/living/L = attacker
var/datum/block_parry_data/data = get_parry_data()
var/list/effect_text = list()
// Always proc so items can override behavior easily
switch(parrying)
if(ITEM_PARRY)
active_parry_item.active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency, effect_text)
if(UNARMED_PARRY)
active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency, effect_text)
if(MARTIAL_PARRY)
mind.martial_art.active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency, effect_text)
if(Adjacent(attacker) || data.parry_data[PARRY_COUNTERATTACK_IGNORE_ADJACENCY])
if(data.parry_data[PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN])
switch(parrying)
if(ITEM_PARRY)
active_parry_item.melee_attack_chain(src, attacker, null, ATTACKCHAIN_PARRY_COUNTERATTACK, data.parry_data[PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN])
effect_text += "reflexively counterattacking with [active_parry_item]"
if(UNARMED_PARRY) // WARNING: If you are using these two, the attackchain parry counterattack flags and damage multipliers are unimplemented. Be careful with how you handle this.
UnarmedAttack(attacker)
effect_text += "reflexively counterattacking in the process"
if(MARTIAL_PARRY) // Not well implemeneted, recommend custom implementation using the martial art datums.
UnarmedAttack(attacker)
effect_text += "reflexively maneuvering to retaliate"
if(data.parry_data[PARRY_DISARM_ATTACKER])
L.drop_all_held_items()
effect_text += "disarming"
if(data.parry_data[PARRY_KNOCKDOWN_ATTACKER])
L.DefaultCombatKnockdown(data.parry_data[PARRY_KNOCKDOWN_ATTACKER])
effect_text += "knocking them to the ground"
if(data.parry_data[PARRY_STAGGER_ATTACKER])
L.Stagger(data.parry_data[PARRY_STAGGER_ATTACKER])
effect_text += "staggering"
if(data.parry_data[PARRY_DAZE_ATTACKER])
L.Daze(data.parry_data[PARRY_DAZE_ATTACKER])
effect_text += "dazing"
return effect_text
/// Gets the datum/block_parry_data we're going to use to parry.
/mob/living/proc/get_parry_data()
if(parrying == ITEM_PARRY)
return active_parry_item.get_block_parry_data()
else if(parrying == UNARMED_PARRY)
return return_block_parry_datum(block_parry_data)
else if(parrying == MARTIAL_PARRY)
return return_block_parry_datum(mind.martial_art.block_parry_data)
/// Effects
/obj/effect/abstract/parry
icon = 'icons/effects/block_parry.dmi'
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
layer = FLOAT_LAYER
plane = FLOAT_PLANE
vis_flags = VIS_INHERIT_LAYER|VIS_INHERIT_PLANE
/// The person we're on
var/mob/living/owner
/obj/effect/abstract/parry/main
name = null
/obj/effect/abstract/parry/main/Initialize(mapload, autorun, mob/living/owner, set_icon_state, windup, active, spindown)
. = ..()
icon_state = set_icon_state
if(owner)
attach_to(owner)
if(autorun)
INVOKE_ASYNC(src, .proc/run_animation, windup, active, spindown)
/obj/effect/abstract/parry/main/Destroy()
detach_from(owner)
return ..()
/obj/effect/abstract/parry/main/proc/attach_to(mob/living/attaching)
if(owner)
detach_from(owner)
owner = attaching
owner.vis_contents += src
/obj/effect/abstract/parry/main/proc/detach_from(mob/living/detaching)
if(detaching == owner)
owner = null
detaching.vis_contents -= src
/obj/effect/abstract/parry/main/proc/run_animation(windup_time = 2, active_time = 5, spindown_time = 3)
var/matrix/current = transform
transform = matrix(0.1, 0, 0, 0, 0.1, 0)
animate(src, transform = current, time = windup_time)
sleep(active_time)
animate(src, alpha = 0, spindown_time)
+32 -41
View File
@@ -1,6 +1,31 @@
// This file has a weird name, but it's for anything related to the checks for shields, blocking, dodging,
// and similar "stop this attack before it actually impacts the target" as opposed to "defend once it has hit".
/*
/// You can find the mob_check_block() and mob_run_block() macros in __DEFINES/combat.dm
/// Bitflags for check_block() and run_block(). Meant to be combined. You can be hit and still reflect, for example, if you do not use BLOCK_SUCCESS.
/// Attack was not blocked
#define BLOCK_NONE NONE
/// Attack was blocked, do not do damage. THIS FLAG MUST BE THERE FOR DAMAGE/EFFECT PREVENTION!
#define BLOCK_SUCCESS (1<<1)
/// The below are for "metadata" on "how" the attack was blocked.
/// Attack was and should be reflected (NOTE: the SHOULD here is important, as it says "the thing blocking isn't handling the reflecting for you so do it yourself"!)
#define BLOCK_SHOULD_REFLECT (1<<2)
/// Attack was manually redirected (including reflected) by any means by the defender. For when YOU are handling the reflection, rather than the thing hitting you. (see sleeping carp)
#define BLOCK_REDIRECTED (1<<3)
/// Attack was blocked by something like a shield.
#define BLOCK_PHYSICAL_EXTERNAL (1<<4)
/// Attack was blocked by something worn on you.
#define BLOCK_PHYSICAL_INTERNAL (1<<5)
/// Attack should pass through. Like SHOULD_REFLECT but for.. well, passing through harmlessly.
#define BLOCK_SHOULD_PASSTHROUGH (1<<6)
/// Attack outright missed because the target dodged. Should usually be combined with SHOULD_PASSTHROUGH or something (see martial arts)
#define BLOCK_TARGET_DODGED (1<<7)
*/
/** The actual proc for block checks. DO NOT USE THIS DIRECTLY UNLESS YOU HAVE VERY GOOD REASON TO. To reduce copypaste for differences between handling for real attacks and virtual checks.
* Automatically checks all held items for /obj/item/proc/run_block() with the same parameters.
* @params
@@ -14,31 +39,21 @@
* attacker - Set to the mob attacking IF KNOWN. Do not expect this to always be set!
* def_zone - The zone this'll impact.
* return_list - If something wants to grab things from what items/whatever put into list/block_return on obj/item/run_block and the comsig, pass in a list so you can grab anything put in it after block runs.
* attack_direction - Direction of the attack. It is highly recommended to put this in, as the automatic guesswork that's done otherwise is quite inaccurate at times.
*/
/mob/living/proc/do_run_block(real_attack = TRUE, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), attack_direction)
if(real_attack)
. = run_parry(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) //Parry - Highest priority!
if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN))
return
/mob/living/proc/do_run_block(real_attack = TRUE, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list())
// Component signal block runs have highest priority.. for now.
. = SEND_SIGNAL(src, COMSIG_LIVING_RUN_BLOCK, real_attack, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, attack_direction)
. = SEND_SIGNAL(src, COMSIG_LIVING_RUN_BLOCK, real_attack, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list)
if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN))
return_list[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] = 100
return
var/list/obj/item/tocheck = get_blocking_items()
sortTim(tocheck, /proc/cmp_numeric_dsc, TRUE)
sortTim(tocheck, /proc/cmp_item_block_priority_asc)
// i don't like this
var/block_chance_modifier = round(damage / -3)
if(real_attack)
for(var/obj/item/I in tocheck)
// i don't like this too
var/final_block_chance = I.block_chance - (clamp((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example
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
results = I.run_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list)
var/results = I.run_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list)
. |= results
if((results & BLOCK_SUCCESS) && !(results & BLOCK_CONTINUE_CHAIN))
break
@@ -46,29 +61,17 @@
for(var/obj/item/I in tocheck)
// i don't like this too
var/final_block_chance = I.block_chance - (clamp((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example
if(I == active_block_item) //block is long termed enough we give a damn. parry, not so much.
I.check_active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list, attack_direction)
else
I.check_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list)
if(. & BLOCK_SUCCESS)
return_list[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] = 100
else if(isnull(return_list[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE]))
return_list[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] = return_list[BLOCK_RETURN_MITIGATION_PERCENT]
I.check_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list)
/// Gets an unsortedlist of objects to run block checks on. List must have associative values for priorities!
/// Gets an unsortedlist of objects to run block checks on.
/mob/living/proc/get_blocking_items()
. = list()
if(active_block_item)
var/datum/block_parry_data/data = active_block_item.get_block_parry_data()
.[active_block_item] = data.block_active_priority
SEND_SIGNAL(src, COMSIG_LIVING_GET_BLOCKING_ITEMS, .)
for(var/obj/item/I in held_items)
// this is a bad check but i am not removing it until a better catchall is made
if(istype(I, /obj/item/clothing))
continue
if(.[I]) //don't override block/parry.
continue
.[I] = I.block_priority
. |= I
/obj/item
/// The 0% to 100% chance for the default implementation of random block rolls.
@@ -92,15 +95,3 @@
SEND_SIGNAL(src, COMSIG_ITEM_CHECK_BLOCK, owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return)
var/existing = block_return[BLOCK_RETURN_NORMAL_BLOCK_CHANCE]
block_return[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] = max(existing || 0, final_block_chance)
// HELPER PROCS
/**
* Considers a block return_list and calculates damage to use from that.
*/
/proc/block_calculate_resultant_damage(damage, list/block_return)
if(!isnull(block_return[BLOCK_RETURN_SET_DAMAGE_TO])) // higher priority
return block_return[BLOCK_RETURN_SET_DAMAGE_TO]
else if(!isnull(block_return[BLOCK_RETURN_MITIGATION_PERCENT]))
return damage * ((100 - block_return[BLOCK_RETURN_MITIGATION_PERCENT]) * 0.01)
return damage
@@ -1,310 +0,0 @@
// yell at me later for file naming
// This file contains stuff relating to the new directional blocking and parry system.
GLOBAL_LIST_EMPTY(block_parry_data)
/proc/return_block_parry_datum(datum/block_parry_data/type_id_datum)
if(istype(type_id_datum))
return type_id_datum
if(ispath(type_id_datum))
. = GLOB.block_parry_data["[type_id_datum]"]
if(!.)
. = GLOB.block_parry_data["[type_id_datum]"] = new type_id_datum
else //text id
return GLOB.block_parry_data["[type_id_datum]"]
/proc/set_block_parry_datum(id, datum/block_parry_data/data)
if(ispath(id))
CRASH("Path-fetching of block parry data is only to grab static data, do not attempt to modify global caches of paths. Use string IDs.")
GLOB.block_parry_data["[id]"] = data
/// Carries data like list data that would be a waste of memory if we initialized the list on every /item as we can cache datums easier.
/datum/block_parry_data
/////////// BLOCKING ////////////
/// NOTE: FOR ATTACK_TYPE_DEFINE, you MUST wrap it in "[DEFINE_HERE]"! The defines are bitflags, and therefore, NUMBERS!
/// See defines. Point of reference is someone facing north.
var/can_block_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST
/// Attacks we can block
var/can_block_attack_types = ALL
/// Our slowdown added while blocking
var/block_slowdown = 1
/// Clickdelay added to user after block ends
var/block_end_click_cd_add = 0
/// Disallow attacking during block
var/block_lock_attacking = TRUE
/// Disallow sprinting during block
var/block_lock_sprinting = FALSE
/// The priority we get in [mob/do_run_block()] while we're being used to parry.
var/block_active_priority = BLOCK_PRIORITY_ACTIVE_BLOCK
/// Windup before we have our blocking active.
var/block_start_delay = 5
/// Amount of "free" damage blocking absorbs
var/block_damage_absorption = 10
/// Override absorption, list("[ATTACK_TYPE_DEFINE]" = absorption), see [block_damage_absorption]
var/list/block_damage_absorption_override
/// Ratio of damage to allow through above absorption and below limit. Multiplied by damage to determine how much to let through. Lower is better.
var/block_damage_multiplier = 0.5
/// Override damage overrun efficiency, list("[ATTACK_TYPE_DEFINE]" = absorption), see [block_damage_efficiency]
var/list/block_damage_multiplier_override
/// Upper bound of damage block, anything above this will go right through.
var/block_damage_limit = 80
/// Override upper bound of damage block, list("[ATTACK_TYPE_DEFINE]" = absorption), see [block_damage_limit]
var/list/block_damage_limit_override
/// The blocked variable of on_hit() on projectiles is impacted by this. Higher is better, 0 to 100, percentage.
var/block_projectile_mitigation = 50
/*
* NOTE: Overrides for attack types for most the block_stamina variables were removed,
* because at the time of writing nothing needed to use it. Add them if you need it,
* it should be pretty easy, just copy [active_block_damage_mitigation]
* for how to override with list.
*/
/// Default damage-to-stamina coefficient, higher is better. This is based on amount of damage BLOCKED, not initial damage, to prevent damage from "double dipping".
var/block_stamina_efficiency = 2
/// Override damage-to-stamina coefficient, see [block_efficiency], this should be list("[ATTACK_TYPE_DEFINE]" = coefficient_number)
var/list/block_stamina_efficiency_override
/// Ratio of stamina incurred by blocking that goes to the arm holding the object instead of the chest. Has no effect if this is not held in hand.
var/block_stamina_limb_ratio = 0.5
/// Ratio of stamina incurred by chest (so after [block_stamina_limb_ratio] runs) that is buffered.
var/block_stamina_buffer_ratio = 1
/// Stamina dealt directly via adjustStaminaLossBuffered() per SECOND of block.
var/block_stamina_cost_per_second = 1.5
/// Bitfield for attack types that we can block while down. This will work in any direction.
var/block_resting_attack_types_anydir = ATTACK_TYPE_MELEE | ATTACK_TYPE_UNARMED | ATTACK_TYPE_TACKLE
/// Bitfield for attack types that we can block while down but only in our normal directions.
var/block_resting_attack_types_directional = ATTACK_TYPE_PROJECTILE | ATTACK_TYPE_THROWN
/// Multiplier to stamina damage taken for attacks blocked while downed.
var/block_resting_stamina_penalty_multiplier = 1.5
/// Override list for multiplier to stamina damage taken for attacks blocked while down. list("[ATTACK_TYPE_DEFINE]" = multiplier_number)
var/list/block_resting_stamina_penalty_multiplier_override
/// Sounds for blocking
var/list/block_sounds = list('sound/block_parry/block_metal1.ogg' = 1, 'sound/block_parry/block_metal1.ogg' = 1)
/////////// PARRYING ////////////
/// Prioriry 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
/// 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
/// Parry windup duration in deciseconds. 0 to this is windup, afterwards is main stage.
var/parry_time_windup = 2
/// 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]
var/parry_time_active = 5
// Visual overrides
/// If set, overrides visual duration of windup
var/parry_time_windup_visual_override
/// If set, overrides visual duration of active period
var/parry_time_active_visual_override
/// If set, overrides visual duration of spindown
var/parry_time_spindown_visual_override
/// Perfect parry window in deciseconds from the start of the main window. 3 with main 5 = perfect on third decisecond of main window.
var/parry_time_perfect = 2.5
/// Time on both sides of perfect parry that still counts as part of the perfect window.
var/parry_time_perfect_leeway = 1
/// [parry_time_perfect_leeway] override for attack types, list("[ATTACK_TYPE_DEFINE]" = deciseconds)
var/list/parry_time_perfect_leeway_override
/// Parry "efficiency" falloff in percent per decisecond once perfect window is over.
var/parry_imperfect_falloff_percent = 20
/// [parry_imperfect_falloff_percent] override for attack types, list("[ATTACK_TYPE_DEFINE]" = deciseconds)
var/list/parry_imperfect_falloff_percent_override
/// Efficiency in percent on perfect parry.
var/parry_efficiency_perfect = 120
/// Parry effect data.
var/list/parry_data = list(
PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 1
)
/// 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
/// 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
var/parry_effect_icon_state = "parry_bm_hold"
/// Parrying cooldown, separate of clickdelay. It must be this much deciseconds since their last parry for them to parry with this object.
var/parry_cooldown = 0
/// Parry start sound
var/parry_start_sound = 'sound/block_parry/sfx-parry.ogg'
/// Sounds for parrying
var/list/parry_sounds = list('sound/block_parry/block_metal1.ogg' = 1, 'sound/block_parry/block_metal1.ogg' = 1)
/// 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
/**
* Quirky proc to get average of flags in list that are in attack_type because why is attack_type a flag.
*/
/datum/block_parry_data/proc/attack_type_list_scan(list/L, attack_type)
var/total = 0
var/div = 0
for(var/flagtext in L)
if(attack_type & text2num(flagtext))
total += L[flagtext]
div++
// if none, return null.
if(!div)
return
return total/div //groan
/**
* Gets the percentage efficiency of our parry.
*
* Returns a percentage in normal 0 to 100 scale, but not clamped to just 0 to 100.
* This is a proc to allow for overriding.
* @params
* * attack_type - int, bitfield of the attack type(s)
* * parry_time - deciseconds since start of the parry.
*/
/datum/block_parry_data/proc/get_parry_efficiency(attack_type, parry_time)
var/difference = abs(parry_time - (parry_time_perfect + parry_time_windup))
var/leeway = attack_type_list_scan(parry_time_perfect_leeway_override, attack_type)
if(isnull(leeway))
leeway = parry_time_perfect_leeway
difference -= leeway
. = parry_efficiency_perfect
if(difference <= 0)
return
var/falloff = attack_type_list_scan(parry_imperfect_falloff_percent_override, attack_type)
if(isnull(falloff))
falloff = parry_imperfect_falloff_percent
. -= falloff * difference
#define RENDER_VARIABLE_SIMPLE(varname, desc) dat += "<tr><th>[#varname]<br><i>[desc]</i></th><th>[varname]</th></tr>"
#define RENDER_OVERRIDE_LIST(varname, desc) \
dat += "<tr><th>[#varname]<br><i>[desc]</i></th><th>"; \
var/list/assembled__##varname = list(); \
for(var/textbit in varname){ \
assembled__##varname += "[GLOB.attack_type_names[textbit]] = [varname[textbit]]"; \
} \
dat += "[english_list(assembled__##varname)]</th>";
#define RENDER_ATTACK_TYPES(varname, desc) dat += "<tr><th>[#varname]<br><i>[desc]</i></th><th>"; \
var/list/assembled__##varname = list(); \
for(var/bit in bitfield2list(varname)){ \
var/name = GLOB.attack_type_names[num2text(bit)]; \
if(name){ \
assembled__##varname += "[name]"; \
} \
} \
dat += "[english_list(assembled__##varname)]</th>";
#define RENDER_BLOCK_DIRECTIONS(varname, desc) \
dat += "<tr><th>[#varname]<br><i>[desc]</i></th><th>"; \
var/list/assembled__##varname = list(); \
for(var/bit in bitfield2list(varname)){ \
var/name = GLOB.block_direction_names[num2text(bit)]; \
if(name){ \
assembled__##varname += "[name]"; \
} \
} \
dat += "[english_list(assembled__##varname)]</th>";
/datum/block_parry_data/Topic(href, href_list)
. = ..()
if(.)
return
if(href_list["render"])
var/datum/browser/B = new(usr, REF(src), href_list["name"], 800, 1000)
B.set_content(render_html_readout(href_list["block"], href_list["parry"]))
B.open()
/**
* Generates a HTML render of this datum for self-documentation
* Maybe make this tgui-next someday haha god this is ugly as sin.
* Does NOT include the popout or title or anything. Just the variables and explanations..
*/
/datum/block_parry_data/proc/render_html_readout(block_data = FALSE, parry_data = FALSE)
var/list/dat = list()
if(block_data)
dat += "<div class='statusDisplay'><h3>Block Stats</h3><table style='width:100%'><tr><th>Name/Description</th><th>Value</th></tr>"
RENDER_BLOCK_DIRECTIONS(can_block_directions, "Which directions this can block in.")
RENDER_ATTACK_TYPES(can_block_attack_types, "The kinds of attacks this can block.")
RENDER_VARIABLE_SIMPLE(block_slowdown, "How much slowdown is applied to the user while blocking. Lower is better.")
RENDER_VARIABLE_SIMPLE(block_end_click_cd_add, "How much click delay in deciseconds is applied to the user when blocking ends. Lower is better.")
RENDER_VARIABLE_SIMPLE(block_lock_attacking, "Whether or not (1 or 0) the user is locked from atacking and/or item usage while blocking.")
RENDER_VARIABLE_SIMPLE(block_active_priority, "The priority of this item in the block sequence. This will probably mean nothing to you unless you are a coder.")
RENDER_VARIABLE_SIMPLE(block_start_delay, "The amount of time in deciseconds it takes to start a block with this item. Lower is better.")
RENDER_VARIABLE_SIMPLE(block_damage_absorption, "The amount of damage that is absorbed by default. Higher is better.")
RENDER_OVERRIDE_LIST(block_damage_absorption_override, "Overrides for the above for each attack type")
RENDER_VARIABLE_SIMPLE(block_damage_multiplier, "Damage between absorption and limit is multiplied by this. Lower is better.")
RENDER_OVERRIDE_LIST(block_damage_multiplier_override, "Overrides for the above for each attack type")
RENDER_VARIABLE_SIMPLE(block_damage_limit, "Damage above this passes right through and is not impacted. Higher is better.")
RENDER_OVERRIDE_LIST(block_damage_limit_override, "Overrides for the above for each attack type.")
RENDER_VARIABLE_SIMPLE(block_stamina_efficiency, "Coefficient for stamina damage dealt to user by damage blocked. Higher is better.")
RENDER_OVERRIDE_LIST(block_stamina_efficiency_override, "Overrides for the above for each attack type.")
RENDER_VARIABLE_SIMPLE(block_stamina_limb_ratio, "The ratio of stamina that is applied to the limb holding this object (if applicable) rather than whole body/chest.")
RENDER_VARIABLE_SIMPLE(block_stamina_buffer_ratio, "The ratio of stamina incurred by chest/whole body that is buffered rather than direct (buffer = your stamina buffer, direct = direct stamina damage like from a disabler.)")
RENDER_VARIABLE_SIMPLE(block_stamina_cost_per_second, "The buffered stamina damage the user incurs per second of block. Lower is better.")
RENDER_ATTACK_TYPES(block_resting_attack_types_anydir, "The kinds of attacks you can block while resting/otherwise knocked to the floor from any direction. can_block_attack_types takes precedence.")
RENDER_ATTACK_TYPES(block_resting_attack_types_directional, "The kinds of attacks you can block wihle resting/otherwise knocked to the floor that are directional only. can_block_attack_types takes precedence.")
RENDER_VARIABLE_SIMPLE(block_resting_stamina_penalty_multiplier, "Multiplier to stamina damage incurred from blocking while downed. Lower is better.")
RENDER_OVERRIDE_LIST(block_resting_stamina_penalty_multiplier, "Overrides for the above for each attack type.")
dat += "</div></table>"
if(parry_data)
dat += "<div class='statusDisplay'><h3>Parry Stats</h3><table style='width:100%'><tr><th>Name/Description</th><th>Value</th></tr>"
RENDER_VARIABLE_SIMPLE(parry_respect_clickdelay, "Whether or not (1 or 0) you can only parry if your attack cooldown isn't in effect.")
RENDER_VARIABLE_SIMPLE(parry_stamina_cost, "Buffered stamina damage incurred by you for parrying with this.")
RENDER_ATTACK_TYPES(parry_attack_types, "Attack types you can parry.")
// parry_flags
dat += ""
RENDER_VARIABLE_SIMPLE(parry_time_windup, "Deciseconds of parry windup.")
RENDER_VARIABLE_SIMPLE(parry_time_spindown, "Deciseconds of parry spindown.")
RENDER_VARIABLE_SIMPLE(parry_time_active, "Deciseconds of active parry window - This is the ONLY time your parry is active.")
RENDER_VARIABLE_SIMPLE(parry_time_windup_visual_override, "Visual effect length override")
RENDER_VARIABLE_SIMPLE(parry_time_spindown_visual_override, "Visual effect length override")
RENDER_VARIABLE_SIMPLE(parry_time_active_visual_override, "Visual effect length override")
RENDER_VARIABLE_SIMPLE(parry_time_perfect, "Deciseconds <b>into the active window</b> considered the 'center' of the perfect period.")
RENDER_VARIABLE_SIMPLE(parry_time_perfect_leeway, "Leeway on both sides of the perfect period's center still considered perfect.")
RENDER_OVERRIDE_LIST(parry_time_perfect_leeway_override, "Override for the above for each attack type")
RENDER_VARIABLE_SIMPLE(parry_imperfect_falloff_percent, "Linear falloff in percent per decisecond for attacks parried outside of perfect window.")
RENDER_OVERRIDE_LIST(parry_imperfect_falloff_percent_override, "Override for the above for each attack type")
RENDER_VARIABLE_SIMPLE(parry_efficiency_perfect, "Efficiency in percentage a parry in the perfect window is considered.")
// parry_data
dat += ""
RENDER_VARIABLE_SIMPLE(parry_efficiency_considered_successful, "Minimum parry efficiency to be considered a successful parry.")
RENDER_VARIABLE_SIMPLE(parry_efficiency_to_counterattack, "Minimum parry efficiency to trigger counterattack effects.")
RENDER_VARIABLE_SIMPLE(parry_max_attacks, "Max attacks parried per parry cycle.")
RENDER_VARIABLE_SIMPLE(parry_effect_icon_state, "Parry effect image name")
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 += "</div></table>"
return dat.Join("")
#undef RENDER_VARIABLE_SIMPLE
#undef RENDER_OVERRIDE_LIST
#undef RENDER_ATTACK_TYPES
#undef RENDER_BLOCK_DIRECTIONS
// MOB PROCS
/**
* Called every life tick to handle blocking/parrying effects.
*/
/mob/living/proc/handle_block_parry(seconds = 1)
if(combat_flags & COMBAT_FLAG_ACTIVE_BLOCKING)
var/datum/block_parry_data/data = return_block_parry_datum(active_block_item.block_parry_data)
adjustStaminaLossBuffered(data.block_stamina_cost_per_second * seconds)
/mob/living/on_item_dropped(obj/item/I)
if(I == active_block_item)
stop_active_blocking()
if(I == active_parry_item)
end_parry_sequence()
return ..()
+35 -35
View File
@@ -1,10 +1,7 @@
/mob/living/proc/run_armor_check(def_zone = null, attack_flag = "melee", absorb_text = "Your armor absorbs the blow!", soften_text = "Your armor softens the blow!", armour_penetration, penetrated_text = "Your armor was penetrated!", silent=FALSE)
/mob/living/proc/run_armor_check(def_zone = null, attack_flag = "melee", absorb_text = "Your armor absorbs the blow!", soften_text = "Your armor softens the blow!", armour_penetration, penetrated_text = "Your armor was penetrated!")
var/armor = getarmor(def_zone, attack_flag)
if(silent)
return max(0, armor - armour_penetration)
//the if "armor" check is because this is used for everything on /living, including humans
if(armor && armour_penetration)
armor = max(0, armor - armour_penetration)
@@ -69,30 +66,22 @@
CRASH("Invalid rediretion mode [redirection_mode]")
/mob/living/bullet_act(obj/item/projectile/P, def_zone)
var/totaldamage = P.damage
var/final_percent = 0
if(P.original != src || P.firer != src) //try to block or reflect the bullet, can't do so when shooting oneself
var/list/returnlist = list()
var/returned = mob_run_block(P, P.damage, "the [P.name]", ATTACK_TYPE_PROJECTILE, P.armour_penetration, P.firer, def_zone, returnlist)
final_percent = returnlist[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE]
if(returned & BLOCK_SHOULD_REDIRECT)
handle_projectile_attack_redirection(P, returnlist[BLOCK_RETURN_REDIRECT_METHOD])
if(returned & BLOCK_REDIRECTED)
return BULLET_ACT_FORCE_PIERCE
if(returned & BLOCK_SUCCESS)
P.on_hit(src, final_percent, def_zone)
P.on_hit(src, 100, def_zone)
return BULLET_ACT_BLOCK
totaldamage = block_calculate_resultant_damage(totaldamage, returnlist)
var/armor = run_armor_check(def_zone, P.flag, null, null, P.armour_penetration, null)
if(!P.nodamage)
apply_damage(totaldamage, P.damage_type, def_zone, armor)
apply_damage(P.damage, P.damage_type, def_zone, armor)
if(P.dismemberment)
check_projectile_dismemberment(P, def_zone)
var/missing = 100 - final_percent
var/armor_ratio = armor * 0.01
if(missing > 0)
final_percent += missing * armor_ratio
return P.on_hit(src, final_percent, def_zone) ? BULLET_ACT_HIT : BULLET_ACT_BLOCK
return P.on_hit(src, armor) ? BULLET_ACT_HIT : BULLET_ACT_BLOCK
/mob/living/proc/check_projectile_dismemberment(obj/item/projectile/P, def_zone)
return 0
@@ -108,6 +97,12 @@
/mob/living/proc/catch_item(obj/item/I, skip_throw_mode_check = FALSE)
return FALSE
/mob/living/proc/embed_item(obj/item/I)
return
/mob/living/proc/can_embed(obj/item/I)
return FALSE
/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
// Throwingdatum can be null if someone had an accident() while slipping with an item in hand.
var/obj/item/I
@@ -116,29 +111,39 @@
I = AM
throwpower = I.throwforce
var/impacting_zone = ran_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest
var/list/block_return = list()
var/total_damage = I.throwforce
if(mob_run_block(AM, throwpower, "\the [AM.name]", ATTACK_TYPE_THROWN, 0, throwingdatum?.thrower, impacting_zone, block_return) & BLOCK_SUCCESS)
if(mob_run_block(AM, throwpower, "\the [AM.name]", ATTACK_TYPE_THROWN, 0, throwingdatum?.thrower, impacting_zone, null) & BLOCK_SUCCESS)
hitpush = FALSE
skipcatch = TRUE
blocked = TRUE
total_damage = block_calculate_resultant_damage(total_damage, block_return)
else if(I && I.throw_speed >= EMBED_THROWSPEED_THRESHOLD && can_embed(I, src) && prob(I.embedding.embed_chance) && !HAS_TRAIT(src, TRAIT_PIERCEIMMUNE) && (!HAS_TRAIT(src, TRAIT_AUTO_CATCH_ITEM) || incapacitated() || get_active_held_item()))
embed_item(I)
hitpush = FALSE
skipcatch = TRUE //can't catch the now embedded item
if(I)
var/nosell_hit = SEND_SIGNAL(I, COMSIG_MOVABLE_IMPACT_ZONE, src, impacting_zone, throwingdatum, FALSE, blocked)
if(nosell_hit)
skipcatch = TRUE
hitpush = FALSE
if(!skipcatch && isturf(I.loc) && catch_item(I))
return TRUE
var/dtype = BRUTE
var/volume = I.get_volume_by_throwforce_and_or_w_class()
SEND_SIGNAL(I, COMSIG_MOVABLE_IMPACT_ZONE, src, impacting_zone)
dtype = I.damtype
if (I.throwforce > 0) //If the weapon's throwforce is greater than zero...
if (I.throwhitsound) //...and throwhitsound is defined...
playsound(loc, I.throwhitsound, volume, 1, -1) //...play the weapon's throwhitsound.
else if(I.hitsound) //Otherwise, if the weapon's hitsound is defined...
playsound(loc, I.hitsound, volume, 1, -1) //...play the weapon's hitsound.
else if(!I.throwhitsound) //Otherwise, if throwhitsound isn't defined...
playsound(loc, 'sound/weapons/genhit.ogg',volume, 1, -1) //...play genhit.ogg.
else if(!I.throwhitsound && I.throwforce > 0) //Otherwise, if the item doesn't have a throwhitsound and has a throwforce greater than zero...
playsound(loc, 'sound/weapons/genhit.ogg', volume, 1, -1)//...play genhit.ogg
if(!I.throwforce)// Otherwise, if the item's throwforce is 0...
playsound(loc, 'sound/weapons/throwtap.ogg', 1, volume, -1)//...play throwtap.ogg.
if(!blocked)
visible_message("<span class='danger'>[src] has been hit by [I].</span>", \
"<span class='userdanger'>You have been hit by [I].</span>")
var/armor = run_armor_check(impacting_zone, "melee", "Your armor has protected your [parse_zone(impacting_zone)].", "Your armor has softened hit to your [parse_zone(impacting_zone)].",I.armour_penetration)
apply_damage(total_damage, dtype, impacting_zone, armor)
apply_damage(I.throwforce, dtype, impacting_zone, armor)
if(I.thrownby)
log_combat(I.thrownby, src, "threw and hit", I)
else
@@ -308,10 +313,8 @@
var/damage = rand(5, 35)
if(M.is_adult)
damage = rand(20, 40)
var/list/block_return = list()
if(mob_run_block(M, damage, "the [M.name]", ATTACK_TYPE_MELEE, null, M, check_zone(M.zone_selected), block_return) & BLOCK_SUCCESS)
if(mob_run_block(M, damage, "the [M.name]", ATTACK_TYPE_MELEE, null, M, check_zone(M.zone_selected), null) & BLOCK_SUCCESS)
return FALSE
damage = block_calculate_resultant_damage(damage, block_return)
if (stat != DEAD)
log_combat(M, src, "attacked")
@@ -327,16 +330,13 @@
M.visible_message("<span class='notice'>\The [M] [M.friendly_verb_continuous] [src]!</span>",
"<span class='notice'>You [M.friendly_verb_simple] [src]!</span>", target = src,
target_message = "<span class='notice'>\The [M] [M.friendly_verb_continuous] you!</span>")
return 0
return FALSE
else
if(HAS_TRAIT(M, TRAIT_PACIFISM))
to_chat(M, "<span class='notice'>You don't want to hurt anyone!</span>")
return FALSE
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
var/list/return_list = list()
if(mob_run_block(M, damage, "the [M.name]", ATTACK_TYPE_MELEE, M.armour_penetration, M, check_zone(M.zone_selected), return_list) & BLOCK_SUCCESS)
return 0
damage = block_calculate_resultant_damage(damage, return_list)
if(mob_run_block(M, rand(M.melee_damage_lower, M.melee_damage_upper), "the [M.name]", ATTACK_TYPE_MELEE, M.armour_penetration, M, check_zone(M.zone_selected), null) & BLOCK_SUCCESS)
return FALSE
if(M.attack_sound)
playsound(loc, M.attack_sound, 50, 1, 1)
M.do_attack_animation(src)
@@ -344,7 +344,7 @@
"<span class='userdanger'>\The [M] [M.attack_verb_continuous] you!</span>", null, COMBAT_MESSAGE_RANGE, null,
M, "<span class='danger'>You [M.attack_verb_simple] [src]!</span>")
log_combat(M, src, "attacked")
return damage
return TRUE
/mob/living/attack_paw(mob/living/carbon/monkey/M)
if (M.a_intent == INTENT_HARM)
-20
View File
@@ -29,26 +29,6 @@
var/mobility_flags = MOBILITY_FLAGS_DEFAULT
// Combat - Blocking/Parrying system
/// Our block_parry_data for unarmed blocks/parries. Currently only used for parrying, as unarmed block isn't implemented yet. YOU MUST RUN [get_block_parry_data(this)] INSTEAD OF DIRECTLY ACCESSING!
var/datum/block_parry_data/block_parry_data = /datum/block_parry_data // defaults to *something* because [combat_flags] dictates whether or not we can unarmed block/parry.
// Blocking
/// The item the user is actively blocking with if any.
var/obj/item/active_block_item
// Parrying
/// Whether or not the user is in the middle of an active parry. Set to [UNARMED_PARRY], [ITEM_PARRY], [MARTIAL_PARRY] if parrying.
var/parrying = FALSE
/// The itme the user is currently parrying with, if any.
var/obj/item/active_parry_item
/// world.time of parry action start
var/parry_start_time = 0
/// Current parry effect.
var/obj/effect/abstract/parry/parry_visual_effect
/// world.time of last parry end
var/parry_end_time_last = 0
/// Successful parries within the current parry cycle. It's a list of efficiency percentages.
var/list/successful_parries
var/confused = 0 //Makes the mob move in random directions.
var/hallucination = 0 //Directly affects how long a mob will hallucinate for
+1 -7
View File
@@ -96,13 +96,7 @@
mobility_flags &= ~MOBILITY_STAND
setMovetype(movement_type | CRAWLING)
if(!lying) //force them on the ground
switch(dir)
if(NORTH, SOUTH)
lying = pick(90, 270)
if(EAST)
lying = 90
else //West
lying = 270
lying = pick(90, 270)
if(has_gravity() && !buckled)
playsound(src, "bodyfall", 20, 1)
else
+4 -15
View File
@@ -3,21 +3,10 @@
update_turf_movespeed(loc)
//Hide typing indicator if we move.
clear_typing_indicator()
update_pixel_shifting(TRUE)
/mob/living/setDir(newdir, ismousemovement)
. = ..()
if(ismousemovement)
update_pixel_shifting()
/mob/living/proc/update_pixel_shifting(moved = FALSE)
if(combat_flags & COMBAT_FLAG_ACTIVE_BLOCKING)
animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, flags = ANIMATION_END_NOW)
else if(moved)
if(is_shifted)
is_shifted = FALSE
pixel_x = get_standard_pixel_x_offset(lying)
pixel_y = get_standard_pixel_y_offset(lying)
if(is_shifted)
is_shifted = FALSE
pixel_x = get_standard_pixel_x_offset(lying)
pixel_y = get_standard_pixel_y_offset(lying)
/mob/living/CanPass(atom/movable/mover, turf/target)
if((mover.pass_flags & PASSMOB))
@@ -1,4 +1,4 @@
/mob/living/silicon/ai/attacked_by(obj/item/I, mob/living/user, def_zone, attackchain_flags = NONE, damage_multiplier = 1)
/mob/living/silicon/ai/attacked_by(obj/item/I, mob/living/user, def_zone)
. = ..()
if(!.)
return FALSE
+31 -31
View File
@@ -3,48 +3,48 @@
#define POWER_RESTORATION_SEARCH_APC 2
#define POWER_RESTORATION_APC_FOUND 3
/mob/living/silicon/ai/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
/mob/living/silicon/ai/Life()
if (stat == DEAD)
return
//I'm not removing that shitton of tabs, unneeded as they are. -- Urist
else //I'm not removing that shitton of tabs, unneeded as they are. -- Urist
//Being dead doesn't mean your temperature never changes
update_gravity(mob_has_gravity())
update_gravity(mob_has_gravity())
handle_status_effects()
handle_status_effects()
if(malfhack && malfhack.aidisabled)
deltimer(malfhacking)
// This proc handles cleanup of screen notifications and
// messenging the client
malfhacked(malfhack)
if(malfhack && malfhack.aidisabled)
deltimer(malfhacking)
// This proc handles cleanup of screen notifications and
// messenging the client
malfhacked(malfhack)
if(isturf(loc) && (QDELETED(eyeobj) || !eyeobj.loc))
view_core()
if(isturf(loc) && (QDELETED(eyeobj) || !eyeobj.loc))
view_core()
if(machine)
machine.check_eye(src)
if(machine)
machine.check_eye(src)
// Handle power damage (oxy)
if(aiRestorePowerRoutine)
// Lost power
adjustOxyLoss(1)
else
// Gain Power
if(getOxyLoss())
adjustOxyLoss(-1)
// Handle power damage (oxy)
if(aiRestorePowerRoutine)
// Lost power
adjustOxyLoss(1)
else
// Gain Power
if(getOxyLoss())
adjustOxyLoss(-1)
if(!lacks_power())
var/area/home = get_area(src)
if(home.powered(EQUIP))
home.use_power(1000, EQUIP)
if(!lacks_power())
var/area/home = get_area(src)
if(home.powered(EQUIP))
home.use_power(1000, EQUIP)
if(aiRestorePowerRoutine >= POWER_RESTORATION_SEARCH_APC)
ai_restore_power()
return
if(aiRestorePowerRoutine >= POWER_RESTORATION_SEARCH_APC)
ai_restore_power()
return
else if(!aiRestorePowerRoutine)
ai_lose_power()
else if(!aiRestorePowerRoutine)
ai_lose_power()
/mob/living/silicon/ai/proc/lacks_power()
var/turf/T = get_turf(src)
@@ -1,5 +1,5 @@
/mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE)
/mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE)
var/hit_percent = (100-blocked)/100
if(!damage || (!forced && hit_percent <= 0))
return 0
+6 -9
View File
@@ -146,11 +146,10 @@
if(possible_chassis[chassis])
AddElement(/datum/element/mob_holder, chassis, 'icons/mob/pai_item_head.dmi', 'icons/mob/pai_item_rh.dmi', 'icons/mob/pai_item_lh.dmi', ITEM_SLOT_HEAD)
/mob/living/silicon/pai/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/silicon/pai/Life()
if(hacking)
process_hack()
return ..()
/mob/living/silicon/pai/proc/process_hack()
@@ -283,19 +282,17 @@
. = ..()
. += "A personal AI in holochassis mode. Its master ID string seems to be [master]."
/mob/living/silicon/pai/PhysicalLife()
. = ..()
/mob/living/silicon/pai/Life()
if(stat == DEAD)
return
if(cable)
if(get_dist(src, cable) > 1)
var/turf/T = get_turf(src.loc)
T.visible_message("<span class='warning'>[src.cable] rapidly retracts back into its spool.</span>", "<span class='italics'>You hear a click and the sound of wire spooling rapidly.</span>")
qdel(src.cable)
cable = null
/mob/living/silicon/pai/BiologicalLife()
if(!(. = ..()))
return
silent = max(silent - 1, 0)
. = ..()
/mob/living/silicon/pai/updatehealth()
if(status_flags & GODMODE)
@@ -1,6 +1,9 @@
/mob/living/silicon/robot/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
/mob/living/silicon/robot/Life()
set invisibility = 0
if (src.notransform)
return
..()
adjustOxyLoss(-10) //we're a robot!
handle_robot_hud_updates()
handle_robot_cell()
@@ -315,7 +315,7 @@
if (getFireLoss() > 0 || getToxLoss() > 0)
if(src == user)
to_chat(user, "<span class='notice'>You start fixing yourself...</span>")
if(!W.use_tool(src, user, 50, 1, skill_gain_mult = TRIVIAL_USE_TOOL_MULT))
if(!W.use_tool(src, user, 50, 1, max_level = JOB_SKILL_TRAINED))
to_chat(user, "<span class='warning'>You need more cable to repair [src]!</span>")
return
adjustFireLoss(-10)
@@ -128,7 +128,7 @@
S.source = get_or_create_estorage(/datum/robot_energy_storage/wrapping_paper)
if(S && S.source)
S.set_custom_materials(null)
S.custom_materials = null
S.is_cyborg = 1
if(I.loc != src)
@@ -259,7 +259,7 @@
var/prev_locked_down = R.locked_down
sleep(1)
flick("[cyborg_base_icon]_transform", R)
R.mob_transforming = TRUE
R.notransform = TRUE
R.SetLockdown(1)
R.anchored = TRUE
sleep(1)
@@ -270,7 +270,7 @@
R.SetLockdown(0)
R.setDir(SOUTH)
R.anchored = FALSE
R.mob_transforming = FALSE
R.notransform = FALSE
R.update_headlamp()
R.notify_ai(NEW_MODULE)
if(R.hud_used)
@@ -340,7 +340,7 @@
/obj/item/organ_storage,
/obj/item/borg/lollipop,
/obj/item/sensor_device,
/obj/item/shockpaddles/cyborg)
/obj/item/twohanded/shockpaddles/cyborg)
emag_modules = list(/obj/item/reagent_containers/borghypo/hacked)
ratvar_modules = list(
/obj/item/clockwork/slab/cyborg/medical,
@@ -359,8 +359,7 @@
"Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekmed"),
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinamed"),
"Eyebot" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "eyebotmed"),
"Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymed"),
"Zoomba" = image(icon = 'icons/mob/robots.dmi', icon_state = "zoomba_med")
"Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymed")
)
var/list/L = list("Medihound" = "medihound", "Medihound Dark" = "medihounddark", "Vale" = "valemed")
for(var/a in L)
@@ -376,8 +375,6 @@
switch(med_borg_icon)
if("Default")
cyborg_base_icon = "medical"
if("Zoomba")
cyborg_base_icon = "zoomba_med"
if("Droid")
cyborg_base_icon = "medical"
cyborg_icon_override = 'modular_citadel/icons/mob/robots.dmi'
@@ -479,8 +476,7 @@
"Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "caneng"),
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinaeng"),
"Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "spidereng"),
"Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyeng"),
"Zoomba" = image(icon = 'icons/mob/robots.dmi', icon_state = "zoomba_engi")
"Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyeng")
)
var/list/L = list("Pup Dozer" = "pupdozer", "Vale" = "valeeng")
for(var/a in L)
@@ -496,8 +492,6 @@
switch(engi_borg_icon)
if("Default")
cyborg_base_icon = "engineer"
if("Zoomba")
cyborg_base_icon = "zoomba_engi"
if("Default - Treads")
cyborg_base_icon = "engi-tread"
special_light_key = "engineer"
@@ -578,8 +572,7 @@
"Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "cansec"),
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinasec"),
"Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "spidersec"),
"Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavysec"),
"Zoomba" = image(icon = 'icons/mob/robots.dmi', icon_state = "zoomba_sec")
"Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavysec")
)
var/list/L = list("K9" = "k9", "Vale" = "valesec", "K9 Dark" = "k9dark")
for(var/a in L)
@@ -595,8 +588,6 @@
switch(sec_borg_icon)
if("Default")
cyborg_base_icon = "sec"
if("Zoomba")
cyborg_base_icon = "zoomba_sec"
if("Default - Treads")
cyborg_base_icon = "sec-tread"
special_light_key = "sec"
@@ -836,7 +827,6 @@
"(Janitor) Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekjan"),
"(Janitor) Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "canjan"),
"(Janitor) Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyjan"),
"Zoomba" = image(icon = 'icons/mob/robots.dmi', icon_state = "zoomba_jani")
)
var/list/L = list("(Service) DarkK9" = "k50", "(Service) Vale" = "valeserv", "(Service) ValeDark" = "valeservdark",
"(Janitor) Scrubpuppy" = "scrubpup")
@@ -851,8 +841,6 @@
service_icons = sortList(service_icons)
var/service_robot_icon = show_radial_menu(R, R , service_icons, custom_check = CALLBACK(src, .proc/check_menu, R), radius = 42, require_near = TRUE)
switch(service_robot_icon)
if("Zoomba")
cyborg_base_icon = "zoomba_jani"
if("(Service) Waitress")
cyborg_base_icon = "service_f"
special_light_key = "service"
@@ -923,7 +911,7 @@
/obj/item/borg/sight/meson,
/obj/item/storage/bag/ore/cyborg,
/obj/item/pickaxe/drill/cyborg,
/obj/item/kinetic_crusher/cyborg,
/obj/item/twohanded/kinetic_crusher/cyborg,
/obj/item/weldingtool/mini,
/obj/item/storage/bag/sheetsnatcher/borg,
/obj/item/t_scanner/adv_mining_scanner,
@@ -956,7 +944,6 @@
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinamin"),
"Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "canmin"),
"Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymin"),
"Zoomba" = image(icon = 'icons/mob/robots.dmi', icon_state = "zoomba_miner")
)
var/list/L = list("Blade" = "blade", "Vale" = "valemine")
for(var/a in L)
@@ -1000,8 +987,6 @@
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
sleeper_overlay = "valeminesleeper"
dogborg = TRUE
if("Zoomba")
cyborg_base_icon = "zoomba_miner"
else
return FALSE
return ..()
@@ -1043,7 +1028,7 @@
/obj/item/extinguisher/mini,
/obj/item/crowbar/cyborg,
/obj/item/reagent_containers/borghypo/syndicate,
/obj/item/shockpaddles/syndicate,
/obj/item/twohanded/shockpaddles/syndicate,
/obj/item/healthanalyzer/advanced,
/obj/item/surgical_drapes/advanced,
/obj/item/retractor,
@@ -396,12 +396,6 @@
return aicamera.selectpicture(user)
/mob/living/silicon/proc/ai_roster()
if(!client)
return
if(world.time < client.crew_manifest_delay)
return
client.crew_manifest_delay = world.time + (1 SECONDS)
var/dat = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Crew Roster</title></head><body><b>Crew Roster:</b><br><br>"
dat += GLOB.data_core.get_manifest()
@@ -33,7 +33,7 @@
/mob/living/silicon/attack_animal(mob/living/simple_animal/M)
. = ..()
if(.)
var/damage = .
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
if(prob(damage))
for(var/mob/living/N in buckled_mobs)
N.DefaultCombatKnockdown(20)
@@ -120,7 +120,6 @@
flash_act(affect_silicon = 1)
/mob/living/silicon/bullet_act(obj/item/projectile/P, def_zone)
var/totaldamage = P.damage
if(P.original != src || P.firer != src) //try to block or reflect the bullet, can't do so when shooting oneself
var/list/returnlist = list()
var/returned = mob_run_block(P, P.damage, "the [P.name]", ATTACK_TYPE_PROJECTILE, P.armour_penetration, P.firer, def_zone, returnlist)
@@ -129,18 +128,22 @@
if(returned & BLOCK_REDIRECTED)
return BULLET_ACT_FORCE_PIERCE
if(returned & BLOCK_SUCCESS)
P.on_hit(src, returnlist[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE], def_zone)
P.on_hit(src, 100, def_zone)
return BULLET_ACT_BLOCK
totaldamage = block_calculate_resultant_damage(totaldamage, returnlist)
if((P.damage_type == BRUTE || P.damage_type == BURN))
adjustBruteLoss(totaldamage)
if((P.damage >= 10) || P.stun || P.knockdown || (P.stamina >= 20))
adjustBruteLoss(P.damage)
if(prob(P.damage*1.5))
for(var/mob/living/M in buckled_mobs)
M.visible_message("<span class='boldwarning'>[M] is knocked off of [src]!</span>",
"<span class='boldwarning'>You are knocked off of [src]!</span>")
unbuckle_mob(M)
M.DefaultCombatKnockdown(40)
if(P.stun || P.knockdown)
for(var/mob/living/M in buckled_mobs)
unbuckle_mob(M)
M.visible_message("<span class='boldwarning'>[M] is knocked off of [src] by the [P]!</span>",
"<span class='boldwarning'>You are knocked off of [src] by the [P]!</span>")
unbuckle_mob(M)
M.DefaultCombatKnockdown(40)
P.on_hit(src, 0, def_zone)
P.on_hit(src)
return BULLET_ACT_HIT
/mob/living/silicon/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash/static)
@@ -94,7 +94,7 @@
/mob/living/simple_animal/attack_animal(mob/living/simple_animal/M)
. = ..()
if(.)
var/damage = .
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
return attack_threshold_check(damage, M.melee_damage_type)
/mob/living/simple_animal/attack_slime(mob/living/simple_animal/slime/M)
@@ -62,7 +62,7 @@
log_reagent("FERMICHEM: [src] has astrally transmitted [message] into [A]")
//Delete the mob if there's no mind! Pay that mob no mind.
/mob/living/simple_animal/astral/PhysicalLife(seconds, times_fired)
. = ..()
if(!mind && !QDELETED(src))
/mob/living/simple_animal/astral/Life()
if(!mind)
qdel(src)
. = ..()
@@ -98,10 +98,6 @@
hud_possible = list(DIAG_STAT_HUD, DIAG_BOT_HUD, DIAG_HUD, DIAG_PATH_HUD = HUD_LIST_LIST) //Diagnostic HUD views
var/commissioned = FALSE // Will other (noncommissioned) bots salute this bot?
var/can_salute = TRUE
var/salute_delay = 60 SECONDS
/mob/living/simple_animal/bot/proc/get_mode()
if(client) //Player bots do not have modes, thus the override. Also an easy way for PDA users/AI to know when a bot is a player.
if(paicard)
@@ -255,14 +251,6 @@
if(!on || client)
return
if(!commissioned && can_salute)
for(var/mob/living/simple_animal/bot/B in get_hearers_in_view(5, get_turf(src)))
if(B.commissioned)
visible_message("<b>[src]</b> performs an elaborate salute for [B]!")
can_salute = FALSE
addtimer(VARSET_CALLBACK(src, can_salute, TRUE), salute_delay)
break
switch(mode) //High-priority overrides are processed first. Bots can do nothing else while under direct command.
if(BOT_RESPONDING) //Called by the AI.
call_mode()
@@ -14,7 +14,7 @@
model = "Cleanbot"
bot_core_type = /obj/machinery/bot_core/cleanbot
window_id = "autoclean"
window_name = "Automatic Station Cleaner v1.3"
window_name = "Automatic Station Cleaner v1.2"
pass_flags = PASSMOB
path_image_color = "#993299"
weather_immunities = list("lava","ash")
@@ -36,62 +36,8 @@
var/next_dest
var/next_dest_loc
var/obj/item/weapon
var/weapon_orig_force = 0
var/chosen_name
var/list/stolen_valor
var/static/list/officers = list("Captain", "Head of Personnel", "Head of Security")
var/static/list/command = list("Captain" = "Cpt.","Head of Personnel" = "Lt.")
var/static/list/security = list("Head of Security" = "Maj.", "Warden" = "Sgt.", "Detective" = "Det.", "Security Officer" = "Officer")
var/static/list/engineering = list("Chief Engineer" = "Chief Engineer", "Station Engineer" = "Engineer", "Atmospherics Technician" = "Technician")
var/static/list/medical = list("Chief Medical Officer" = "C.M.O.", "Medical Doctor" = "M.D.", "Chemist" = "Pharm.D.")
var/static/list/research = list("Research Director" = "Ph.D.", "Roboticist" = "M.S.", "Scientist" = "B.S.")
var/static/list/legal = list("Lawyer" = "Esq.")
var/list/prefixes
var/list/suffixes
/mob/living/simple_animal/bot/cleanbot/proc/deputize(obj/item/W, mob/user)
if(in_range(src, user))
to_chat(user, "<span class='notice'>You attach \the [W] to \the [src].</span>")
user.transferItemToLoc(W, src)
weapon = W
weapon_orig_force = weapon.force
if(!emagged)
weapon.force = weapon.force / 2
add_overlay(image(icon=weapon.lefthand_file,icon_state=weapon.item_state))
/mob/living/simple_animal/bot/cleanbot/proc/update_titles()
var/working_title = ""
for(var/pref in prefixes)
for(var/title in pref)
if(title in stolen_valor)
working_title += pref[title] + " "
if(title in officers)
commissioned = TRUE
break
working_title += chosen_name
for(var/suf in suffixes)
for(var/title in suf)
if(title in stolen_valor)
working_title += " " + suf[title]
break
name = working_title
/mob/living/simple_animal/bot/cleanbot/examine(mob/user)
. = ..()
if(weapon)
. += " <span class='warning'>Is that \a [weapon] taped to it...?</span>"
/mob/living/simple_animal/bot/cleanbot/Initialize()
. = ..()
chosen_name = name
get_targets()
icon_state = "cleanbot[on]"
@@ -99,18 +45,6 @@
access_card.access += J.get_access()
prev_access = access_card.access
stolen_valor = list()
prefixes = list(command, security, engineering)
suffixes = list(research, medical, legal)
/mob/living/simple_animal/bot/cleanbot/Destroy()
if(weapon)
var/atom/Tsec = drop_location()
weapon.force = weapon_orig_force
drop_part(weapon, Tsec)
return ..()
/mob/living/simple_animal/bot/cleanbot/turn_on()
..()
icon_state = "cleanbot[on]"
@@ -123,8 +57,6 @@
/mob/living/simple_animal/bot/cleanbot/bot_reset()
..()
if(weapon && (emagged == 2))
weapon.force = weapon_orig_force
ignore_list = list() //Allows the bot to clean targets it previously ignored due to being unreachable.
target = null
oldloc = null
@@ -134,22 +66,6 @@
text_dehack = "[name]'s software has been reset!"
text_dehack_fail = "[name] does not seem to respond to your repair code!"
/mob/living/simple_animal/bot/cleanbot/Crossed(atom/movable/AM)
. = ..()
zone_selected = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
if(weapon && has_gravity() && ismob(AM))
var/mob/living/carbon/C = AM
if(!istype(C))
return
if(!(C.job in stolen_valor))
stolen_valor += C.job
update_titles()
weapon.attack(C, src)
C.Knockdown(20)
/mob/living/simple_animal/bot/cleanbot/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/card/id)||istype(W, /obj/item/pda))
if(bot_core.allowed(user) && !open && !emagged)
@@ -163,11 +79,6 @@
else
to_chat(user, "<span class='notice'>The [src] doesn't seem to respect your authority.</span>")
else if(istype(W, /obj/item/kitchen/knife) && user.a_intent != INTENT_HARM)
to_chat(user, "<span class='notice'>You start attaching \the [W] to \the [src]...</span>")
if(do_after(user, 25, target = src))
deputize(W, user)
else if(istype(W, /obj/item/mop/advanced))
if(bot_core.allowed(user) && open && !CHECK_BITFIELD(upgrades,UPGRADE_CLEANER_ADVANCED_MOP))
to_chat(user, "<span class='notice'>You replace \the [src] old mop with a new better one!</span>")
@@ -176,15 +87,15 @@
window_name = "Automatic Station Cleaner v2.1 BETA" //New!
qdel(W)
if(!open)
to_chat(user, "<span class='notice'>The [src] access panel is not open!</span>")
to_chat(user, "<span class='notice'>The [src] access pannle is not open!</span>")
return
if(!bot_core.allowed(user))
to_chat(user, "<span class='notice'>The [src] access panel locked off to you!</span>")
to_chat(user, "<span class='notice'>The [src] access pannel locked off to you!</span>")
return
else
to_chat(user, "<span class='notice'>The [src] already has this mop!</span>")
else if(istype(W, /obj/item/broom))
else if(istype(W, /obj/item/twohanded/broom))
if(bot_core.allowed(user) && open && !CHECK_BITFIELD(upgrades,UPGRADE_CLEANER_BROOM))
to_chat(user, "<span class='notice'>You add to \the [src] a broom speeding it up!</span>")
upgrades |= UPGRADE_CLEANER_BROOM
@@ -205,8 +116,6 @@
/mob/living/simple_animal/bot/cleanbot/emag_act(mob/user)
. = ..()
if(emagged == 2)
if(weapon)
weapon.force = weapon_orig_force
if(user)
to_chat(user, "<span class='danger'>[src] buzzes and beeps.</span>")
@@ -1,13 +1,7 @@
//MEDBOT
//MEDBOT PATHFINDING
//MEDBOT ASSEMBLY
#define MEDBOT_PANIC_NONE 0
#define MEDBOT_PANIC_LOW 15
#define MEDBOT_PANIC_MED 35
#define MEDBOT_PANIC_HIGH 55
#define MEDBOT_PANIC_FUCK 70
#define MEDBOT_PANIC_ENDING 90
#define MEDBOT_PANIC_END 100
/mob/living/simple_animal/bot/medbot
name = "\improper Medibot"
@@ -70,13 +64,6 @@
var/upgraded_dispenser_3 //Do we have the nicer chemicals? - replaces bic with sal acid
var/upgraded_dispenser_4 //Do we have the nicer chemicals? - replaces charcoal/toxin with pentetic acid / pentetic jelly
//How panicked we are about being tipped over (why would you do this?)
var/tipped_status = MEDBOT_PANIC_NONE
//The name we got when we were tipped
var/tipper_name
//The last time we were tipped/righted and said a voice line, to avoid spam
var/last_tipping_action_voice = 0
/mob/living/simple_animal/bot/medbot/mysterious
name = "\improper Mysterious Medibot"
desc = "International Medibot of mystery."
@@ -386,91 +373,10 @@
else
return
/mob/living/simple_animal/bot/medbot/proc/tip_over(mob/user)
mobility_flags &= ~MOBILITY_MOVE
playsound(src, 'sound/machines/warning-buzzer.ogg', 50)
user.visible_message("<span class='danger'>[user] tips over [src]!</span>", "<span class='danger'>You tip [src] over!</span>")
mode = BOT_TIPPED
var/matrix/mat = transform
transform = mat.Turn(180)
/mob/living/simple_animal/bot/medbot/proc/set_right(mob/user)
mobility_flags &= MOBILITY_MOVE
var/list/messagevoice
if(user)
user.visible_message("<span class='notice'>[user] sets [src] right-side up!</span>", "<span class='green'>You set [src] right-side up!</span>")
if(user.name == tipper_name)
messagevoice = list("I forgive you." = 'sound/voice/medbot/forgive.ogg')
else
messagevoice = list("Thank you!" = 'sound/voice/medbot/thank_you.ogg', "You are a good person." = 'sound/voice/medbot/youre_good.ogg')
else
visible_message("<span class='notice'>[src] manages to writhe wiggle enough to right itself.</span>")
messagevoice = list("Fuck you." = 'sound/voice/medbot/fuck_you.ogg', "Your behavior has been reported, have a nice day." = 'sound/voice/medbot/reported.ogg')
tipper_name = null
if(world.time > last_tipping_action_voice + 15 SECONDS)
last_tipping_action_voice = world.time
var/message = pick(messagevoice)
speak(message)
playsound(src, messagevoice[message], 70)
tipped_status = MEDBOT_PANIC_NONE
mode = BOT_IDLE
transform = matrix()
// if someone tipped us over, check whether we should ask for help or just right ourselves eventually
/mob/living/simple_animal/bot/medbot/proc/handle_panic()
tipped_status++
var/list/messagevoice
switch(tipped_status)
if(MEDBOT_PANIC_LOW)
messagevoice = list("I require assistance." = 'sound/voice/medbot/i_require_asst.ogg')
if(MEDBOT_PANIC_MED)
messagevoice = list("Please put me back." = 'sound/voice/medbot/please_put_me_back.ogg')
if(MEDBOT_PANIC_HIGH)
messagevoice = list("Please, I am scared!" = 'sound/voice/medbot/please_im_scared.ogg')
if(MEDBOT_PANIC_FUCK)
messagevoice = list("I don't like this, I need help!" = 'sound/voice/medbot/dont_like.ogg', "This hurts, my pain is real!" = 'sound/voice/medbot/pain_is_real.ogg')
if(MEDBOT_PANIC_ENDING)
messagevoice = list("Is this the end?" = 'sound/voice/medbot/is_this_the_end.ogg', "Nooo!" = 'sound/voice/medbot/nooo.ogg')
if(MEDBOT_PANIC_END)
speak("PSYCH ALERT: Crewmember [tipper_name] recorded displaying antisocial tendencies torturing bots in [get_area(src)]. Please schedule psych evaluation.", radio_channel)
set_right() // strong independent medbot
if(prob(tipped_status))
do_jitter_animation(tipped_status * 0.1)
if(messagevoice)
var/message = pick(messagevoice)
speak(message)
playsound(src, messagevoice[message], 70)
else if(prob(tipped_status * 0.2))
playsound(src, 'sound/machines/warning-buzzer.ogg', 30, extrarange=-2)
/mob/living/simple_animal/bot/medbot/examine(mob/user)
. = ..()
if(tipped_status == MEDBOT_PANIC_NONE)
return
switch(tipped_status)
if(MEDBOT_PANIC_NONE to MEDBOT_PANIC_LOW)
. += "It appears to be tipped over, and is quietly waiting for someone to set it right."
if(MEDBOT_PANIC_LOW to MEDBOT_PANIC_MED)
. += "It is tipped over and requesting help."
if(MEDBOT_PANIC_MED to MEDBOT_PANIC_HIGH)
. += "They are tipped over and appear visibly distressed." // now we humanize the medbot as a they, not an it
if(MEDBOT_PANIC_HIGH to MEDBOT_PANIC_FUCK)
. += "<span class='warning'>They are tipped over and visibly panicking!</span>"
if(MEDBOT_PANIC_FUCK to INFINITY)
. += "<span class='warning'><b>They are freaking out from being tipped over!</b></span>"
/mob/living/simple_animal/bot/medbot/handle_automated_action()
if(!..())
return
if(mode == BOT_TIPPED)
handle_panic()
return
if(mode == BOT_HEALING)
return
@@ -486,14 +392,10 @@
if(QDELETED(patient))
if(!shut_up && prob(1))
if(emagged && prob(30))
var/list/i_need_scissors = list('sound/voice/medbot/fuck_you.ogg', 'sound/voice/medbot/turn_off.ogg', 'sound/voice/medbot/im_different.ogg', 'sound/voice/medbot/close.ogg', 'sound/voice/medbot/shindemashou.ogg')
playsound(src, pick(i_need_scissors), 70)
else
var/list/messagevoice = list("Radar, put a mask on!" = 'sound/voice/medbot/radar.ogg',"There's always a catch, and I'm the best there is." = 'sound/voice/medbot/catch.ogg',"I knew it, I should've been a plastic surgeon." = 'sound/voice/medbot/surgeon.ogg',"What kind of medbay is this? Everyone's dropping like flies." = 'sound/voice/medbot/flies.ogg',"Delicious!" = 'sound/voice/medbot/delicious.ogg', "Why are we still here? Just to suffer?" = 'sound/voice/medbot/why.ogg')
var/message = pick(messagevoice)
speak(message)
playsound(src, messagevoice[message], 50)
var/list/messagevoice = list("Radar, put a mask on!" = 'sound/voice/medbot/radar.ogg',"There's always a catch, and I'm the best there is." = 'sound/voice/medbot/catch.ogg',"I knew it, I should've been a plastic surgeon." = 'sound/voice/medbot/surgeon.ogg',"What kind of medbay is this? Everyone's dropping like flies." = 'sound/voice/medbot/flies.ogg',"Delicious!" = 'sound/voice/medbot/delicious.ogg')
var/message = pick(messagevoice)
speak(message)
playsound(loc, messagevoice[message], 50, 0)
var/scan_range = (stationary_mode ? 1 : DEFAULT_SCAN_RANGE) //If in stationary mode, scan range is limited to adjacent patients.
patient = scan(/mob/living/carbon/human, oldpatient, scan_range)
oldpatient = patient
@@ -604,27 +506,6 @@
/mob/living/simple_animal/bot/medbot/proc/get_healchem_toxin(mob/M)
return HAS_TRAIT(M, TRAIT_TOXINLOVER)? treatment_tox_toxlover : treatment_tox
/mob/living/simple_animal/bot/medbot/attack_hand(mob/living/carbon/human/H)
if(H.a_intent == INTENT_DISARM && mode != BOT_TIPPED)
H.visible_message("<span class='danger'>[H] begins tipping over [src].</span>", "<span class='warning'>You begin tipping over [src]...</span>")
if(world.time > last_tipping_action_voice + 15 SECONDS)
last_tipping_action_voice = world.time // message for tipping happens when we start interacting, message for righting comes after finishing
var/list/messagevoice = list("Hey, wait..." = 'sound/voice/medbot/hey_wait.ogg',"Please don't..." = 'sound/voice/medbot/please_dont.ogg',"I trusted you..." = 'sound/voice/medbot/i_trusted_you.ogg', "Nooo..." = 'sound/voice/medbot/nooo.ogg', "Oh fuck-" = 'sound/voice/medbot/oh_fuck.ogg')
var/message = pick(messagevoice)
speak(message)
playsound(src, messagevoice[message], 70, FALSE)
if(do_after(H, 3 SECONDS, target=src))
tip_over(H)
else if(H.a_intent == INTENT_HELP && mode == BOT_TIPPED)
H.visible_message("<span class='notice'>[H] begins righting [src].</span>", "<span class='notice'>You begin righting [src]...</span>")
if(do_after(H, 3 SECONDS, target=src))
set_right(H)
else
..()
/mob/living/simple_animal/bot/medbot/UnarmedAttack(atom/A)
if(iscarbon(A))
var/mob/living/carbon/C = A
@@ -783,11 +664,3 @@
/obj/machinery/bot_core/medbot
req_one_access = list(ACCESS_MEDICAL, ACCESS_ROBOTICS)
#undef MEDBOT_PANIC_NONE
#undef MEDBOT_PANIC_LOW
#undef MEDBOT_PANIC_MED
#undef MEDBOT_PANIC_HIGH
#undef MEDBOT_PANIC_FUCK
#undef MEDBOT_PANIC_ENDING
#undef MEDBOT_PANIC_END
@@ -115,14 +115,13 @@
Read_Memory()
. = ..()
/mob/living/simple_animal/pet/cat/Runtime/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/pet/cat/Runtime/Life()
if(!cats_deployed && SSticker.current_state >= GAME_STATE_SETTING_UP)
Deploy_The_Cats()
if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved)
Write_Memory()
memory_saved = TRUE
..()
/mob/living/simple_animal/pet/cat/Runtime/make_babies()
var/mob/baby = ..()
@@ -178,9 +177,7 @@
gold_core_spawnable = NO_SPAWN
unique_pet = TRUE
/mob/living/simple_animal/pet/cat/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/pet/cat/Life()
if(!stat && !buckled && !client)
if(prob(1))
emote("me", EMOTE_VISIBLE, pick("stretches out for a belly rub.", "wags its tail.", "lies down."))
@@ -272,9 +269,8 @@
to_chat(src, "<span class='notice'>Your name is now <b>\"new_name\"</b>!</span>")
name = new_name
/mob/living/simple_animal/pet/cat/cak/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/pet/cat/cak/Life()
..()
if(stat)
return
if(health < maxHealth)
@@ -27,9 +27,8 @@
var/obj/item/inventory_mask
gold_core_spawnable = FRIENDLY_SPAWN
/mob/living/simple_animal/crab/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/crab/Life()
..()
//CRAB movement
if(!ckey && !stat)
if(isturf(loc) && !resting && !buckled) //This is so it only moves if it's not inside a closet, gentics machine, etc.
@@ -366,12 +366,11 @@
RemoveElement(/datum/element/mob_holder, held_icon)
AddElement(/datum/element/mob_holder, "old_corgi")
/mob/living/simple_animal/pet/dog/corgi/Ian/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/pet/dog/corgi/Ian/Life()
if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved)
Write_Memory(FALSE)
memory_saved = TRUE
..()
/mob/living/simple_animal/pet/dog/corgi/Ian/death()
if(!memory_saved)
@@ -420,9 +419,8 @@
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/mob/living/simple_animal/pet/dog/corgi/Ian/BiologicalLife()
if(!(. = ..()))
return
/mob/living/simple_animal/pet/dog/corgi/Ian/Life()
..()
//Feeding, chasing food, FOOOOODDDD
if(!stat && CHECK_MULTIPLE_BITFIELDS(mobility_flags, MOBILITY_STAND|MOBILITY_MOVE) && !buckled)
@@ -492,9 +490,8 @@
nofur = TRUE
unique_pet = TRUE
/mob/living/simple_animal/pet/dog/corgi/narsie/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/pet/dog/corgi/narsie/Life()
..()
for(var/mob/living/simple_animal/pet/P in range(1, src))
if(P != src && prob(5))
visible_message("<span class='warning'>[src] devours [P]!</span>", \
@@ -618,9 +615,8 @@
return
..()
/mob/living/simple_animal/pet/dog/corgi/Lisa/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/pet/dog/corgi/Lisa/Life()
..()
make_babies()
@@ -632,9 +628,8 @@
setDir(i)
sleep(1)
/mob/living/simple_animal/pet/dog/pug/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/pet/dog/pug/Life()
..()
if(!stat && CHECK_MULTIPLE_BITFIELDS(mobility_flags, MOBILITY_STAND|MOBILITY_MOVE) && !buckled)
if(prob(1))
emote("me", EMOTE_VISIBLE, pick("chases its tail."))
@@ -19,7 +19,7 @@
return 0
/mob/living/simple_animal/drone/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, clothing_check = FALSE, list/return_warning)
/mob/living/simple_animal/drone/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE)
switch(slot)
if(SLOT_HEAD)
if(head)
@@ -33,12 +33,11 @@
stop_automated_movement_when_pulled = 1
blood_volume = BLOOD_VOLUME_NORMAL
var/obj/item/udder/udder = null
var/datum/reagent/milk_reagent = /datum/reagent/consumable/milk
footstep_type = FOOTSTEP_MOB_SHOE
/mob/living/simple_animal/hostile/retaliate/goat/Initialize(/datum/reagent/milk_reagent)
udder = new (null, milk_reagent)
/mob/living/simple_animal/hostile/retaliate/goat/Initialize()
udder = new()
. = ..()
/mob/living/simple_animal/hostile/retaliate/goat/Destroy()
@@ -46,10 +45,9 @@
udder = null
return ..()
/mob/living/simple_animal/hostile/retaliate/goat/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
if(stat == CONSCIOUS)
/mob/living/simple_animal/hostile/retaliate/goat/Life()
. = ..()
if(.)
//chance to go crazy and start wacking stuff
if(!enemies.len && prob(1))
Retaliate()
@@ -58,7 +56,8 @@
enemies = list()
LoseTarget()
src.visible_message("<span class='notice'>[src] calms down.</span>")
udder.generateMilk(milk_reagent)
if(stat == CONSCIOUS)
udder.generateMilk()
eat_plants()
if(!pulledby)
for(var/direction in shuffle(list(1,2,4,8,5,6,9,10)))
@@ -138,14 +137,13 @@
health = 50
maxHealth = 50
var/obj/item/udder/udder = null
var/datum/reagent/milk_reagent = /datum/reagent/consumable/milk
gold_core_spawnable = FRIENDLY_SPAWN
blood_volume = BLOOD_VOLUME_NORMAL
footstep_type = FOOTSTEP_MOB_SHOE
/mob/living/simple_animal/cow/Initialize()
udder = new(null, milk_reagent)
udder = new()
. = ..()
/mob/living/simple_animal/cow/Destroy()
@@ -160,11 +158,10 @@
else
return ..()
/mob/living/simple_animal/cow/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/cow/Life()
. = ..()
if(stat == CONSCIOUS)
udder.generateMilk(milk_reagent)
udder.generateMilk()
/mob/living/simple_animal/cow/attack_hand(mob/living/carbon/M)
if(!stat && M.a_intent == INTENT_DISARM && icon_state != icon_dead)
@@ -192,19 +189,6 @@
else
..()
//Wisdom cow, speaks and bestows great wisdoms
/mob/living/simple_animal/cow/wisdom
name = "wisdom cow"
desc = "Known for its wisdom, shares it with all"
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/wisdomcow = 1) //truly the best meat
gold_core_spawnable = FALSE
speak_chance = 30 //the cow is eager to share its wisdom!
milk_reagent = /datum/reagent/medicine/liquid_wisdom
/mob/living/simple_animal/cow/wisdom/Initialize()
. = ..()
speak = GLOB.wisdoms //Done here so it's setup properly
/mob/living/simple_animal/chick
name = "\improper chick"
desc = "Adorable! They make such a racket though."
@@ -245,8 +229,9 @@
pixel_x = rand(-6, 6)
pixel_y = rand(0, 10)
/mob/living/simple_animal/chick/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
/mob/living/simple_animal/chick/Life()
. =..()
if(!.)
return
if(!stat && !ckey)
amount_grown += rand(1,2)
@@ -254,9 +239,8 @@
new /mob/living/simple_animal/chicken(src.loc)
qdel(src)
/mob/living/simple_animal/chick/holo/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/chick/holo/Life()
..()
amount_grown = 0
/mob/living/simple_animal/chicken
@@ -329,8 +313,9 @@
else
..()
/mob/living/simple_animal/chicken/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
/mob/living/simple_animal/chicken/Life()
. =..()
if(!.)
return
if((!stat && prob(3) && eggsleft > 0) && egg_type)
visible_message("<span class='alertalien'>[src] [pick(layMessage)]</span>")
@@ -403,8 +388,9 @@
. = ..()
++kiwi_count
/mob/living/simple_animal/kiwi/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
/mob/living/simple_animal/kiwi/Life()
. =..()
if(!.)
return
if((!stat && prob(3) && eggsleft > 0) && egg_type)
visible_message("[src] [pick(layMessage)]")
@@ -477,8 +463,9 @@
pixel_x = rand(-6, 6)
pixel_y = rand(0, 10)
/mob/living/simple_animal/babyKiwi/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
/mob/living/simple_animal/babyKiwi/Life()
. =..()
if(!.)
return
if(!stat && !ckey)
amount_grown += rand(1,2)
@@ -494,16 +481,14 @@
/obj/item/udder
name = "udder"
/obj/item/udder/Initialize(loc, milk_reagent)
if(!milk_reagent)
milk_reagent = /datum/reagent/consumable/milk
/obj/item/udder/Initialize()
create_reagents(50, NONE, NO_REAGENTS_VALUE)
reagents.add_reagent(milk_reagent, 20)
reagents.add_reagent(/datum/reagent/consumable/milk, 20)
. = ..()
/obj/item/udder/proc/generateMilk(datum/reagent/milk_reagent)
/obj/item/udder/proc/generateMilk()
if(prob(5))
reagents.add_reagent(milk_reagent, rand(5, 10))
reagents.add_reagent(/datum/reagent/consumable/milk, rand(5, 10))
/obj/item/udder/proc/milkAnimal(obj/O, mob/user)
var/obj/item/reagent_containers/glass/G = O
@@ -544,4 +529,4 @@
health = 75
maxHealth = 75
blood_volume = BLOOD_VOLUME_NORMAL
footstep_type = FOOTSTEP_MOB_SHOE
footstep_type = FOOTSTEP_MOB_SHOE
@@ -21,6 +21,3 @@
response_harm_simple = "kick"
gold_core_spawnable = FRIENDLY_SPAWN
footstep_type = FOOTSTEP_MOB_CLAW
/mob/living/simple_animal/pet/redpanda/stinky
name = "Stinky"
@@ -72,8 +72,8 @@
qdel(src)
//low regen over time
/mob/living/simple_animal/pet/plushie/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
/mob/living/simple_animal/pet/plushie/Life()
if(stat)
return
if(health < maxHealth)
heal_overall_damage(5) //Decent life regen, they're not able to hurt anyone so this shouldn't be an issue (butterbear for reference has 10 regen)
@@ -160,7 +160,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
to_chat(src, "<span class='notice'>Your new name <span class='name'>[new_name]</span> anchors itself in your mind.</span>")
fully_replace_character_name(null, new_name)
/mob/living/simple_animal/hostile/guardian/PhysicalLife() //Dies if the summoner dies
/mob/living/simple_animal/hostile/guardian/Life() //Dies if the summoner dies
. = ..()
update_health_hud() //we need to update all of our health displays to match our summoner and we can't practically give the summoner a hook to do it
med_hud_set_health()
@@ -19,9 +19,8 @@
. = ..()
stealthcooldown = 0
/mob/living/simple_animal/hostile/guardian/assassin/PhysicalLife()
if(!(. = ..()))
return
/mob/living/simple_animal/hostile/guardian/assassin/Life()
. = ..()
updatestealthalert()
if(loc == summoner && toggle)
ToggleMode(0)
@@ -11,9 +11,8 @@
var/charging = 0
var/obj/screen/alert/chargealert
/mob/living/simple_animal/hostile/guardian/charger/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/hostile/guardian/charger/Life()
. = ..()
if(ranged_cooldown <= world.time)
if(!chargealert)
chargealert = throw_alert("charge", /obj/screen/alert/cancharge)
@@ -50,7 +50,7 @@
return 1
return 0
/mob/living/simple_animal/hostile/guardian/dextrous/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, clothing_check = FALSE, list/return_warning)
/mob/living/simple_animal/hostile/guardian/dextrous/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE)
switch(slot)
if(SLOT_GENERC_DEXTROUS_STORAGE)
if(internal_storage)
@@ -13,9 +13,8 @@
tech_fluff_string = "<span class='holoparasite'>Boot sequence complete. Crowd control modules activated. Holoparasite swarm online.</span>"
carp_fluff_string = "<span class='holoparasite'>CARP CARP CARP! You caught one! OH GOD, EVERYTHING'S ON FIRE. Except you and the fish.</span>"
/mob/living/simple_animal/hostile/guardian/fire/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/hostile/guardian/fire/Life()
. = ..()
if(summoner)
summoner.ExtinguishMob()
summoner.adjust_fire_stacks(-20)
@@ -125,7 +125,7 @@ mob/living/simple_animal/hostile/bear/butter //The mighty companion to Cak. Seve
attack_verb_continuous = "slaps"
attack_verb_simple = "slap"
/mob/living/simple_animal/hostile/bear/butter/BiologicalLife(seconds, times_fired) //Heals butter bear really fast when he takes damage.
/mob/living/simple_animal/hostile/bear/butter/Life() //Heals butter bear really fast when he takes damage.
if(stat)
return
if(health < maxHealth)
@@ -47,9 +47,8 @@
if(regen_amount)
regen_cooldown = world.time + REGENERATION_DELAY
/mob/living/simple_animal/hostile/carp/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/hostile/carp/Life()
. = ..()
if(regen_amount && regen_cooldown < world.time)
heal_overall_damage(regen_amount)
@@ -67,10 +67,11 @@
foes = null
return ..()
/mob/living/simple_animal/hostile/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
/mob/living/simple_animal/hostile/Life()
. = ..()
if(!.) //dead
walk(src, 0) //stops walking
return
return 0
/mob/living/simple_animal/hostile/handle_automated_action()
if(AIStatus == AI_OFF)
@@ -116,7 +117,7 @@
Move(get_step(src,chosen_dir))
face_atom(target) //Looks better if they keep looking at you when dodging
/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1)
/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user)
if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user)
FindTarget(list(user), 1)
return ..()
@@ -23,12 +23,13 @@
deathmessage = "vanishes into thin air! It was a fake!"
has_field_of_vision = FALSE //not meant to be played anyway.
/mob/living/simple_animal/hostile/illusion/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/hostile/illusion/Life()
..()
if(world.time > life_span)
death()
/mob/living/simple_animal/hostile/illusion/proc/Copy_Parent(mob/living/original, life = 50, hp = 100, damage = 0, replicate = 0 )
appearance = original.appearance
parent_mob = original
@@ -166,9 +166,8 @@
if(!hopping)
Hop()
/mob/living/simple_animal/hostile/jungle/leaper/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/hostile/jungle/leaper/Life()
. = ..()
update_icons()
/mob/living/simple_animal/hostile/jungle/leaper/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
@@ -198,7 +197,7 @@
hopping = TRUE
density = FALSE
pass_flags |= PASSMOB
mob_transforming = TRUE
notransform = TRUE
var/turf/new_turf = locate((target.x + rand(-3,3)),(target.y + rand(-3,3)),target.z)
if(player_hop)
new_turf = get_turf(target)
@@ -210,7 +209,7 @@
/mob/living/simple_animal/hostile/jungle/leaper/proc/FinishHop()
density = TRUE
mob_transforming = FALSE
notransform = FALSE
pass_flags &= ~PASSMOB
hopping = FALSE
playsound(src.loc, 'sound/effects/meteorimpact.ogg', 100, 1)
@@ -221,7 +220,7 @@
/mob/living/simple_animal/hostile/jungle/leaper/proc/BellyFlop()
var/turf/new_turf = get_turf(target)
hopping = TRUE
mob_transforming = TRUE
notransform = TRUE
new /obj/effect/temp_visual/leaper_crush(new_turf)
addtimer(CALLBACK(src, .proc/BellyFlopHop, new_turf), 30)
@@ -232,7 +231,7 @@
/mob/living/simple_animal/hostile/jungle/leaper/proc/Crush()
hopping = FALSE
density = TRUE
mob_transforming = FALSE
notransform = FALSE
playsound(src, 'sound/effects/meteorimpact.ogg', 200, 1)
for(var/mob/living/L in orange(1, src))
L.adjustBruteLoss(35)
@@ -27,9 +27,8 @@
footstep_type = FOOTSTEP_MOB_CLAW
/mob/living/simple_animal/hostile/jungle/mega_arachnid/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/hostile/jungle/mega_arachnid/Life()
..()
if(target && ranged_cooldown > world.time && iscarbon(target))
var/mob/living/carbon/C = target
if(!C.legcuffed && C.health < 50)
@@ -41,6 +40,7 @@
minimum_distance = 0
alpha = 255
/mob/living/simple_animal/hostile/jungle/mega_arachnid/Aggro()
..()
alpha = 255
@@ -65,9 +65,8 @@ 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
/mob/living/simple_animal/hostile/megafauna/bubblegum/Life()
..()
move_to_delay = clamp(round((health/maxHealth) * 10), 3, 10)
/mob/living/simple_animal/hostile/megafauna/bubblegum/OpenFire()
@@ -731,7 +731,7 @@ Difficulty: Very Hard
/obj/structure/closet/stasis/Entered(atom/A)
if(isliving(A) && holder_animal)
var/mob/living/L = A
L.mob_transforming = 1
L.notransform = 1
ADD_TRAIT(L, TRAIT_MUTE, STASIS_MUTE)
L.status_flags |= GODMODE
L.mind.transfer_to(holder_animal)
@@ -744,7 +744,7 @@ Difficulty: Very Hard
for(var/mob/living/L in src)
REMOVE_TRAIT(L, TRAIT_MUTE, STASIS_MUTE)
L.status_flags &= ~GODMODE
L.mob_transforming = 0
L.notransform = 0
if(holder_animal)
holder_animal.mind.transfer_to(L)
L.mind.RemoveSpell(/obj/effect/proc_holder/spell/targeted/exit_possession)
@@ -88,10 +88,9 @@ Difficulty: Normal
/mob/living/simple_animal/hostile/megafauna/hierophant/spawn_crusher_loot()
new /obj/item/crusher_trophy/vortex_talisman(get_turf(spawned_beacon))
/mob/living/simple_animal/hostile/megafauna/hierophant/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
if(spawned_beacon && !QDELETED(spawned_beacon) && !client)
/mob/living/simple_animal/hostile/megafauna/hierophant/Life()
. = ..()
if(. && spawned_beacon && !QDELETED(spawned_beacon) && !client)
if(target || loc == spawned_beacon.loc)
timeout_time = initial(timeout_time)
else
@@ -159,7 +159,7 @@
var/client/C = L.client
SSmedals.UnlockMedal("Boss [BOSS_KILL_MEDAL]", C)
SSmedals.UnlockMedal("[medaltype] [BOSS_KILL_MEDAL]", C)
if(crusher_kill && istype(L.get_active_held_item(), /obj/item/kinetic_crusher))
if(crusher_kill && istype(L.get_active_held_item(), /obj/item/twohanded/kinetic_crusher))
SSmedals.UnlockMedal("[medaltype] [BOSS_KILL_MEDAL_CRUSHER]", C)
SSmedals.SetScore(BOSS_SCORE, C, 1)
SSmedals.SetScore(score_type, C, 1)
@@ -73,13 +73,14 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa
step(R, ddir) //Step the swarmers, instead of spawning them there, incase the turf is solid
/mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
var/createtype = GetUncappedAISwarmerType()
if(createtype && world.time > swarmer_spawn_cooldown && GLOB.AISwarmers.len < (GetTotalAISwarmerCap()*0.5))
swarmer_spawn_cooldown = world.time + swarmer_spawn_cooldown_amt
new createtype(loc)
/mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon/Life()
. = ..()
if(.)
var/createtype = GetUncappedAISwarmerType()
if(createtype && world.time > swarmer_spawn_cooldown && GLOB.AISwarmers.len < (GetTotalAISwarmerCap()*0.5))
swarmer_spawn_cooldown = world.time + swarmer_spawn_cooldown_amt
new createtype(loc)
/mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
. = ..()
@@ -118,9 +118,8 @@ GLOBAL_LIST_INIT(protected_objects, list(/obj/structure/table, /obj/structure/ca
overlay_googly_eyes = FALSE
CopyObject(copy, creator, destroy_original)
/mob/living/simple_animal/hostile/mimic/copy/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/hostile/mimic/copy/Life()
..()
if(idledamage && !target && !ckey) //Objects eventually revert to normal if no one is around to terrorize
adjustBruteLoss(1)
for(var/mob/living/M in contents) //a fix for animated statues from the flesh to stone spell
@@ -88,9 +88,8 @@
wanted_objects = list(/obj/item/pen/survival, /obj/item/stack/ore/diamond)
field_of_vision_type = FOV_270_DEGREES //Obviously, it's one eyeball.
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/Life()
. = ..()
if(stat == CONSCIOUS)
consume_bait()
@@ -103,7 +103,7 @@ IGNORE_PROC_IF_NOT_TARGET(attack_slime)
return BULLET_ACT_FORCE_PIERCE
return ..()
/mob/living/simple_animal/hostile/asteroid/curseblob/attacked_by(obj/item/I, mob/living/L, attackchain_flags = NONE, damage_multiplier = 1)
/mob/living/simple_animal/hostile/asteroid/curseblob/attacked_by(obj/item/I, mob/living/L)
if(L != set_target)
L.changeNext_move(I.click_delay) //pre_attacked_by not called
return
@@ -97,8 +97,9 @@
if(CALL_CHILDREN)
call_children()
/mob/living/simple_animal/hostile/asteroid/elite/broodmother/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
/mob/living/simple_animal/hostile/asteroid/elite/broodmother/Life()
. = ..()
if(!.) //Checks if they are dead as a rock.
return
if(health < maxHealth * 0.5 && rand_tent < world.time)
rand_tent = world.time + 30

Some files were not shown because too many files have changed in this diff Show More