* [READY] Several fixes/changes to mood, longterm mood effects, beauty component (#36344) cl Floyd / Qustinnus del: Removes short-term effects of mood add; Adds long-term effects of mood by implementing sanity which goes up with good mood, down with bad mood, but takes time to change. Your sanity can be seen as your average mood in the recent past. All effects of moods are now covered by this system add: Beauty component, currently only attached to cleanables, but you could attach it to any atom/movable and make them pretty/ugly, affecting mood of anyone in the room. refactor: Removes the original way of adding mood events, uses signals properly instead. fix: Cleanables "giving" area's free beauty during initialization fix: Fixes some events not clearing properly /cl Fixes #36444 From now on mood no longer affects you directly, instead it decides whether your sanity goes up or down, when your sanity gets too low you will get the effects of what mood did before. This means getting hit with bad moods due to being attacked while not mean you are doomed anymore, and you get a large timeframe to get away and just fix your mood later. I also added the beauty component, you could add this to any object and it would either make a room prettier or uglier, comparable to DF or Rimworld. You could add traits to make certain people ugly, for example. * [READY] Several fixes/changes to mood, longterm mood effects, beauty component
877 lines
27 KiB
Plaintext
877 lines
27 KiB
Plaintext
/mob/living/carbon
|
|
blood_volume = BLOOD_VOLUME_NORMAL
|
|
|
|
/mob/living/carbon/Initialize()
|
|
. = ..()
|
|
create_reagents(1000)
|
|
update_body_parts() //to update the carbon's new bodyparts appearance
|
|
GLOB.carbon_list += src
|
|
|
|
/mob/living/carbon/Destroy()
|
|
//This must be done first, so the mob ghosts correctly before DNA etc is nulled
|
|
. = ..()
|
|
|
|
QDEL_LIST(internal_organs)
|
|
QDEL_LIST(stomach_contents)
|
|
QDEL_LIST(bodyparts)
|
|
QDEL_LIST(implants)
|
|
remove_from_all_data_huds()
|
|
QDEL_NULL(dna)
|
|
GLOB.carbon_list -= src
|
|
|
|
/mob/living/carbon/relaymove(mob/user, direction)
|
|
if(user in src.stomach_contents)
|
|
if(prob(40))
|
|
if(prob(25))
|
|
audible_message("<span class='warning'>You hear something rumbling inside [src]'s stomach...</span>", \
|
|
"<span class='warning'>You hear something rumbling.</span>", 4,\
|
|
"<span class='userdanger'>Something is rumbling inside your stomach!</span>")
|
|
var/obj/item/I = user.get_active_held_item()
|
|
if(I && I.force)
|
|
var/d = rand(round(I.force / 4), I.force)
|
|
var/obj/item/bodypart/BP = get_bodypart(BODY_ZONE_CHEST)
|
|
if(BP.receive_damage(d, 0))
|
|
update_damage_overlays()
|
|
visible_message("<span class='danger'>[user] attacks [src]'s stomach wall with the [I.name]!</span>", \
|
|
"<span class='userdanger'>[user] attacks your stomach wall with the [I.name]!</span>")
|
|
playsound(user.loc, 'sound/effects/attackblob.ogg', 50, 1)
|
|
|
|
if(prob(src.getBruteLoss() - 50))
|
|
for(var/atom/movable/A in stomach_contents)
|
|
A.forceMove(drop_location())
|
|
stomach_contents.Remove(A)
|
|
src.gib()
|
|
|
|
|
|
/mob/living/carbon/swap_hand(held_index)
|
|
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)
|
|
var/obj/screen/inventory/hand/H
|
|
H = hud_used.hand_slots["[oindex]"]
|
|
if(H)
|
|
H.update_icon()
|
|
H = hud_used.hand_slots["[held_index]"]
|
|
if(H)
|
|
H.update_icon()
|
|
|
|
|
|
/mob/living/carbon/activate_hand(selhand) //l/r OR 1-held_items.len
|
|
if(!selhand)
|
|
selhand = (active_hand_index % held_items.len)+1
|
|
|
|
if(istext(selhand))
|
|
selhand = lowertext(selhand)
|
|
if(selhand == "right" || selhand == "r")
|
|
selhand = 2
|
|
if(selhand == "left" || selhand == "l")
|
|
selhand = 1
|
|
|
|
if(selhand != active_hand_index)
|
|
swap_hand(selhand)
|
|
else
|
|
mode() // Activate held item
|
|
|
|
/mob/living/carbon/attackby(obj/item/I, mob/user, params)
|
|
if(lying && surgeries.len)
|
|
if(user != src && user.a_intent == INTENT_HELP)
|
|
for(var/datum/surgery/S in surgeries)
|
|
if(S.next_step(user))
|
|
return 1
|
|
return ..()
|
|
|
|
/mob/living/carbon/throw_impact(atom/hit_atom, throwingdatum)
|
|
. = ..()
|
|
var/hurt = TRUE
|
|
if(istype(throwingdatum, /datum/thrownthing))
|
|
var/datum/thrownthing/D = throwingdatum
|
|
if(iscyborg(D.thrower))
|
|
var/mob/living/silicon/robot/R = D.thrower
|
|
if(!R.emagged)
|
|
hurt = FALSE
|
|
if(hit_atom.density && isturf(hit_atom))
|
|
if(hurt)
|
|
Knockdown(20)
|
|
take_bodypart_damage(10)
|
|
if(iscarbon(hit_atom) && hit_atom != src)
|
|
var/mob/living/carbon/victim = hit_atom
|
|
if(victim.movement_type & FLYING)
|
|
return
|
|
if(hurt)
|
|
victim.take_bodypart_damage(10)
|
|
take_bodypart_damage(10)
|
|
victim.Knockdown(20)
|
|
Knockdown(20)
|
|
visible_message("<span class='danger'>[src] crashes into [victim], knocking them both over!</span>",\
|
|
"<span class='userdanger'>You violently crash into [victim]!</span>")
|
|
playsound(src,'sound/weapons/punch1.ogg',50,1)
|
|
|
|
|
|
//Throwing stuff
|
|
/mob/living/carbon/proc/toggle_throw_mode()
|
|
if(stat)
|
|
return
|
|
if(in_throw_mode)
|
|
throw_mode_off()
|
|
else
|
|
throw_mode_on()
|
|
|
|
|
|
/mob/living/carbon/proc/throw_mode_off()
|
|
in_throw_mode = 0
|
|
if(client && hud_used)
|
|
hud_used.throw_icon.icon_state = "act_throw_off"
|
|
|
|
|
|
/mob/living/carbon/proc/throw_mode_on()
|
|
in_throw_mode = 1
|
|
if(client && hud_used)
|
|
hud_used.throw_icon.icon_state = "act_throw_on"
|
|
|
|
/mob/proc/throw_item(atom/target)
|
|
return
|
|
|
|
/mob/living/carbon/throw_item(atom/target)
|
|
throw_mode_off()
|
|
if(!target || !isturf(loc))
|
|
return
|
|
if(istype(target, /obj/screen))
|
|
return
|
|
|
|
//CIT CHANGES - makes it impossible to throw while in stamina softcrit
|
|
if(staminaloss >= STAMINA_SOFTCRIT)
|
|
to_chat(src, "<span class='warning'>You're too exhausted.</span>")
|
|
return
|
|
//END OF CIT CHANGES
|
|
|
|
var/atom/movable/thrown_thing
|
|
var/obj/item/I = src.get_active_held_item()
|
|
|
|
if(!I)
|
|
if(pulling && isliving(pulling) && grab_state >= GRAB_AGGRESSIVE)
|
|
var/mob/living/throwable_mob = pulling
|
|
if(!throwable_mob.buckled)
|
|
thrown_thing = throwable_mob
|
|
stop_pulling()
|
|
if(has_trait(TRAIT_PACIFISM))
|
|
to_chat(src, "<span class='notice'>You gently let go of [throwable_mob].</span>")
|
|
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)
|
|
var/start_T_descriptor = "<font color='#6b5d00'>tile at [start_T.x], [start_T.y], [start_T.z] in area [get_area(start_T)]</font>"
|
|
var/end_T_descriptor = "<font color='#6b4400'>tile at [end_T.x], [end_T.y], [end_T.z] in area [get_area(end_T)]</font>"
|
|
add_logs(src, throwable_mob, "thrown", addition="from [start_T_descriptor] with the target [end_T_descriptor]")
|
|
|
|
else if(!(I.flags_1 & (NODROP_1|ABSTRACT_1)))
|
|
thrown_thing = I
|
|
dropItemToGround(I)
|
|
|
|
if(has_trait(TRAIT_PACIFISM) && I.throwforce)
|
|
to_chat(src, "<span class='notice'>You set [I] down gently on the ground.</span>")
|
|
return
|
|
|
|
adjustStaminaLossBuffered(I.getweight()*2)//CIT CHANGE - throwing items shall be more tiring than swinging em. Doubly so.
|
|
|
|
if(thrown_thing)
|
|
visible_message("<span class='danger'>[src] has thrown [thrown_thing].</span>")
|
|
add_logs(src, thrown_thing, "has thrown")
|
|
newtonian_move(get_dir(target, src))
|
|
thrown_thing.throw_at(target, thrown_thing.throw_range, thrown_thing.throw_speed, src)
|
|
|
|
/mob/living/carbon/restrained(ignore_grab)
|
|
. = (handcuffed || (!ignore_grab && pulledby && pulledby.grab_state >= GRAB_AGGRESSIVE))
|
|
|
|
/mob/living/carbon/proc/canBeHandcuffed()
|
|
return 0
|
|
|
|
|
|
/mob/living/carbon/show_inv(mob/user)
|
|
user.set_machine(src)
|
|
var/dat = {"
|
|
<HR>
|
|
<B><FONT size=3>[name]</FONT></B>
|
|
<HR>
|
|
<BR><B>Head:</B> <A href='?src=[REF(src)];item=[slot_head]'> [(head && !(head.flags_1&ABSTRACT_1)) ? head : "Nothing"]</A>
|
|
<BR><B>Mask:</B> <A href='?src=[REF(src)];item=[slot_wear_mask]'> [(wear_mask && !(wear_mask.flags_1&ABSTRACT_1)) ? wear_mask : "Nothing"]</A>
|
|
<BR><B>Neck:</B> <A href='?src=[REF(src)];item=[slot_neck]'> [(wear_neck && !(wear_neck.flags_1&ABSTRACT_1)) ? wear_neck : "Nothing"]</A>"}
|
|
|
|
for(var/i in 1 to held_items.len)
|
|
var/obj/item/I = get_item_for_held_index(i)
|
|
dat += "<BR><B>[get_held_index_name(i)]:</B></td><td><A href='?src=[REF(src)];item=[slot_hands];hand_index=[i]'>[(I && !(I.flags_1 & ABSTRACT_1)) ? I : "Nothing"]</a>"
|
|
|
|
dat += "<BR><B>Back:</B> <A href='?src=[REF(src)];item=[slot_back]'>[back ? back : "Nothing"]</A>"
|
|
|
|
if(istype(wear_mask, /obj/item/clothing/mask) && istype(back, /obj/item/tank))
|
|
dat += "<BR><A href='?src=[REF(src)];internal=1'>[internal ? "Disable Internals" : "Set Internals"]</A>"
|
|
|
|
if(handcuffed)
|
|
dat += "<BR><A href='?src=[REF(src)];item=[slot_handcuffed]'>Handcuffed</A>"
|
|
if(legcuffed)
|
|
dat += "<BR><A href='?src=[REF(src)];item=[slot_legcuffed]'>Legcuffed</A>"
|
|
|
|
dat += {"
|
|
<BR>
|
|
<BR><A href='?src=[REF(user)];mach_close=mob[REF(src)]'>Close</A>
|
|
"}
|
|
user << browse(dat, "window=mob[REF(src)];size=325x500")
|
|
onclose(user, "mob[REF(src)]")
|
|
|
|
/mob/living/carbon/Topic(href, href_list)
|
|
..()
|
|
//strip panel
|
|
if(usr.canUseTopic(src, BE_CLOSE, NO_DEXTERY))
|
|
if(href_list["internal"])
|
|
var/slot = text2num(href_list["internal"])
|
|
var/obj/item/ITEM = get_item_by_slot(slot)
|
|
if(ITEM && istype(ITEM, /obj/item/tank) && wear_mask && (wear_mask.flags_1 & MASKINTERNALS_1))
|
|
visible_message("<span class='danger'>[usr] tries to [internal ? "close" : "open"] the valve on [src]'s [ITEM.name].</span>", \
|
|
"<span class='userdanger'>[usr] tries to [internal ? "close" : "open"] the valve on [src]'s [ITEM.name].</span>")
|
|
if(do_mob(usr, src, POCKET_STRIP_DELAY))
|
|
if(internal)
|
|
internal = null
|
|
update_internals_hud_icon(0)
|
|
else if(ITEM && istype(ITEM, /obj/item/tank))
|
|
if((wear_mask && (wear_mask.flags_1 & MASKINTERNALS_1)) || getorganslot(ORGAN_SLOT_BREATHING_TUBE))
|
|
internal = ITEM
|
|
update_internals_hud_icon(1)
|
|
|
|
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 [src]'s [ITEM.name].</span>")
|
|
|
|
|
|
/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
|
|
|
|
/mob/living/carbon/is_muzzled()
|
|
return(istype(src.wear_mask, /obj/item/clothing/mask/muzzle))
|
|
|
|
/mob/living/carbon/hallucinating()
|
|
if(hallucination)
|
|
return TRUE
|
|
else
|
|
return FALSE
|
|
|
|
/mob/living/carbon/resist_buckle()
|
|
if(restrained())
|
|
changeNext_move(CLICK_CD_BREAKOUT)
|
|
last_special = world.time + CLICK_CD_BREAKOUT
|
|
visible_message("<span class='warning'>[src] attempts to unbuckle themself!</span>", \
|
|
"<span class='notice'>You attempt to unbuckle yourself... (This will take around one minute and you need to stay still.)</span>")
|
|
if(do_after(src, 600, 0, target = src))
|
|
if(!buckled)
|
|
return
|
|
buckled.user_unbuckle_mob(src,src)
|
|
else
|
|
if(src && buckled)
|
|
to_chat(src, "<span class='warning'>You fail to unbuckle yourself!</span>")
|
|
else
|
|
buckled.user_unbuckle_mob(src,src)
|
|
|
|
/mob/living/carbon/resist_fire()
|
|
fire_stacks -= 5
|
|
Knockdown(60, TRUE, TRUE)
|
|
spin(32,2)
|
|
visible_message("<span class='danger'>[src] rolls on the floor, trying to put themselves out!</span>", \
|
|
"<span class='notice'>You stop, drop, and roll!</span>")
|
|
sleep(30)
|
|
if(fire_stacks <= 0)
|
|
visible_message("<span class='danger'>[src] has successfully extinguished themselves!</span>", \
|
|
"<span class='notice'>You extinguish yourself.</span>")
|
|
ExtinguishMob()
|
|
return
|
|
|
|
/mob/living/carbon/resist_restraints()
|
|
var/obj/item/I = null
|
|
var/type = 0
|
|
if(handcuffed)
|
|
I = handcuffed
|
|
type = 1
|
|
else if(legcuffed)
|
|
I = legcuffed
|
|
type = 2
|
|
if(I)
|
|
if(type == 1)
|
|
changeNext_move(CLICK_CD_BREAKOUT)
|
|
last_special = world.time + CLICK_CD_BREAKOUT
|
|
if(type == 2)
|
|
changeNext_move(CLICK_CD_RANGE)
|
|
last_special = world.time + CLICK_CD_RANGE
|
|
cuff_resist(I)
|
|
|
|
|
|
/mob/living/carbon/proc/cuff_resist(obj/item/I, breakouttime = 600, cuff_break = 0)
|
|
if(I.item_flags & BEING_REMOVED)
|
|
to_chat(src, "<span class='warning'>You're already attempting to remove [I]!</span>")
|
|
return
|
|
I.item_flags |= BEING_REMOVED
|
|
breakouttime = I.breakouttime
|
|
if(!cuff_break)
|
|
visible_message("<span class='warning'>[src] attempts to remove [I]!</span>")
|
|
to_chat(src, "<span class='notice'>You attempt to remove [I]... (This will take around [DisplayTimeText(breakouttime)] and you need to stand still.)</span>")
|
|
if(do_after(src, breakouttime, 0, target = src))
|
|
clear_cuffs(I, cuff_break)
|
|
else
|
|
to_chat(src, "<span class='warning'>You fail to remove [I]!</span>")
|
|
|
|
else if(cuff_break == FAST_CUFFBREAK)
|
|
breakouttime = 50
|
|
visible_message("<span class='warning'>[src] is trying to break [I]!</span>")
|
|
to_chat(src, "<span class='notice'>You attempt to break [I]... (This will take around 5 seconds and you need to stand still.)</span>")
|
|
if(do_after(src, breakouttime, 0, target = src))
|
|
clear_cuffs(I, cuff_break)
|
|
else
|
|
to_chat(src, "<span class='warning'>You fail to break [I]!</span>")
|
|
|
|
else if(cuff_break == INSTANT_CUFFBREAK)
|
|
clear_cuffs(I, cuff_break)
|
|
I.item_flags &= ~BEING_REMOVED
|
|
|
|
/mob/living/carbon/proc/uncuff()
|
|
if (handcuffed)
|
|
var/obj/item/W = handcuffed
|
|
handcuffed = null
|
|
if (buckled && buckled.buckle_requires_restraints)
|
|
buckled.unbuckle_mob(src)
|
|
update_handcuffed()
|
|
if (client)
|
|
client.screen -= W
|
|
if (W)
|
|
W.forceMove(drop_location())
|
|
W.dropped(src)
|
|
if (W)
|
|
W.layer = initial(W.layer)
|
|
W.plane = initial(W.plane)
|
|
changeNext_move(0)
|
|
if (legcuffed)
|
|
var/obj/item/W = legcuffed
|
|
legcuffed = null
|
|
update_inv_legcuffed()
|
|
if (client)
|
|
client.screen -= W
|
|
if (W)
|
|
W.forceMove(drop_location())
|
|
W.dropped(src)
|
|
if (W)
|
|
W.layer = initial(W.layer)
|
|
W.plane = initial(W.plane)
|
|
changeNext_move(0)
|
|
|
|
/mob/living/carbon/proc/clear_cuffs(obj/item/I, cuff_break)
|
|
if(!I.loc || buckled)
|
|
return
|
|
visible_message("<span class='danger'>[src] manages to [cuff_break ? "break" : "remove"] [I]!</span>")
|
|
to_chat(src, "<span class='notice'>You successfully [cuff_break ? "break" : "remove"] [I].</span>")
|
|
|
|
if(cuff_break)
|
|
. = !((I == handcuffed) || (I == legcuffed))
|
|
qdel(I)
|
|
return
|
|
|
|
else
|
|
if(I == handcuffed)
|
|
handcuffed.forceMove(drop_location())
|
|
handcuffed.dropped(src)
|
|
handcuffed = null
|
|
if(buckled && buckled.buckle_requires_restraints)
|
|
buckled.unbuckle_mob(src)
|
|
update_handcuffed()
|
|
return
|
|
if(I == legcuffed)
|
|
legcuffed.forceMove(drop_location())
|
|
legcuffed.dropped()
|
|
legcuffed = null
|
|
update_inv_legcuffed()
|
|
return
|
|
else
|
|
dropItemToGround(I)
|
|
return
|
|
return TRUE
|
|
|
|
/mob/living/carbon/get_standard_pixel_y_offset(lying = 0)
|
|
if(lying)
|
|
return -6
|
|
else
|
|
return initial(pixel_y)
|
|
|
|
/mob/living/carbon/proc/accident(obj/item/I)
|
|
if(!I || (I.flags_1 & (NODROP_1|ABSTRACT_1)))
|
|
return
|
|
|
|
//dropItemToGround(I) CIT CHANGE - makes it so the item doesn't drop if the modifier rolls above 100
|
|
|
|
var/modifier = 0
|
|
if(has_trait(TRAIT_CLUMSY))
|
|
modifier -= 40 //Clumsy people are more likely to hit themselves -Honk!
|
|
|
|
//CIT CHANGES START HERE
|
|
else if(combatmode)
|
|
modifier += 50
|
|
|
|
if(modifier < 100)
|
|
dropItemToGround(I)
|
|
//END OF CIT CHANGES
|
|
|
|
switch(rand(1,100)+modifier) //91-100=Nothing special happens
|
|
if(-INFINITY to 0) //attack yourself
|
|
I.attack(src,src)
|
|
if(1 to 30) //throw it at yourself
|
|
I.throw_impact(src)
|
|
if(31 to 60) //Throw object in facing direction
|
|
var/turf/target = get_turf(loc)
|
|
var/range = rand(2,I.throw_range)
|
|
for(var/i = 1; i < range; i++)
|
|
var/turf/new_turf = get_step(target, dir)
|
|
target = new_turf
|
|
if(new_turf.density)
|
|
break
|
|
I.throw_at(target,I.throw_range,I.throw_speed,src)
|
|
if(61 to 90) //throw it down to the floor
|
|
var/turf/target = get_turf(loc)
|
|
I.throw_at(target,I.throw_range,I.throw_speed,src)
|
|
|
|
/mob/living/carbon/Stat()
|
|
..()
|
|
if(statpanel("Status"))
|
|
var/obj/item/organ/alien/plasmavessel/vessel = getorgan(/obj/item/organ/alien/plasmavessel)
|
|
if(vessel)
|
|
stat(null, "Plasma Stored: [vessel.storedPlasma]/[vessel.max_plasma]")
|
|
if(locate(/obj/item/device/assembly/health) in src)
|
|
stat(null, "Health: [health]")
|
|
|
|
add_abilities_to_panel()
|
|
|
|
/mob/living/carbon/attack_ui(slot)
|
|
if(!has_hand_for_held_index(active_hand_index))
|
|
return 0
|
|
return ..()
|
|
|
|
/mob/living/carbon/proc/vomit(lost_nutrition = 10, blood = FALSE, stun = TRUE, distance = 1, message = TRUE, toxic = FALSE)
|
|
if(has_trait(TRAIT_NOHUNGER))
|
|
return 1
|
|
|
|
if(nutrition < 100 && !blood)
|
|
if(message)
|
|
visible_message("<span class='warning'>[src] dry heaves!</span>", \
|
|
"<span class='userdanger'>You try to throw up, but there's nothing in your stomach!</span>")
|
|
if(stun)
|
|
Knockdown(200)
|
|
return 1
|
|
|
|
if(is_mouth_covered()) //make this add a blood/vomit overlay later it'll be hilarious
|
|
if(message)
|
|
visible_message("<span class='danger'>[src] throws up all over themself!</span>", \
|
|
"<span class='userdanger'>You throw up all over yourself!</span>")
|
|
distance = 0
|
|
else
|
|
if(message)
|
|
visible_message("<span class='danger'>[src] throws up!</span>", "<span class='userdanger'>You throw up!</span>")
|
|
|
|
if(stun)
|
|
Stun(80)
|
|
|
|
playsound(get_turf(src), 'sound/effects/splat.ogg', 50, 1)
|
|
var/turf/T = get_turf(src)
|
|
if(!blood)
|
|
nutrition -= lost_nutrition
|
|
adjustToxLoss(-3)
|
|
for(var/i=0 to distance)
|
|
if(blood)
|
|
if(T)
|
|
add_splatter_floor(T)
|
|
if(stun)
|
|
adjustBruteLoss(3)
|
|
else
|
|
if(T)
|
|
T.add_vomit_floor(src, toxic)//toxic barf looks different
|
|
T = get_step(T, dir)
|
|
if (is_blocked_turf(T))
|
|
break
|
|
return 1
|
|
|
|
/mob/living/carbon/proc/spew_organ(power = 5)
|
|
if(!internal_organs.len)
|
|
return //Guess we're out of organs
|
|
var/obj/item/organ/guts = pick(internal_organs)
|
|
var/turf/T = get_turf(src)
|
|
guts.Remove(src)
|
|
guts.forceMove(T)
|
|
var/atom/throw_target = get_edge_target_turf(guts, dir)
|
|
guts.throw_at(throw_target, power, 4, src)
|
|
|
|
|
|
/mob/living/carbon/fully_replace_character_name(oldname,newname)
|
|
..()
|
|
if(dna)
|
|
dna.real_name = real_name
|
|
|
|
//Updates the mob's health from bodyparts and mob damage variables
|
|
/mob/living/carbon/updatehealth()
|
|
if(status_flags & GODMODE)
|
|
return
|
|
var/total_burn = 0
|
|
var/total_brute = 0
|
|
for(var/X in bodyparts) //hardcoded to streamline things a bit
|
|
var/obj/item/bodypart/BP = X
|
|
total_brute += BP.brute_dam
|
|
total_burn += BP.burn_dam
|
|
health = maxHealth - getOxyLoss() - getToxLoss() - getCloneLoss() - total_burn - total_brute
|
|
update_stat()
|
|
if(((maxHealth - total_burn) < HEALTH_THRESHOLD_DEAD) && stat == DEAD )
|
|
become_husk("burn")
|
|
med_hud_set_health()
|
|
|
|
/mob/living/carbon/update_sight()
|
|
if(!client)
|
|
return
|
|
if(stat == DEAD)
|
|
sight = (SEE_TURFS|SEE_MOBS|SEE_OBJS)
|
|
see_in_dark = 8
|
|
see_invisible = SEE_INVISIBLE_OBSERVER
|
|
return
|
|
|
|
sight = initial(sight)
|
|
lighting_alpha = initial(lighting_alpha)
|
|
var/obj/item/organ/eyes/E = getorganslot(ORGAN_SLOT_EYES)
|
|
if(!E)
|
|
update_tint()
|
|
else
|
|
see_invisible = E.see_invisible
|
|
see_in_dark = E.see_in_dark
|
|
sight |= E.sight_flags
|
|
if(!isnull(E.lighting_alpha))
|
|
lighting_alpha = E.lighting_alpha
|
|
|
|
if(client.eye != src)
|
|
var/atom/A = client.eye
|
|
if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates.
|
|
return
|
|
|
|
if(glasses)
|
|
var/obj/item/clothing/glasses/G = glasses
|
|
sight |= G.vision_flags
|
|
see_in_dark = max(G.darkness_view, see_in_dark)
|
|
if(G.invis_override)
|
|
see_invisible = G.invis_override
|
|
else
|
|
see_invisible = min(G.invis_view, see_invisible)
|
|
if(!isnull(G.lighting_alpha))
|
|
lighting_alpha = min(lighting_alpha, G.lighting_alpha)
|
|
if(dna)
|
|
for(var/X in dna.mutations)
|
|
var/datum/mutation/M = X
|
|
if(M.name == XRAY)
|
|
sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
|
|
see_in_dark = max(see_in_dark, 8)
|
|
|
|
if(see_override)
|
|
see_invisible = see_override
|
|
. = ..()
|
|
|
|
|
|
//to recalculate and update the mob's total tint from tinted equipment it's wearing.
|
|
/mob/living/carbon/proc/update_tint()
|
|
if(!GLOB.tinted_weldhelh)
|
|
return
|
|
tinttotal = get_total_tint()
|
|
if(tinttotal >= TINT_BLIND)
|
|
become_blind(EYES_COVERED)
|
|
else if(tinttotal >= TINT_DARKENED)
|
|
cure_blind(EYES_COVERED)
|
|
overlay_fullscreen("tint", /obj/screen/fullscreen/impaired, 2)
|
|
else
|
|
cure_blind(EYES_COVERED)
|
|
clear_fullscreen("tint", 0)
|
|
|
|
/mob/living/carbon/proc/get_total_tint()
|
|
. = 0
|
|
if(istype(head, /obj/item/clothing/head))
|
|
var/obj/item/clothing/head/HT = head
|
|
. += HT.tint
|
|
if(wear_mask)
|
|
. += wear_mask.tint
|
|
|
|
var/obj/item/organ/eyes/E = getorganslot(ORGAN_SLOT_EYES)
|
|
if(E)
|
|
. += E.tint
|
|
|
|
else
|
|
. += INFINITY
|
|
|
|
//this handles hud updates
|
|
/mob/living/carbon/update_damage_hud()
|
|
|
|
if(!client)
|
|
return
|
|
|
|
if(health <= HEALTH_THRESHOLD_CRIT)
|
|
var/severity = 0
|
|
switch(health)
|
|
if(-20 to -10)
|
|
severity = 1
|
|
if(-30 to -20)
|
|
severity = 2
|
|
if(-40 to -30)
|
|
severity = 3
|
|
if(-50 to -40)
|
|
severity = 4
|
|
if(-50 to -40)
|
|
severity = 5
|
|
if(-60 to -50)
|
|
severity = 6
|
|
if(-70 to -60)
|
|
severity = 7
|
|
if(-90 to -70)
|
|
severity = 8
|
|
if(-95 to -90)
|
|
severity = 9
|
|
if(-INFINITY to -95)
|
|
severity = 10
|
|
if(!InFullCritical())
|
|
var/visionseverity = 4
|
|
switch(health)
|
|
if(-8 to -4)
|
|
visionseverity = 5
|
|
if(-12 to -8)
|
|
visionseverity = 6
|
|
if(-16 to -12)
|
|
visionseverity = 7
|
|
if(-20 to -16)
|
|
visionseverity = 8
|
|
if(-24 to -20)
|
|
visionseverity = 9
|
|
if(-INFINITY to -24)
|
|
visionseverity = 10
|
|
overlay_fullscreen("critvision", /obj/screen/fullscreen/crit/vision, visionseverity)
|
|
else
|
|
clear_fullscreen("critvision")
|
|
overlay_fullscreen("crit", /obj/screen/fullscreen/crit, severity)
|
|
else
|
|
clear_fullscreen("crit")
|
|
clear_fullscreen("critvision")
|
|
|
|
//Oxygen damage overlay
|
|
if(oxyloss)
|
|
var/severity = 0
|
|
switch(oxyloss)
|
|
if(10 to 20)
|
|
severity = 1
|
|
if(20 to 25)
|
|
severity = 2
|
|
if(25 to 30)
|
|
severity = 3
|
|
if(30 to 35)
|
|
severity = 4
|
|
if(35 to 40)
|
|
severity = 5
|
|
if(40 to 45)
|
|
severity = 6
|
|
if(45 to INFINITY)
|
|
severity = 7
|
|
overlay_fullscreen("oxy", /obj/screen/fullscreen/oxy, severity)
|
|
else
|
|
clear_fullscreen("oxy")
|
|
|
|
//Fire and Brute damage overlay (BSSR)
|
|
var/hurtdamage = getBruteLoss() + getFireLoss() + damageoverlaytemp
|
|
if(hurtdamage)
|
|
var/severity = 0
|
|
switch(hurtdamage)
|
|
if(5 to 15)
|
|
severity = 1
|
|
if(15 to 30)
|
|
severity = 2
|
|
if(30 to 45)
|
|
severity = 3
|
|
if(45 to 70)
|
|
severity = 4
|
|
if(70 to 85)
|
|
severity = 5
|
|
if(85 to INFINITY)
|
|
severity = 6
|
|
overlay_fullscreen("brute", /obj/screen/fullscreen/brute, severity)
|
|
else
|
|
clear_fullscreen("brute")
|
|
|
|
/mob/living/carbon/update_health_hud(shown_health_amount)
|
|
if(!client || !hud_used)
|
|
return
|
|
if(hud_used.healths)
|
|
if(stat != DEAD)
|
|
. = 1
|
|
if(!shown_health_amount)
|
|
shown_health_amount = health
|
|
if(shown_health_amount >= maxHealth)
|
|
hud_used.healths.icon_state = "health0"
|
|
else if(shown_health_amount > maxHealth*0.8)
|
|
hud_used.healths.icon_state = "health1"
|
|
else if(shown_health_amount > maxHealth*0.6)
|
|
hud_used.healths.icon_state = "health2"
|
|
else if(shown_health_amount > maxHealth*0.4)
|
|
hud_used.healths.icon_state = "health3"
|
|
else if(shown_health_amount > maxHealth*0.2)
|
|
hud_used.healths.icon_state = "health4"
|
|
else if(shown_health_amount > 0)
|
|
hud_used.healths.icon_state = "health5"
|
|
else
|
|
hud_used.healths.icon_state = "health6"
|
|
else
|
|
hud_used.healths.icon_state = "health7"
|
|
|
|
/mob/living/carbon/proc/update_internals_hud_icon(internal_state = 0)
|
|
if(hud_used && hud_used.internals)
|
|
hud_used.internals.icon_state = "internal[internal_state]"
|
|
|
|
/mob/living/carbon/update_stat()
|
|
if(status_flags & GODMODE)
|
|
return
|
|
if(stat != DEAD)
|
|
if(health <= HEALTH_THRESHOLD_DEAD)
|
|
death()
|
|
return
|
|
if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (has_trait(TRAIT_FAKEDEATH)) || health <= HEALTH_THRESHOLD_FULLCRIT)
|
|
stat = UNCONSCIOUS
|
|
blind_eyes(1)
|
|
else
|
|
if(health <= HEALTH_THRESHOLD_CRIT)
|
|
stat = SOFT_CRIT
|
|
else
|
|
stat = CONSCIOUS
|
|
adjust_blindness(-1)
|
|
update_canmove()
|
|
update_damage_hud()
|
|
update_health_hud()
|
|
med_hud_set_status()
|
|
|
|
//called when we get cuffed/uncuffed
|
|
/mob/living/carbon/proc/update_handcuffed()
|
|
if(handcuffed)
|
|
drop_all_held_items()
|
|
stop_pulling()
|
|
throw_alert("handcuffed", /obj/screen/alert/restrained/handcuffed, new_master = src.handcuffed)
|
|
SendSignal(COMSIG_ADD_MOOD_EVENT, "handcuffed", /datum/mood_event/handcuffed)
|
|
else
|
|
clear_alert("handcuffed")
|
|
SendSignal(COMSIG_CLEAR_MOOD_EVENT, "handcuffed")
|
|
update_action_buttons_icon() //some of our action buttons might be unusable when we're handcuffed.
|
|
update_inv_handcuffed()
|
|
update_hud_handcuffed()
|
|
|
|
/mob/living/carbon/fully_heal(admin_revive = FALSE)
|
|
if(reagents)
|
|
reagents.clear_reagents()
|
|
var/obj/item/organ/brain/B = getorgan(/obj/item/organ/brain)
|
|
if(B)
|
|
B.damaged_brain = FALSE
|
|
for(var/thing in diseases)
|
|
var/datum/disease/D = thing
|
|
if(D.severity != DISEASE_SEVERITY_POSITIVE)
|
|
D.cure(FALSE)
|
|
if(admin_revive)
|
|
regenerate_limbs()
|
|
regenerate_organs()
|
|
handcuffed = initial(handcuffed)
|
|
for(var/obj/item/restraints/R in contents) //actually remove cuffs from inventory
|
|
qdel(R)
|
|
update_handcuffed()
|
|
if(reagents)
|
|
reagents.addiction_list = list()
|
|
cure_all_traumas(TRAUMA_RESILIENCE_MAGIC)
|
|
..()
|
|
// heal ears after healing traits, since ears check TRAIT_DEAF trait
|
|
// when healing.
|
|
restoreEars()
|
|
|
|
/mob/living/carbon/can_be_revived()
|
|
. = ..()
|
|
if(!getorgan(/obj/item/organ/brain) && (!mind || !mind.has_antag_datum(/datum/antagonist/changeling)))
|
|
return 0
|
|
|
|
/mob/living/carbon/harvest(mob/living/user)
|
|
if(QDELETED(src))
|
|
return
|
|
var/organs_amt = 0
|
|
for(var/X in internal_organs)
|
|
var/obj/item/organ/O = X
|
|
if(prob(50))
|
|
organs_amt++
|
|
O.Remove(src)
|
|
O.forceMove(drop_location())
|
|
if(organs_amt)
|
|
to_chat(user, "<span class='notice'>You retrieve some of [src]\'s internal organs!</span>")
|
|
|
|
/mob/living/carbon/ExtinguishMob()
|
|
for(var/X in get_equipped_items())
|
|
var/obj/item/I = X
|
|
I.acid_level = 0 //washes off the acid on our clothes
|
|
I.extinguish() //extinguishes our clothes
|
|
..()
|
|
|
|
/mob/living/carbon/fakefire(var/fire_icon = "Generic_mob_burning")
|
|
var/mutable_appearance/new_fire_overlay = mutable_appearance('icons/mob/OnFire.dmi', fire_icon, -FIRE_LAYER)
|
|
new_fire_overlay.appearance_flags = RESET_COLOR
|
|
overlays_standing[FIRE_LAYER] = new_fire_overlay
|
|
apply_overlay(FIRE_LAYER)
|
|
|
|
/mob/living/carbon/fakefireextinguish()
|
|
remove_overlay(FIRE_LAYER)
|
|
|
|
|
|
/mob/living/carbon/proc/devour_mob(mob/living/carbon/C, devour_time = 130)
|
|
C.visible_message("<span class='danger'>[src] is attempting to devour [C]!</span>", \
|
|
"<span class='userdanger'>[src] is attempting to devour you!</span>")
|
|
if(!do_mob(src, C, devour_time))
|
|
return
|
|
if(pulling && pulling == C && grab_state >= GRAB_AGGRESSIVE && a_intent == INTENT_GRAB)
|
|
C.visible_message("<span class='danger'>[src] devours [C]!</span>", \
|
|
"<span class='userdanger'>[src] devours you!</span>")
|
|
C.forceMove(src)
|
|
stomach_contents.Add(C)
|
|
add_logs(src, C, "devoured")
|
|
|
|
/mob/living/carbon/proc/create_bodyparts()
|
|
var/l_arm_index_next = -1
|
|
var/r_arm_index_next = 0
|
|
for(var/X in bodyparts)
|
|
var/obj/item/bodypart/O = new X()
|
|
O.owner = src
|
|
bodyparts.Remove(X)
|
|
bodyparts.Add(O)
|
|
if(O.body_part == ARM_LEFT)
|
|
l_arm_index_next += 2
|
|
O.held_index = l_arm_index_next //1, 3, 5, 7...
|
|
hand_bodyparts += O
|
|
else if(O.body_part == ARM_RIGHT)
|
|
r_arm_index_next += 2
|
|
O.held_index = r_arm_index_next //2, 4, 6, 8...
|
|
hand_bodyparts += O
|
|
|
|
|
|
/mob/living/carbon/proc/create_internal_organs()
|
|
for(var/X in internal_organs)
|
|
var/obj/item/organ/I = X
|
|
I.Insert(src)
|
|
|
|
/mob/living/carbon/vv_get_dropdown()
|
|
. = ..()
|
|
. += "---"
|
|
.["Make AI"] = "?_src_=vars;[HrefToken()];makeai=[REF(src)]"
|
|
.["Modify bodypart"] = "?_src_=vars;[HrefToken()];editbodypart=[REF(src)]"
|
|
.["Modify organs"] = "?_src_=vars;[HrefToken()];editorgans=[REF(src)]"
|
|
.["Hallucinate"] = "?_src_=vars;[HrefToken()];hallucinate=[REF(src)]"
|
|
.["Give brain trauma"] = "?_src_=vars;[HrefToken()];givetrauma=[REF(src)]"
|
|
.["Cure brain traumas"] = "?_src_=vars;[HrefToken()];curetraumas=[REF(src)]"
|
|
|
|
/mob/living/carbon/can_resist()
|
|
return bodyparts.len > 2 && ..()
|