From dd579bd5fde3dd1c6380fd53ac98649ccc8d76c7 Mon Sep 17 00:00:00 2001 From: cib Date: Wed, 4 Jul 2012 17:42:21 +0200 Subject: [PATCH] More work on amorphs. --- baystation12.dme | 7 + code/game/hud.dm | 3 + code/modules/mining/satchel_ore_box.dm | 2 +- code/modules/mob/emote.dm | 50 ++ .../mob/living/carbon/amorph/amorph.dm | 590 ++++++++++++++++++ .../mob/living/carbon/amorph/amorph_attack.dm | 248 ++++++++ .../mob/living/carbon/amorph/amorph_damage.dm | 12 + .../mob/living/carbon/amorph/amorph_hud.dm | 290 +++++++++ code/modules/mob/living/carbon/amorph/life.dm | 516 +++++++++++++++ code/modules/mob/living/carbon/amorph/say.dm | 6 + code/modules/mob/mob_helpers.dm | 3 + 11 files changed, 1726 insertions(+), 1 deletion(-) create mode 100644 code/modules/mob/emote.dm create mode 100644 code/modules/mob/living/carbon/amorph/amorph.dm create mode 100644 code/modules/mob/living/carbon/amorph/amorph_attack.dm create mode 100644 code/modules/mob/living/carbon/amorph/amorph_damage.dm create mode 100644 code/modules/mob/living/carbon/amorph/amorph_hud.dm create mode 100644 code/modules/mob/living/carbon/amorph/life.dm create mode 100644 code/modules/mob/living/carbon/amorph/say.dm diff --git a/baystation12.dme b/baystation12.dme index 52310b50e6..a5e0a39cf0 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -118,6 +118,7 @@ #define FILE_DIR "code/modules/mob/living/carbon/alien/humanoid/caste" #define FILE_DIR "code/modules/mob/living/carbon/alien/larva" #define FILE_DIR "code/modules/mob/living/carbon/alien/special" +#define FILE_DIR "code/modules/mob/living/carbon/amorph" #define FILE_DIR "code/modules/mob/living/carbon/brain" #define FILE_DIR "code/modules/mob/living/carbon/human" #define FILE_DIR "code/modules/mob/living/carbon/metroid" @@ -918,6 +919,7 @@ #include "code\modules\mining\ores_materials_coins.dm" #include "code\modules\mining\satchel_ore_box.dm" #include "code\modules\mob\death.dm" +#include "code\modules\mob\emote.dm" #include "code\modules\mob\login.dm" #include "code\modules\mob\logout.dm" #include "code\modules\mob\mob.dm" @@ -972,6 +974,11 @@ #include "code\modules\mob\living\carbon\alien\special\muton.dm" #include "code\modules\mob\living\carbon\alien\special\sectoid.dm" #include "code\modules\mob\living\carbon\alien\special\snakeman.dm" +#include "code\modules\mob\living\carbon\amorph\amorph.dm" +#include "code\modules\mob\living\carbon\amorph\amorph_damage.dm" +#include "code\modules\mob\living\carbon\amorph\amorph_hud.dm" +#include "code\modules\mob\living\carbon\amorph\life.dm" +#include "code\modules\mob\living\carbon\amorph\say.dm" #include "code\modules\mob\living\carbon\brain\brain.dm" #include "code\modules\mob\living\carbon\brain\death.dm" #include "code\modules\mob\living\carbon\brain\hud.dm" diff --git a/code/game/hud.dm b/code/game/hud.dm index b46567040e..506b0cfeb2 100644 --- a/code/game/hud.dm +++ b/code/game/hud.dm @@ -194,6 +194,9 @@ obj/hud/New(var/type = 0) else if(isrobot(mymob)) robot_hud() + else if(isamorph(mymob)) + amorph_hud() + // else if(ishivebot(mymob)) // hivebot_hud() diff --git a/code/modules/mining/satchel_ore_box.dm b/code/modules/mining/satchel_ore_box.dm index 75c99097c5..35378419e0 100644 --- a/code/modules/mining/satchel_ore_box.dm +++ b/code/modules/mining/satchel_ore_box.dm @@ -45,7 +45,7 @@ /**********************Ore box**************************/ /obj/structure/ore_box - icon = 'mining.dmi' + //icon = 'mining.dmi' icon_state = "orebox0" name = "Ore Box" desc = "It's heavy" diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm new file mode 100644 index 0000000000..783f3404e8 --- /dev/null +++ b/code/modules/mob/emote.dm @@ -0,0 +1,50 @@ +// All mobs should have custom emote, really.. +mob/proc/custom_emote(var/m_type=1,var/message = null) + + if(!emote_allowed && usr == src) + usr << "You are unable to emote." + return + + var/muzzled = istype(src.wear_mask, /obj/item/clothing/mask/muzzle) + if(m_type == 2 && muzzled) return + + var/input + if(!message) + input = copytext(sanitize(input(src,"Choose an emote to display.") as text|null),1,MAX_MESSAGE_LEN) + else + input = message + if(input) + message = "[src] [input]" + else + return + + + if (message) + log_emote("[name]/[key] : [message]") + + //Hearing gasp and such every five seconds is not good emotes were not global for a reason. + // Maybe some people are okay with that. + + for(var/mob/M in world) + if (!M.client) + continue //skip monkeys and leavers + if (istype(M, /mob/new_player)) + continue + if(findtext(message," snores.")) //Because we have so many sleeping people. + continue + if(M.stat == 2 && M.client.ghost_sight && !(M in viewers(src,null))) + M.show_message(message) + + + if (m_type & 1) + for (var/mob/O in viewers(src, null)) + if(istype(O,/mob/living/carbon/human)) + for(var/mob/living/parasite/P in O:parasites) + P.show_message(message, m_type) + O.show_message(message, m_type) + else if (m_type & 2) + for (var/mob/O in hearers(src.loc, null)) + if(istype(O,/mob/living/carbon/human)) + for(var/mob/living/parasite/P in O:parasites) + P.show_message(message, m_type) + O.show_message(message, m_type) diff --git a/code/modules/mob/living/carbon/amorph/amorph.dm b/code/modules/mob/living/carbon/amorph/amorph.dm new file mode 100644 index 0000000000..c2af2ff75d --- /dev/null +++ b/code/modules/mob/living/carbon/amorph/amorph.dm @@ -0,0 +1,590 @@ +/mob/living/carbon/amorph + name = "amorph" + real_name = "amorph" + voice_name = "amorph" + icon = 'icons/mob/amorph.dmi' + icon_state = "" + + + var/species = "Amorph" + age = 30.0 + + var/used_skillpoints = 0 + var/skill_specialization = null + var/list/skills = null + + var/obj/item/l_ear = null + + // might use this later to recolor armorphs with icon.SwapColor + var/slime_color = null + + var/examine_text = "" + + +/mob/living/carbon/amorph/New() + + ..() + + // Amorphs don't have a blood vessel, but they can have reagents in their body + var/datum/reagents/R = new/datum/reagents(1000) + reagents = R + R.my_atom = src + + // Amorphs have no DNA(they're more like carbon-based machines) + + // Amorphs don't have organs + ..() + +/mob/living/carbon/amorph/Bump(atom/movable/AM as mob|obj, yes) + if ((!( yes ) || now_pushing)) + return + now_pushing = 1 + if (ismob(AM)) + var/mob/tmob = AM + +//BubbleWrap - Should stop you pushing a restrained person out of the way + + if(istype(tmob, /mob/living/carbon/human)) + + for(var/mob/M in range(tmob, 1)) + if( ((M.pulling == tmob && ( tmob.restrained() && !( M.restrained() ) && M.stat == 0)) || locate(/obj/item/weapon/grab, tmob.grabbed_by.len)) ) + if ( !(world.time % 5) ) + src << "\red [tmob] is restrained, you cannot push past" + now_pushing = 0 + return + if( tmob.pulling == M && ( M.restrained() && !( tmob.restrained() ) && tmob.stat == 0) ) + if ( !(world.time % 5) ) + src << "\red [tmob] is restraining [M], you cannot push past" + now_pushing = 0 + return + + //BubbleWrap: people in handcuffs are always switched around as if they were on 'help' intent to prevent a person being pulled from being seperated from their puller + if((tmob.a_intent == "help" || tmob.restrained()) && (a_intent == "help" || src.restrained()) && tmob.canmove && canmove) // mutual brohugs all around! + var/turf/oldloc = loc + loc = tmob.loc + tmob.loc = oldloc + now_pushing = 0 + for(var/mob/living/carbon/metroid/Metroid in view(1,tmob)) + if(Metroid.Victim == tmob) + Metroid.UpdateFeed() + return + + if(tmob.r_hand && istype(tmob.r_hand, /obj/item/weapon/shield/riot)) + if(prob(99)) + now_pushing = 0 + return + if(tmob.l_hand && istype(tmob.l_hand, /obj/item/weapon/shield/riot)) + if(prob(99)) + now_pushing = 0 + return + if(tmob.nopush) + now_pushing = 0 + return + + tmob.LAssailant = src + + now_pushing = 0 + spawn(0) + ..() + if (!istype(AM, /atom/movable)) + return + if (!now_pushing) + now_pushing = 1 + + if (!AM.anchored) + var/t = get_dir(src, AM) + if (istype(AM, /obj/structure/window)) + if(AM:ini_dir == NORTHWEST || AM:ini_dir == NORTHEAST || AM:ini_dir == SOUTHWEST || AM:ini_dir == SOUTHEAST) + for(var/obj/structure/window/win in get_step(AM,t)) + now_pushing = 0 + return + step(AM, t) + now_pushing = 0 + return + return + +/mob/living/carbon/amorph/movement_delay() + var/tally = 2 // amorphs are a bit slower than humans + var/mob/M = pulling + + if(reagents.has_reagent("hyperzine")) return -1 + + if(reagents.has_reagent("nuka_cola")) return -1 + + if(analgesic) return -1 + + if (istype(loc, /turf/space)) return -1 // It's hard to be slowed down in space by... anything + + var/health_deficiency = traumatic_shock + if(health_deficiency >= 40) tally += (health_deficiency / 25) + + var/hungry = (500 - nutrition)/5 // So overeat would be 100 and default level would be 80 + if (hungry >= 70) tally += hungry/300 + + if (bodytemperature < 283.222) + tally += (283.222 - bodytemperature) / 10 * 1.75 + if (stuttering < 10) + stuttering = 10 + + if(shock_stage >= 10) tally += 3 + + if(tally < 0) + tally = 0 + + if(istype(M) && M.lying) //Pulling lying down people is slower + tally += 3 + + if(mRun in mutations) + tally = 0 + + return tally + +/mob/living/carbon/amorph/Stat() + ..() + statpanel("Status") + + stat(null, "Intent: [a_intent]") + stat(null, "Move Mode: [m_intent]") + if(ticker && ticker.mode && ticker.mode.name == "AI malfunction") + if(ticker.mode:malf_mode_declared) + stat(null, "Time left: [max(ticker.mode:AI_win_timeleft/(ticker.mode:apcs/3), 0)]") + if(emergency_shuttle) + if(emergency_shuttle.online && emergency_shuttle.location < 2) + var/timeleft = emergency_shuttle.timeleft() + if (timeleft) + stat(null, "ETA-[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]") + + if (client.statpanel == "Status") + if (internal) + if (!internal.air_contents) + del(internal) + else + stat("Internal Atmosphere Info", internal.name) + stat("Tank Pressure", internal.air_contents.return_pressure()) + stat("Distribution Pressure", internal.distribute_pressure) + if (mind) + if (mind.special_role == "Changeling" && changeling) + stat("Chemical Storage", changeling.chem_charges) + stat("Genetic Damage Time", changeling.geneticdamage) + +/mob/living/carbon/amorph/ex_act(severity) + flick("flash", flash) + + var/shielded = 0 + var/b_loss = null + var/f_loss = null + switch (severity) + if (1.0) + b_loss += 500 + if (!prob(getarmor(null, "bomb"))) + gib() + return + else + var/atom/target = get_edge_target_turf(src, get_dir(src, get_step_away(src, src))) + throw_at(target, 200, 4) + + if (2.0) + if (!shielded) + b_loss += 60 + + f_loss += 60 + + if (!prob(getarmor(null, "bomb"))) + b_loss = b_loss/1.5 + f_loss = f_loss/1.5 + + if(3.0) + b_loss += 30 + if (!prob(getarmor(null, "bomb"))) + b_loss = b_loss/2 + if (prob(50) && !shielded) + Paralyse(10) + + src.bruteloss += b_loss + src.fireloss += f_loss + + UpdateDamageIcon() + + +/mob/living/carbon/amorph/blob_act() + if(stat == 2) return + show_message("\red The blob attacks you!") + src.bruteloss += rand(30,40) + UpdateDamageIcon() + return + +/mob/living/carbon/amorph/u_equip(obj/item/W as obj) + // These are the only slots an amorph has + if (W == l_ear) + l_ear = null + else if (W == r_hand) + r_hand = null + + update_clothing() + +/mob/living/carbon/amorph/db_click(text, t1) + var/obj/item/W = equipped() + var/emptyHand = (W == null) + if ((!emptyHand) && (!istype(W, /obj/item))) + return + if (emptyHand) + usr.next_move = usr.prev_move + usr:lastDblClick -= 3 //permit the double-click redirection to proceed. + switch(text) + if("l_ear") + if (l_ear) + if (emptyHand) + l_ear.DblClick() + return + else if(emptyHand) + return + if (!( istype(W, /obj/item/clothing/ears) ) && !( istype(W, /obj/item/device/radio/headset) ) && W.w_class != 1) + return + u_equip(W) + l_ear = W + W.equipped(src, text) + + update_clothing() + + return + +/mob/living/carbon/amorph/meteorhit(O as obj) + for(var/mob/M in viewers(src, null)) + if ((M.client && !( M.blinded ))) + M.show_message(text("\red [] has been hit by []", src, O), 1) + if (health > 0) + if (istype(O, /obj/effect/immovablerod)) + src.bruteloss += 101 + else + src.bruteloss += 25 + UpdateDamageIcon() + updatehealth() + return + +/mob/living/carbon/amorph/Move(a, b, flag) + + if (buckled) + return + + if (restrained()) + pulling = null + + + var/t7 = 1 + if (restrained()) + for(var/mob/M in range(src, 1)) + if ((M.pulling == src && M.stat == 0 && !( M.restrained() ))) + t7 = null + if ((t7 && (pulling && ((get_dist(src, pulling) <= 1 || pulling.loc == loc) && (client && client.moving))))) + var/turf/T = loc + . = ..() + + if (pulling && pulling.loc) + if(!( isturf(pulling.loc) )) + pulling = null + return + else + if(Debug) + diary <<"pulling disappeared? at [__LINE__] in mob.dm - pulling = [pulling]" + diary <<"REPORT THIS" + + ///// + if(pulling && pulling.anchored) + pulling = null + return + + if (!restrained()) + var/diag = get_dir(src, pulling) + if ((diag - 1) & diag) + else + diag = null + if ((get_dist(src, pulling) > 1 || diag)) + if (ismob(pulling)) + var/mob/M = pulling + var/ok = 1 + if (locate(/obj/item/weapon/grab, M.grabbed_by)) + if (prob(75)) + var/obj/item/weapon/grab/G = pick(M.grabbed_by) + if (istype(G, /obj/item/weapon/grab)) + for(var/mob/O in viewers(M, null)) + O.show_message(text("\red [] has been pulled from []'s grip by []", G.affecting, G.assailant, src), 1) + //G = null + del(G) + else + ok = 0 + if (locate(/obj/item/weapon/grab, M.grabbed_by.len)) + ok = 0 + if (ok) + var/t = M.pulling + M.pulling = null + + //this is the gay blood on floor shit -- Added back -- Skie + if (M.lying && (prob(M.getBruteLoss() / 6))) + var/turf/location = M.loc + if (istype(location, /turf/simulated)) + location.add_blood(M) + if(ishuman(M)) + var/mob/living/carbon/H = M + var/blood_volume = round(H:vessel.get_reagent_amount("blood")) + if(blood_volume > 0) + H:vessel.remove_reagent("blood",1) + if(prob(5)) + M.adjustBruteLoss(1) + visible_message("\red \The [M]'s wounds open more from being dragged!") + if(M.pull_damage()) + if(prob(25)) + M.adjustBruteLoss(2) + visible_message("\red \The [M]'s wounds worsen terribly from being dragged!") + var/turf/location = M.loc + if (istype(location, /turf/simulated)) + location.add_blood(M) + if(ishuman(M)) + var/mob/living/carbon/H = M + var/blood_volume = round(H:vessel.get_reagent_amount("blood")) + if(blood_volume > 0) + H:vessel.remove_reagent("blood",1) + + step(pulling, get_dir(pulling.loc, T)) + M.pulling = t + else + if (pulling) + if (istype(pulling, /obj/structure/window)) + if(pulling:ini_dir == NORTHWEST || pulling:ini_dir == NORTHEAST || pulling:ini_dir == SOUTHWEST || pulling:ini_dir == SOUTHEAST) + for(var/obj/structure/window/win in get_step(pulling,get_dir(pulling.loc, T))) + pulling = null + if (pulling) + step(pulling, get_dir(pulling.loc, T)) + else + pulling = null + . = ..() + if ((s_active && !( s_active in contents ) )) + s_active.close(src) + + for(var/mob/living/carbon/metroid/M in view(1,src)) + M.UpdateFeed(src) + return + +/mob/living/carbon/amorph/proc/misc_clothing_updates() + // Temporary proc to shove stuff in that was put into update_clothing() + // for questionable reasons + + if (client) + if (i_select) + if (intent) + client.screen += hud_used.intents + + var/list/L = dd_text2list(intent, ",") + L[1] += ":-11" + i_select.screen_loc = dd_list2text(L,",") //ICONS4 + else + i_select.screen_loc = null + if (m_select) + if (m_int) + client.screen += hud_used.mov_int + + var/list/L = dd_text2list(m_int, ",") + L[1] += ":-11" + m_select.screen_loc = dd_list2text(L,",") //ICONS4 + else + m_select.screen_loc = null + + // Probably a lazy way to make sure all items are on the screen exactly once + if (client) + client.screen -= contents + client.screen += contents + +/mob/living/carbon/amorph/rebuild_appearance() + // Lazy method: Just rebuild everything. + // This can be called when the mob is created, but on other occasions, rebuild_body_overlays(), + // rebuild_clothing_overlays() etc. should be called individually. + + misc_clothing_updates() // silly stuff + +/mob/living/carbon/amorph/update_body_appearance() + // Should be called whenever something about the body appearance itself changes. + + misc_clothing_updates() // silly stuff + + if(lying) + icon_state = "lying" + else + icon_state = "standing" + +/mob/living/carbon/amorph/update_lying() + // Should be called whenever something about the lying status of the mob might have changed. + + if(lying) + icon_state = "lying" + else + icon_state = "standing" + +/mob/living/carbon/amorph/hand_p(mob/M as mob) + // not even sure what this is meant to do + return + +/mob/living/carbon/amorph/restrained() + if (handcuffed) + return 0 // handcuffs don't work on amorphs + return 0 + +/mob/living/carbon/amorph/var/co2overloadtime = null +/mob/living/carbon/amorph/var/temperature_resistance = T0C+75 + +/mob/living/carbon/amorph/show_inv(mob/user as mob) + // TODO: add a window for extracting stuff from an amorph's mouth + +// called when something steps onto an amorph +// this could be made more general, but for now just handle mulebot +/mob/living/carbon/amorph/HasEntered(var/atom/movable/AM) + var/obj/machinery/bot/mulebot/MB = AM + if(istype(MB)) + MB.RunOver(src) + +//gets assignment from ID or ID inside PDA or PDA itself +//Useful when player do something with computers +/mob/living/carbon/amorph/proc/get_assignment(var/if_no_id = "No id", var/if_no_job = "No job") + // TODO: get the ID from the amorph's contents + return + +//gets name from ID or ID inside PDA or PDA itself +//Useful when player do something with computers +/mob/living/carbon/amorph/proc/get_authentification_name(var/if_no_id = "Unknown") + // TODO: get the ID from the amorph's contents + return + +//repurposed proc. Now it combines get_id_name() and get_face_name() to determine a mob's name variable. Made into a seperate proc as it'll be useful elsewhere +/mob/living/carbon/amorph/proc/get_visible_name() + // amorphs can't wear clothes or anything, so always return face_name + return get_face_name() + +//Returns "Unknown" if facially disfigured and real_name if not. Useful for setting name when polyacided or when updating a human's name variable +/mob/living/carbon/amorph/proc/get_face_name() + // there might later be ways for amorphs to change the appearance of their face + return "[real_name]" + + +//gets ID card object from special clothes slot or null. +/mob/living/carbon/amorph/proc/get_idcard() + // TODO: get the ID from the amorph's contents + + +// heal the amorph +/mob/living/carbon/amorph/heal_overall_damage(var/brute, var/burn) + bruteloss -= brute + fireloss -= burn + bruteloss = max(bruteloss, 0) + fireloss = max(fireloss, 0) + + updatehealth() + UpdateDamageIcon() + +// damage MANY external organs, in random order +/mob/living/carbon/amorph/take_overall_damage(var/brute, var/burn, var/used_weapon = null) + bruteloss += brute + fireloss += burn + + updatehealth() + UpdateDamageIcon() + +/mob/living/carbon/amorph/Topic(href, href_list) + if (href_list["refresh"]) + if((machine)&&(in_range(src, usr))) + show_inv(machine) + + if (href_list["mach_close"]) + var/t1 = text("window=[]", href_list["mach_close"]) + machine = null + src << browse(null, t1) + + if ((href_list["item"] && !( usr.stat ) && usr.canmove && !( usr.restrained() ) && in_range(src, usr) && ticker)) //if game hasn't started, can't make an equip_e + var/obj/effect/equip_e/human/O = new /obj/effect/equip_e/human( ) + O.source = usr + O.target = src + O.item = usr.equipped() + O.s_loc = usr.loc + O.t_loc = loc + O.place = href_list["item"] + if(href_list["loc"]) + O.internalloc = href_list["loc"] + requests += O + spawn( 0 ) + O.process() + return + + if (href_list["criminal"]) + if(istype(usr, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = usr + if(istype(H.glasses, /obj/item/clothing/glasses/hud/security) || istype(H.glasses, /obj/item/clothing/glasses/sunglasses/sechud)) + var/perpname = "wot" + var/modified = 0 + + /*if(wear_id) + if(istype(wear_id,/obj/item/weapon/card/id)) + perpname = wear_id:registered_name + else if(istype(wear_id,/obj/item/device/pda)) + var/obj/item/device/pda/tempPda = wear_id + perpname = tempPda.owner + else*/ + perpname = src.name + + for (var/datum/data/record/E in data_core.general) + if (E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.security) + if (R.fields["id"] == E.fields["id"]) + + var/setcriminal = input(usr, "Specify a new criminal status for this person.", "Security HUD", R.fields["criminal"]) in list("None", "*Arrest*", "Incarcerated", "Parolled", "Released", "Cancel") + + if(istype(H.glasses, /obj/item/clothing/glasses/hud/security) || istype(H.glasses, /obj/item/clothing/glasses/sunglasses/sechud)) + if(setcriminal != "Cancel") + R.fields["criminal"] = setcriminal + modified = 1 + + spawn() + H.handle_regular_hud_updates() + + if(!modified) + usr << "\red Unable to locate a data core entry for this person." + ..() + return + + +///eyecheck() +///Returns a number between -1 to 2 +/mob/living/carbon/amorph/eyecheck() + return 1 + + +/mob/living/carbon/amorph/IsAdvancedToolUser() + return 1//Amorphs can use guns and such + + +/mob/living/carbon/amorph/updatehealth() + if(src.nodamage) + src.health = 100 + src.stat = 0 + return + src.health = 100 - src.getOxyLoss() - src.getToxLoss() - src.getFireLoss() - src.getBruteLoss() - src.getCloneLoss() -src.halloss + return + +/mob/living/carbon/amorph/abiotic(var/full_body = 0) + return 0 + +/mob/living/carbon/amorph/abiotic2(var/full_body2 = 0) + return 0 + +/mob/living/carbon/amorph/getBruteLoss() + return src.bruteloss + +/mob/living/carbon/amorph/adjustBruteLoss(var/amount, var/used_weapon = null) + src.bruteloss += amount + if(bruteloss < 0) bruteloss = 0 + +/mob/living/carbon/amorph/getFireLoss() + return src.fireloss + +/mob/living/carbon/amorph/adjustFireLoss(var/amount,var/used_weapon = null) + src.fireloss += amount + if(fireloss < 0) fireloss = 0 + +/mob/living/carbon/amorph/get_visible_gender() + return gender diff --git a/code/modules/mob/living/carbon/amorph/amorph_attack.dm b/code/modules/mob/living/carbon/amorph/amorph_attack.dm new file mode 100644 index 0000000000..b8c4aa8b60 --- /dev/null +++ b/code/modules/mob/living/carbon/amorph/amorph_attack.dm @@ -0,0 +1,248 @@ + + +/mob/living/carbon/amorph/attack_paw(mob/living/carbon/monkey/M as mob) + if (!ticker) + M << "You cannot attack people before the game has started." + return + + ..() + + switch(M.a_intent) + + if ("help") + help_shake_act(M) + else + if (istype(wear_mask, /obj/item/clothing/mask/muzzle)) + return + if (health > 0) + attacked += 10 + playsound(loc, 'bite.ogg', 50, 1, -1) + for(var/mob/O in viewers(src, null)) + if ((O.client && !( O.blinded ))) + O.show_message(text("\red [M.name] has bit [src]!"), 1) + adjustBruteLoss(rand(0, 1)) + updatehealth() + return + +/mob/living/carbon/amorph/attack_hand(mob/living/carbon/human/M as mob) + + if(M.gloves && istype(M.gloves,/obj/item/clothing/gloves)) + var/obj/item/clothing/gloves/G = M.gloves + if(G.cell) + if(M.a_intent == "hurt")//Stungloves. Any contact will stun the alien. + if(G.cell.charge >= 2500) + G.cell.charge -= 2500 + Weaken(5) + if (stuttering < 5) + stuttering = 5 + Stun(5) + + for(var/mob/O in viewers(src, null)) + if (O.client) + O.show_message("\red [src] has been touched with the stun gloves by [M]!", 1, "\red You hear someone fall", 2) + return + else + M << "\red Not enough charge! " + return + + if (M.a_intent == "help") + help_shake_act(M) + else + if (M.a_intent == "hurt") + var/attack_verb + switch(M.mutantrace) + if("lizard") + attack_verb = "scratch" + if("plant") + attack_verb = "slash" + else + attack_verb = "punch" + + if(M.type == /mob/living/carbon/human/tajaran) + attack_verb = "slash" + + if ((prob(75) && health > 0)) + for(var/mob/O in viewers(src, null)) + if ((O.client && !( O.blinded ))) + O.show_message(text("\red [] has [attack_verb]ed [name]!", M), 1) + + var/damage = rand(5, 10) + if(M.type != /mob/living/carbon/human/tajaran) + playsound(loc, "punch", 25, 1, -1) + else if(M.type == /mob/living/carbon/human/tajaran) + damage += 10 + playsound(loc, 'slice.ogg', 25, 1, -1) + adjustBruteLoss(damage/10) + updatehealth() + else + if(M.type != /mob/living/carbon/human/tajaran) + playsound(loc, 'punchmiss.ogg', 25, 1, -1) + else if(M.type == /mob/living/carbon/human/tajaran) + playsound(loc, 'slashmiss.ogg', 25, 1, -1) + for(var/mob/O in viewers(src, null)) + if ((O.client && !( O.blinded ))) + O.show_message(text("\red [] has attempted to [attack_verb] [name]!", M), 1) + else + if (M.a_intent == "grab") + if (M == src) + return + + var/obj/item/weapon/grab/G = new /obj/item/weapon/grab( M ) + G.assailant = M + if (M.hand) + M.l_hand = G + else + M.r_hand = G + G.layer = 20 + G.affecting = src + grabbed_by += G + G.synch() + + LAssailant = M + + playsound(loc, 'thudswoosh.ogg', 50, 1, -1) + for(var/mob/O in viewers(src, null)) + O.show_message(text("\red [] has grabbed [name] passively!", M), 1) + + else + if (!( paralysis )) + drop_item() + playsound(loc, 'thudswoosh.ogg', 50, 1, -1) + for(var/mob/O in viewers(src, null)) + if ((O.client && !( O.blinded ))) + O.show_message(text("\red [] has disarmed [name]!", M), 1) + return + + + +/mob/living/carbon/amorph/attack_alien(mob/living/carbon/alien/humanoid/M as mob) + + switch(M.a_intent) + if ("help") + for(var/mob/O in viewers(src, null)) + if ((O.client && !( O.blinded ))) + O.show_message(text("\blue [M] caresses [src] with its scythe like arm."), 1) + + if ("hurt") + if ((prob(95) && health > 0)) + playsound(loc, 'slice.ogg', 25, 1, -1) + var/damage = rand(15, 30) + for(var/mob/O in viewers(src, null)) + if ((O.client && !( O.blinded ))) + O.show_message(text("\red [] has slashed [name]!", M), 1) + adjustBruteLoss(damage/10) + updatehealth() + react_to_attack(M) + else + playsound(loc, 'slashmiss.ogg', 25, 1, -1) + for(var/mob/O in viewers(src, null)) + if ((O.client && !( O.blinded ))) + O.show_message(text("\red [] has attempted to lunge at [name]!", M), 1) + + if ("grab") + if (M == src) + return + var/obj/item/weapon/grab/G = new /obj/item/weapon/grab( M ) + G.assailant = M + if (M.hand) + M.l_hand = G + else + M.r_hand = G + G.layer = 20 + G.affecting = src + grabbed_by += G + G.synch() + + LAssailant = M + + playsound(loc, 'thudswoosh.ogg', 50, 1, -1) + for(var/mob/O in viewers(src, null)) + O.show_message(text("\red [] has grabbed [name] passively!", M), 1) + + if ("disarm") + playsound(loc, 'pierce.ogg', 25, 1, -1) + var/damage = 5 + if(prob(95)) + Weaken(rand(10,15)) + for(var/mob/O in viewers(src, null)) + if ((O.client && !( O.blinded ))) + O.show_message(text("\red [] has tackled down [name]!", M), 1) + else + drop_item() + for(var/mob/O in viewers(src, null)) + if ((O.client && !( O.blinded ))) + O.show_message(text("\red [] has disarmed [name]!", M), 1) + adjustBruteLoss(damage) + react_to_attack(M) + updatehealth() + return + + + +/mob/living/carbon/amorph/attack_animal(mob/living/simple_animal/M as mob) + if(M.melee_damage_upper == 0) + M.emote("[M.friendly] [src]") + else + for(var/mob/O in viewers(src, null)) + O.show_message("\red [M] [M.attacktext] [src]!", 1) + var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + bruteloss += damage + +/mob/living/carbon/amorph/attack_metroid(mob/living/carbon/metroid/M as mob) + if(M.Victim) return // can't attack while eating! + + if (health > -100) + + for(var/mob/O in viewers(src, null)) + if ((O.client && !( O.blinded ))) + O.show_message(text("\red The [M.name] has [pick("bit","slashed")] []!", src), 1) + + var/damage = rand(1, 3) + + if(istype(M, /mob/living/carbon/metroid/adult)) + damage = rand(10, 35) + else + damage = rand(5, 25) + + src.cloneloss += damage + + UpdateDamageIcon() + + + if(M.powerlevel > 0) + var/stunprob = 10 + var/power = M.powerlevel + rand(0,3) + + switch(M.powerlevel) + if(1 to 2) stunprob = 20 + if(3 to 4) stunprob = 30 + if(5 to 6) stunprob = 40 + if(7 to 8) stunprob = 60 + if(9) stunprob = 70 + if(10) stunprob = 95 + + if(prob(stunprob)) + M.powerlevel -= 3 + if(M.powerlevel < 0) + M.powerlevel = 0 + + for(var/mob/O in viewers(src, null)) + if ((O.client && !( O.blinded ))) + O.show_message(text("\red The [M.name] has shocked []!", src), 1) + + Weaken(power) + if (stuttering < power) + stuttering = power + Stun(power) + + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + + if (prob(stunprob) && M.powerlevel >= 8) + adjustFireLoss(M.powerlevel * rand(6,10)) + + + updatehealth() + + return diff --git a/code/modules/mob/living/carbon/amorph/amorph_damage.dm b/code/modules/mob/living/carbon/amorph/amorph_damage.dm new file mode 100644 index 0000000000..8e52d04292 --- /dev/null +++ b/code/modules/mob/living/carbon/amorph/amorph_damage.dm @@ -0,0 +1,12 @@ +/mob/living/carbon/amorph/proc/HealDamage(zone, brute, burn) + return heal_overall_damage(brute, burn) + +/mob/living/carbon/amorph/UpdateDamageIcon() + // no damage sprites for amorphs yet + return + +/mob/living/carbon/amorph/apply_damage(var/damage = 0,var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/sharp = 0, var/used_weapon = null) + if(damagetype == BRUTE) + take_overall_damage(damage, 0) + else + take_overall_damage(0, damage) \ No newline at end of file diff --git a/code/modules/mob/living/carbon/amorph/amorph_hud.dm b/code/modules/mob/living/carbon/amorph/amorph_hud.dm new file mode 100644 index 0000000000..91710d8053 --- /dev/null +++ b/code/modules/mob/living/carbon/amorph/amorph_hud.dm @@ -0,0 +1,290 @@ +/obj/hud/proc/amorph_hud(var/ui_style='screen1_old.dmi') + + src.adding = list( ) + src.other = list( ) + src.intents = list( ) + src.mon_blo = list( ) + src.m_ints = list( ) + src.mov_int = list( ) + src.vimpaired = list( ) + src.darkMask = list( ) + src.intent_small_hud_objects = list( ) + + src.g_dither = new src.h_type( src ) + src.g_dither.screen_loc = "WEST,SOUTH to EAST,NORTH" + src.g_dither.name = "Mask" + src.g_dither.icon = ui_style + src.g_dither.icon_state = "dither12g" + src.g_dither.layer = 18 + src.g_dither.mouse_opacity = 0 + + src.alien_view = new src.h_type(src) + src.alien_view.screen_loc = "WEST,SOUTH to EAST,NORTH" + src.alien_view.name = "Alien" + src.alien_view.icon = ui_style + src.alien_view.icon_state = "alien" + src.alien_view.layer = 18 + src.alien_view.mouse_opacity = 0 + + src.blurry = new src.h_type( src ) + src.blurry.screen_loc = "WEST,SOUTH to EAST,NORTH" + src.blurry.name = "Blurry" + src.blurry.icon = ui_style + src.blurry.icon_state = "blurry" + src.blurry.layer = 17 + src.blurry.mouse_opacity = 0 + + src.druggy = new src.h_type( src ) + src.druggy.screen_loc = "WEST,SOUTH to EAST,NORTH" + src.druggy.name = "Druggy" + src.druggy.icon = ui_style + src.druggy.icon_state = "druggy" + src.druggy.layer = 17 + src.druggy.mouse_opacity = 0 + + var/obj/screen/using + + using = new src.h_type( src ) + using.name = "act_intent" + using.dir = SOUTHWEST + using.icon = ui_style + using.icon_state = (mymob.a_intent == "hurt" ? "harm" : mymob.a_intent) + using.screen_loc = ui_acti + using.layer = 20 + src.adding += using + action_intent = using + +//intent small hud objects + using = new src.h_type( src ) + using.name = "help" + using.icon = ui_style + using.icon_state = (mymob.a_intent == "help" ? "help_small_active" : "help_small") + using.screen_loc = ui_help_small + using.layer = 21 + src.adding += using + help_intent = using + + using = new src.h_type( src ) + using.name = "disarm" + using.icon = ui_style + using.icon_state = (mymob.a_intent == "disarm" ? "disarm_small_active" : "disarm_small") + using.screen_loc = ui_disarm_small + using.layer = 21 + src.adding += using + disarm_intent = using + + using = new src.h_type( src ) + using.name = "grab" + using.icon = ui_style + using.icon_state = (mymob.a_intent == "grab" ? "grab_small_active" : "grab_small") + using.screen_loc = ui_grab_small + using.layer = 21 + src.adding += using + grab_intent = using + + using = new src.h_type( src ) + using.name = "harm" + using.icon = ui_style + using.icon_state = (mymob.a_intent == "hurt" ? "harm_small_active" : "harm_small") + using.screen_loc = ui_harm_small + using.layer = 21 + src.adding += using + hurt_intent = using + +//end intent small hud objects + + using = new src.h_type( src ) + using.name = "mov_intent" + using.dir = SOUTHWEST + using.icon = ui_style + using.icon_state = (mymob.m_intent == "run" ? "running" : "walking") + using.screen_loc = ui_movi + using.layer = 20 + src.adding += using + move_intent = using + + using = new src.h_type( src ) + using.name = "drop" + using.icon = ui_style + using.icon_state = "act_drop" + using.screen_loc = ui_dropbutton + using.layer = 19 + src.adding += using + + using = new src.h_type( src ) + using.name = "r_hand" + using.dir = WEST + using.icon = ui_style + using.icon_state = "hand_inactive" + if(mymob && !mymob.hand) //This being 0 or null means the right hand is in use + using.icon_state = "hand_active" + using.screen_loc = ui_rhand + using.layer = 19 + src.r_hand_hud_object = using + src.adding += using + + using = new src.h_type( src ) + using.name = "l_hand" + using.dir = EAST + using.icon = ui_style + using.icon_state = "hand_inactive" + if(mymob && mymob.hand) //This being 1 means the left hand is in use + using.icon_state = "hand_active" + using.screen_loc = ui_lhand + using.layer = 19 + src.l_hand_hud_object = using + src.adding += using + + using = new src.h_type( src ) + using.name = "hand" + using.dir = SOUTH + using.icon = ui_style + using.icon_state = "hand1" + using.screen_loc = ui_swaphand1 + using.layer = 19 + src.adding += using + + using = new src.h_type( src ) + using.name = "hand" + using.dir = SOUTH + using.icon = ui_style + using.icon_state = "hand2" + using.screen_loc = ui_swaphand2 + using.layer = 19 + src.adding += using + + using = new src.h_type( src ) + using.name = "mask" + using.dir = NORTH + using.icon = ui_style + using.icon_state = "equip" + using.screen_loc = ui_monkey_mask + using.layer = 19 + src.adding += using + + using = new src.h_type( src ) + using.name = "back" + using.dir = NORTHEAST + using.icon = ui_style + using.icon_state = "equip" + using.screen_loc = ui_back + using.layer = 19 + src.adding += using + + using = new src.h_type( src ) + using.name = null + using.icon = ui_style + using.icon_state = "dither50" + using.screen_loc = "1,1 to 5,15" + using.layer = 17 + using.mouse_opacity = 0 + src.vimpaired += using + using = new src.h_type( src ) + using.name = null + using.icon = ui_style + using.icon_state = "dither50" + using.screen_loc = "5,1 to 10,5" + using.layer = 17 + using.mouse_opacity = 0 + src.vimpaired += using + using = new src.h_type( src ) + using.name = null + using.icon = ui_style + using.icon_state = "dither50" + using.screen_loc = "6,11 to 10,15" + using.layer = 17 + using.mouse_opacity = 0 + src.vimpaired += using + using = new src.h_type( src ) + using.name = null + using.icon = ui_style + using.icon_state = "dither50" + using.screen_loc = "11,1 to 15,15" + using.layer = 17 + using.mouse_opacity = 0 + src.vimpaired += using + + mymob.throw_icon = new /obj/screen(null) + mymob.throw_icon.icon = ui_style + mymob.throw_icon.icon_state = "act_throw_off" + mymob.throw_icon.name = "throw" + mymob.throw_icon.screen_loc = ui_throw + + mymob.oxygen = new /obj/screen( null ) + mymob.oxygen.icon = ui_style + mymob.oxygen.icon_state = "oxy0" + mymob.oxygen.name = "oxygen" + mymob.oxygen.screen_loc = ui_oxygen + + mymob.pressure = new /obj/screen( null ) + mymob.pressure.icon = ui_style + mymob.pressure.icon_state = "pressure0" + mymob.pressure.name = "pressure" + mymob.pressure.screen_loc = ui_pressure + + mymob.toxin = new /obj/screen( null ) + mymob.toxin.icon = ui_style + mymob.toxin.icon_state = "tox0" + mymob.toxin.name = "toxin" + mymob.toxin.screen_loc = ui_toxin + + mymob.internals = new /obj/screen( null ) + mymob.internals.icon = ui_style + mymob.internals.icon_state = "internal0" + mymob.internals.name = "internal" + mymob.internals.screen_loc = ui_internal + + mymob.fire = new /obj/screen( null ) + mymob.fire.icon = ui_style + mymob.fire.icon_state = "fire0" + mymob.fire.name = "fire" + mymob.fire.screen_loc = ui_fire + + mymob.bodytemp = new /obj/screen( null ) + mymob.bodytemp.icon = ui_style + mymob.bodytemp.icon_state = "temp1" + mymob.bodytemp.name = "body temperature" + mymob.bodytemp.screen_loc = ui_temp + + mymob.healths = new /obj/screen( null ) + mymob.healths.icon = ui_style + mymob.healths.icon_state = "health0" + mymob.healths.name = "health" + mymob.healths.screen_loc = ui_health + + mymob.pullin = new /obj/screen( null ) + mymob.pullin.icon = ui_style + mymob.pullin.icon_state = "pull0" + mymob.pullin.name = "pull" + mymob.pullin.screen_loc = ui_pull + + mymob.blind = new /obj/screen( null ) + mymob.blind.icon = ui_style + mymob.blind.icon_state = "blackanimate" + mymob.blind.name = " " + mymob.blind.screen_loc = "1,1 to 15,15" + mymob.blind.layer = 0 + mymob.blind.mouse_opacity = 0 + + mymob.flash = new /obj/screen( null ) + mymob.flash.icon = ui_style + mymob.flash.icon_state = "blank" + mymob.flash.name = "flash" + mymob.flash.screen_loc = "1,1 to 15,15" + mymob.flash.layer = 17 + + mymob.zone_sel = new /obj/screen/zone_sel( null ) + mymob.zone_sel.overlays = null + mymob.zone_sel.overlays += image("icon" = 'zone_sel.dmi', "icon_state" = text("[]", mymob.zone_sel.selecting)) + + mymob.gun_setting_icon = new /obj/screen/gun/mode(null) + + mymob.client.screen = null + + //, mymob.i_select, mymob.m_select + mymob.client.screen += list( mymob.throw_icon, mymob.zone_sel, mymob.oxygen, mymob.pressure, mymob.toxin, mymob.bodytemp, mymob.internals, mymob.fire, mymob.healths, mymob.pullin, mymob.blind, mymob.flash, mymob.gun_setting_icon) //, mymob.hands, mymob.rest, mymob.sleep, mymob.mach, mymob.hands, ) + mymob.client.screen += src.adding + src.other + + //if(istype(mymob,/mob/living/carbon/monkey)) mymob.client.screen += src.mon_blo + + return diff --git a/code/modules/mob/living/carbon/amorph/life.dm b/code/modules/mob/living/carbon/amorph/life.dm new file mode 100644 index 0000000000..51bc3d833e --- /dev/null +++ b/code/modules/mob/living/carbon/amorph/life.dm @@ -0,0 +1,516 @@ +/mob/living/carbon/amorph + var/obj/item/weapon/card/id/wear_id = null // Fix for station bounced radios -- Skie + + var/oxygen_alert = 0 + var/toxins_alert = 0 + var/fire_alert = 0 + + var/temperature_alert = 0 + + +/mob/living/carbon/amorph/Life() + set invisibility = 0 + set background = 1 + + if (src.monkeyizing) + return + + ..() + + var/datum/gas_mixture/environment // Added to prevent null location errors-- TLE + if(src.loc) + environment = loc.return_air() + + if (src.stat != 2) //still breathing + + //First, resolve location and get a breath + + if(air_master.current_cycle%4==2) + //Only try to take a breath every 4 seconds, unless suffocating + breathe() + + else //Still give containing object the chance to interact + if(istype(loc, /obj/)) + var/obj/location_as_object = loc + location_as_object.handle_internal_lifeform(src, 0) + + //Apparently, the person who wrote this code designed it so that + //blinded get reset each cycle and then get activated later in the + //code. Very ugly. I dont care. Moving this stuff here so its easy + //to find it. + src.blinded = null + + //Disease Check + handle_virus_updates() + + //Handle temperature/pressure differences between body and environment + if(environment) // More error checking -- TLE + handle_environment(environment) + + //Mutations and radiation + handle_mutations_and_radiation() + + //Chemicals in the body + handle_chemicals_in_body() + + //Disabilities + handle_disabilities() + + //Status updates, death etc. + UpdateLuminosity() + handle_regular_status_updates() + + if(client) + handle_regular_hud_updates() + + //Being buckled to a chair or bed + check_if_buckled() + + // Yup. + update_canmove() + + clamp_values() + + // Grabbing + for(var/obj/item/weapon/grab/G in src) + G.process() + +/mob/living/carbon/amorph + proc + + clamp_values() + + AdjustStunned(0) + AdjustParalysis(0) + AdjustWeakened(0) + + handle_disabilities() + if (src.disabilities & 4) + if ((prob(5) && src.paralysis <= 1 && src.r_ch_cou < 1)) + src.drop_item() + spawn( 0 ) + emote("cough") + return + if (src.disabilities & 8) + if ((prob(10) && src.paralysis <= 1 && src.r_Tourette < 1)) + Stun(10) + spawn( 0 ) + emote("twitch") + return + if (src.disabilities & 16) + if (prob(10)) + src.stuttering = max(10, src.stuttering) + + update_mind() + if(!mind && client) + mind = new + mind.current = src + mind.key = key + + handle_mutations_and_radiation() + // amorphs are immune to this stuff + + breathe() + if(src.reagents) + + if(src.reagents.has_reagent("lexorin")) return + + if(!loc) return //probably ought to make a proper fix for this, but :effort: --NeoFite + + var/datum/gas_mixture/environment = loc.return_air() + var/datum/gas_mixture/breath + + if(losebreath>0) //Suffocating so do not take a breath + src.losebreath-- + if (prob(75)) //High chance of gasping for air + spawn emote("gasp") + if(istype(loc, /obj/)) + var/obj/location_as_object = loc + location_as_object.handle_internal_lifeform(src, 0) + else + //First, check for air from internal atmosphere (using an air tank and mask generally) + breath = get_breath_from_internal(BREATH_VOLUME) + + //No breath from internal atmosphere so get breath from location + if(!breath) + if(istype(loc, /obj/)) + var/obj/location_as_object = loc + breath = location_as_object.handle_internal_lifeform(src, BREATH_VOLUME) + else if(istype(loc, /turf/)) + var/breath_moles = environment.total_moles*BREATH_PERCENTAGE + breath = loc.remove_air(breath_moles) + + // Handle chem smoke effect -- Doohl + var/block = 0 + if(wear_mask) + if(istype(wear_mask, /obj/item/clothing/mask/gas)) + block = 1 + + if(!block) + + for(var/obj/effect/effect/chem_smoke/smoke in view(1, src)) + if(smoke.reagents.total_volume) + smoke.reagents.reaction(src, INGEST) + spawn(5) + if(smoke) + smoke.reagents.copy_to(src, 10) // I dunno, maybe the reagents enter the blood stream through the lungs? + break // If they breathe in the nasty stuff once, no need to continue checking + + + else //Still give containing object the chance to interact + if(istype(loc, /obj/)) + var/obj/location_as_object = loc + location_as_object.handle_internal_lifeform(src, 0) + + handle_breath(breath) + + if(breath) + loc.assume_air(breath) + + + get_breath_from_internal(volume_needed) + if(internal) + if (!contents.Find(src.internal)) + internal = null + if (!wear_mask || !(wear_mask.flags|MASKINTERNALS) ) + internal = null + if(internal) + if (src.internals) + src.internals.icon_state = "internal1" + return internal.remove_air_volume(volume_needed) + else + if (src.internals) + src.internals.icon_state = "internal0" + return null + + update_canmove() + if(paralysis || stunned || weakened || buckled || (changeling && changeling.changeling_fakedeath)) canmove = 0 + else canmove = 1 + + handle_breath(datum/gas_mixture/breath) + if(src.nodamage) + return + + if(!breath || (breath.total_moles == 0)) + adjustOxyLoss(7) + + oxygen_alert = max(oxygen_alert, 1) + + return 0 + + var/safe_oxygen_min = 8 // Minimum safe partial pressure of O2, in kPa + //var/safe_oxygen_max = 140 // Maximum safe partial pressure of O2, in kPa (Not used for now) + var/SA_para_min = 0.5 + var/SA_sleep_min = 5 + var/oxygen_used = 0 + var/breath_pressure = (breath.total_moles*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME + + //Partial pressure of the O2 in our breath + var/O2_pp = (breath.oxygen/breath.total_moles)*breath_pressure + + if(O2_pp < safe_oxygen_min) // Too little oxygen + if(prob(20)) + spawn(0) emote("gasp") + if (O2_pp == 0) + O2_pp = 0.01 + var/ratio = safe_oxygen_min/O2_pp + adjustOxyLoss(min(5*ratio, 7)) // Don't fuck them up too fast (space only does 7 after all!) + oxygen_used = breath.oxygen*ratio/6 + oxygen_alert = max(oxygen_alert, 1) + else // We're in safe limits + adjustOxyLoss(-5) + oxygen_used = breath.oxygen/6 + oxygen_alert = 0 + + breath.oxygen -= oxygen_used + breath.carbon_dioxide += oxygen_used + + if(breath.trace_gases.len) // If there's some other shit in the air lets deal with it here. + for(var/datum/gas/sleeping_agent/SA in breath.trace_gases) + var/SA_pp = (SA.moles/breath.total_moles)*breath_pressure + if(SA_pp > SA_para_min) // Enough to make us paralysed for a bit + Paralyse(3) // 3 gives them one second to wake up and run away a bit! + if(SA_pp > SA_sleep_min) // Enough to make us sleep as well + src.sleeping = max(src.sleeping+2, 10) + else if(SA_pp > 0.01) // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning + if(prob(20)) + spawn(0) emote(pick("giggle", "laugh")) + + return 1 + + handle_environment(datum/gas_mixture/environment) + if(!environment) + return + var/environment_heat_capacity = environment.heat_capacity() + if(istype(loc, /turf/space)) + environment_heat_capacity = loc:heat_capacity + + if((environment.temperature > (T0C + 50)) || (environment.temperature < (T0C + 10))) + var/transfer_coefficient + + transfer_coefficient = 1 + if(wear_mask && (wear_mask.body_parts_covered & HEAD) && (environment.temperature < wear_mask.protective_temperature)) + transfer_coefficient *= wear_mask.heat_transfer_coefficient + + handle_temperature_damage(HEAD, environment.temperature, environment_heat_capacity*transfer_coefficient) + + if(stat==2) + bodytemperature += 0.1*(environment.temperature - bodytemperature)*environment_heat_capacity/(environment_heat_capacity + 270000) + + //Account for massive pressure differences + + + var/pressure = environment.return_pressure() + + // if(!wear_suit) Monkies cannot into space. + // if(!istype(wear_suit, /obj/item/clothing/suit/space)) + + /*if(pressure < 20) + if(prob(25)) + src << "You feel the splittle on your lips and the fluid on your eyes boiling away, the capillteries in your skin breaking." + adjustBruteLoss(5) + */ + + if(pressure > HAZARD_HIGH_PRESSURE) + + adjustBruteLoss(min((10+(round(pressure/(HIGH_STEP_PRESSURE)-2)*5)),MAX_PRESSURE_DAMAGE)) + + + + return //TODO: DEFERRED + + handle_temperature_damage(body_part, exposed_temperature, exposed_intensity) + if(src.nodamage) return + var/discomfort = min( abs(exposed_temperature - bodytemperature)*(exposed_intensity)/2000000, 1.0) + if(exposed_temperature > bodytemperature) + adjustFireLoss(20.0*discomfort) + + else + adjustFireLoss(5.0*discomfort) + + handle_chemicals_in_body() + // most chemicals will have no effect on amorphs + //if(reagents) reagents.metabolize(src) + + if (src.drowsyness) + src.drowsyness-- + src.eye_blurry = max(2, src.eye_blurry) + if (prob(5)) + src.sleeping += 1 + Paralyse(5) + + confused = max(0, confused - 1) + // decrement dizziness counter, clamped to 0 + if(resting) + dizziness = max(0, dizziness - 5) + else + dizziness = max(0, dizziness - 1) + + src.updatehealth() + + return //TODO: DEFERRED + + handle_regular_status_updates() + + health = 100 - (getOxyLoss() + getToxLoss() + getFireLoss() + getBruteLoss() + getCloneLoss()) + + if(getOxyLoss() > 25) Paralyse(3) + + if(src.sleeping) + Paralyse(5) + if (prob(1) && health) spawn(0) emote("snore") + + if(src.resting) + Weaken(5) + + if(health < config.health_threshold_dead && stat != 2) + death() + else if(src.health < config.health_threshold_crit) + if(src.health <= 20 && prob(1)) spawn(0) emote("gasp") + + // shuffle around the chemical effects for amorphs a little ;) + if(!src.reagents.has_reagent("antitoxin") && src.stat != 2) src.adjustOxyLoss(2) + + if(src.stat != 2) src.stat = 1 + Paralyse(5) + + if (src.stat != 2) //Alive. + + if (src.paralysis || src.stunned || src.weakened) //Stunned etc. + if (src.stunned > 0) + AdjustStunned(-1) + src.stat = 0 + if (src.weakened > 0) + AdjustWeakened(-1) + src.lying = 1 + src.stat = 0 + if (src.paralysis > 0) + AdjustParalysis(-1) + src.blinded = 1 + src.lying = 1 + src.stat = 1 + var/h = src.hand + src.hand = 0 + drop_item() + src.hand = 1 + drop_item() + src.hand = h + + else //Not stunned. + src.lying = 0 + src.stat = 0 + + else //Dead. + src.lying = 1 + src.blinded = 1 + src.stat = 2 + + if (src.stuttering) src.stuttering-- + if (src.slurring) src.slurring-- + + if (src.eye_blind) + src.eye_blind-- + src.blinded = 1 + + if (src.ear_deaf > 0) src.ear_deaf-- + if (src.ear_damage < 25) + src.ear_damage -= 0.05 + src.ear_damage = max(src.ear_damage, 0) + + src.density = !( src.lying ) + + if (src.disabilities & 128) + src.blinded = 1 + if (src.disabilities & 32) + src.ear_deaf = 1 + + if (src.eye_blurry > 0) + src.eye_blurry-- + src.eye_blurry = max(0, src.eye_blurry) + + if (src.druggy > 0) + src.druggy-- + src.druggy = max(0, src.druggy) + + return 1 + + handle_regular_hud_updates() + + if (src.stat == 2 || (XRAY in mutations)) + src.sight |= SEE_TURFS + src.sight |= SEE_MOBS + src.sight |= SEE_OBJS + src.see_in_dark = 8 + src.see_invisible = 2 + else if (src.stat != 2) + src.sight &= ~SEE_TURFS + src.sight &= ~SEE_MOBS + src.sight &= ~SEE_OBJS + src.see_in_dark = 2 + src.see_invisible = 0 + + if (src.sleep) + src.sleep.icon_state = text("sleep[]", src.sleeping > 0 ? 1 : 0) + src.sleep.overlays = null + if(src.sleeping_willingly) + src.sleep.overlays += icon(src.sleep.icon, "sleep_willing") + if (src.rest) src.rest.icon_state = text("rest[]", src.resting) + + if (src.healths) + if (src.stat != 2) + switch(health) + if(100 to INFINITY) + src.healths.icon_state = "health0" + if(80 to 100) + src.healths.icon_state = "health1" + if(60 to 80) + src.healths.icon_state = "health2" + if(40 to 60) + src.healths.icon_state = "health3" + if(20 to 40) + src.healths.icon_state = "health4" + if(0 to 20) + src.healths.icon_state = "health5" + else + src.healths.icon_state = "health6" + else + src.healths.icon_state = "health7" + + if (pressure) + var/datum/gas_mixture/environment = loc.return_air() + if(environment) + switch(environment.return_pressure()) + + if(HAZARD_HIGH_PRESSURE to INFINITY) + pressure.icon_state = "pressure2" + if(WARNING_HIGH_PRESSURE to HAZARD_HIGH_PRESSURE) + pressure.icon_state = "pressure1" + if(WARNING_LOW_PRESSURE to WARNING_HIGH_PRESSURE) + pressure.icon_state = "pressure0" + if(HAZARD_LOW_PRESSURE to WARNING_LOW_PRESSURE) + pressure.icon_state = "pressure-1" + else + pressure.icon_state = "pressure-2" + + if(src.pullin) src.pullin.icon_state = "pull[src.pulling ? 1 : 0]" + + + if (src.toxin) src.toxin.icon_state = "tox[src.toxins_alert ? 1 : 0]" + if (src.oxygen) src.oxygen.icon_state = "oxy[src.oxygen_alert ? 1 : 0]" + if (src.fire) src.fire.icon_state = "fire[src.fire_alert ? 1 : 0]" + //NOTE: the alerts dont reset when youre out of danger. dont blame me, + //blame the person who coded them. Temporary fix added. + + if(bodytemp) + switch(src.bodytemperature) //310.055 optimal body temp + if(345 to INFINITY) + src.bodytemp.icon_state = "temp4" + if(335 to 345) + src.bodytemp.icon_state = "temp3" + if(327 to 335) + src.bodytemp.icon_state = "temp2" + if(316 to 327) + src.bodytemp.icon_state = "temp1" + if(300 to 316) + src.bodytemp.icon_state = "temp0" + if(295 to 300) + src.bodytemp.icon_state = "temp-1" + if(280 to 295) + src.bodytemp.icon_state = "temp-2" + if(260 to 280) + src.bodytemp.icon_state = "temp-3" + else + src.bodytemp.icon_state = "temp-4" + + src.client.screen -= src.hud_used.blurry + src.client.screen -= src.hud_used.druggy + src.client.screen -= src.hud_used.vimpaired + + if ((src.blind && src.stat != 2)) + if ((src.blinded)) + src.blind.layer = 18 + else + src.blind.layer = 0 + + if (src.disabilities & 1) + src.client.screen += src.hud_used.vimpaired + + if (src.eye_blurry) + src.client.screen += src.hud_used.blurry + + if (src.druggy) + src.client.screen += src.hud_used.druggy + + if (src.stat != 2) + if (src.machine) + if (!( src.machine.check_eye(src) )) + src.reset_view(null) + else + if(!client.adminobs) + reset_view(null) + + return 1 + + handle_virus_updates() + // amorphs can't come down with human diseases + return \ No newline at end of file diff --git a/code/modules/mob/living/carbon/amorph/say.dm b/code/modules/mob/living/carbon/amorph/say.dm new file mode 100644 index 0000000000..4663a847a1 --- /dev/null +++ b/code/modules/mob/living/carbon/amorph/say.dm @@ -0,0 +1,6 @@ +/mob/living/carbon/amorph/emote(var/act,var/m_type=1,var/message = null) + if(act == "me") + return custom_emote(m_type, message) + +/mob/living/carbon/amorph/say_quote(var/text) + return "[src.say_message], \"[text]\""; diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index c01be193cc..a844f2cc78 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -5,6 +5,9 @@ return 1 return 0 +/proc/isamorph(A) + return istype(A, /mob/living/carbon/amorph) + /proc/ismonkey(A) if(A && istype(A, /mob/living/carbon/monkey)) return 1