diff --git a/code/ZAS/Airflow.dm b/code/ZAS/Airflow.dm index 01a84fdf5d..8dd637e58f 100644 --- a/code/ZAS/Airflow.dm +++ b/code/ZAS/Airflow.dm @@ -228,13 +228,16 @@ mob/living/carbon/human/airflow_hit(atom/A) var/b_loss = airflow_speed * vsc.airflow_damage var/blocked = run_armor_check(BP_HEAD,"melee") - apply_damage(b_loss/3, BRUTE, BP_HEAD, blocked, 0, "Airflow") + var/soaked = get_armor_soak(BP_HEAD,"melee") + apply_damage(b_loss/3, BRUTE, BP_HEAD, blocked, soaked, 0, "Airflow") blocked = run_armor_check(BP_TORSO,"melee") - apply_damage(b_loss/3, BRUTE, BP_TORSO, blocked, 0, "Airflow") + soaked = get_armor_soak(BP_TORSO,"melee") + apply_damage(b_loss/3, BRUTE, BP_TORSO, blocked, soaked, 0, "Airflow") blocked = run_armor_check(BP_GROIN,"melee") - apply_damage(b_loss/3, BRUTE, BP_GROIN, blocked, 0, "Airflow") + soaked = get_armor_soak(BP_GROIN,"melee") + apply_damage(b_loss/3, BRUTE, BP_GROIN, blocked, soaked, 0, "Airflow") if(airflow_speed > 10) Paralyse(round(airflow_speed * vsc.airflow_stun)) diff --git a/code/controllers/communications.dm b/code/controllers/communications.dm index 97d193bc5b..796fb0b664 100644 --- a/code/controllers/communications.dm +++ b/code/controllers/communications.dm @@ -108,6 +108,7 @@ var/const/ERT_FREQ = 1345 var/const/AI_FREQ = 1343 var/const/DTH_FREQ = 1341 var/const/SYND_FREQ = 1213 +var/const/RAID_FREQ = 1277 var/const/ENT_FREQ = 1461 //entertainment frequency. This is not a diona exclusive frequency. // department channels @@ -133,6 +134,7 @@ var/list/radiochannels = list( "Response Team" = ERT_FREQ, "Special Ops" = DTH_FREQ, "Mercenary" = SYND_FREQ, + "Raider" = RAID_FREQ, "Supply" = SUP_FREQ, "Service" = SRV_FREQ, "AI Private" = AI_FREQ, @@ -145,7 +147,7 @@ var/list/radiochannels = list( var/list/CENT_FREQS = list(ERT_FREQ, DTH_FREQ) // Antag channels, i.e. Syndicate -var/list/ANTAG_FREQS = list(SYND_FREQ) +var/list/ANTAG_FREQS = list(SYND_FREQ, RAID_FREQ) //Department channels, arranged lexically var/list/DEPT_FREQS = list(AI_FREQ, COMM_FREQ, ENG_FREQ, ENT_FREQ, MED_FREQ, SEC_FREQ, SCI_FREQ, SRV_FREQ, SUP_FREQ) diff --git a/code/datums/supplypacks/medical.dm b/code/datums/supplypacks/medical.dm index e7b29fd8d9..13a69cfc1a 100644 --- a/code/datums/supplypacks/medical.dm +++ b/code/datums/supplypacks/medical.dm @@ -66,6 +66,27 @@ containername = "Surgery crate" access = access_medical +/datum/supply_packs/med/deathalarm + name = "Death Alarm crate" + contains = list( + /obj/item/weapon/storage/box/cdeathalarm_kit, + /obj/item/weapon/storage/box/cdeathalarm_kit + ) + cost = 40 + containertype = "/obj/structure/closet/crate/secure" + containername = "Death Alarm crate" + access = access_medical + +/datum/supply_packs/med/clotting + name = "Clotting Medicine crate" + contains = list( + /obj/item/weapon/storage/firstaid/clotting + ) + cost = 40 + containertype = "/obj/structure/closet/crate/secure" + containername = "Clotting Medicine crate" + access = access_medical + /datum/supply_packs/med/sterile name = "Sterile equipment crate" contains = list( diff --git a/code/datums/uplink/medical.dm b/code/datums/uplink/medical.dm index 381221127d..eab9d9cd02 100644 --- a/code/datums/uplink/medical.dm +++ b/code/datums/uplink/medical.dm @@ -19,6 +19,16 @@ item_cost = 5 path = /obj/item/weapon/storage/box/ambrosia +/datum/uplink_item/item/medical/clotting + name = "Clotting Medicine injector" + item_cost = 10 + path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/clotting + +/datum/uplink_item/item/medical/bonemeds + name = "Bone Repair injector" + item_cost = 10 + path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/bonemed + /datum/uplink_item/item/medical/ambrosiadeusseeds name = "Box of 7x ambrosia deus seed packets" item_cost = 10 diff --git a/code/game/antagonist/antagonist_create.dm b/code/game/antagonist/antagonist_create.dm index 733ec3eda0..a53037ead3 100644 --- a/code/game/antagonist/antagonist_create.dm +++ b/code/game/antagonist/antagonist_create.dm @@ -43,12 +43,15 @@ /datum/antagonist/proc/create_radio(var/freq, var/mob/living/carbon/human/player) var/obj/item/device/radio/R - if(freq == SYND_FREQ) - R = new/obj/item/device/radio/headset/syndicate(player) - else - R = new/obj/item/device/radio/headset(player) + switch(freq) + if(SYND_FREQ) + R = new/obj/item/device/radio/headset/syndicate(player) + if(RAID_FREQ) + R = new/obj/item/device/radio/headset/raider(player) + else + R = new/obj/item/device/radio/headset(player) + R.set_frequency(freq) - R.set_frequency(freq) player.equip_to_slot_or_del(R, slot_l_ear) return R diff --git a/code/game/antagonist/outsider/raider.dm b/code/game/antagonist/outsider/raider.dm index 1a7da8954e..4124fc9af9 100644 --- a/code/game/antagonist/outsider/raider.dm +++ b/code/game/antagonist/outsider/raider.dm @@ -232,7 +232,7 @@ var/datum/antagonist/raider/raiders W.handle_item_insertion(id) player.equip_to_slot_or_del(W, slot_wear_id) spawn_money(rand(50,150)*10,W) - create_radio(SYND_FREQ, player) + create_radio(RAID_FREQ, player) return 1 diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index da165448da..75ba732950 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -305,7 +305,7 @@ if(subject.isSynthetic()) scantemp = "Error: Majority of subject is non-organic." return - if (subject.suiciding == 1) + if (subject.suiciding) scantemp = "Error: Subject's brain is not responding to scanning stimuli." return if ((!subject.ckey) || (!subject.client)) diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm index 7d27c94b65..9838d80722 100644 --- a/code/game/machinery/telecomms/broadcaster.dm +++ b/code/game/machinery/telecomms/broadcaster.dm @@ -374,6 +374,8 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept blackbox.msg_deathsquad += blackbox_msg if(SYND_FREQ) blackbox.msg_syndicate += blackbox_msg + if(RAID_FREQ) + blackbox.msg_raider += blackbox_msg if(SUP_FREQ) blackbox.msg_cargo += blackbox_msg if(SRV_FREQ) @@ -554,6 +556,8 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept blackbox.msg_deathsquad += blackbox_msg if(SYND_FREQ) blackbox.msg_syndicate += blackbox_msg + if(RAID_FREQ) + blackbox.msg_raider += blackbox_msg if(SUP_FREQ) blackbox.msg_cargo += blackbox_msg if(SRV_FREQ) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 0559b626e1..4316da3c49 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -44,6 +44,7 @@ var/slowdown = 0 // How much clothing is slowing you down. Negative values speeds you up var/canremove = 1 //Mostly for Ninja code at this point but basically will not allow the item to be removed if set to 0. /N var/list/armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + var/list/armorsoak = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) var/list/allowed = null //suit storage stuff. var/obj/item/device/uplink/hidden/hidden_uplink = null // All items can have an uplink hidden inside, just remember to add the triggers. var/zoomdevicename = null //name used for message when binoculars/scope is used diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm index f08585da31..7bec1014d7 100644 --- a/code/game/objects/items/devices/radio/encryptionkey.dm +++ b/code/game/objects/items/devices/radio/encryptionkey.dm @@ -19,6 +19,12 @@ origin_tech = list(TECH_ILLEGAL = 3) syndie = 1//Signifies that it de-crypts Syndicate transmissions +/obj/item/device/encryptionkey/raider + icon_state = "cypherkey" + channels = list("Raider" = 1) + origin_tech = list(TECH_ILLEGAL = 2) + syndie = 1 + /obj/item/device/encryptionkey/binary icon_state = "cypherkey" translate_binary = 1 diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index fce4ec3175..a56ea4fa98 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -75,6 +75,15 @@ syndie = 1 ks1type = /obj/item/device/encryptionkey/syndicate +/obj/item/device/radio/headset/raider + origin_tech = list(TECH_ILLEGAL = 2) + syndie = 1 + ks1type = /obj/item/device/encryptionkey/raider + +/obj/item/device/radio/headset/raider/initialize() + ..() + set_frequency(RAID_FREQ) + /obj/item/device/radio/headset/binary origin_tech = list(TECH_ILLEGAL = 3) ks1type = /obj/item/device/encryptionkey/binary @@ -289,6 +298,7 @@ desc = "The headset of the boss's boss." icon_state = "com_headset" item_state = "headset" + centComm = 1 // freerange = 1 ks2type = /obj/item/device/encryptionkey/ert diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index c480d2dd16..8e9a72d203 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -27,6 +27,8 @@ /obj/item/device/radio/intercom/specops name = "\improper Spec Ops intercom" frequency = ERT_FREQ + subspace_transmission = 1 + centComm = 1 /obj/item/device/radio/intercom/department canhear_range = 5 @@ -81,6 +83,17 @@ ..() internal_channels[num2text(SYND_FREQ)] = list(access_syndicate) +/obj/item/device/radio/intercom/raider + name = "illicit intercom" + desc = "Pirate radio, but not in the usual sense of the word." + frequency = RAID_FREQ + subspace_transmission = 1 + syndie = 1 + +/obj/item/device/radio/intercom/raider/New() + ..() + internal_channels[num2text(RAID_FREQ)] = list(access_syndicate) + /obj/item/device/radio/intercom/Destroy() processing_objects -= src ..() diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index cec23e818f..1bd7f9349a 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -40,6 +40,7 @@ var/global/list/default_medbay_channels = list( var/list/channels = list() //see communications.dm for full list. First channel is a "default" for :h var/subspace_transmission = 0 var/syndie = 0//Holder to see if it's a syndicate encrypted radio + var/centComm = 0//Holder to see if it's a CentComm encrypted radio flags = CONDUCT slot_flags = SLOT_BELT throw_speed = 2 @@ -508,6 +509,9 @@ var/global/list/default_medbay_channels = list( if(freq in ANTAG_FREQS) if(!(src.syndie))//Checks to see if it's allowed on that frequency, based on the encryption keys return -1 + if(freq in CENT_FREQS) + if(!(src.centComm))//Checks to see if it's allowed on that frequency, based on the encryption keys + return -1 if (!on) return -1 if (!freq) //recieved on main frequency diff --git a/code/game/objects/items/weapons/implants/implant.dm b/code/game/objects/items/weapons/implants/implant.dm index c3ddb4fb39..6554e80f8b 100644 --- a/code/game/objects/items/weapons/implants/implant.dm +++ b/code/game/objects/items/weapons/implants/implant.dm @@ -452,23 +452,27 @@ the implant may become unstable and either pre-maturely inject the subject or si var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) if(istype(t, /area/syndicate_station) || istype(t, /area/syndicate_mothership) || istype(t, /area/shuttle/syndicate_elite) ) //give the syndies a bit of stealth - a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm", "Security") - a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm", "Medical") + a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm", "General") +// a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm", "Security") +// a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm", "Medical") else - a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm", "Security") - a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm", "Medical") + a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm", "General") +// a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm", "Security") +// a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm", "Medical") qdel(a) processing_objects.Remove(src) if ("emp") var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) var/name = prob(50) ? t.name : pick(teleportlocs) - a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm", "Security") - a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm", "Medical") + a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm", "General") +// a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm", "Security") +// a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm", "Medical") qdel(a) else var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) - a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm", "Security") - a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm", "Medical") + a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm", "General") +// a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm", "Security") +// a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm", "Medical") qdel(a) processing_objects.Remove(src) diff --git a/code/game/objects/items/weapons/material/knives.dm b/code/game/objects/items/weapons/material/knives.dm index a70a253579..8ebf5697c9 100644 --- a/code/game/objects/items/weapons/material/knives.dm +++ b/code/game/objects/items/weapons/material/knives.dm @@ -35,6 +35,13 @@ icon_state = "switchblade" unbreakable = 1 +/obj/item/weapon/material/butterfly/boxcutter + name = "box cutter" + desc = "A thin, inexpensive razor-blade knife designed to open cardboard boxes." + icon_state = "boxcutter" + force_divisor = 0.1 // 6 when wielded with hardness 60 (steel) + thrown_force_divisor = 0.2 // 4 when thrown with weight 20 (steel) + /obj/item/weapon/material/butterfly/attack_self(mob/user) active = !active if(active) diff --git a/code/game/objects/items/weapons/material/shards.dm b/code/game/objects/items/weapons/material/shards.dm index 78ee869604..92cb782e8a 100644 --- a/code/game/objects/items/weapons/material/shards.dm +++ b/code/game/objects/items/weapons/material/shards.dm @@ -90,7 +90,7 @@ if(affecting.take_damage(force, 0)) H.UpdateDamageIcon() H.updatehealth() - if(affecting.can_feel_pain()) + if(affecting.organ_can_feel_pain()) H.Weaken(3) return check -= picked diff --git a/code/game/objects/items/weapons/storage/firstaid.dm b/code/game/objects/items/weapons/storage/firstaid.dm index 1c77e1f6cb..61a9a99f96 100644 --- a/code/game/objects/items/weapons/storage/firstaid.dm +++ b/code/game/objects/items/weapons/storage/firstaid.dm @@ -126,6 +126,7 @@ new /obj/item/weapon/storage/pill_bottle/dylovene(src) new /obj/item/weapon/storage/pill_bottle/tramadol(src) new /obj/item/weapon/storage/pill_bottle/spaceacillin(src) + new /obj/item/weapon/reagent_containers/hypospray/autoinjector/clotting(src) new /obj/item/stack/medical/splint(src) return @@ -150,6 +151,19 @@ new /obj/item/stack/medical/advanced/bruise_pack(src) return +/obj/item/weapon/storage/firstaid/clotting + name = "clotting kit" + desc = "Contains chemicals to stop bleeding." + max_storage_space = ITEMSIZE_COST_SMALL * 7 + +/obj/item/weapon/storage/firstaid/clotting/New() + ..() + if (empty) + return + for(var/i = 1 to 8) + new /obj/item/weapon/reagent_containers/hypospray/autoinjector/clotting(src) + return + /* * Pill Bottles */ diff --git a/code/game/objects/items/weapons/tools.dm b/code/game/objects/items/weapons/tools.dm index c39053b5aa..8e039ee2da 100644 --- a/code/game/objects/items/weapons/tools.dm +++ b/code/game/objects/items/weapons/tools.dm @@ -172,7 +172,8 @@ /obj/item/weapon/weldingtool/examine(mob/user) if(..(user, 0)) - user << text("\icon[] [] contains []/[] units of fuel!", src, src.name, get_fuel(),src.max_fuel ) + if(max_fuel) + user << text("\icon[] [] contains []/[] units of fuel!", src, src.name, get_fuel(),src.max_fuel ) /obj/item/weapon/weldingtool/attackby(obj/item/W as obj, mob/living/user as mob) @@ -234,18 +235,22 @@ /obj/item/weapon/weldingtool/afterattack(obj/O as obj, mob/user as mob, proximity) if(!proximity) return - if (istype(O, /obj/structure/reagent_dispensers/fueltank) && get_dist(src,O) <= 1 && !src.welding) - O.reagents.trans_to_obj(src, max_fuel) - user << "Welder refueled" - playsound(src.loc, 'sound/effects/refill.ogg', 50, 1, -6) - return - else if (istype(O, /obj/structure/reagent_dispensers/fueltank) && get_dist(src,O) <= 1 && src.welding) - message_admins("[key_name_admin(user)] triggered a fueltank explosion with a welding tool.") - log_game("[key_name(user)] triggered a fueltank explosion with a welding tool.") - user << "You begin welding on the fueltank and with a moment of lucidity you realize, this might not have been the smartest thing you've ever done." - var/obj/structure/reagent_dispensers/fueltank/tank = O - tank.explode() - return + if (istype(O, /obj/structure/reagent_dispensers/fueltank) && get_dist(src,O) <= 1 && !welding) + if(!welding && max_fuel) + O.reagents.trans_to_obj(src, max_fuel) + user << "Welder refueled" + playsound(src.loc, 'sound/effects/refill.ogg', 50, 1, -6) + return + else if(!welding) + user << "[src] doesn't use fuel." + return + else + message_admins("[key_name_admin(user)] triggered a fueltank explosion with a welding tool.") + log_game("[key_name(user)] triggered a fueltank explosion with a welding tool.") + user << "You begin welding on the fueltank and with a moment of lucidity you realize, this might not have been the smartest thing you've ever done." + var/obj/structure/reagent_dispensers/fueltank/tank = O + tank.explode() + return if (src.welding) remove_fuel(1) var/turf/location = get_turf(user) @@ -286,7 +291,7 @@ /obj/item/weapon/weldingtool/update_icon() ..() - icon_state = welding ? "welder1" : "welder" + icon_state = welding ? "[icon_state]1" : "[initial(icon_state)]" var/mob/M = loc if(istype(M)) M.update_inv_l_hand() @@ -313,7 +318,8 @@ processing_objects |= src else if(M) - M << "You need more welding fuel to complete this task." + var/msg = max_fuel ? "welding fuel" : "charge" + M << "You need more [msg] to complete this task." return //Otherwise else if(!set_welding && welding) @@ -370,7 +376,6 @@ user.disabilities &= ~NEARSIGHTED return - /obj/item/weapon/weldingtool/largetank name = "industrial welding tool" max_fuel = 40 @@ -405,6 +410,108 @@ if(get_fuel() < max_fuel && nextrefueltick < world.time) nextrefueltick = world.time + 10 reagents.add_reagent("fuel", 1) + +/* + * Electric/Arc Welder + */ + +/obj/item/weapon/weldingtool/electric //AND HIS WELDING WAS ELECTRIC + name = "electric welding tool" + icon_state = "arcwelder" + max_fuel = 0 //We'll handle the consumption later. + var/obj/item/weapon/cell/power_supply //What type of power cell this uses + var/charge_cost = 24 //The rough equivalent of 1 unit of fuel, based on us wanting 10 welds per battery + var/cell_type = null + var/use_external_power = 0 //If in a borg or hardsuit, this needs to = 1 + +/obj/item/weapon/weldingtool/electric/New() + ..() + if(cell_type) + power_supply = new cell_type(src) + else + power_supply = new /obj/item/weapon/cell/device(src) + update_icon() + +/obj/item/weapon/weldingtool/electric/examine(mob/user) + ..() + if(power_supply) + user << text("\icon[] [] has [] charge left.", src, src.name, get_fuel()) + else + user << text("\icon[] [] has power for no power cell!", src, src.name) + +/obj/item/weapon/weldingtool/electric/get_fuel() + if(use_external_power) + var/obj/item/weapon/cell/external = get_external_power_supply() + return external.charge + else if(power_supply) + return power_supply.charge + else + return 0 + +/obj/item/weapon/weldingtool/electric/remove_fuel(var/amount = 1, var/mob/M = null) + if(!welding) + return 0 + if(get_fuel() >= amount) + power_supply.checked_use(charge_cost) + if(use_external_power) + var/obj/item/weapon/cell/external = get_external_power_supply() + if(!external || !external.use(charge_cost)) //Take power from the borg... + power_supply.give(charge_cost) //Give it back to the cell. + if(M) + eyecheck(M) + return 1 + else + if(M) + M << "You need more energy to complete this task." + return 0 + +/obj/item/weapon/weldingtool/electric/attack_hand(mob/user as mob) + if(user.get_inactive_hand() == src) + if(power_supply) + power_supply.update_icon() + user.put_in_hands(power_supply) + power_supply = null + user << "You remove the cell from the [src]." + setWelding(0) + update_icon() + return + ..() + else + return ..() + +/obj/item/weapon/weldingtool/electric/attackby(obj/item/weapon/W, mob/user as mob) + if(istype(W, /obj/item/weapon/cell)) + if(istype(W, /obj/item/weapon/cell/device)) + if(!power_supply) + user.drop_item() + W.loc = src + power_supply = W + user << "You install a cell in \the [src]." + update_icon() + else + user << "\The [src] already has a cell." + else + user << "\The [src] cannot use that type of cell." + else + ..() + +/obj/item/weapon/weldingtool/electric/proc/get_external_power_supply() + if(isrobot(src.loc)) + var/mob/living/silicon/robot/R = src.loc + return R.cell + if(istype(src.loc, /obj/item/rig_module)) + var/obj/item/rig_module/module = src.loc + if(module.holder && module.holder.wearer) + var/mob/living/carbon/human/H = module.holder.wearer + if(istype(H) && H.back) + var/obj/item/weapon/rig/suit = H.back + if(istype(suit)) + return suit.cell + return null + +/obj/item/weapon/weldingtool/electric/mounted + use_external_power = 1 + /* * Crowbar */ diff --git a/code/game/objects/items/weapons/traps.dm b/code/game/objects/items/weapons/traps.dm index e5a655d7f9..cd11af3400 100644 --- a/code/game/objects/items/weapons/traps.dm +++ b/code/game/objects/items/weapons/traps.dm @@ -77,11 +77,15 @@ //armour var/blocked = L.run_armor_check(target_zone, "melee") + var/soaked = L.get_armor_soak(target_zone, "melee") if(blocked >= 100) return - if(!L.apply_damage(30, BRUTE, target_zone, blocked, used_weapon=src)) + if(soaked >= 30) + return + + if(!L.apply_damage(30, BRUTE, target_zone, blocked, soaked, used_weapon=src)) return 0 //trap the victim in place diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index f4e7f4d056..928246b2b5 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -174,7 +174,7 @@ return 0 if (user.stat || user.paralysis || user.sleeping || user.lying || user.weakened) return 0 - if (issilicon(user)) + if (isAI(user)) user << "You need hands for this." return 0 return 1 diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm index ae4fe4d740..0e6064785d 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm @@ -150,20 +150,22 @@ var/def_zone = ran_zone() var/blocked = occupant.run_armor_check(def_zone, "melee") + var/soaked = occupant.get_armor_soak(def_zone, "melee") occupant.throw_at(A, 3, propelled) occupant.apply_effect(6, STUN, blocked) occupant.apply_effect(6, WEAKEN, blocked) occupant.apply_effect(6, STUTTER, blocked) - occupant.apply_damage(10, BRUTE, def_zone, blocked) + occupant.apply_damage(10, BRUTE, def_zone, blocked, soaked) playsound(src.loc, 'sound/weapons/punch1.ogg', 50, 1, -1) if(istype(A, /mob/living)) var/mob/living/victim = A def_zone = ran_zone() blocked = victim.run_armor_check(def_zone, "melee") + soaked = victim.get_armor_soak(def_zone, "melee") victim.apply_effect(6, STUN, blocked) victim.apply_effect(6, WEAKEN, blocked) victim.apply_effect(6, STUTTER, blocked) - victim.apply_damage(10, BRUTE, def_zone, blocked) + victim.apply_damage(10, BRUTE, def_zone, blocked, soaked) occupant.visible_message("[occupant] crashed into \the [A]!") /obj/structure/bed/chair/office/light diff --git a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm index 2bcbf8aa87..9ae19769eb 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm @@ -149,20 +149,22 @@ var/def_zone = ran_zone() var/blocked = occupant.run_armor_check(def_zone, "melee") + var/soaked = occupant.get_armor_soak(def_zone, "melee") occupant.throw_at(A, 3, propelled) occupant.apply_effect(6, STUN, blocked) occupant.apply_effect(6, WEAKEN, blocked) occupant.apply_effect(6, STUTTER, blocked) - occupant.apply_damage(10, BRUTE, def_zone) + occupant.apply_damage(10, BRUTE, def_zone, soaked) playsound(src.loc, 'sound/weapons/punch1.ogg', 50, 1, -1) if(istype(A, /mob/living)) var/mob/living/victim = A def_zone = ran_zone() blocked = victim.run_armor_check(def_zone, "melee") + soaked = victim.get_armor_soak(def_zone, "melee") victim.apply_effect(6, STUN, blocked) victim.apply_effect(6, WEAKEN, blocked) victim.apply_effect(6, STUTTER, blocked) - victim.apply_damage(10, BRUTE, def_zone) + victim.apply_damage(10, BRUTE, def_zone, soaked) if(pulling) occupant.visible_message("[pulling] has thrusted \the [name] into \the [A], throwing \the [occupant] out of it!") diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index 08da1a66dd..25ad0ce9cd 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -178,3 +178,5 @@ this.blood_DNA["UNKNOWN BLOOD"] = "X*" else if( istype(M, /mob/living/silicon/robot )) new /obj/effect/decal/cleanable/blood/oil(src) + else if(ishuman(M)) + add_blood(M) \ No newline at end of file diff --git a/code/game/verbs/suicide.dm b/code/game/verbs/suicide.dm index 874b6f3998..ec84ee4232 100644 --- a/code/game/verbs/suicide.dm +++ b/code/game/verbs/suicide.dm @@ -26,7 +26,7 @@ if(!canmove || restrained()) //just while I finish up the new 'fun' suiciding verb. This is to prevent metagaming via suicide src << "You can't commit suicide whilst restrained! ((You can type Ghost instead however.))" return - suiciding = 1 + suiciding = 15 does_not_breathe = 0 //Prevents ling-suicide zombies, or something var/obj/item/held_item = get_active_hand() if(held_item) @@ -73,10 +73,16 @@ return log_and_message_admins("[key_name(src)] commited suicide") - viewers(src) << pick("[src] is attempting to bite \his tongue off! It looks like \he's trying to commit suicide.", \ + + var/suicidemsg + suicidemsg = pick("[src] is attempting to bite \his tongue off! It looks like \he's trying to commit suicide.", \ "[src] is jamming \his thumbs into \his eye sockets! It looks like \he's trying to commit suicide.", \ "[src] is twisting \his own neck! It looks like \he's trying to commit suicide.", \ "[src] is holding \his breath! It looks like \he's trying to commit suicide.") + if(isSynthetic()) + suicidemsg = "[src] is attempting to switch \his power off! It looks like \he's trying to commit suicide." + visible_message(suicidemsg) + adjustOxyLoss(max(175 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0)) updatehealth() @@ -172,7 +178,7 @@ var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No") if(confirm == "Yes") - suiciding = 1 + suiciding = 30 setOxyLoss(100) adjustBruteLoss(100 - getBruteLoss()) setToxLoss(100) diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm index bcd8163853..820543f082 100644 --- a/code/modules/clothing/spacesuits/rig/modules/utility.dm +++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm @@ -157,6 +157,7 @@ list("antibiotics", "spaceacillin", 0, 20), list("antitoxins", "anti_toxin", 0, 20), list("nutrients", "glucose", 0, 80), + list("clotting agent", "myelamine", 0, 80), list("hyronalin", "hyronalin", 0, 20), list("radium", "radium", 0, 20) ) @@ -246,6 +247,7 @@ list("hyperzine", "hyperzine", 0, 30), list("oxycodone", "oxycodone", 0, 30), list("nutrients", "glucose", 0, 80), + list("clotting agent", "myelamine", 0, 80) ) interface_name = "combat chem dispenser" @@ -263,6 +265,20 @@ interface_name = "mounted chem injector" interface_desc = "Dispenses loaded chemicals via an arm-mounted injector." +/obj/item/rig_module/chem_dispenser/injector/advanced + + charges = list( + list("tricordrazine", "tricordrazine", 0, 80), + list("tramadol", "tramadol", 0, 80), + list("dexalin plus", "dexalinp", 0, 80), + list("antibiotics", "spaceacillin", 0, 80), + list("antitoxins", "anti_toxin", 0, 80), + list("nutrients", "glucose", 0, 80), + list("hyronalin", "hyronalin", 0, 80), + list("radium", "radium", 0, 80), + list("clotting agent", "myelamine", 0, 80) + ) + /obj/item/rig_module/voice name = "hardsuit voice synthesiser" diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index 0bd14ef9d1..855aa49c7e 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -161,6 +161,7 @@ piece.permeability_coefficient = permeability_coefficient piece.unacidable = unacidable if(islist(armor)) piece.armor = armor.Copy() + if(islist(armorsoak)) piece.armorsoak = armorsoak.Copy() update_icon(1) diff --git a/code/modules/clothing/spacesuits/rig/suits/ert.dm b/code/modules/clothing/spacesuits/rig/suits/ert.dm index 8b13a57c8d..5d0dd4cb8f 100644 --- a/code/modules/clothing/spacesuits/rig/suits/ert.dm +++ b/code/modules/clothing/spacesuits/rig/suits/ert.dm @@ -49,7 +49,7 @@ /obj/item/rig_module/ai_container, /obj/item/rig_module/maneuvering_jets, /obj/item/rig_module/device/healthscanner, - /obj/item/rig_module/chem_dispenser/injector + /obj/item/rig_module/chem_dispenser/injector/advanced ) /obj/item/weapon/rig/ert/security diff --git a/code/modules/food/recipes_microwave.dm b/code/modules/food/recipes_microwave.dm index 3d63261a95..756aab62e4 100644 --- a/code/modules/food/recipes_microwave.dm +++ b/code/modules/food/recipes_microwave.dm @@ -105,7 +105,7 @@ I said no! /datum/recipe/xenoburger items = list( /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/xenomeat + /obj/item/weapon/reagent_containers/food/snacks/spidermeat // /obj/item/weapon/reagent_containers/food/snacks/xenomeat ) result = /obj/item/weapon/reagent_containers/food/snacks/xenoburger @@ -224,9 +224,9 @@ I said no! /obj/item/weapon/reagent_containers/food/snacks/dough, /obj/item/weapon/reagent_containers/food/snacks/dough, /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/xenomeat, - /obj/item/weapon/reagent_containers/food/snacks/xenomeat, - /obj/item/weapon/reagent_containers/food/snacks/xenomeat, + /obj/item/weapon/reagent_containers/food/snacks/spidermeat, //xenomeat, + /obj/item/weapon/reagent_containers/food/snacks/spidermeat, //xenomeat, + /obj/item/weapon/reagent_containers/food/snacks/spidermeat, //xenomeat, /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index f6388e0190..1d92705dec 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -114,8 +114,11 @@ if(!target_limb) target_limb = pick(BP_ALL) var/blocked = target.run_armor_check(target_limb, "melee") + var/soaked = target.get_armor_soak(target_limb, "melee") + if(blocked >= 100) return + var/obj/item/organ/external/affecting = target.get_organ(target_limb) var/damage = 0 var/has_edge = 0 @@ -125,7 +128,7 @@ if(affecting) to_chat(target, "\The [fruit]'s thorns pierce your [affecting.name] greedily!") - target.apply_damage(damage, BRUTE, target_limb, blocked, "Thorns", sharp=1, edge=has_edge) + target.apply_damage(damage, BRUTE, target_limb, blocked, soaked, "Thorns", sharp=1, edge=has_edge) else to_chat(target, "\The [fruit]'s thorns pierce your flesh greedily!") target.adjustBruteLoss(damage) diff --git a/code/modules/mob/living/carbon/brain/life.dm b/code/modules/mob/living/carbon/brain/life.dm index cb889b570f..3f41f21ce5 100644 --- a/code/modules/mob/living/carbon/brain/life.dm +++ b/code/modules/mob/living/carbon/brain/life.dm @@ -71,15 +71,11 @@ /mob/living/carbon/brain/handle_chemicals_in_body() chem_effects.Cut() - analgesic = 0 if(touching) touching.metabolize() if(ingested) ingested.metabolize() if(bloodstr) bloodstr.metabolize() - if(CE_PAINKILLER in chem_effects) - analgesic = chem_effects[CE_PAINKILLER] - confused = max(0, confused - 1) // decrement dizziness counter, clamped to 0 if(resting) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index db59f6bc83..66ba7ecd51 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -4,7 +4,7 @@ return null ..() -/mob/living/carbon/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/hit_zone) +/mob/living/carbon/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/soaked, var/hit_zone) if(!effective_force || blocked >= 100) return 0 @@ -12,6 +12,10 @@ if(HULK in user.mutations) effective_force *= 2 + //If the armor soaks all of the damage, it just skips the rest of the checks + if(effective_force <= soaked) + return 0 + //Apply weapon damage var/weapon_sharp = is_sharp(I) var/weapon_edge = has_edge(I) diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index d6deb9b964..532b2daa7c 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -7,8 +7,7 @@ var/last_eating = 0 //Not sure what this does... I found it hidden in food.dm var/life_tick = 0 // The amount of life ticks that have processed on this mob. - var/analgesic = 0 // when this is set, the mob isn't affected by shock or pain - // life should decrease this by 1 every tick + // total amount of wounds on mob, used to spread out healing and the like over all wounds var/number_wounds = 0 var/obj/item/handcuffed = null //Whether or not the mob is handcuffed diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index fb18cd2df2..fad3c7de1b 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1050,7 +1050,7 @@ "A spike of pain jolts your [organ.name] as you bump [O] inside.", \ "Your movement jostles [O] in your [organ.name] painfully.", \ "Your movement jostles [O] in your [organ.name] painfully.") - src << msg + custom_pain(msg, 40) organ.take_damage(rand(1,3), 0, 0) if(!(organ.robotic >= ORGAN_ROBOT) && (should_have_organ(O_HEART))) //There is no blood in protheses. @@ -1489,7 +1489,7 @@ if(check_organ) if(!istype(check_organ)) return 0 - return check_organ.can_feel_pain() + return check_organ.organ_can_feel_pain() return !(species.flags & NO_PAIN) /mob/living/carbon/human/is_muzzled() diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index fccf6afbf6..01005b031b 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -43,6 +43,7 @@ return 0 var/obj/item/organ/external/affecting = get_organ(ran_zone(H.zone_sel.selecting)) var/armor_block = run_armor_check(affecting, "melee") + var/armor_soak = get_armor_soak(affecting, "melee") if(HULK in H.mutations) damage += 5 @@ -51,7 +52,10 @@ visible_message("\red [H] has punched [src]!") - apply_damage(damage, HALLOSS, affecting, armor_block) + if(armor_soak >= damage) + return + + apply_damage(damage, HALLOSS, affecting, armor_block, armor_soak) if(damage >= 9) visible_message("\red [H] has weakened [src]!") apply_effect(4, WEAKEN, armor_block) @@ -245,11 +249,12 @@ real_damage = max(1, real_damage) var/armour = run_armor_check(affecting, "melee") + var/soaked = get_armor_soak(affecting, "melee") // Apply additional unarmed effects. attack.apply_effects(H, src, armour, rand_damage, hit_zone) // Finally, apply damage to target - apply_damage(real_damage, (attack.deal_halloss ? HALLOSS : BRUTE), affecting, armour, sharp=attack.sharp, edge=attack.edge) + apply_damage(real_damage, (attack.deal_halloss ? HALLOSS : BRUTE), affecting, armour, soaked, sharp=attack.sharp, edge=attack.edge) if(I_DISARM) M.attack_log += text("\[[time_stamp()]\] Disarmed [src.name] ([src.ckey])") @@ -325,7 +330,8 @@ var/dam_zone = pick(organs_by_name) var/obj/item/organ/external/affecting = get_organ(ran_zone(dam_zone)) var/armor_block = run_armor_check(affecting, "melee") - apply_damage(damage, BRUTE, affecting, armor_block) + var/armor_soak = get_armor_soak(affecting, "melee") + apply_damage(damage, BRUTE, affecting, armor_block, armor_soak) updatehealth() return 1 diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index 50b2732eeb..fa3d19ebab 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -376,7 +376,7 @@ This function restores all organs. if((damagetype != BRUTE) && (damagetype != BURN)) if(damagetype == HALLOSS) if((damage > 25 && prob(20)) || (damage > 50 && prob(60))) - if(organ && organ.can_feel_pain()) + if(organ && organ.organ_can_feel_pain()) emote("scream") ..(damage, damagetype, def_zone, blocked) return 1 diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index a6471c4aeb..86bbdebc15 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -67,7 +67,7 @@ emp_act emote("me", 1, "drops what they were holding, their [affected.name] malfunctioning!") else var/emote_scream = pick("screams in pain and ", "lets out a sharp cry and ", "cries out and ") - emote("me", 1, "[affected.can_feel_pain() ? "" : emote_scream]drops what they were holding in their [affected.name]!") + emote("me", 1, "[affected.organ_can_feel_pain() ? "" : emote_scream] drops what they were holding in their [affected.name]!") ..(stun_amount, agony_amount, def_zone) @@ -93,6 +93,29 @@ emp_act total += weight return (armorval/max(total, 1)) +//Like getarmor, but the value it returns will be numerical damage reduction +/mob/living/carbon/human/getsoak(var/def_zone, var/type) + var/soakval = 0 + var/total = 0 + + if(def_zone) + if(isorgan(def_zone)) + return getsoak_organ(def_zone, type) + var/obj/item/organ/external/affecting = get_organ(def_zone) + if(affecting) + return getsoak_organ(affecting, type) + //If a specific bodypart is targetted, check how that bodypart is protected and return the value. + + //If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values + for(var/organ_name in organs_by_name) + if (organ_name in organ_rel_size) + var/obj/item/organ/external/organ = organs_by_name[organ_name] + if(organ) + var/weight = organ_rel_size[organ_name] + soakval += getsoak_organ(organ, type) * weight + total += weight + return (soakval/max(total, 1)) + //this proc returns the Siemens coefficient of electrical resistivity for a particular external organ. /mob/living/carbon/human/proc/get_siemens_coefficient_organ(var/obj/item/organ/external/def_zone) if (!def_zone) @@ -119,6 +142,17 @@ emp_act protection += C.armor[type] return protection +/mob/living/carbon/human/proc/getsoak_organ(var/obj/item/organ/external/def_zone, var/type) + if(!type || !def_zone) return 0 + var/soaked = 0 + var/list/protective_gear = list(head, wear_mask, wear_suit, w_uniform, gloves, shoes) + for(var/gear in protective_gear) + if(gear && istype(gear ,/obj/item/clothing)) + var/obj/item/clothing/C = gear + if(istype(C) && C.body_parts_covered & def_zone.body_part) + soaked += C.armorsoak[type] + return soaked + /mob/living/carbon/human/proc/check_head_coverage() var/list/body_parts = list(head, wear_mask, wear_suit, w_uniform) @@ -195,25 +229,35 @@ emp_act visible_message("[src] has been [I.attack_verb.len? pick(I.attack_verb) : "attacked"] in the [affecting.name] with [I.name] by [user]!") + var/soaked = get_armor_soak(hit_zone, "melee", I.armor_penetration) + + if(soaked >= effective_force) + src << "Your armor absorbs the force of [I.name]!" + return + var/blocked = run_armor_check(hit_zone, "melee", I.armor_penetration, "Your armor has protected your [affecting.name].", "Your armor has softened the blow to your [affecting.name].") - standard_weapon_hit_effects(I, user, effective_force, blocked, hit_zone) + + standard_weapon_hit_effects(I, user, effective_force, blocked, soaked, hit_zone) return blocked -/mob/living/carbon/human/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/hit_zone) +/mob/living/carbon/human/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/soaked, var/hit_zone) var/obj/item/organ/external/affecting = get_organ(hit_zone) if(!affecting) return 0 + if(soaked >= effective_force) + return 0 + // Handle striking to cripple. if(user.a_intent == I_DISARM) effective_force *= 0.5 //reduced effective force... - if(!..(I, user, effective_force, blocked, hit_zone)) + if(!..(I, user, effective_force, blocked, soaked, hit_zone)) return 0 //set the dislocate mult less than the effective force mult so that //dislocating limbs on disarm is a bit easier than breaking limbs on harm - attack_joint(affecting, I, effective_force, 0.75, blocked) //...but can dislocate joints + attack_joint(affecting, I, effective_force, 0.75, blocked, soaked) //...but can dislocate joints else if(!..()) return 0 @@ -243,7 +287,7 @@ emp_act switch(hit_zone) if("head")//Harder to score a stun but if you do it lasts a bit longer if(prob(effective_force)) - apply_effect(20, PARALYZE, blocked) + apply_effect(20, PARALYZE, blocked, soaked) visible_message("\The [src] has been knocked unconscious!") if(bloody)//Apply blood if(wear_mask) @@ -257,15 +301,15 @@ emp_act update_inv_glasses(0) if("chest")//Easier to score a stun but lasts less time if(prob(effective_force + 10)) - apply_effect(6, WEAKEN, blocked) + apply_effect(6, WEAKEN, blocked, soaked) visible_message("\The [src] has been knocked down!") if(bloody) bloody_body(src) return 1 -/mob/living/carbon/human/proc/attack_joint(var/obj/item/organ/external/organ, var/obj/item/W, var/effective_force, var/dislocate_mult, var/blocked) - if(!organ || (organ.dislocated == 2) || (organ.dislocated == -1) || blocked >= 100) +/mob/living/carbon/human/proc/attack_joint(var/obj/item/organ/external/organ, var/obj/item/W, var/effective_force, var/dislocate_mult, var/blocked, var/soaked) + if(!organ || (organ.dislocated == 2) || (organ.dislocated == -1) || blocked >= 100 || soaked > effective_force) return 0 if(W.damtype != BRUTE) @@ -338,10 +382,6 @@ emp_act var/hit_area = affecting.name src.visible_message("\red [src] has been hit in the [hit_area] by [O].") - var/armor = run_armor_check(affecting, "melee", O.armor_penetration, "Your armor has protected your [hit_area].", "Your armor has softened hit to your [hit_area].") //I guess "melee" is the best fit here - - if(armor < 100) - apply_damage(throw_damage, dtype, zone, armor, is_sharp(O), has_edge(O), O) if(ismob(O.thrower)) var/mob/M = O.thrower @@ -352,12 +392,25 @@ emp_act if(!istype(src,/mob/living/simple_animal/mouse)) msg_admin_attack("[src.name] ([src.ckey]) was hit by a [O], thrown by [M.name] ([assailant.ckey]) (JMP)") + //If the armor absorbs all of the damage, skip the rest of the calculations + var/soaked = get_armor_soak(affecting, "melee", O.armor_penetration) + if(soaked >= throw_damage) + src << "Your armor absorbs the force of [O.name]!" + return + + var/armor = run_armor_check(affecting, "melee", O.armor_penetration, "Your armor has protected your [hit_area].", "Your armor has softened hit to your [hit_area].") //I guess "melee" is the best fit here + if(armor < 100) + apply_damage(throw_damage, dtype, zone, armor, soaked, is_sharp(O), has_edge(O), O) + + //thrown weapon embedded object code. if(dtype == BRUTE && istype(O,/obj/item)) var/obj/item/I = O if (!is_robot_module(I)) var/sharp = is_sharp(I) var/damage = throw_damage + if (soaked) + damage -= soaked if (armor) damage /= armor+1 diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm index 0338876627..7c4e38730f 100644 --- a/code/modules/mob/living/carbon/human/human_organs.dm +++ b/code/modules/mob/living/carbon/human/human_organs.dm @@ -56,7 +56,7 @@ if (!lying && !buckled && world.time - l_move_time < 15) //Moving around with fractured ribs won't do you any good - if (prob(10) && !stat && can_feel_pain() && analgesic < 50 && E.is_broken() && E.internal_organs.len) + if (prob(10) && !stat && can_feel_pain() && chem_effects[CE_PAINKILLER] < 50 && E.is_broken() && E.internal_organs.len) custom_pain("Pain jolts through your broken [E.encased ? E.encased : E.name], staggering you!", 50) drop_item(loc) Stun(2) @@ -100,7 +100,7 @@ else if (E.is_dislocated()) stance_damage += 0.5 - if(E) limb_pain = E.can_feel_pain() + if(E) limb_pain = E.organ_can_feel_pain() // Canes and crutches help you stand (if the latter is ever added) // One cane mitigates a broken leg+foot, or a missing foot. @@ -159,7 +159,7 @@ drop_from_inventory(r_hand) var/emote_scream = pick("screams in pain and ", "lets out a sharp cry and ", "cries out and ") - emote("me", 1, "[(E.can_feel_pain()) ? "" : emote_scream ]drops what they were holding in their [E.name]!") + emote("me", 1, "[(can_feel_pain()) ? "" : emote_scream ]drops what they were holding in their [E.name]!") else if(E.is_malfunctioning()) switch(E.body_part) diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 014ba7eb0d..7ecf8cd8ef 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -186,7 +186,7 @@ var/rn = rand(0, 200) if(getBrainLoss() >= 5) if(0 <= rn && rn <= 3) - custom_pain("Your head feels numb and painful.") + custom_pain("Your head feels numb and painful.", 10) if(getBrainLoss() >= 15) if(4 <= rn && rn <= 6) if(eye_blurry <= 0) src << "It becomes hard to see for some reason." @@ -328,17 +328,20 @@ if(status_flags & GODMODE) return + if(suiciding) + failed_last_breath = 1 + adjustOxyLoss(2)//If you are suiciding, you should die a little bit faster + oxygen_alert = max(oxygen_alert, 1) + suiciding -- + return 0 + if(does_not_breathe) failed_last_breath = 0 adjustOxyLoss(-5) return - if(!breath || (breath.total_moles == 0) || suiciding) + if(!breath || (breath.total_moles == 0)) failed_last_breath = 1 - if(suiciding) - adjustOxyLoss(2)//If you are suiciding, you should die a little bit faster - oxygen_alert = max(oxygen_alert, 1) - return 0 if(health > config.health_threshold_crit) adjustOxyLoss(HUMAN_MAX_OXYLOSS) else @@ -350,8 +353,11 @@ rupture_lung() if(should_have_organ("brain")) + var/brainOxPercent = 0.02 //Default2% of your current oxyloss is applied as brain damage, 50 oxyloss is 1 brain damage + if(CE_STABLE in chem_effects) + brainOxPercent = 0.01 //Halved in effect if(prob(5)) - adjustBrainLoss(0.02 * oxyloss) //2% of your current oxyloss is applied as brain damage, 50 oxyloss is 1 brain damage + adjustBrainLoss(brainOxPercent * oxyloss) oxygen_alert = max(oxygen_alert, 1) @@ -794,7 +800,6 @@ if(reagents) chem_effects.Cut() - analgesic = 0 if(!isSynthetic()) @@ -802,9 +807,6 @@ if(ingested) ingested.metabolize() if(bloodstr) bloodstr.metabolize() - if(CE_PAINKILLER in chem_effects) - analgesic = chem_effects[CE_PAINKILLER] - var/total_phoronloss = 0 for(var/obj/item/I in src) if(I.contaminated) @@ -1155,7 +1157,7 @@ see_invisible = SEE_INVISIBLE_LIVING if(healths) - if (analgesic > 100) + if (chem_effects[CE_PAINKILLER] > 100) healths.icon_state = "health_numb" else // Generate a by-limb health display. @@ -1437,8 +1439,11 @@ shock_stage = max(shock_stage-1, 0) return + if(stat) + return 0 + if(shock_stage == 10) - src << "[pick("It hurts so much", "You really need some painkillers", "Dear god, the pain")]!" + custom_pain("[pick("It hurts so much", "You really need some painkillers", "Dear god, the pain")]!", 40) if(shock_stage >= 30) if(shock_stage == 30) emote("me",1,"is having trouble keeping their eyes open.") diff --git a/code/modules/mob/living/carbon/human/unarmed_attack.dm b/code/modules/mob/living/carbon/human/unarmed_attack.dm index af3e65b091..2de060887f 100644 --- a/code/modules/mob/living/carbon/human/unarmed_attack.dm +++ b/code/modules/mob/living/carbon/human/unarmed_attack.dm @@ -97,7 +97,7 @@ var/global/list/sparring_attack_cache = list() if(eyes) eyes.take_damage(rand(3,4), 1) user.visible_message("[user] presses \his [eye_attack_text] into [target]'s [eyes.name]!") - var/eye_pain = eyes.can_feel_pain() + var/eye_pain = eyes.organ_can_feel_pain() target << "You experience[(eye_pain) ? "" : " immense pain as you feel" ] [eye_attack_text_victim] being pressed into your [eyes.name][(eye_pain)? "." : "!"]" return user.visible_message("[user] attempts to press \his [eye_attack_text] into [target]'s eyes, but they don't have any!") diff --git a/code/modules/mob/living/carbon/metroid/life.dm b/code/modules/mob/living/carbon/metroid/life.dm index 69ddb59038..7bbc4110dd 100644 --- a/code/modules/mob/living/carbon/metroid/life.dm +++ b/code/modules/mob/living/carbon/metroid/life.dm @@ -67,15 +67,11 @@ /mob/living/carbon/slime/handle_chemicals_in_body() chem_effects.Cut() - analgesic = 0 if(touching) touching.metabolize() if(ingested) ingested.metabolize() if(bloodstr) bloodstr.metabolize() - if(CE_PAINKILLER in chem_effects) - analgesic = chem_effects[CE_PAINKILLER] - src.updatehealth() return //TODO: DEFERRED diff --git a/code/modules/mob/living/carbon/metroid/powers.dm b/code/modules/mob/living/carbon/metroid/powers.dm index 2e1cc29595..933596ef5e 100644 --- a/code/modules/mob/living/carbon/metroid/powers.dm +++ b/code/modules/mob/living/carbon/metroid/powers.dm @@ -60,7 +60,7 @@ var/painMes = pick("You can feel your body becoming weak!", "You feel like you're about to die!", "You feel every part of your body screaming in agony!", "A low, rolling pain passes through your body!", "Your body feels as if it's falling apart!", "You feel extremely weak!", "A sharp, deep pain bathes every inch of your body!") if (ishuman(M)) var/mob/living/carbon/human/H = M - H.custom_pain(painMes) + H.custom_pain(painMes, 100) else if (istype(M, /mob/living/carbon)) var/mob/living/carbon/C = M if (C.can_feel_pain()) diff --git a/code/modules/mob/living/carbon/shock.dm b/code/modules/mob/living/carbon/shock.dm index a742bc8959..894a0a98b0 100644 --- a/code/modules/mob/living/carbon/shock.dm +++ b/code/modules/mob/living/carbon/shock.dm @@ -14,7 +14,7 @@ 1.2 * src.getShockBruteLoss() + \ 1.7 * src.getCloneLoss() + \ 2 * src.halloss + \ - -1 * src.analgesic + -1 * src.chem_effects[CE_PAINKILLER] if(src.slurring) src.traumatic_shock -= 20 diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index f09eb3741f..552319e6fe 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -8,11 +8,13 @@ Returns standard 0 if fail */ -/mob/living/proc/apply_damage(var/damage = 0,var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/used_weapon = null, var/sharp = 0, var/edge = 0) +/mob/living/proc/apply_damage(var/damage = 0,var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/soaked = 0, var/used_weapon = null, var/sharp = 0, var/edge = 0) if(Debug2) world.log << "## DEBUG: apply_damage() was called on [src], with [damage] damage, and an armor value of [blocked]." - if(!damage || (blocked >= 100)) + if(!damage || (blocked >= 100) || soaked >= damage) return 0 + if(soaked) + damage -= soaked blocked = (100-blocked)/100 switch(damagetype) if(BRUTE) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 7680b6e163..dfe57c55b2 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -716,11 +716,20 @@ default behaviour is: src << "You feel like you are about to throw up!" sleep(100) //and you have 10 more for mad dash to the bucket + //Damaged livers cause you to vomit blood. + if(!blood_vomit) + if(ishuman(src)) + var/mob/living/carbon/human/H = src + if(!H.isSynthetic()) + var/obj/item/organ/internal/liver/L = H.internal_organs_by_name["liver"] + if(L.is_broken()) + blood_vomit = 1 + Stun(5) src.visible_message("[src] throws up!","You throw up!") playsound(loc, 'sound/effects/splat.ogg', 50, 1) - var/turf/simulated/T = get_turf(src) + var/turf/simulated/T = get_turf(src) //TODO: Make add_blood_floor remove blood from human mobs if(istype(T)) if(blood_vomit) T.add_blood_floor(src) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 17eff101bb..c52311820e 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -77,10 +77,20 @@ return 0 */ +//Certain pieces of armor actually absorb flat amounts of damage from income attacks +/mob/living/proc/get_armor_soak(var/def_zone = null, var/attack_flag = "melee", var/armour_pen = 0) + var/soaked = getsoak(def_zone, attack_flag) + //5 points of armor pen negate one point of soak + if(armour_pen) + soaked = max(soaked - (armour_pen/5), 0) + return soaked + //if null is passed for def_zone, then this should return something appropriate for all zones (e.g. area effect damage) /mob/living/proc/getarmor(var/def_zone, var/type) return 0 +/mob/living/proc/getsoak(var/def_zone, var/type) + return 0 /mob/living/bullet_act(var/obj/item/projectile/P, var/def_zone) @@ -93,6 +103,7 @@ signaler.signal() //Armor + var/soaked = get_armor_soak(def_zone, P.check_armour, P.armor_penetration) var/absorb = run_armor_check(def_zone, P.check_armour, P.armor_penetration) var/proj_sharp = is_sharp(P) var/proj_edge = has_edge(P) @@ -105,13 +116,13 @@ stun_effect_act(0, P.agony, def_zone, P) src <<"\red You have been hit by [P]!" if(!P.nodamage) - apply_damage(P.damage, P.damage_type, def_zone, absorb, 0, P, sharp=proj_sharp, edge=proj_edge) + apply_damage(P.damage, P.damage_type, def_zone, absorb, soaked, 0, P, sharp=proj_sharp, edge=proj_edge) qdel(P) return if(!P.nodamage) - apply_damage(P.damage, P.damage_type, def_zone, absorb, 0, P, sharp=proj_sharp, edge=proj_edge) - P.on_hit(src, absorb, def_zone) + apply_damage(P.damage, P.damage_type, def_zone, absorb, soaked, 0, P, sharp=proj_sharp, edge=proj_edge) + P.on_hit(src, absorb, soaked, def_zone) if(absorb == 100) return 2 @@ -153,17 +164,21 @@ /mob/living/proc/hit_with_weapon(obj/item/I, mob/living/user, var/effective_force, var/hit_zone) visible_message("[src] has been [I.attack_verb.len? pick(I.attack_verb) : "attacked"] with [I.name] by [user]!") + var/soaked = get_armor_soak(hit_zone, "melee") var/blocked = run_armor_check(hit_zone, "melee") - standard_weapon_hit_effects(I, user, effective_force, blocked, hit_zone) - if(I.damtype == BRUTE && prob(33)) // Added blood for whacking non-humans too - var/turf/simulated/location = get_turf(src) - if(istype(location)) location.add_blood_floor(src) + //If the armor absorbs all of the damage, skip the damage calculation and the blood + if(!soaked > effective_force) + standard_weapon_hit_effects(I, user, effective_force, blocked, soaked, hit_zone) + + if(I.damtype == BRUTE && prob(33)) // Added blood for whacking non-humans too + var/turf/simulated/location = get_turf(src) + if(istype(location)) location.add_blood_floor(src) return blocked //returns 0 if the effects failed to apply for some reason, 1 otherwise. -/mob/living/proc/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/hit_zone) +/mob/living/proc/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/soaked, var/hit_zone) if(!effective_force || blocked >= 100) return 0 @@ -171,6 +186,10 @@ if(HULK in user.mutations) effective_force *= 2 + //Armor soak + if(soaked >= effective_force) + return 0 + //Apply weapon damage var/weapon_sharp = is_sharp(I) var/weapon_edge = has_edge(I) @@ -178,7 +197,7 @@ weapon_sharp = 0 weapon_edge = 0 - apply_damage(effective_force, I.damtype, hit_zone, blocked, sharp=weapon_sharp, edge=weapon_edge, used_weapon=I) + apply_damage(effective_force, I.damtype, hit_zone, blocked, soaked, sharp=weapon_sharp, edge=weapon_edge, used_weapon=I) return 1 @@ -200,8 +219,10 @@ src.visible_message("\red [src] has been hit by [O].") var/armor = run_armor_check(null, "melee") + var/soaked = get_armor_soak(null, "melee") - apply_damage(throw_damage, dtype, null, armor, is_sharp(O), has_edge(O), O) + + apply_damage(throw_damage, dtype, null, armor, soaked, is_sharp(O), has_edge(O), O) O.throwing = 0 //it hit, so stop moving @@ -230,6 +251,9 @@ if(!O || !src) return if(O.sharp) //Projectile is suitable for pinning. + if(soaked >= throw_damage) //Don't embed if it didn't actually damage + return + //Handles embedding for non-humans and simple_animals. embed(O) diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index c7856f5082..fd4f706b15 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -11,6 +11,7 @@ var/list/department_radio_keys = list( ":s" = "Security", ".s" = "Security", ":w" = "whisper", ".w" = "whisper", ":t" = "Mercenary", ".t" = "Mercenary", + ":x" = "Raider", ".x" = "Raider", ":u" = "Supply", ".u" = "Supply", ":v" = "Service", ".v" = "Service", ":p" = "AI Private", ".p" = "AI Private", @@ -26,6 +27,7 @@ var/list/department_radio_keys = list( ":S" = "Security", ".S" = "Security", ":W" = "whisper", ".W" = "whisper", ":T" = "Mercenary", ".T" = "Mercenary", + ":X" = "Raider", ".X" = "Raider", ":U" = "Supply", ".U" = "Supply", ":V" = "Service", ".V" = "Service", ":P" = "AI Private", ".P" = "AI Private", diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index f16ead995d..ccfd19e8aa 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -347,7 +347,7 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/weapon/rcd/borg(src) src.modules += new /obj/item/weapon/screwdriver(src) src.modules += new /obj/item/weapon/wrench(src) - src.modules += new /obj/item/weapon/weldingtool/largetank(src) + src.modules += new /obj/item/weapon/weldingtool/electric/mounted(src) src.modules += new /obj/item/weapon/pickaxe/plasmacutter(src) src.modules += new /obj/item/device/pipe_painter(src) src.modules += new /obj/item/device/floor_painter(src) @@ -383,7 +383,7 @@ var/global/list/robot_modules = list( /obj/item/weapon/robot_module/robot/engineering/general/New() ..() src.modules += new /obj/item/borg/sight/meson(src) - src.modules += new /obj/item/weapon/weldingtool/largetank(src) + src.modules += new /obj/item/weapon/weldingtool/electric/mounted(src) src.modules += new /obj/item/weapon/screwdriver(src) src.modules += new /obj/item/weapon/wrench(src) src.modules += new /obj/item/weapon/wirecutters(src) @@ -680,7 +680,7 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/weapon/gripper/no_use/loader(src) src.modules += new /obj/item/device/robotanalyzer(src) src.modules += new /obj/item/weapon/card/robot(src) - src.modules += new /obj/item/weapon/weldingtool/largetank(src) + src.modules += new /obj/item/weapon/weldingtool/electric/mounted(src) src.modules += new /obj/item/weapon/screwdriver(src) src.modules += new /obj/item/weapon/wrench(src) src.modules += new /obj/item/weapon/wirecutters(src) @@ -776,7 +776,8 @@ var/global/list/robot_modules = list( /obj/item/weapon/robot_module/drone/New(var/mob/living/silicon/robot/robot) ..() - src.modules += new /obj/item/weapon/weldingtool(src) + src.modules += new /obj/item/borg/sight/meson(src) + src.modules += new /obj/item/weapon/weldingtool/electric/mounted(src) src.modules += new /obj/item/weapon/screwdriver(src) src.modules += new /obj/item/weapon/wrench(src) src.modules += new /obj/item/weapon/crowbar(src) diff --git a/code/modules/mob/living/simple_animal/animals/bear.dm b/code/modules/mob/living/simple_animal/animals/bear.dm index 9d1da5e08b..228e92e5b7 100644 --- a/code/modules/mob/living/simple_animal/animals/bear.dm +++ b/code/modules/mob/living/simple_animal/animals/bear.dm @@ -1,125 +1,125 @@ -//Space bears! -/mob/living/simple_animal/hostile/bear - name = "space bear" - desc = "RawrRawr!!" - icon_state = "bear" - icon_living = "bear" - icon_dead = "bear_dead" - icon_gib = "bear_gib" - - faction = "russian" - cooperative = 1 - - maxHealth = 60 - health = 60 - turns_per_move = 5 - see_in_dark = 6 - stop_when_pulled = 0 - - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "pokes" - - melee_damage_lower = 20 - melee_damage_upper = 30 - - //Space bears aren't affected by atmos. - min_oxy = 0 - max_oxy = 0 - min_tox = 0 - max_tox = 0 - min_co2 = 0 - max_co2 = 0 - min_n2 = 0 - max_n2 = 0 - minbodytemp = 0 - - speak_chance = 1 - speak = list("RAWR!","Rawr!","GRR!","Growl!") - speak_emote = list("growls", "roars") - emote_hear = list("rawrs","grumbles","grawls") - emote_see = list("stares ferociously", "stomps") - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/bearmeat - - var/stance_step = 0 - -/mob/living/simple_animal/hostile/bear/handle_stance() - switch(stance) - if(STANCE_TIRED) - stop_automated_movement = 1 - stance_step++ - if(stance_step >= 10) //rests for 10 ticks - if(target_mob && target_mob in ListTargets(10)) - handle_stance(STANCE_ATTACK) //If the mob he was chasing is still nearby, resume the attack, otherwise go idle. - else - handle_stance(STANCE_IDLE) - - if(STANCE_ALERT) - stop_automated_movement = 1 - var/found_mob = 0 - if(target_mob && target_mob in ListTargets(10)) - if(!(SA_attackable(target_mob))) - stance_step = max(0, stance_step) //If we have not seen a mob in a while, the stance_step will be negative, we need to reset it to 0 as soon as we see a mob again. - stance_step++ - found_mob = 1 - src.set_dir(get_dir(src,target_mob)) //Keep staring at the mob - - if(stance_step in list(1,4,7)) //every 3 ticks - var/action = pick( list( "growls at [target_mob]", "stares angrily at [target_mob]", "prepares to attack [target_mob]", "closely watches [target_mob]" ) ) - if(action) - custom_emote(1,action) - if(!found_mob) - stance_step-- - - if(stance_step <= -20) //If we have not found a mob for 20-ish ticks, revert to idle mode - handle_stance(STANCE_IDLE) - if(stance_step >= 7) //If we have been staring at a mob for 7 ticks, - handle_stance(STANCE_ATTACK) - - if(STANCE_ATTACKING) - if(stance_step >= 20) //attacks for 20 ticks, then it gets tired and needs to rest - custom_emote(1, "is worn out and needs to rest." ) - handle_stance(STANCE_TIRED) - stance_step = 0 - walk(src, 0) //This stops the bear's walking - return - else - ..() - -/mob/living/simple_animal/hostile/bear/update_icons() - ..() - if(!stat) - if(loc && istype(loc,/turf/space)) - icon_state = "bear" - else - icon_state = "bearfloor" - -/mob/living/simple_animal/hostile/bear/Process_Spacemove(var/check_drift = 0) - return - -/mob/living/simple_animal/hostile/bear/FindTarget() - . = ..() - if(.) - custom_emote(1,"stares alertly at [.]") - handle_stance(STANCE_ALERT) - -/mob/living/simple_animal/hostile/bear/PunchTarget() - if(!Adjacent(target_mob)) - return - custom_emote(1, pick( list("slashes at [target_mob]", "bites [target_mob]") ) ) - - var/damage = rand(melee_damage_lower, melee_damage_upper) - - if(ishuman(target_mob)) - var/mob/living/carbon/human/H = target_mob - var/dam_zone = pick(BP_TORSO, BP_L_HAND, BP_R_HAND, BP_L_LEG, BP_R_LEG) - var/obj/item/organ/external/affecting = H.get_organ(ran_zone(dam_zone)) - H.apply_damage(damage, BRUTE, affecting, H.run_armor_check(affecting, "melee"), sharp=1, edge=1) - return H - else if(isliving(target_mob)) - var/mob/living/L = target_mob - L.adjustBruteLoss(damage) - return L - else - ..() +//Space bears! +/mob/living/simple_animal/hostile/bear + name = "space bear" + desc = "RawrRawr!!" + icon_state = "bear" + icon_living = "bear" + icon_dead = "bear_dead" + icon_gib = "bear_gib" + + faction = "russian" + cooperative = 1 + + maxHealth = 60 + health = 60 + turns_per_move = 5 + see_in_dark = 6 + stop_when_pulled = 0 + + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "pokes" + + melee_damage_lower = 20 + melee_damage_upper = 30 + + //Space bears aren't affected by atmos. + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + + speak_chance = 1 + speak = list("RAWR!","Rawr!","GRR!","Growl!") + speak_emote = list("growls", "roars") + emote_hear = list("rawrs","grumbles","grawls") + emote_see = list("stares ferociously", "stomps") + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/bearmeat + + var/stance_step = 0 + +/mob/living/simple_animal/hostile/bear/handle_stance() + switch(stance) + if(STANCE_TIRED) + stop_automated_movement = 1 + stance_step++ + if(stance_step >= 10) //rests for 10 ticks + if(target_mob && target_mob in ListTargets(10)) + handle_stance(STANCE_ATTACK) //If the mob he was chasing is still nearby, resume the attack, otherwise go idle. + else + handle_stance(STANCE_IDLE) + + if(STANCE_ALERT) + stop_automated_movement = 1 + var/found_mob = 0 + if(target_mob && target_mob in ListTargets(10)) + if(!(SA_attackable(target_mob))) + stance_step = max(0, stance_step) //If we have not seen a mob in a while, the stance_step will be negative, we need to reset it to 0 as soon as we see a mob again. + stance_step++ + found_mob = 1 + src.set_dir(get_dir(src,target_mob)) //Keep staring at the mob + + if(stance_step in list(1,4,7)) //every 3 ticks + var/action = pick( list( "growls at [target_mob]", "stares angrily at [target_mob]", "prepares to attack [target_mob]", "closely watches [target_mob]" ) ) + if(action) + custom_emote(1,action) + if(!found_mob) + stance_step-- + + if(stance_step <= -20) //If we have not found a mob for 20-ish ticks, revert to idle mode + handle_stance(STANCE_IDLE) + if(stance_step >= 7) //If we have been staring at a mob for 7 ticks, + handle_stance(STANCE_ATTACK) + + if(STANCE_ATTACKING) + if(stance_step >= 20) //attacks for 20 ticks, then it gets tired and needs to rest + custom_emote(1, "is worn out and needs to rest." ) + handle_stance(STANCE_TIRED) + stance_step = 0 + walk(src, 0) //This stops the bear's walking + return + else + ..() + +/mob/living/simple_animal/hostile/bear/update_icons() + ..() + if(!stat) + if(loc && istype(loc,/turf/space)) + icon_state = "bear" + else + icon_state = "bearfloor" + +/mob/living/simple_animal/hostile/bear/Process_Spacemove(var/check_drift = 0) + return + +/mob/living/simple_animal/hostile/bear/FindTarget() + . = ..() + if(.) + custom_emote(1,"stares alertly at [.]") + handle_stance(STANCE_ALERT) + +/mob/living/simple_animal/hostile/bear/PunchTarget() + if(!Adjacent(target_mob)) + return + custom_emote(1, pick( list("slashes at [target_mob]", "bites [target_mob]") ) ) + + var/damage = rand(melee_damage_lower, melee_damage_upper) + + if(ishuman(target_mob)) + var/mob/living/carbon/human/H = target_mob + var/dam_zone = pick(BP_TORSO, BP_L_HAND, BP_R_HAND, BP_L_LEG, BP_R_LEG) + var/obj/item/organ/external/affecting = H.get_organ(ran_zone(dam_zone)) + H.apply_damage(damage, BRUTE, affecting, H.run_armor_check(affecting, "melee"), H.get_armor_soak(affecting, "melee"), sharp=1, edge=1) + return H + else if(isliving(target_mob)) + var/mob/living/L = target_mob + L.adjustBruteLoss(damage) + return L + else + ..() diff --git a/code/modules/mob/living/simple_animal/animals/giant_spider.dm b/code/modules/mob/living/simple_animal/animals/giant_spider.dm index ed692ab237..acb1d7a1b7 100644 --- a/code/modules/mob/living/simple_animal/animals/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/animals/giant_spider.dm @@ -36,7 +36,7 @@ speak_emote = list("chitters") emote_hear = list("chitters") - meat_type = /obj/item/weapon/reagent_containers/food/snacks/xenomeat + meat_type = /obj/item/weapon/reagent_containers/food/snacks/spidermeat var/busy = 0 var/poison_per_bite = 5 diff --git a/code/modules/mob/living/simple_animal/animals/parrot.dm b/code/modules/mob/living/simple_animal/animals/parrot.dm index d388939fcd..03f0261688 100644 --- a/code/modules/mob/living/simple_animal/animals/parrot.dm +++ b/code/modules/mob/living/simple_animal/animals/parrot.dm @@ -1,761 +1,761 @@ -/* Parrots! - * Contains - * Defines - * Inventory (headset stuff) - * Attack responces - * AI - * Procs / Verbs (usable by players) - * Sub-types - */ - -/* - * Defines - */ - -//Parrot is too snowflake for me to rewrite right now, someone should make it use the new -//simple_animal movement stuff. -Aro - -//Only a maximum of one action and one intent should be active at any given time. -//Actions -#define PARROT_PERCH 1 //Sitting/sleeping, not moving -#define PARROT_SWOOP 2 //Moving towards or away from a target -#define PARROT_WANDER 4 //Moving without a specific target in mind - -//Intents -#define PARROT_STEAL 8 //Flying towards a target to steal it/from it -#define PARROT_ATTACK 16 //Flying towards a target to attack it -#define PARROT_RETURN 32 //Flying towards its perch -#define PARROT_FLEE 64 //Flying away from its attacker - - -/mob/living/simple_animal/parrot - name = "\improper Parrot" - desc = "The parrot squaks, \"It's a Parrot! BAWWK!\"" - icon = 'icons/mob/animal.dmi' - icon_state = "parrot_fly" - icon_living = "parrot_fly" - icon_dead = "parrot_dead" - - turns_per_move = 5 - pass_flags = PASSTABLE - mob_size = MOB_SMALL - - response_help = "pets" - response_disarm = "gently moves aside" - response_harm = "swats" - stop_automated_movement = 1 - universal_speak = 1 - - speak_chance = 2 - speak = list("Hi","Hello!","Cracker?","BAWWWWK george mellons griffing me") - speak_emote = list("squawks","says","yells") - emote_hear = list("squawks","bawks") - emote_see = list("flutters its wings") - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/cracker - - var/parrot_state = PARROT_WANDER //Hunt for a perch when created - var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in life() being run every single tick. - var/parrot_sleep_dur = 25 //Same as above, this is the var that physically counts down - var/parrot_dam_zone = list(BP_TORSO, BP_HEAD, BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG) //For humans, select a bodypart to attack - - var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower. - var/parrot_been_shot = 0 //Parrots get a speed bonus after being shot. This will deincrement every Life() and at 0 the parrot will return to regular speed. - - var/list/speech_buffer = list() - var/list/available_channels = list() - - //Headset for Poly to yell at engineers :) - var/obj/item/device/radio/headset/ears = null - - //The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from, - //mobs it wants to attack or mobs that have attacked it - var/atom/movable/parrot_interest = null - - //Parrots will generally sit on their pertch unless something catches their eye. - //These vars store their preffered perch and if they dont have one, what they can use as a perch - var/obj/parrot_perch = null - var/obj/desired_perches = list(/obj/structure/frame, /obj/structure/displaycase, \ - /obj/structure/filingcabinet, /obj/machinery/teleport, \ - /obj/machinery/computer, /obj/machinery/clonepod, \ - /obj/machinery/dna_scannernew, /obj/machinery/telecomms, \ - /obj/machinery/nuclearbomb, /obj/machinery/particle_accelerator, \ - /obj/machinery/recharge_station, /obj/machinery/smartfridge, \ - /obj/machinery/suit_storage_unit) - - //Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. - var/obj/item/held_item = null - - -/mob/living/simple_animal/parrot/New() - ..() - if(!ears) - var/headset = pick(/obj/item/device/radio/headset/headset_sec, \ - /obj/item/device/radio/headset/headset_eng, \ - /obj/item/device/radio/headset/headset_med, \ - /obj/item/device/radio/headset/headset_sci, \ - /obj/item/device/radio/headset/headset_cargo) - ears = new headset(src) - - parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var - - verbs.Add(/mob/living/simple_animal/parrot/proc/steal_from_ground, \ - /mob/living/simple_animal/parrot/proc/steal_from_mob, \ - /mob/living/simple_animal/parrot/verb/drop_held_item_player, \ - /mob/living/simple_animal/parrot/proc/perch_player) - - -/mob/living/simple_animal/parrot/death() - if(held_item) - held_item.forceMove(src.loc) - held_item = null - walk(src,0) - ..() - -/mob/living/simple_animal/parrot/Stat() - ..() - stat("Held Item", held_item) - -/* - * Inventory - */ -/mob/living/simple_animal/parrot/show_inv(mob/user as mob) - user.set_machine(src) - if(user.stat) return - - var/dat = "
"
- if(ears)
- dat += "
Headset: [ears] (Remove)"
- else
- dat += "
Headset: Nothing"
-
- user << browse(dat, text("window=mob[];size=325x500", name))
- onclose(user, "mob[real_name]")
- return
-
-/mob/living/simple_animal/parrot/Topic(href, href_list)
-
- //Can the usr physically do this?
- if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr))
- return
-
- //Is the usr's mob type able to do this?
- if(ishuman(usr) || issmall(usr) || isrobot(usr))
-
- //Removing from inventory
- if(href_list["remove_inv"])
- var/remove_from = href_list["remove_inv"]
- switch(remove_from)
- if("ears")
- if(ears)
- if(available_channels.len)
- src.say("[pick(available_channels)] BAWWWWWK LEAVE THE HEADSET BAWKKKKK!")
- else
- src.say("BAWWWWWK LEAVE THE HEADSET BAWKKKKK!")
- ears.forceMove(src.loc)
- ears = null
- for(var/possible_phrase in speak)
- if(copytext(possible_phrase,1,3) in department_radio_keys)
- possible_phrase = copytext(possible_phrase,3,length(possible_phrase))
- else
- usr << "\red There is nothing to remove from its [remove_from]."
- return
-
- //Adding things to inventory
- else if(href_list["add_inv"])
- var/add_to = href_list["add_inv"]
- if(!usr.get_active_hand())
- usr << "\red You have nothing in your hand to put on its [add_to]."
- return
- switch(add_to)
- if("ears")
- if(ears)
- usr << "\red It's already wearing something."
- return
- else
- var/obj/item/item_to_add = usr.get_active_hand()
- if(!item_to_add)
- return
-
- if( !istype(item_to_add, /obj/item/device/radio/headset) )
- usr << "\red This object won't fit."
- return
-
- var/obj/item/device/radio/headset/headset_to_add = item_to_add
-
- usr.drop_item()
- headset_to_add.forceMove(src)
- src.ears = headset_to_add
- usr << "You fit the headset onto [src]."
-
- clearlist(available_channels)
- for(var/ch in headset_to_add.channels)
- switch(ch)
- if("Engineering")
- available_channels.Add(":e")
- if("Command")
- available_channels.Add(":c")
- if("Security")
- available_channels.Add(":s")
- if("Science")
- available_channels.Add(":n")
- if("Medical")
- available_channels.Add(":m")
- if("Mining")
- available_channels.Add(":d")
- if("Cargo")
- available_channels.Add(":q")
-
- if(headset_to_add.translate_binary)
- available_channels.Add(":b")
- else
- ..()
-
-
-/*
- * Attack responces
- */
-//Humans, monkeys, aliens
-/mob/living/simple_animal/parrot/attack_hand(mob/living/carbon/M as mob)
- ..()
- if(client) return
- if(!stat && M.a_intent == I_HURT)
-
- icon_state = "parrot_fly" //It is going to be flying regardless of whether it flees or attacks
-
- if(parrot_state == PARROT_PERCH)
- parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched
-
- parrot_interest = M
- parrot_state = PARROT_SWOOP //The parrot just got hit, it WILL move, now to pick a direction..
-
- if(M.health < 50) //Weakened mob? Fight back!
- parrot_state |= PARROT_ATTACK
- else
- parrot_state |= PARROT_FLEE //Otherwise, fly like a bat out of hell!
- drop_held_item(0)
- return
-
-//Mobs with objects
-/mob/living/simple_animal/parrot/attackby(var/obj/item/O as obj, var/mob/user as mob)
- ..()
- if(!stat && !client && !istype(O, /obj/item/stack/medical))
- if(O.force)
- if(parrot_state == PARROT_PERCH)
- parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched
-
- parrot_interest = user
- parrot_state = PARROT_SWOOP | PARROT_FLEE
- icon_state = "parrot_fly"
- drop_held_item(0)
- return
-
-//Bullets
-/mob/living/simple_animal/parrot/bullet_act(var/obj/item/projectile/Proj)
- ..()
- if(!stat && !client)
- if(parrot_state == PARROT_PERCH)
- parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched
-
- parrot_interest = null
- parrot_state = PARROT_WANDER //OWFUCK, Been shot! RUN LIKE HELL!
- parrot_been_shot += 5
- icon_state = "parrot_fly"
- drop_held_item(0)
- return
-
-
-/*
- * AI - Not really intelligent, but I'm calling it AI anyway.
- */
-/mob/living/simple_animal/parrot/Life()
- ..()
-
- //Sprite and AI update for when a parrot gets pulled
- if(pulledby && stat == CONSCIOUS)
- icon_state = "parrot_fly"
- if(!client)
- parrot_state = PARROT_WANDER
- return
-
- if(client || stat)
- return //Lets not force players or dead/incap parrots to move
-
- if(!isturf(src.loc) || !canmove || buckled)
- return //If it can't move, dont let it move. (The buckled check probably isn't necessary thanks to canmove)
-
-
-//-----SPEECH
- /* Parrot speech mimickry!
- Phrases that the parrot hears in mob/living/say() get added to speach_buffer.
- Every once in a while, the parrot picks one of the lines from the buffer and replaces an element of the 'speech' list.
- Then it clears the buffer to make sure they dont magically remember something from hours ago. */
- if(speech_buffer.len && prob(10))
- if(speak.len)
- speak.Remove(pick(speak))
-
- speak.Add(pick(speech_buffer))
- clearlist(speech_buffer)
-
-
-//-----SLEEPING
- if(parrot_state == PARROT_PERCH)
- if(parrot_perch && parrot_perch.loc != src.loc) //Make sure someone hasnt moved our perch on us
- if(parrot_perch in view(src))
- parrot_state = PARROT_SWOOP | PARROT_RETURN
- icon_state = "parrot_fly"
- return
- else
- parrot_state = PARROT_WANDER
- icon_state = "parrot_fly"
- return
-
- if(--parrot_sleep_dur) //Zzz
- return
-
- else
- //This way we only call the stuff below once every [sleep_max] ticks.
- parrot_sleep_dur = parrot_sleep_max
-
- //Cycle through message modes for the headset
- if(speak.len)
- var/list/newspeak = list()
-
- if(available_channels.len && src.ears)
- for(var/possible_phrase in speak)
-
- //50/50 chance to not use the radio at all
- var/useradio = 0
- if(prob(50))
- useradio = 1
-
- if(copytext(possible_phrase,1,3) in department_radio_keys)
- possible_phrase = "[useradio?pick(available_channels):""] [copytext(possible_phrase,3,length(possible_phrase)+1)]" //crop out the channel prefix
- else
- possible_phrase = "[useradio?pick(available_channels):""] [possible_phrase]"
-
- newspeak.Add(possible_phrase)
-
- else //If we have no headset or channels to use, dont try to use any!
- for(var/possible_phrase in speak)
- if(copytext(possible_phrase,1,3) in department_radio_keys)
- possible_phrase = "[copytext(possible_phrase,3,length(possible_phrase)+1)]" //crop out the channel prefix
- newspeak.Add(possible_phrase)
- speak = newspeak
-
- //Search for item to steal
- parrot_interest = search_for_item()
- if(parrot_interest)
- visible_emote("looks in [parrot_interest]'s direction and takes flight")
- parrot_state = PARROT_SWOOP | PARROT_STEAL
- icon_state = "parrot_fly"
- return
-
-//-----WANDERING - This is basically a 'I dont know what to do yet' state
- else if(parrot_state == PARROT_WANDER)
- //Stop movement, we'll set it later
- walk(src, 0)
- parrot_interest = null
-
- //Wander around aimlessly. This will help keep the loops from searches down
- //and possibly move the mob into a new are in view of something they can use
- if(prob(90))
- step(src, pick(cardinal))
- return
-
- if(!held_item && !parrot_perch) //If we've got nothing to do.. look for something to do.
- var/atom/movable/AM = search_for_perch_and_item() //This handles checking through lists so we know it's either a perch or stealable item
- if(AM)
- if(istype(AM, /obj/item) || isliving(AM)) //If stealable item
- parrot_interest = AM
- visible_emote("turns and flies towards [parrot_interest]")
- parrot_state = PARROT_SWOOP | PARROT_STEAL
- return
- else //Else it's a perch
- parrot_perch = AM
- parrot_state = PARROT_SWOOP | PARROT_RETURN
- return
- return
-
- if(parrot_interest && parrot_interest in view(src))
- parrot_state = PARROT_SWOOP | PARROT_STEAL
- return
-
- if(parrot_perch && parrot_perch in view(src))
- parrot_state = PARROT_SWOOP | PARROT_RETURN
- return
-
- else //Have an item but no perch? Find one!
- parrot_perch = search_for_perch()
- if(parrot_perch)
- parrot_state = PARROT_SWOOP | PARROT_RETURN
- return
-//-----STEALING
- else if(parrot_state == (PARROT_SWOOP | PARROT_STEAL))
- walk(src,0)
- if(!parrot_interest || held_item)
- parrot_state = PARROT_SWOOP | PARROT_RETURN
- return
-
- if(!(parrot_interest in view(src)))
- parrot_state = PARROT_SWOOP | PARROT_RETURN
- return
-
- if(in_range(src, parrot_interest))
-
- if(isliving(parrot_interest))
- steal_from_mob()
-
- else //This should ensure that we only grab the item we want, and make sure it's not already collected on our perch
- if(!parrot_perch || parrot_interest.loc != parrot_perch.loc)
- held_item = parrot_interest
- parrot_interest.forceMove(src)
- visible_message("[src] grabs the [held_item]!", "\blue You grab the [held_item]!", "You hear the sounds of wings flapping furiously.")
-
- parrot_interest = null
- parrot_state = PARROT_SWOOP | PARROT_RETURN
- return
-
- walk_to(src, parrot_interest, 1, parrot_speed)
- return
-
-//-----RETURNING TO PERCH
- else if(parrot_state == (PARROT_SWOOP | PARROT_RETURN))
- walk(src, 0)
- if(!parrot_perch || !isturf(parrot_perch.loc)) //Make sure the perch exists and somehow isnt inside of something else.
- parrot_perch = null
- parrot_state = PARROT_WANDER
- return
-
- if(in_range(src, parrot_perch))
- src.forceMove(parrot_perch.loc)
- drop_held_item()
- parrot_state = PARROT_PERCH
- icon_state = "parrot_sit"
- return
-
- walk_to(src, parrot_perch, 1, parrot_speed)
- return
-
-//-----FLEEING
- else if(parrot_state == (PARROT_SWOOP | PARROT_FLEE))
- walk(src,0)
- if(!parrot_interest || !isliving(parrot_interest)) //Sanity
- parrot_state = PARROT_WANDER
-
- walk_away(src, parrot_interest, 1, parrot_speed-parrot_been_shot)
- parrot_been_shot--
- return
-
-//-----ATTACKING
- else if(parrot_state == (PARROT_SWOOP | PARROT_ATTACK))
-
- //If we're attacking a nothing, an object, a turf or a ghost for some stupid reason, switch to wander
- if(!parrot_interest || !isliving(parrot_interest))
- parrot_interest = null
- parrot_state = PARROT_WANDER
- return
-
- var/mob/living/L = parrot_interest
-
- //If the mob is close enough to interact with
- if(in_range(src, parrot_interest))
-
- //If the mob we've been chasing/attacking dies or falls into crit, check for loot!
- if(L.stat)
- parrot_interest = null
- if(!held_item)
- held_item = steal_from_ground()
- if(!held_item)
- held_item = steal_from_mob() //Apparently it's possible for dead mobs to hang onto items in certain circumstances.
- if(parrot_perch in view(src)) //If we have a home nearby, go to it, otherwise find a new home
- parrot_state = PARROT_SWOOP | PARROT_RETURN
- else
- parrot_state = PARROT_WANDER
- return
-
- //Time for the hurt to begin!
- var/damage = rand(5,10)
-
- if(ishuman(parrot_interest))
- var/mob/living/carbon/human/H = parrot_interest
- var/obj/item/organ/external/affecting = H.get_organ(ran_zone(pick(parrot_dam_zone)))
-
- H.apply_damage(damage, BRUTE, affecting, H.run_armor_check(affecting, "melee"), sharp=1)
- visible_emote(pick("pecks [H]'s [affecting].", "cuts [H]'s [affecting] with its talons."))
-
- else
- L.adjustBruteLoss(damage)
- visible_emote(pick("pecks at [L].", "claws [L]."))
- return
-
- //Otherwise, fly towards the mob!
- else
- walk_to(src, parrot_interest, 1, parrot_speed)
- return
-//-----STATE MISHAP
- else //This should not happen. If it does lets reset everything and try again
- walk(src,0)
- parrot_interest = null
- parrot_perch = null
- drop_held_item()
- parrot_state = PARROT_WANDER
- return
-
-/*
- * Procs
- */
-
-/mob/living/simple_animal/parrot/movement_delay()
- if(client && stat == CONSCIOUS && parrot_state != "parrot_fly")
- icon_state = "parrot_fly"
- ..()
-
-/mob/living/simple_animal/parrot/proc/search_for_item()
- for(var/atom/movable/AM in view(src))
- //Skip items we already stole or are wearing or are too big
- if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src)
- continue
-
- if(istype(AM, /obj/item))
- var/obj/item/I = AM
- if(I.w_class < ITEMSIZE_SMALL)
- return I
-
- if(iscarbon(AM))
- var/mob/living/carbon/C = AM
- if((C.l_hand && C.l_hand.w_class <= ITEMSIZE_SMALL) || (C.r_hand && C.r_hand.w_class <= ITEMSIZE_SMALL))
- return C
- return null
-
-/mob/living/simple_animal/parrot/proc/search_for_perch()
- for(var/obj/O in view(src))
- for(var/path in desired_perches)
- if(istype(O, path))
- return O
- return null
-
-//This proc was made to save on doing two 'in view' loops seperatly
-/mob/living/simple_animal/parrot/proc/search_for_perch_and_item()
- for(var/atom/movable/AM in view(src))
- for(var/perch_path in desired_perches)
- if(istype(AM, perch_path))
- return AM
-
- //Skip items we already stole or are wearing or are too big
- if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src)
- continue
-
- if(istype(AM, /obj/item))
- var/obj/item/I = AM
- if(I.w_class <= ITEMSIZE_SMALL)
- return I
-
- if(iscarbon(AM))
- var/mob/living/carbon/C = AM
- if(C.l_hand && C.l_hand.w_class <= ITEMSIZE_SMALL || C.r_hand && C.r_hand.w_class <= ITEMSIZE_SMALL)
- return C
- return null
-
-
-/*
- * Verbs - These are actually procs, but can be used as verbs by player-controlled parrots.
- */
-/mob/living/simple_animal/parrot/proc/steal_from_ground()
- set name = "Steal from ground"
- set category = "Parrot"
- set desc = "Grabs a nearby item."
-
- if(stat)
- return -1
-
- if(held_item)
- src << "\red You are already holding the [held_item]"
- return 1
-
- for(var/obj/item/I in view(1,src))
- //Make sure we're not already holding it and it's small enough
- if(I.loc != src && I.w_class <= ITEMSIZE_SMALL)
-
- //If we have a perch and the item is sitting on it, continue
- if(!client && parrot_perch && I.loc == parrot_perch.loc)
- continue
-
- held_item = I
- I.forceMove(src)
- visible_message("[src] grabs the [held_item]!", "\blue You grab the [held_item]!", "You hear the sounds of wings flapping furiously.")
- return held_item
-
- src << "\red There is nothing of interest to take."
- return 0
-
-/mob/living/simple_animal/parrot/proc/steal_from_mob()
- set name = "Steal from mob"
- set category = "Parrot"
- set desc = "Steals an item right out of a person's hand!"
-
- if(stat)
- return -1
-
- if(held_item)
- src << "\red You are already holding the [held_item]"
- return 1
-
- var/obj/item/stolen_item = null
-
- for(var/mob/living/carbon/C in view(1,src))
- if(C.l_hand && C.l_hand.w_class <= ITEMSIZE_SMALL)
- stolen_item = C.l_hand
-
- if(C.r_hand && C.r_hand.w_class <= ITEMSIZE_SMALL)
- stolen_item = C.r_hand
-
- if(stolen_item)
- C.remove_from_mob(stolen_item)
- held_item = stolen_item
- stolen_item.forceMove(src)
- visible_message("[src] grabs the [held_item] out of [C]'s hand!", "\blue You snag the [held_item] out of [C]'s hand!", "You hear the sounds of wings flapping furiously.")
- return held_item
-
- src << "\red There is nothing of interest to take."
- return 0
-
-/mob/living/simple_animal/parrot/verb/drop_held_item_player()
- set name = "Drop held item"
- set category = "Parrot"
- set desc = "Drop the item you're holding."
-
- if(stat)
- return
-
- src.drop_held_item()
-
- return
-
-/mob/living/simple_animal/parrot/proc/drop_held_item(var/drop_gently = 1)
- set name = "Drop held item"
- set category = "Parrot"
- set desc = "Drop the item you're holding."
-
- if(stat)
- return -1
-
- if(!held_item)
- usr << "\red You have nothing to drop!"
- return 0
-
- if(!drop_gently)
- if(istype(held_item, /obj/item/weapon/grenade))
- var/obj/item/weapon/grenade/G = held_item
- G.forceMove(src.loc)
- G.prime()
- src << "You let go of the [held_item]!"
- held_item = null
- return 1
-
- src << "You drop the [held_item]."
-
- held_item.forceMove(src.loc)
- held_item = null
- return 1
-
-/mob/living/simple_animal/parrot/proc/perch_player()
- set name = "Sit"
- set category = "Parrot"
- set desc = "Sit on a nice comfy perch."
-
- if(stat || !client)
- return
-
- if(icon_state == "parrot_fly")
- for(var/atom/movable/AM in view(src,1))
- for(var/perch_path in desired_perches)
- if(istype(AM, perch_path))
- src.forceMove(AM.loc)
- icon_state = "parrot_sit"
- return
- src << "\red There is no perch nearby to sit on."
- return
-
-/*
- * Sub-types
- */
-/mob/living/simple_animal/parrot/Poly
- name = "Poly"
- desc = "Poly the Parrot. An expert on quantum cracker theory."
- speak = list("Poly wanna cracker!", ":e Check the singlo, you chucklefucks!",":e Wire the solars, you lazy bums!",":e WHO TOOK THE DAMN HARDSUITS?",":e OH GOD ITS FREE CALL THE SHUTTLE")
-
-/mob/living/simple_animal/parrot/Poly/New()
- ears = new /obj/item/device/radio/headset/headset_eng(src)
- available_channels = list(":e")
- ..()
-
-/mob/living/simple_animal/parrot/say(var/message)
-
- if(stat)
- return
-
- var/verb = "says"
- if(speak_emote.len)
- verb = pick(speak_emote)
-
-
- var/message_mode=""
- if(copytext(message,1,2) == ";")
- message_mode = "headset"
- message = copytext(message,2)
-
- if(length(message) >= 2)
- var/channel_prefix = copytext(message, 1 ,3)
- message_mode = department_radio_keys[channel_prefix]
-
- if(copytext(message,1,2) == ":")
- var/positioncut = 3
- message = trim(copytext(message,positioncut))
-
- message = capitalize(trim_left(message))
-
- if(message_mode)
- if(message_mode in radiochannels)
- if(ears && istype(ears,/obj/item/device/radio))
- ears.talk_into(src,sanitize(message), message_mode, verb, null)
-
-
- ..(message)
-
-
-/mob/living/simple_animal/parrot/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/speaker = null)
- if(prob(50))
- parrot_hear(message)
- ..(message,verb,language,alt_name,italics,speaker)
-
-
-
-/mob/living/simple_animal/parrot/hear_radio(var/message, var/verb="says", var/datum/language/language=null, var/part_a, var/part_b, var/part_c, var/mob/speaker = null, var/hard_to_hear = 0)
- if(prob(50))
- parrot_hear("[pick(available_channels)] [message]")
- ..(message,verb,language,part_a,part_b,speaker,hard_to_hear)
-
-
-/mob/living/simple_animal/parrot/proc/parrot_hear(var/message="")
- if(!message || stat)
- return
- speech_buffer.Add(message)
-
-/mob/living/simple_animal/parrot/attack_generic(var/mob/user, var/damage, var/attack_message)
-
- var/success = ..()
-
- if(client)
- return success
-
- if(parrot_state == PARROT_PERCH)
- parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched
-
- if(!success)
- return 0
-
- parrot_interest = user
- parrot_state = PARROT_SWOOP | PARROT_ATTACK //Attack other animals regardless
- icon_state = "parrot_fly"
+/* Parrots!
+ * Contains
+ * Defines
+ * Inventory (headset stuff)
+ * Attack responces
+ * AI
+ * Procs / Verbs (usable by players)
+ * Sub-types
+ */
+
+/*
+ * Defines
+ */
+
+//Parrot is too snowflake for me to rewrite right now, someone should make it use the new
+//simple_animal movement stuff. -Aro
+
+//Only a maximum of one action and one intent should be active at any given time.
+//Actions
+#define PARROT_PERCH 1 //Sitting/sleeping, not moving
+#define PARROT_SWOOP 2 //Moving towards or away from a target
+#define PARROT_WANDER 4 //Moving without a specific target in mind
+
+//Intents
+#define PARROT_STEAL 8 //Flying towards a target to steal it/from it
+#define PARROT_ATTACK 16 //Flying towards a target to attack it
+#define PARROT_RETURN 32 //Flying towards its perch
+#define PARROT_FLEE 64 //Flying away from its attacker
+
+
+/mob/living/simple_animal/parrot
+ name = "\improper Parrot"
+ desc = "The parrot squaks, \"It's a Parrot! BAWWK!\""
+ icon = 'icons/mob/animal.dmi'
+ icon_state = "parrot_fly"
+ icon_living = "parrot_fly"
+ icon_dead = "parrot_dead"
+
+ turns_per_move = 5
+ pass_flags = PASSTABLE
+ mob_size = MOB_SMALL
+
+ response_help = "pets"
+ response_disarm = "gently moves aside"
+ response_harm = "swats"
+ stop_automated_movement = 1
+ universal_speak = 1
+
+ speak_chance = 2
+ speak = list("Hi","Hello!","Cracker?","BAWWWWK george mellons griffing me")
+ speak_emote = list("squawks","says","yells")
+ emote_hear = list("squawks","bawks")
+ emote_see = list("flutters its wings")
+
+ meat_type = /obj/item/weapon/reagent_containers/food/snacks/cracker
+
+ var/parrot_state = PARROT_WANDER //Hunt for a perch when created
+ var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in life() being run every single tick.
+ var/parrot_sleep_dur = 25 //Same as above, this is the var that physically counts down
+ var/parrot_dam_zone = list(BP_TORSO, BP_HEAD, BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG) //For humans, select a bodypart to attack
+
+ var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower.
+ var/parrot_been_shot = 0 //Parrots get a speed bonus after being shot. This will deincrement every Life() and at 0 the parrot will return to regular speed.
+
+ var/list/speech_buffer = list()
+ var/list/available_channels = list()
+
+ //Headset for Poly to yell at engineers :)
+ var/obj/item/device/radio/headset/ears = null
+
+ //The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from,
+ //mobs it wants to attack or mobs that have attacked it
+ var/atom/movable/parrot_interest = null
+
+ //Parrots will generally sit on their pertch unless something catches their eye.
+ //These vars store their preffered perch and if they dont have one, what they can use as a perch
+ var/obj/parrot_perch = null
+ var/obj/desired_perches = list(/obj/structure/frame, /obj/structure/displaycase, \
+ /obj/structure/filingcabinet, /obj/machinery/teleport, \
+ /obj/machinery/computer, /obj/machinery/clonepod, \
+ /obj/machinery/dna_scannernew, /obj/machinery/telecomms, \
+ /obj/machinery/nuclearbomb, /obj/machinery/particle_accelerator, \
+ /obj/machinery/recharge_station, /obj/machinery/smartfridge, \
+ /obj/machinery/suit_storage_unit)
+
+ //Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding.
+ var/obj/item/held_item = null
+
+
+/mob/living/simple_animal/parrot/New()
+ ..()
+ if(!ears)
+ var/headset = pick(/obj/item/device/radio/headset/headset_sec, \
+ /obj/item/device/radio/headset/headset_eng, \
+ /obj/item/device/radio/headset/headset_med, \
+ /obj/item/device/radio/headset/headset_sci, \
+ /obj/item/device/radio/headset/headset_cargo)
+ ears = new headset(src)
+
+ parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var
+
+ verbs.Add(/mob/living/simple_animal/parrot/proc/steal_from_ground, \
+ /mob/living/simple_animal/parrot/proc/steal_from_mob, \
+ /mob/living/simple_animal/parrot/verb/drop_held_item_player, \
+ /mob/living/simple_animal/parrot/proc/perch_player)
+
+
+/mob/living/simple_animal/parrot/death()
+ if(held_item)
+ held_item.forceMove(src.loc)
+ held_item = null
+ walk(src,0)
+ ..()
+
+/mob/living/simple_animal/parrot/Stat()
+ ..()
+ stat("Held Item", held_item)
+
+/*
+ * Inventory
+ */
+/mob/living/simple_animal/parrot/show_inv(mob/user as mob)
+ user.set_machine(src)
+ if(user.stat) return
+
+ var/dat = "
"
+ if(ears)
+ dat += "
Headset: [ears] (Remove)"
+ else
+ dat += "
Headset: Nothing"
+
+ user << browse(dat, text("window=mob[];size=325x500", name))
+ onclose(user, "mob[real_name]")
+ return
+
+/mob/living/simple_animal/parrot/Topic(href, href_list)
+
+ //Can the usr physically do this?
+ if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr))
+ return
+
+ //Is the usr's mob type able to do this?
+ if(ishuman(usr) || issmall(usr) || isrobot(usr))
+
+ //Removing from inventory
+ if(href_list["remove_inv"])
+ var/remove_from = href_list["remove_inv"]
+ switch(remove_from)
+ if("ears")
+ if(ears)
+ if(available_channels.len)
+ src.say("[pick(available_channels)] BAWWWWWK LEAVE THE HEADSET BAWKKKKK!")
+ else
+ src.say("BAWWWWWK LEAVE THE HEADSET BAWKKKKK!")
+ ears.forceMove(src.loc)
+ ears = null
+ for(var/possible_phrase in speak)
+ if(copytext(possible_phrase,1,3) in department_radio_keys)
+ possible_phrase = copytext(possible_phrase,3,length(possible_phrase))
+ else
+ usr << "\red There is nothing to remove from its [remove_from]."
+ return
+
+ //Adding things to inventory
+ else if(href_list["add_inv"])
+ var/add_to = href_list["add_inv"]
+ if(!usr.get_active_hand())
+ usr << "\red You have nothing in your hand to put on its [add_to]."
+ return
+ switch(add_to)
+ if("ears")
+ if(ears)
+ usr << "\red It's already wearing something."
+ return
+ else
+ var/obj/item/item_to_add = usr.get_active_hand()
+ if(!item_to_add)
+ return
+
+ if( !istype(item_to_add, /obj/item/device/radio/headset) )
+ usr << "\red This object won't fit."
+ return
+
+ var/obj/item/device/radio/headset/headset_to_add = item_to_add
+
+ usr.drop_item()
+ headset_to_add.forceMove(src)
+ src.ears = headset_to_add
+ usr << "You fit the headset onto [src]."
+
+ clearlist(available_channels)
+ for(var/ch in headset_to_add.channels)
+ switch(ch)
+ if("Engineering")
+ available_channels.Add(":e")
+ if("Command")
+ available_channels.Add(":c")
+ if("Security")
+ available_channels.Add(":s")
+ if("Science")
+ available_channels.Add(":n")
+ if("Medical")
+ available_channels.Add(":m")
+ if("Mining")
+ available_channels.Add(":d")
+ if("Cargo")
+ available_channels.Add(":q")
+
+ if(headset_to_add.translate_binary)
+ available_channels.Add(":b")
+ else
+ ..()
+
+
+/*
+ * Attack responces
+ */
+//Humans, monkeys, aliens
+/mob/living/simple_animal/parrot/attack_hand(mob/living/carbon/M as mob)
+ ..()
+ if(client) return
+ if(!stat && M.a_intent == I_HURT)
+
+ icon_state = "parrot_fly" //It is going to be flying regardless of whether it flees or attacks
+
+ if(parrot_state == PARROT_PERCH)
+ parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched
+
+ parrot_interest = M
+ parrot_state = PARROT_SWOOP //The parrot just got hit, it WILL move, now to pick a direction..
+
+ if(M.health < 50) //Weakened mob? Fight back!
+ parrot_state |= PARROT_ATTACK
+ else
+ parrot_state |= PARROT_FLEE //Otherwise, fly like a bat out of hell!
+ drop_held_item(0)
+ return
+
+//Mobs with objects
+/mob/living/simple_animal/parrot/attackby(var/obj/item/O as obj, var/mob/user as mob)
+ ..()
+ if(!stat && !client && !istype(O, /obj/item/stack/medical))
+ if(O.force)
+ if(parrot_state == PARROT_PERCH)
+ parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched
+
+ parrot_interest = user
+ parrot_state = PARROT_SWOOP | PARROT_FLEE
+ icon_state = "parrot_fly"
+ drop_held_item(0)
+ return
+
+//Bullets
+/mob/living/simple_animal/parrot/bullet_act(var/obj/item/projectile/Proj)
+ ..()
+ if(!stat && !client)
+ if(parrot_state == PARROT_PERCH)
+ parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched
+
+ parrot_interest = null
+ parrot_state = PARROT_WANDER //OWFUCK, Been shot! RUN LIKE HELL!
+ parrot_been_shot += 5
+ icon_state = "parrot_fly"
+ drop_held_item(0)
+ return
+
+
+/*
+ * AI - Not really intelligent, but I'm calling it AI anyway.
+ */
+/mob/living/simple_animal/parrot/Life()
+ ..()
+
+ //Sprite and AI update for when a parrot gets pulled
+ if(pulledby && stat == CONSCIOUS)
+ icon_state = "parrot_fly"
+ if(!client)
+ parrot_state = PARROT_WANDER
+ return
+
+ if(client || stat)
+ return //Lets not force players or dead/incap parrots to move
+
+ if(!isturf(src.loc) || !canmove || buckled)
+ return //If it can't move, dont let it move. (The buckled check probably isn't necessary thanks to canmove)
+
+
+//-----SPEECH
+ /* Parrot speech mimickry!
+ Phrases that the parrot hears in mob/living/say() get added to speach_buffer.
+ Every once in a while, the parrot picks one of the lines from the buffer and replaces an element of the 'speech' list.
+ Then it clears the buffer to make sure they dont magically remember something from hours ago. */
+ if(speech_buffer.len && prob(10))
+ if(speak.len)
+ speak.Remove(pick(speak))
+
+ speak.Add(pick(speech_buffer))
+ clearlist(speech_buffer)
+
+
+//-----SLEEPING
+ if(parrot_state == PARROT_PERCH)
+ if(parrot_perch && parrot_perch.loc != src.loc) //Make sure someone hasnt moved our perch on us
+ if(parrot_perch in view(src))
+ parrot_state = PARROT_SWOOP | PARROT_RETURN
+ icon_state = "parrot_fly"
+ return
+ else
+ parrot_state = PARROT_WANDER
+ icon_state = "parrot_fly"
+ return
+
+ if(--parrot_sleep_dur) //Zzz
+ return
+
+ else
+ //This way we only call the stuff below once every [sleep_max] ticks.
+ parrot_sleep_dur = parrot_sleep_max
+
+ //Cycle through message modes for the headset
+ if(speak.len)
+ var/list/newspeak = list()
+
+ if(available_channels.len && src.ears)
+ for(var/possible_phrase in speak)
+
+ //50/50 chance to not use the radio at all
+ var/useradio = 0
+ if(prob(50))
+ useradio = 1
+
+ if(copytext(possible_phrase,1,3) in department_radio_keys)
+ possible_phrase = "[useradio?pick(available_channels):""] [copytext(possible_phrase,3,length(possible_phrase)+1)]" //crop out the channel prefix
+ else
+ possible_phrase = "[useradio?pick(available_channels):""] [possible_phrase]"
+
+ newspeak.Add(possible_phrase)
+
+ else //If we have no headset or channels to use, dont try to use any!
+ for(var/possible_phrase in speak)
+ if(copytext(possible_phrase,1,3) in department_radio_keys)
+ possible_phrase = "[copytext(possible_phrase,3,length(possible_phrase)+1)]" //crop out the channel prefix
+ newspeak.Add(possible_phrase)
+ speak = newspeak
+
+ //Search for item to steal
+ parrot_interest = search_for_item()
+ if(parrot_interest)
+ visible_emote("looks in [parrot_interest]'s direction and takes flight")
+ parrot_state = PARROT_SWOOP | PARROT_STEAL
+ icon_state = "parrot_fly"
+ return
+
+//-----WANDERING - This is basically a 'I dont know what to do yet' state
+ else if(parrot_state == PARROT_WANDER)
+ //Stop movement, we'll set it later
+ walk(src, 0)
+ parrot_interest = null
+
+ //Wander around aimlessly. This will help keep the loops from searches down
+ //and possibly move the mob into a new are in view of something they can use
+ if(prob(90))
+ step(src, pick(cardinal))
+ return
+
+ if(!held_item && !parrot_perch) //If we've got nothing to do.. look for something to do.
+ var/atom/movable/AM = search_for_perch_and_item() //This handles checking through lists so we know it's either a perch or stealable item
+ if(AM)
+ if(istype(AM, /obj/item) || isliving(AM)) //If stealable item
+ parrot_interest = AM
+ visible_emote("turns and flies towards [parrot_interest]")
+ parrot_state = PARROT_SWOOP | PARROT_STEAL
+ return
+ else //Else it's a perch
+ parrot_perch = AM
+ parrot_state = PARROT_SWOOP | PARROT_RETURN
+ return
+ return
+
+ if(parrot_interest && parrot_interest in view(src))
+ parrot_state = PARROT_SWOOP | PARROT_STEAL
+ return
+
+ if(parrot_perch && parrot_perch in view(src))
+ parrot_state = PARROT_SWOOP | PARROT_RETURN
+ return
+
+ else //Have an item but no perch? Find one!
+ parrot_perch = search_for_perch()
+ if(parrot_perch)
+ parrot_state = PARROT_SWOOP | PARROT_RETURN
+ return
+//-----STEALING
+ else if(parrot_state == (PARROT_SWOOP | PARROT_STEAL))
+ walk(src,0)
+ if(!parrot_interest || held_item)
+ parrot_state = PARROT_SWOOP | PARROT_RETURN
+ return
+
+ if(!(parrot_interest in view(src)))
+ parrot_state = PARROT_SWOOP | PARROT_RETURN
+ return
+
+ if(in_range(src, parrot_interest))
+
+ if(isliving(parrot_interest))
+ steal_from_mob()
+
+ else //This should ensure that we only grab the item we want, and make sure it's not already collected on our perch
+ if(!parrot_perch || parrot_interest.loc != parrot_perch.loc)
+ held_item = parrot_interest
+ parrot_interest.forceMove(src)
+ visible_message("[src] grabs the [held_item]!", "\blue You grab the [held_item]!", "You hear the sounds of wings flapping furiously.")
+
+ parrot_interest = null
+ parrot_state = PARROT_SWOOP | PARROT_RETURN
+ return
+
+ walk_to(src, parrot_interest, 1, parrot_speed)
+ return
+
+//-----RETURNING TO PERCH
+ else if(parrot_state == (PARROT_SWOOP | PARROT_RETURN))
+ walk(src, 0)
+ if(!parrot_perch || !isturf(parrot_perch.loc)) //Make sure the perch exists and somehow isnt inside of something else.
+ parrot_perch = null
+ parrot_state = PARROT_WANDER
+ return
+
+ if(in_range(src, parrot_perch))
+ src.forceMove(parrot_perch.loc)
+ drop_held_item()
+ parrot_state = PARROT_PERCH
+ icon_state = "parrot_sit"
+ return
+
+ walk_to(src, parrot_perch, 1, parrot_speed)
+ return
+
+//-----FLEEING
+ else if(parrot_state == (PARROT_SWOOP | PARROT_FLEE))
+ walk(src,0)
+ if(!parrot_interest || !isliving(parrot_interest)) //Sanity
+ parrot_state = PARROT_WANDER
+
+ walk_away(src, parrot_interest, 1, parrot_speed-parrot_been_shot)
+ parrot_been_shot--
+ return
+
+//-----ATTACKING
+ else if(parrot_state == (PARROT_SWOOP | PARROT_ATTACK))
+
+ //If we're attacking a nothing, an object, a turf or a ghost for some stupid reason, switch to wander
+ if(!parrot_interest || !isliving(parrot_interest))
+ parrot_interest = null
+ parrot_state = PARROT_WANDER
+ return
+
+ var/mob/living/L = parrot_interest
+
+ //If the mob is close enough to interact with
+ if(in_range(src, parrot_interest))
+
+ //If the mob we've been chasing/attacking dies or falls into crit, check for loot!
+ if(L.stat)
+ parrot_interest = null
+ if(!held_item)
+ held_item = steal_from_ground()
+ if(!held_item)
+ held_item = steal_from_mob() //Apparently it's possible for dead mobs to hang onto items in certain circumstances.
+ if(parrot_perch in view(src)) //If we have a home nearby, go to it, otherwise find a new home
+ parrot_state = PARROT_SWOOP | PARROT_RETURN
+ else
+ parrot_state = PARROT_WANDER
+ return
+
+ //Time for the hurt to begin!
+ var/damage = rand(5,10)
+
+ if(ishuman(parrot_interest))
+ var/mob/living/carbon/human/H = parrot_interest
+ var/obj/item/organ/external/affecting = H.get_organ(ran_zone(pick(parrot_dam_zone)))
+
+ H.apply_damage(damage, BRUTE, affecting, H.run_armor_check(affecting, "melee"), H.get_armor_soak(affecting, "melee"), sharp=1)
+ visible_emote(pick("pecks [H]'s [affecting].", "cuts [H]'s [affecting] with its talons."))
+
+ else
+ L.adjustBruteLoss(damage)
+ visible_emote(pick("pecks at [L].", "claws [L]."))
+ return
+
+ //Otherwise, fly towards the mob!
+ else
+ walk_to(src, parrot_interest, 1, parrot_speed)
+ return
+//-----STATE MISHAP
+ else //This should not happen. If it does lets reset everything and try again
+ walk(src,0)
+ parrot_interest = null
+ parrot_perch = null
+ drop_held_item()
+ parrot_state = PARROT_WANDER
+ return
+
+/*
+ * Procs
+ */
+
+/mob/living/simple_animal/parrot/movement_delay()
+ if(client && stat == CONSCIOUS && parrot_state != "parrot_fly")
+ icon_state = "parrot_fly"
+ ..()
+
+/mob/living/simple_animal/parrot/proc/search_for_item()
+ for(var/atom/movable/AM in view(src))
+ //Skip items we already stole or are wearing or are too big
+ if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src)
+ continue
+
+ if(istype(AM, /obj/item))
+ var/obj/item/I = AM
+ if(I.w_class < ITEMSIZE_SMALL)
+ return I
+
+ if(iscarbon(AM))
+ var/mob/living/carbon/C = AM
+ if((C.l_hand && C.l_hand.w_class <= ITEMSIZE_SMALL) || (C.r_hand && C.r_hand.w_class <= ITEMSIZE_SMALL))
+ return C
+ return null
+
+/mob/living/simple_animal/parrot/proc/search_for_perch()
+ for(var/obj/O in view(src))
+ for(var/path in desired_perches)
+ if(istype(O, path))
+ return O
+ return null
+
+//This proc was made to save on doing two 'in view' loops seperatly
+/mob/living/simple_animal/parrot/proc/search_for_perch_and_item()
+ for(var/atom/movable/AM in view(src))
+ for(var/perch_path in desired_perches)
+ if(istype(AM, perch_path))
+ return AM
+
+ //Skip items we already stole or are wearing or are too big
+ if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src)
+ continue
+
+ if(istype(AM, /obj/item))
+ var/obj/item/I = AM
+ if(I.w_class <= ITEMSIZE_SMALL)
+ return I
+
+ if(iscarbon(AM))
+ var/mob/living/carbon/C = AM
+ if(C.l_hand && C.l_hand.w_class <= ITEMSIZE_SMALL || C.r_hand && C.r_hand.w_class <= ITEMSIZE_SMALL)
+ return C
+ return null
+
+
+/*
+ * Verbs - These are actually procs, but can be used as verbs by player-controlled parrots.
+ */
+/mob/living/simple_animal/parrot/proc/steal_from_ground()
+ set name = "Steal from ground"
+ set category = "Parrot"
+ set desc = "Grabs a nearby item."
+
+ if(stat)
+ return -1
+
+ if(held_item)
+ src << "\red You are already holding the [held_item]"
+ return 1
+
+ for(var/obj/item/I in view(1,src))
+ //Make sure we're not already holding it and it's small enough
+ if(I.loc != src && I.w_class <= ITEMSIZE_SMALL)
+
+ //If we have a perch and the item is sitting on it, continue
+ if(!client && parrot_perch && I.loc == parrot_perch.loc)
+ continue
+
+ held_item = I
+ I.forceMove(src)
+ visible_message("[src] grabs the [held_item]!", "\blue You grab the [held_item]!", "You hear the sounds of wings flapping furiously.")
+ return held_item
+
+ src << "\red There is nothing of interest to take."
+ return 0
+
+/mob/living/simple_animal/parrot/proc/steal_from_mob()
+ set name = "Steal from mob"
+ set category = "Parrot"
+ set desc = "Steals an item right out of a person's hand!"
+
+ if(stat)
+ return -1
+
+ if(held_item)
+ src << "\red You are already holding the [held_item]"
+ return 1
+
+ var/obj/item/stolen_item = null
+
+ for(var/mob/living/carbon/C in view(1,src))
+ if(C.l_hand && C.l_hand.w_class <= ITEMSIZE_SMALL)
+ stolen_item = C.l_hand
+
+ if(C.r_hand && C.r_hand.w_class <= ITEMSIZE_SMALL)
+ stolen_item = C.r_hand
+
+ if(stolen_item)
+ C.remove_from_mob(stolen_item)
+ held_item = stolen_item
+ stolen_item.forceMove(src)
+ visible_message("[src] grabs the [held_item] out of [C]'s hand!", "\blue You snag the [held_item] out of [C]'s hand!", "You hear the sounds of wings flapping furiously.")
+ return held_item
+
+ src << "\red There is nothing of interest to take."
+ return 0
+
+/mob/living/simple_animal/parrot/verb/drop_held_item_player()
+ set name = "Drop held item"
+ set category = "Parrot"
+ set desc = "Drop the item you're holding."
+
+ if(stat)
+ return
+
+ src.drop_held_item()
+
+ return
+
+/mob/living/simple_animal/parrot/proc/drop_held_item(var/drop_gently = 1)
+ set name = "Drop held item"
+ set category = "Parrot"
+ set desc = "Drop the item you're holding."
+
+ if(stat)
+ return -1
+
+ if(!held_item)
+ usr << "\red You have nothing to drop!"
+ return 0
+
+ if(!drop_gently)
+ if(istype(held_item, /obj/item/weapon/grenade))
+ var/obj/item/weapon/grenade/G = held_item
+ G.forceMove(src.loc)
+ G.prime()
+ src << "You let go of the [held_item]!"
+ held_item = null
+ return 1
+
+ src << "You drop the [held_item]."
+
+ held_item.forceMove(src.loc)
+ held_item = null
+ return 1
+
+/mob/living/simple_animal/parrot/proc/perch_player()
+ set name = "Sit"
+ set category = "Parrot"
+ set desc = "Sit on a nice comfy perch."
+
+ if(stat || !client)
+ return
+
+ if(icon_state == "parrot_fly")
+ for(var/atom/movable/AM in view(src,1))
+ for(var/perch_path in desired_perches)
+ if(istype(AM, perch_path))
+ src.forceMove(AM.loc)
+ icon_state = "parrot_sit"
+ return
+ src << "\red There is no perch nearby to sit on."
+ return
+
+/*
+ * Sub-types
+ */
+/mob/living/simple_animal/parrot/Poly
+ name = "Poly"
+ desc = "Poly the Parrot. An expert on quantum cracker theory."
+ speak = list("Poly wanna cracker!", ":e Check the singlo, you chucklefucks!",":e Wire the solars, you lazy bums!",":e WHO TOOK THE DAMN HARDSUITS?",":e OH GOD ITS FREE CALL THE SHUTTLE")
+
+/mob/living/simple_animal/parrot/Poly/New()
+ ears = new /obj/item/device/radio/headset/headset_eng(src)
+ available_channels = list(":e")
+ ..()
+
+/mob/living/simple_animal/parrot/say(var/message)
+
+ if(stat)
+ return
+
+ var/verb = "says"
+ if(speak_emote.len)
+ verb = pick(speak_emote)
+
+
+ var/message_mode=""
+ if(copytext(message,1,2) == ";")
+ message_mode = "headset"
+ message = copytext(message,2)
+
+ if(length(message) >= 2)
+ var/channel_prefix = copytext(message, 1 ,3)
+ message_mode = department_radio_keys[channel_prefix]
+
+ if(copytext(message,1,2) == ":")
+ var/positioncut = 3
+ message = trim(copytext(message,positioncut))
+
+ message = capitalize(trim_left(message))
+
+ if(message_mode)
+ if(message_mode in radiochannels)
+ if(ears && istype(ears,/obj/item/device/radio))
+ ears.talk_into(src,sanitize(message), message_mode, verb, null)
+
+
+ ..(message)
+
+
+/mob/living/simple_animal/parrot/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/speaker = null)
+ if(prob(50))
+ parrot_hear(message)
+ ..(message,verb,language,alt_name,italics,speaker)
+
+
+
+/mob/living/simple_animal/parrot/hear_radio(var/message, var/verb="says", var/datum/language/language=null, var/part_a, var/part_b, var/part_c, var/mob/speaker = null, var/hard_to_hear = 0)
+ if(prob(50))
+ parrot_hear("[pick(available_channels)] [message]")
+ ..(message,verb,language,part_a,part_b,speaker,hard_to_hear)
+
+
+/mob/living/simple_animal/parrot/proc/parrot_hear(var/message="")
+ if(!message || stat)
+ return
+ speech_buffer.Add(message)
+
+/mob/living/simple_animal/parrot/attack_generic(var/mob/user, var/damage, var/attack_message)
+
+ var/success = ..()
+
+ if(client)
+ return success
+
+ if(parrot_state == PARROT_PERCH)
+ parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched
+
+ if(!success)
+ return 0
+
+ parrot_interest = user
+ parrot_state = PARROT_SWOOP | PARROT_ATTACK //Attack other animals regardless
+ icon_state = "parrot_fly"
return success
\ No newline at end of file
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index a161062f1e..f27a73f404 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -903,7 +903,7 @@ mob/proc/yank_out_object()
if(prob(selection.w_class * 5) && (affected.robotic < ORGAN_ROBOT)) //I'M SO ANEMIC I COULD JUST -DIE-.
var/datum/wound/internal_bleeding/I = new (min(selection.w_class * 5, 15))
affected.wounds += I
- H.custom_pain("Something tears wetly in your [affected] as [selection] is pulled free!", 1)
+ H.custom_pain("Something tears wetly in your [affected] as [selection] is pulled free!", 50)
if (ishuman(U))
var/mob/living/carbon/human/human_user = U
diff --git a/code/modules/mob/mob_grab_specials.dm b/code/modules/mob/mob_grab_specials.dm
index e7b75a2e5d..735d35fe7c 100644
--- a/code/modules/mob/mob_grab_specials.dm
+++ b/code/modules/mob/mob_grab_specials.dm
@@ -20,7 +20,7 @@
user << "You must stand still to feel [E] for fractures."
else if(E.status & ORGAN_BROKEN)
user << "The [E.encased ? E.encased : "bone in the [E.name]"] moves slightly when you poke it!"
- H.custom_pain("Your [E.name] hurts where it's poked.")
+ H.custom_pain("Your [E.name] hurts where it's poked.", 40)
else
user << "The [E.encased ? E.encased : "bones in the [E.name]"] seem to be fine."
@@ -56,7 +56,8 @@
return
var/armor = target.run_armor_check(target, "melee")
- if(armor < 60)
+ var/soaked = target.get_armor_soak(target, "melee")
+ if(armor + soaked < 60)
target << "You feel extreme pain!"
var/max_halloss = round(target.species.total_health * 0.8) //up to 80% of passing out
@@ -100,8 +101,9 @@
damage += hat.force * 3
var/armor = target.run_armor_check(BP_HEAD, "melee")
- target.apply_damage(damage, BRUTE, BP_HEAD, armor)
- attacker.apply_damage(10, BRUTE, BP_HEAD, attacker.run_armor_check(BP_HEAD, "melee"))
+ var/soaked = target.get_armor_soak(BP_HEAD, "melee")
+ target.apply_damage(damage, BRUTE, BP_HEAD, armor, soaked)
+ attacker.apply_damage(10, BRUTE, BP_HEAD, attacker.run_armor_check(BP_HEAD), attacker.get_armor_soak(BP_HEAD), "melee")
if(!armor && target.headcheck(BP_HEAD) && prob(damage))
target.apply_effect(20, PARALYZE)
diff --git a/code/modules/organs/blood.dm b/code/modules/organs/blood.dm
index 2c4b378cde..0f5ea190f2 100644
--- a/code/modules/organs/blood.dm
+++ b/code/modules/organs/blood.dm
@@ -6,6 +6,7 @@ var/const/BLOOD_VOLUME_SAFE = 85
var/const/BLOOD_VOLUME_OKAY = 75
var/const/BLOOD_VOLUME_BAD = 60
var/const/BLOOD_VOLUME_SURVIVE = 40
+var/const/CE_STABLE_THRESHOLD = 0.5
/mob/living/carbon/human/var/datum/reagents/vessel // Container for blood and BLOOD ONLY. Do not transfer other chems here.
/mob/living/carbon/human/var/var/pale = 0 // Should affect how mob sprite is drawn, but currently doesn't.
@@ -68,14 +69,24 @@ var/const/BLOOD_VOLUME_SURVIVE = 40
if(!heart)
blood_volume = 0
- else if(heart.damage > 1 && heart.damage < heart.min_bruised_damage)
- blood_volume *= 0.8
- else if(heart.damage >= heart.min_bruised_damage && heart.damage < heart.min_broken_damage)
- blood_volume *= 0.6
- else if(heart.damage >= heart.min_broken_damage && heart.damage < INFINITY)
+ else if(heart.is_broken())
blood_volume *= 0.3
+ else if(heart.is_bruised())
+ blood_volume *= 0.7
+ else if(heart.damage)
+ blood_volume *= 0.8
//Effects of bloodloss
+ var/dmg_coef = 1 //Lower means less damage taken
+ var/threshold_coef = 1 //Lower means the damage caps off lower
+
+ if(CE_STABLE in chem_effects)
+ dmg_coef = 0.5
+ threshold_coef = 0.75
+// These are Bay bits, do some sort of calculation.
+// dmg_coef = min(1, 10/chem_effects[CE_STABLE]) //TODO: add effect for increased damage
+// threshold_coef = min(dmg_coef / CE_STABLE_THRESHOLD, 1)
+
if(blood_volume >= BLOOD_VOLUME_SAFE)
if(pale)
pale = 0
@@ -89,23 +100,23 @@ var/const/BLOOD_VOLUME_SURVIVE = 40
if(prob(1))
var/word = pick("dizzy","woosey","faint")
src << "\red You feel [word]"
- if(oxyloss < 20)
- oxyloss += 3
+ if(getOxyLoss() < 20 * threshold_coef)
+ adjustOxyLoss(3 * dmg_coef)
else if(blood_volume >= BLOOD_VOLUME_BAD)
if(!pale)
pale = 1
update_body()
eye_blurry = max(eye_blurry,6)
- if(oxyloss < 50)
- oxyloss += 10
- oxyloss += 1
+ if(getOxyLoss() < 50 * threshold_coef)
+ adjustOxyLoss(10 * dmg_coef)
+ adjustOxyLoss(1 * dmg_coef)
if(prob(15))
Paralyse(rand(1,3))
var/word = pick("dizzy","woosey","faint")
src << "\red You feel extremely [word]"
else if(blood_volume >= BLOOD_VOLUME_SURVIVE)
- oxyloss += 5
- toxloss += 3
+ adjustOxyLoss(5 * dmg_coef)
+ adjustToxLoss(3 * dmg_coef)
if(prob(15))
var/word = pick("dizzy","woosey","faint")
src << "\red You feel extremely [word]"
@@ -115,8 +126,8 @@ var/const/BLOOD_VOLUME_SURVIVE = 40
update_body()
eye_blurry = max(eye_blurry,6)
Paralyse(3)
- toxloss += 3
- oxyloss += 75 // 15 more than dexp fixes (also more than dex+dexp+tricord)
+ adjustToxLoss(3 * dmg_coef)
+ adjustOxyLoss(75 * dmg_coef) // 15 more than dexp fixes (also more than dex+dexp+tricord)
// Without enough blood you slowly go hungry.
if(blood_volume < BLOOD_VOLUME_SAFE)
@@ -144,6 +155,8 @@ var/const/BLOOD_VOLUME_SURVIVE = 40
blood_loss_divisor += 5
else if((temp.organ_tag == BP_L_HAND) || (temp.organ_tag == BP_R_HAND) || (temp.organ_tag == BP_L_FOOT) || (temp.organ_tag == BP_R_FOOT))
blood_loss_divisor += 10
+ if(CE_STABLE in chem_effects) //Inaprov slows bloodloss
+ blood_loss_divisor += 10
if(temp.applied_pressure)
if(ishuman(temp.applied_pressure))
var/mob/living/carbon/human/H = temp.applied_pressure
diff --git a/code/modules/organs/internal/lungs.dm b/code/modules/organs/internal/lungs.dm
index 6e4c15f752..c00d5df7a8 100644
--- a/code/modules/organs/internal/lungs.dm
+++ b/code/modules/organs/internal/lungs.dm
@@ -24,7 +24,7 @@
/obj/item/organ/internal/lungs/proc/rupture()
var/obj/item/organ/external/parent = owner.get_organ(parent_organ)
if(istype(parent))
- owner.custom_pain("You feel a stabbing pain in your [parent.name]!", 1)
+ owner.custom_pain("You feel a stabbing pain in your [parent.name]!", 50)
bruise()
/obj/item/organ/internal/lungs/handle_germ_effects()
diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm
index 30a80b6599..4639369c7c 100644
--- a/code/modules/organs/organ.dm
+++ b/code/modules/organs/organ.dm
@@ -256,7 +256,7 @@ var/list/organ_cache = list()
if(owner && parent_organ && amount > 0)
var/obj/item/organ/external/parent = owner.get_organ(parent_organ)
if(parent && !silent)
- owner.custom_pain("Something inside your [parent.name] hurts a lot.", 1)
+ owner.custom_pain("Something inside your [parent.name] hurts a lot.", amount)
/obj/item/organ/proc/bruise()
damage = max(damage, min_bruised_damage)
@@ -372,5 +372,11 @@ var/list/organ_cache = list()
bitten(user)
return
-/obj/item/organ/proc/can_feel_pain()
- return !(robotic >= (ORGAN_ROBOT|ORGAN_DESTROYED)) && !(species.flags & NO_PAIN)
+/obj/item/organ/proc/organ_can_feel_pain()
+ if(species.flags & NO_PAIN)
+ return 0
+ if(status & ORGAN_DESTROYED)
+ return 0
+ if(robotic && robotic < ORGAN_LIFELIKE) //Super fancy humanlike robotics probably have sensors, or something?
+ return 0
+ return 1
\ No newline at end of file
diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm
index b746f1be6f..359aebe776 100644
--- a/code/modules/organs/organ_external.dm
+++ b/code/modules/organs/organ_external.dm
@@ -267,7 +267,7 @@
if(status & ORGAN_BROKEN && brute)
jostle_bone(brute)
- if(can_feel_pain() && prob(40))
+ if(organ_can_feel_pain() && prob(40))
owner.emote("scream") //getting hit on broken hand hurts
if(used_weapon)
add_autopsy_data("[used_weapon]", brute + burn)
@@ -487,7 +487,7 @@ This function completely restores a damaged organ to perfect condition.
if((damage > 15) && (type != BURN) && (local_damage > 30) && prob(damage) && (robotic < ORGAN_ROBOT))
var/datum/wound/internal_bleeding/I = new (min(damage - 15, 15))
wounds += I
- owner.custom_pain("You feel something rip in your [name]!", 1)
+ owner.custom_pain("You feel something rip in your [name]!", 50)
//Burn damage can cause fluid loss due to blistering and cook-off
if((damage > 5 || damage + burn_dam >= 15) && type == BURN && (robotic < ORGAN_ROBOT))
@@ -705,7 +705,7 @@ Note that amputating the affected organ does in fact remove the infection from t
owner.vessel.remove_reagent("blood", wound_update_accuracy * W.damage/40) //line should possibly be moved to handle_blood, so all the bleeding stuff is in one place.
if(prob(1 * wound_update_accuracy))
- owner.custom_pain("You feel a stabbing pain in your [name]!",1)
+ owner.custom_pain("You feel a stabbing pain in your [name]!", 50)
// slow healing
var/heal_amt = 0
@@ -1010,7 +1010,7 @@ Note that amputating the affected organ does in fact remove the infection from t
"Something feels like it shattered in your [name]!",\
"You hear a sickening crack.")
jostle_bone()
- if(can_feel_pain())
+ if(organ_can_feel_pain())
owner.emote("scream")
playsound(src.loc, "fracture", 10, 1, -2)
diff --git a/code/modules/organs/organ_internal.dm b/code/modules/organs/organ_internal.dm
index 6975e747d0..244c0b7971 100644
--- a/code/modules/organs/organ_internal.dm
+++ b/code/modules/organs/organ_internal.dm
@@ -248,20 +248,23 @@
if(inflamed == 1)
if(prob(5))
- owner << "You feel a stinging pain in your abdomen!"
- owner.emote("me", 1, "winces slightly.")
+ if(owner.can_feel_pain())
+ owner.custom_pain("You feel a stinging pain in your abdomen!", 25)
if(inflamed > 1)
if(prob(3))
- owner << "You feel a stabbing pain in your abdomen!"
- owner.emote("me", 1, "winces painfully.")
+ if(owner.can_feel_pain())
+ owner.custom_pain("You feel a stabbing pain in your abdomen!", 50)
owner.adjustToxLoss(1)
if(inflamed > 2)
if(prob(1))
owner.vomit()
+ if(owner.can_feel_pain())
+ owner.custom_pain("You feel a horrible pain in your abdomen!", 70)
if(inflamed > 3)
if(prob(1))
- owner << "Your abdomen is a world of pain!"
- owner.Weaken(10)
+ if(owner.can_feel_pain())
+ owner.custom_pain("You feel a stinging pain in your abdomen!", 100)
+ owner.Weaken(10)
var/obj/item/organ/external/groin = owner.get_organ(BP_GROIN)
var/datum/wound/W = new /datum/wound/internal_bleeding(20)
diff --git a/code/modules/organs/pain.dm b/code/modules/organs/pain.dm
index ea9499fc43..5f3980d608 100644
--- a/code/modules/organs/pain.dm
+++ b/code/modules/organs/pain.dm
@@ -5,118 +5,73 @@ mob/var/list/pain_stored = list()
mob/var/last_pain_message = ""
mob/var/next_pain_time = 0
-// partname is the name of a body part
-// amount is a num from 1 to 100
-mob/living/carbon/proc/pain(var/partname, var/amount, var/force, var/burning = 0)
- if(stat >= 1)
- return
- if(!can_feel_pain())
- return
- if(analgesic > 40)
- return
- if(world.time < next_pain_time && !force)
- return
- if(amount > 10 && istype(src,/mob/living/carbon/human))
- if(src:paralysis)
- src:paralysis = max(0, src:paralysis-round(amount/10))
- if(amount > 50 && prob(amount / 5))
- src:drop_item()
- var/msg
- if(burning)
- switch(amount)
- if(1 to 10)
- msg = "Your [partname] burns."
- if(11 to 90)
- flash_weak_pain()
- msg = "Your [partname] burns badly!"
- if(91 to 10000)
- flash_pain()
- msg = "OH GOD! Your [partname] is on fire!"
- else
- switch(amount)
- if(1 to 10)
- msg = "Your [partname] hurts."
- if(11 to 90)
- flash_weak_pain()
- msg = "Your [partname] hurts badly."
- if(91 to 10000)
- flash_pain()
- msg = "OH GOD! Your [partname] is hurting terribly!"
- if(msg && (msg != last_pain_message || prob(10)))
- last_pain_message = msg
- src << msg
- next_pain_time = world.time + (100 - amount)
-
-
// message is the custom message to be displayed
-// flash_strength is 0 for weak pain flash, 1 for strong pain flash
-mob/living/carbon/human/proc/custom_pain(var/message, var/flash_strength)
- if(stat >= 1)
- return
- if(!can_feel_pain())
- return
- if(reagents.has_reagent("tramadol"))
- return
- if(reagents.has_reagent("oxycodone"))
- return
- if(analgesic)
- return
- var/msg = "[message]"
- if(flash_strength >= 1)
- msg = "[message]"
+// power decides how much painkillers will stop the message
+// force means it ignores anti-spam timer
+mob/living/carbon/proc/custom_pain(message, power, force)
+ if(!message || stat || !can_feel_pain() || chem_effects[CE_PAINKILLER] > power)
+ return 0
+ message = "[message]"
+ if(power >= 50)
+ message = "[message]"
// Anti message spam checks
- if(msg && ((msg != last_pain_message) || (world.time >= next_pain_time)))
- last_pain_message = msg
- src << msg
- next_pain_time = world.time + 100
+ if(force || (message != last_pain_message) || (world.time >= next_pain_time))
+ last_pain_message = message
+ src << message
+ next_pain_time = world.time + (100-power)
mob/living/carbon/human/proc/handle_pain()
- // not when sleeping
+ if(stat)
+ return
- if(!can_feel_pain()) return
+ if(!can_feel_pain())
+ return
- if(stat >= 2) return
- if(analgesic > 70)
+ if(world.time < next_pain_time)
return
var/maxdam = 0
var/obj/item/organ/external/damaged_organ = null
for(var/obj/item/organ/external/E in organs)
- if((E.status & ORGAN_DEAD) || E.robotic >= ORGAN_ROBOT) continue
+ if(!E.organ_can_feel_pain()) continue
var/dam = E.get_damage()
// make the choice of the organ depend on damage,
// but also sometimes use one of the less damaged ones
if(dam > maxdam && (maxdam == 0 || prob(70)) )
damaged_organ = E
maxdam = dam
- if(damaged_organ)
- pain(damaged_organ.name, maxdam, 0)
+ if(damaged_organ && chem_effects[CE_PAINKILLER] < maxdam)
+ if(maxdam > 10 && paralysis)
+ paralysis = max(0, paralysis - round(maxdam/10))
+ if(maxdam > 50 && prob(maxdam / 5))
+ drop_item()
+ var/burning = damaged_organ.burn_dam > damaged_organ.brute_dam
+ var/msg
+ switch(maxdam)
+ if(1 to 10)
+ msg = "Your [damaged_organ.name] [burning ? "burns" : "hurts"]."
+ if(11 to 90)
+ flash_weak_pain()
+ msg = "Your [damaged_organ.name] [burning ? "burns" : "hurts"] badly!"
+ if(91 to 10000)
+ flash_pain()
+ msg = "OH GOD! Your [damaged_organ.name] is [burning ? "on fire" : "hurting terribly"]!"
+ custom_pain(msg, maxdam, prob(10))
// Damage to internal organs hurts a lot.
for(var/obj/item/organ/I in internal_organs)
if((I.status & ORGAN_DEAD) || I.robotic >= ORGAN_ROBOT) continue
if(I.damage > 2) if(prob(2))
var/obj/item/organ/external/parent = get_organ(I.parent_organ)
- src.custom_pain("You feel a sharp pain in your [parent.name]", 1)
+ src.custom_pain("You feel a sharp pain in your [parent.name]", 50)
- var/toxDamageMessage = null
- var/toxMessageProb = 1
- switch(getToxLoss())
- if(1 to 5)
- toxMessageProb = 1
- toxDamageMessage = "Your body stings slightly."
- if(6 to 10)
- toxMessageProb = 2
- toxDamageMessage = "Your whole body hurts a little."
- if(11 to 15)
- toxMessageProb = 2
- toxDamageMessage = "Your whole body hurts."
- if(15 to 25)
- toxMessageProb = 3
- toxDamageMessage = "Your whole body hurts badly."
- if(26 to INFINITY)
- toxMessageProb = 5
- toxDamageMessage = "Your body aches all over, it's driving you mad."
-
- if(toxDamageMessage && prob(toxMessageProb))
- src.custom_pain(toxDamageMessage, getToxLoss() >= 15)
\ No newline at end of file
+ if(prob(2))
+ switch(getToxLoss())
+ if(1 to 10)
+ custom_pain("Your body stings slightly.", getToxLoss())
+ if(11 to 30)
+ custom_pain("Your body hurts a little.", getToxLoss())
+ if(31 to 60)
+ custom_pain("Your whole body hurts badly.", getToxLoss())
+ if(61 to INFINITY)
+ custom_pain("Your body aches all over, it's driving you mad.", getToxLoss())
\ No newline at end of file
diff --git a/code/modules/planet/weather.dm b/code/modules/planet/weather.dm
index 14318a1d7f..c98383a242 100644
--- a/code/modules/planet/weather.dm
+++ b/code/modules/planet/weather.dm
@@ -265,11 +265,15 @@
var/target_zone = pick(BP_ALL)
var/amount_blocked = L.run_armor_check(target_zone, "melee")
+ var/amount_soaked = L.get_armor_soak(target_zone, "melee")
if(amount_blocked >= 100)
return // No need to apply damage.
- L.apply_damage(rand(5, 10), BRUTE, target_zone, amount_blocked, used_weapon = "hail")
+ if(amount_soaked >= 10)
+ return // No need to apply damage.
+
+ L.apply_damage(rand(5, 10), BRUTE, target_zone, amount_blocked, amount_soaked, used_weapon = "hail")
to_chat(L, "The hail raining down on you [L.can_feel_pain() ? "hurts" : "damages you"]!")
/datum/weather/sif/blood_moon
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm
index 44ad6f24a2..091842663d 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm
@@ -349,7 +349,7 @@
if(affecting.take_damage(0, removed * power * 0.1))
H.UpdateDamageIcon()
if(prob(100 * removed / meltdose)) // Applies disfigurement
- if (affecting.can_feel_pain())
+ if (affecting.organ_can_feel_pain())
H.emote("scream")
H.status_flags |= DISFIGURED
else
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm
index c049f3c0e9..3712baa895 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm
@@ -14,8 +14,8 @@
/datum/reagent/inaprovaline/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
if(alien != IS_DIONA)
- M.add_chemical_effect(CE_STABLE)
- M.add_chemical_effect(CE_PAINKILLER, 25)
+ M.add_chemical_effect(CE_STABLE, 15)
+ M.add_chemical_effect(CE_PAINKILLER, 10)
/datum/reagent/bicaridine
name = "Bicaridine"
@@ -76,6 +76,29 @@
M.hallucination = max(0, M.hallucination - 9 * removed)
M.adjustToxLoss(-4 * removed)
+/datum/reagent/carthatoline
+ name = "carthatoline"
+ id = "carthatoline"
+ description = "Carthatoline is strong evacuant used to treat severe poisoning."
+ reagent_state = LIQUID
+ color = "#225722"
+ scannable = 1
+
+/datum/reagent/carthatoline/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
+ if(alien == IS_DIONA)
+ return
+ if(M.getToxLoss() && prob(10))
+ M.vomit(1)
+ M.adjustToxLoss(-8 * removed)
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ var/obj/item/organ/internal/liver/L = H.internal_organs_by_name[O_LIVER]
+ if(istype(L))
+ if(L.robotic >= ORGAN_ROBOT)
+ return
+ if(L.damage > 0)
+ L.damage = max(L.damage - 2 * removed, 0)
+
/datum/reagent/dexalin
name = "Dexalin"
id = "dexalin"
@@ -109,7 +132,7 @@
if(alien == IS_VOX)
M.adjustToxLoss(removed * 9)
else if(alien != IS_DIONA)
- M.adjustOxyLoss(-300 * removed)
+ M.adjustOxyLoss(-150 * removed)
holder.remove_reagent("lexorin", 3 * removed)
@@ -179,7 +202,7 @@
mrate_static = TRUE
/datum/reagent/paracetamol/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
- M.add_chemical_effect(CE_PAINKILLER, 50)
+ M.add_chemical_effect(CE_PAINKILLER, 25)
/datum/reagent/paracetamol/overdose(var/mob/living/carbon/M, var/alien)
..()
@@ -217,6 +240,8 @@
/datum/reagent/oxycodone/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
M.add_chemical_effect(CE_PAINKILLER, 200)
+ M.eye_blurry += 10
+ M.confused += 5
/datum/reagent/oxycodone/overdose(var/mob/living/carbon/M, var/alien)
..()
@@ -246,7 +271,7 @@
holder.remove_reagent("mindbreaker", 5)
M.hallucination = max(0, M.hallucination - 10)
M.adjustToxLoss(5 * removed) // It used to be incredibly deadly due to an oversight. Not anymore!
- M.add_chemical_effect(CE_PAINKILLER, 40)
+ M.add_chemical_effect(CE_PAINKILLER, 20)
/datum/reagent/alkysine
name = "Alkysine"
@@ -307,9 +332,60 @@
continue
if(I.damage > 0) //Peridaxon heals only non-robotic organs
I.damage = max(I.damage - removed, 0)
+ H.confused += 5
if(I.damage <= 5 && I.organ_tag == O_EYES)
+ H.eye_blurry += 10 //Eyes need to reset, or something
H.sdisabilities &= ~BLIND
+/datum/reagent/osteodaxon
+ name = "Osteodaxon"
+ id = "osteodaxon"
+ description = "An experimental drug used to heal bone fractures."
+ reagent_state = LIQUID
+ color = "#C9BCE3"
+ metabolism = REM * 0.5
+ overdose = REAGENTS_OVERDOSE * 0.5
+ scannable = 1
+
+/datum/reagent/osteodaxon/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
+ if(alien == IS_DIONA)
+ return
+ M.heal_organ_damage(3 * removed, 0) //Gives the bones a chance to set properly even without other meds
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ for(var/obj/item/organ/external/O in H.bad_external_organs)
+ if(O.status & ORGAN_BROKEN)
+ O.mend_fracture() //Only works if the bone won't rebreak, as usual
+ H.custom_pain("You feel a terrible agony tear through your bones!",60)
+ H.AdjustWeakened(1) //Bones being regrown will knock you over
+
+/datum/reagent/myelamine
+ name = "Myelamine"
+ id = "myelamine"
+ description = "Used to rapidly clot internal hemorrhages by increasing the effectiveness of platelets."
+ reagent_state = LIQUID
+ color = "#4246C7"
+ metabolism = REM * 0.5
+ overdose = REAGENTS_OVERDOSE * 0.5
+ scannable = 1
+
+/datum/reagent/myelamine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
+ if(alien == IS_DIONA)
+ return
+ M.eye_blurry += (3 * removed)
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ for(var/obj/item/organ/external/O in H.bad_external_organs)
+ for(var/datum/wound/W in O.wounds)
+ if(W.bleeding())
+ W.damage = max(W.damage - removed, 0)
+ if(W.damage <= 0)
+ O.wounds -= W
+ if(W.internal)
+ W.damage = max(W.damage - removed, 0)
+ if(W.damage <= 0)
+ O.wounds -= W
+
/datum/reagent/ryetalyn
name = "Ryetalyn"
id = "ryetalyn"
@@ -364,6 +440,8 @@
scannable = 1
/datum/reagent/hyronalin/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
+ if(alien == IS_DIONA)
+ return
M.radiation = max(M.radiation - 30 * removed, 0)
/datum/reagent/arithrazine
@@ -378,6 +456,8 @@
scannable = 1
/datum/reagent/arithrazine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
+ if(alien == IS_DIONA)
+ return
M.radiation = max(M.radiation - 70 * removed, 0)
M.adjustToxLoss(-10 * removed)
if(prob(60))
@@ -432,11 +512,35 @@
scannable = 1
/datum/reagent/leporazine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
+ if(alien == IS_DIONA)
+ return
if(M.bodytemperature > 310)
M.bodytemperature = max(310, M.bodytemperature - (40 * TEMPERATURE_DAMAGE_COEFFICIENT))
else if(M.bodytemperature < 311)
M.bodytemperature = min(310, M.bodytemperature + (40 * TEMPERATURE_DAMAGE_COEFFICIENT))
+/datum/reagent/rezadone
+ name = "Rezadone"
+ id = "rezadone"
+ description = "A powder with almost magical properties, this substance can effectively treat genetic damage in humanoids, though excessive consumption has side effects."
+ reagent_state = SOLID
+ color = "#669900"
+ overdose = REAGENTS_OVERDOSE
+ scannable = 1
+
+/datum/reagent/rezadone/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
+ if(alien == IS_DIONA)
+ return
+ M.adjustCloneLoss(-20 * removed)
+ M.adjustOxyLoss(-2 * removed)
+ M.heal_organ_damage(20 * removed, 20 * removed)
+ M.adjustToxLoss(-20 * removed)
+ if(dose > 3)
+ M.status_flags &= ~DISFIGURED
+ if(dose > 10)
+ M.make_dizzy(5)
+ M.make_jittery(5)
+
/* Antidepressants */
#define ANTIDEPRESSANT_MESSAGE_DELAY 5*60*10
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm
index 15ff2e4f80..e13a73dd29 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm
@@ -43,6 +43,13 @@
color = "#003333"
strength = 10
+/datum/reagent/toxin/spidertoxin
+ name = "Spidertoxin"
+ id = "spidertoxin"
+ description = "A liquifying toxin produced by giant spiders."
+ color = "#2CE893"
+ strength = 5
+
/datum/reagent/toxin/phoron
name = "Phoron"
id = "phoron"
diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm
index 9a3b324b30..dbe8ca528f 100644
--- a/code/modules/reagents/Chemistry-Recipes.dm
+++ b/code/modules/reagents/Chemistry-Recipes.dm
@@ -160,6 +160,14 @@
required_reagents = list("silicon" = 1, "potassium" = 1, "nitrogen" = 1)
result_amount = 3
+/datum/chemical_reaction/carthatoline
+ name = "Carthatoline"
+ id = "carthatoline"
+ result = "carthatoline"
+ required_reagents = list("dylovene" = 1, "carbon" = 2, "phoron" = 0.1)
+ catalysts = list("phoron" = 1)
+ result_amount = 2
+
/datum/chemical_reaction/tramadol
name = "Tramadol"
id = "tramadol"
@@ -282,6 +290,15 @@
catalysts = list("phoron" = 5)
result_amount = 2
+/datum/chemical_reaction/osteodaxon
+ name = "Osteodaxon"
+ id = "osteodaxon"
+ result = "osteodaxon"
+ required_reagents = list("bicaridine" = 2, "phoron" = 0.1, "carpotoxin" = 1)
+ catalysts = list("phoron" = 5)
+ inhibitors = list("clonexadone" = 1) // Messes with cryox
+ result_amount = 2
+
/datum/chemical_reaction/virus_food
name = "Virus Food"
id = "virusfood"
@@ -349,6 +366,13 @@
inhibitors = list("sugar" = 1) // Messes up with inaprovaline
result_amount = 2
+/datum/chemical_reaction/myelamine
+ name = "Myelamine"
+ id = "myelamine"
+ result = "myelamine"
+ required_reagents = list("bicaridine" = 1, "iron" = 2, "spidertoxin" = 1)
+ result_amount = 2
+
/datum/chemical_reaction/hyperzine
name = "Hyperzine"
id = "hyperzine"
diff --git a/code/modules/reagents/reagent_containers/food/snacks.dm b/code/modules/reagents/reagent_containers/food/snacks.dm
index 64b05fe0d3..5ea2372731 100644
--- a/code/modules/reagents/reagent_containers/food/snacks.dm
+++ b/code/modules/reagents/reagent_containers/food/snacks.dm
@@ -734,6 +734,19 @@
reagents.add_reagent("pacid",6)
src.bitesize = 6
+/obj/item/weapon/reagent_containers/food/snacks/spidermeat
+ name = "meat"
+ desc = "A slab of green meat."
+ icon_state = "xenomeat"
+ filling_color = "#43DE18"
+ center_of_mass = list("x"=16, "y"=10)
+
+/obj/item/weapon/reagent_containers/food/snacks/spidermeat/New()
+ ..()
+ reagents.add_reagent("protein", 6)
+ reagents.add_reagent("spidertoxin",6)
+ src.bitesize = 6
+
/obj/item/weapon/reagent_containers/food/snacks/meatball
name = "meatball"
desc = "A great meal all round."
diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm
index 771ce4cc27..e9e3c62dad 100644
--- a/code/modules/reagents/reagent_containers/hypospray.dm
+++ b/code/modules/reagents/reagent_containers/hypospray.dm
@@ -88,3 +88,33 @@
user << "It is currently loaded."
else
user << "It is spent."
+
+/obj/item/weapon/reagent_containers/hypospray/autoinjector/clotting
+ name = "clotting agent"
+ desc = "A rapid and safe way to administer clotting drugs by untrained or trained personnel."
+ icon_state = "autoinjector"
+ item_state = "autoinjector"
+ amount_per_transfer_from_this = 10
+ volume = 10
+
+/obj/item/weapon/reagent_containers/hypospray/autoinjector/clotting/New()
+ ..()
+ reagents.remove_reagent("inaprovaline", 5)
+ reagents.add_reagent("myelamine", 10)
+ update_icon()
+ return
+
+/obj/item/weapon/reagent_containers/hypospray/autoinjector/bonemed
+ name = "bone repair injector"
+ desc = "A rapid and safe way to administer advanced drugs by untrained or trained personnel."
+ icon_state = "autoinjector"
+ item_state = "autoinjector"
+ amount_per_transfer_from_this = 10
+ volume = 10
+
+/obj/item/weapon/reagent_containers/hypospray/autoinjector/bonemed/New()
+ ..()
+ reagents.remove_reagent("inaprovaline", 5)
+ reagents.add_reagent("osteodaxon", 10)
+ update_icon()
+ return
\ No newline at end of file
diff --git a/code/modules/research/message_server.dm b/code/modules/research/message_server.dm
index f89e8a20cd..53d534a619 100644
--- a/code/modules/research/message_server.dm
+++ b/code/modules/research/message_server.dm
@@ -242,6 +242,7 @@ var/obj/machinery/blackbox_recorder/blackbox
var/list/msg_security = list()
var/list/msg_deathsquad = list()
var/list/msg_syndicate = list()
+ var/list/msg_raider = list()
var/list/msg_cargo = list()
var/list/msg_service = list()
diff --git a/code/modules/supermatter/supermatter.dm b/code/modules/supermatter/supermatter.dm
index 7457b7790d..df9c900d38 100644
--- a/code/modules/supermatter/supermatter.dm
+++ b/code/modules/supermatter/supermatter.dm
@@ -50,7 +50,7 @@
var/public_alert = 0 //Stick to Engineering frequency except for big warnings when integrity bad
var/warning_point = 100
var/warning_alert = "Danger! Crystal hyperstructure instability!"
- var/emergency_point = 700
+ var/emergency_point = 500
var/emergency_alert = "CRYSTAL DELAMINATION IMMINENT."
var/explosion_point = 1000
diff --git a/code/modules/surgery/bones.dm b/code/modules/surgery/bones.dm
index 015661351f..80a7a1f820 100644
--- a/code/modules/surgery/bones.dm
+++ b/code/modules/surgery/bones.dm
@@ -25,7 +25,7 @@
if (affected.stage == 0)
user.visible_message("[user] starts applying medication to the damaged bones in [target]'s [affected.name] with \the [tool]." , \
"You start applying medication to the damaged bones in [target]'s [affected.name] with \the [tool].")
- target.custom_pain("Something in your [affected.name] is causing you a lot of pain!",1)
+ target.custom_pain("Something in your [affected.name] is causing you a lot of pain!", 50)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
@@ -58,7 +58,7 @@
var/obj/item/organ/external/affected = target.get_organ(target_zone)
user.visible_message("[user] is beginning to set the bone in [target]'s [affected.name] in place with \the [tool]." , \
"You are beginning to set the bone in [target]'s [affected.name] in place with \the [tool].")
- target.custom_pain("The pain in your [affected.name] is going to make you pass out!",1)
+ target.custom_pain("The pain in your [affected.name] is going to make you pass out!", 50)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
diff --git a/code/modules/surgery/encased.dm b/code/modules/surgery/encased.dm
index 51200b52bf..abcc0621da 100644
--- a/code/modules/surgery/encased.dm
+++ b/code/modules/surgery/encased.dm
@@ -38,7 +38,7 @@
user.visible_message("[user] begins to cut through [target]'s [affected.encased] with \the [tool].", \
"You begin to cut through [target]'s [affected.encased] with \the [tool].")
- target.custom_pain("Something hurts horribly in your [affected.name]!",1)
+ target.custom_pain("Something hurts horribly in your [affected.name]!", 60)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
@@ -88,7 +88,7 @@
var/msg = "[user] starts to force open the [affected.encased] in [target]'s [affected.name] with \the [tool]."
var/self_msg = "You start to force open the [affected.encased] in [target]'s [affected.name] with \the [tool]."
user.visible_message(msg, self_msg)
- target.custom_pain("Something hurts horribly in your [affected.name]!",1)
+ target.custom_pain("Something hurts horribly in your [affected.name]!", 40)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
@@ -145,7 +145,7 @@
var/msg = "[user] starts bending [target]'s [affected.encased] back into place with \the [tool]."
var/self_msg = "You start bending [target]'s [affected.encased] back into place with \the [tool]."
user.visible_message(msg, self_msg)
- target.custom_pain("Something hurts horribly in your [affected.name]!",1)
+ target.custom_pain("Something hurts horribly in your [affected.name]!", 100)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
@@ -202,7 +202,7 @@
var/msg = "[user] starts applying \the [tool] to [target]'s [affected.encased]."
var/self_msg = "You start applying \the [tool] to [target]'s [affected.encased]."
user.visible_message(msg, self_msg)
- target.custom_pain("Something hurts horribly in your [affected.name]!",1)
+ target.custom_pain("Something hurts horribly in your [affected.name]!", 100)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
diff --git a/code/modules/surgery/generic.dm b/code/modules/surgery/generic.dm
index 81a105a544..513b6096dc 100644
--- a/code/modules/surgery/generic.dm
+++ b/code/modules/surgery/generic.dm
@@ -42,7 +42,7 @@
var/obj/item/organ/external/affected = target.get_organ(target_zone)
user.visible_message("[user] starts the bloodless incision on [target]'s [affected.name] with \the [tool].", \
"You start the bloodless incision on [target]'s [affected.name] with \the [tool].")
- target.custom_pain("You feel a horrible, searing pain in your [affected.name]!",1)
+ target.custom_pain("You feel a horrible, searing pain in your [affected.name]!", 50)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
@@ -81,7 +81,7 @@
var/obj/item/organ/external/affected = target.get_organ(target_zone)
user.visible_message("[user] starts to construct a prepared incision on and within [target]'s [affected.name] with \the [tool].", \
"You start to construct a prepared incision on and within [target]'s [affected.name] with \the [tool].")
- target.custom_pain("You feel a horrible, searing pain in your [affected.name] as it is pushed apart!",1)
+ target.custom_pain("You feel a horrible, searing pain in your [affected.name] as it is pushed apart!", 50)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
@@ -124,7 +124,7 @@
var/obj/item/organ/external/affected = target.get_organ(target_zone)
user.visible_message("[user] starts the incision on [target]'s [affected.name] with \the [tool].", \
"You start the incision on [target]'s [affected.name] with \the [tool].")
- target.custom_pain("You feel a horrible pain as if from a sharp knife in your [affected.name]!",1)
+ target.custom_pain("You feel a horrible pain as if from a sharp knife in your [affected.name]!", 40)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
@@ -163,7 +163,7 @@
var/obj/item/organ/external/affected = target.get_organ(target_zone)
user.visible_message("[user] starts clamping bleeders in [target]'s [affected.name] with \the [tool].", \
"You start clamping bleeders in [target]'s [affected.name] with \the [tool].")
- target.custom_pain("The pain in your [affected.name] is maddening!",1)
+ target.custom_pain("The pain in your [affected.name] is maddening!", 40)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
@@ -205,7 +205,7 @@
msg = "[user] starts to pry open the incision and rearrange the organs in [target]'s lower abdomen with \the [tool]."
self_msg = "You start to pry open the incision and rearrange the organs in [target]'s lower abdomen with \the [tool]."
user.visible_message(msg, self_msg)
- target.custom_pain("It feels like the skin on your [affected.name] is on fire!",1)
+ target.custom_pain("It feels like the skin on your [affected.name] is on fire!", 40)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
@@ -254,7 +254,7 @@
var/obj/item/organ/external/affected = target.get_organ(target_zone)
user.visible_message("[user] is beginning to cauterize the incision on [target]'s [affected.name] with \the [tool]." , \
"You are beginning to cauterize the incision on [target]'s [affected.name] with \the [tool].")
- target.custom_pain("Your [affected.name] is being burned!",1)
+ target.custom_pain("Your [affected.name] is being burned!", 40)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
@@ -295,7 +295,7 @@
var/obj/item/organ/external/affected = target.get_organ(target_zone)
user.visible_message("[user] is beginning to amputate [target]'s [affected.name] with \the [tool]." , \
"You are beginning to cut through [target]'s [affected.amputation_point] with \the [tool].")
- target.custom_pain("Your [affected.amputation_point] is being ripped apart!",1)
+ target.custom_pain("Your [affected.amputation_point] is being ripped apart!", 100)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm
index 6abe4c3646..205089773d 100644
--- a/code/modules/surgery/organs_internal.dm
+++ b/code/modules/surgery/organs_internal.dm
@@ -56,7 +56,7 @@
user.visible_message("[user] starts treating damage to [target]'s [I.name] with [tool_name].", \
"You start treating damage to [target]'s [I.name] with [tool_name]." )
- target.custom_pain("The pain in your [affected.name] is living hell!",1)
+ target.custom_pain("The pain in your [affected.name] is living hell!", 100)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
@@ -146,7 +146,7 @@
user.visible_message("[user] starts to separate [target]'s [target.op_stage.current_organ] with \the [tool].", \
"You start to separate [target]'s [target.op_stage.current_organ] with \the [tool]." )
- target.custom_pain("The pain in your [affected.name] is living hell!",1)
+ target.custom_pain("The pain in your [affected.name] is living hell!", 100)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
@@ -197,7 +197,7 @@
begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
user.visible_message("[user] starts removing [target]'s [target.op_stage.current_organ] with \the [tool].", \
"You start removing [target]'s [target.op_stage.current_organ] with \the [tool].")
- target.custom_pain("Someone's ripping out your [target.op_stage.current_organ]!",1)
+ target.custom_pain("Someone's ripping out your [target.op_stage.current_organ]!", 100)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
@@ -271,7 +271,7 @@
var/obj/item/organ/external/affected = target.get_organ(target_zone)
user.visible_message("[user] starts transplanting \the [tool] into [target]'s [affected.name].", \
"You start transplanting \the [tool] into [target]'s [affected.name].")
- target.custom_pain("Someone's rooting around in your [affected.name]!",1)
+ target.custom_pain("Someone's rooting around in your [affected.name]!", 100)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
@@ -322,7 +322,7 @@
begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
user.visible_message("[user] begins reattaching [target]'s [target.op_stage.current_organ] with \the [tool].", \
"You start reattaching [target]'s [target.op_stage.current_organ] with \the [tool].")
- target.custom_pain("Someone's digging needles into your [target.op_stage.current_organ]!",1)
+ target.custom_pain("Someone's digging needles into your [target.op_stage.current_organ]!", 100)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
diff --git a/code/modules/surgery/other.dm b/code/modules/surgery/other.dm
index 2b93139111..176724290f 100644
--- a/code/modules/surgery/other.dm
+++ b/code/modules/surgery/other.dm
@@ -33,7 +33,7 @@
var/obj/item/organ/external/affected = target.get_organ(target_zone)
user.visible_message("[user] starts patching the damaged vein in [target]'s [affected.name] with \the [tool]." , \
"You start patching the damaged vein in [target]'s [affected.name] with \the [tool].")
- target.custom_pain("The pain in [affected.name] is unbearable!",1)
+ target.custom_pain("The pain in [affected.name] is unbearable!", 100)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
@@ -81,7 +81,7 @@
var/obj/item/organ/external/affected = target.get_organ(target_zone)
user.visible_message("[user] starts cutting away necrotic tissue in [target]'s [affected.name] with \the [tool]." , \
"You start cutting away necrotic tissue in [target]'s [affected.name] with \the [tool].")
- target.custom_pain("The pain in [affected.name] is unbearable!",1)
+ target.custom_pain("The pain in [affected.name] is unbearable!", 100)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
@@ -133,7 +133,7 @@
var/obj/item/organ/external/affected = target.get_organ(target_zone)
user.visible_message("[user] starts applying medication to the affected tissue in [target]'s [affected.name] with \the [tool]." , \
"You start applying medication to the affected tissue in [target]'s [affected.name] with \the [tool].")
- target.custom_pain("Something in your [affected.name] is causing you a lot of pain!",1)
+ target.custom_pain("Something in your [affected.name] is causing you a lot of pain!", 50)
..()
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
diff --git a/code/modules/virus2/effect.dm b/code/modules/virus2/effect.dm
index 8ca82b96a1..5243bae35f 100644
--- a/code/modules/virus2/effect.dm
+++ b/code/modules/virus2/effect.dm
@@ -121,13 +121,11 @@
stage = 4
badness = 3
activate(var/mob/living/carbon/mob,var/multiplier)
- mob.suiciding = 1
+ mob.suiciding = 30
//instead of killing them instantly, just put them at -175 health and let 'em gasp for a while
viewers(mob) << "\red [mob.name] is holding \his breath. It looks like \he's trying to commit suicide."
mob.adjustOxyLoss(175 - mob.getToxLoss() - mob.getFireLoss() - mob.getBruteLoss() - mob.getOxyLoss())
mob.updatehealth()
- spawn(200) //in case they get revived by cryo chamber or something stupid like that, let them suicide again in 20 seconds
- mob.suiciding = 0
/datum/disease2/effect/killertoxins
name = "Toxification Syndrome"
diff --git a/html/changelogs/Anewbe - Medical.yml b/html/changelogs/Anewbe - Medical.yml
new file mode 100644
index 0000000000..758042ae69
--- /dev/null
+++ b/html/changelogs/Anewbe - Medical.yml
@@ -0,0 +1,49 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# bugfix
+# wip (For works in progress)
+# tweak
+# soundadd
+# sounddel
+# rscadd (general adding of nice things)
+# rscdel (general deleting of nice things)
+# imageadd
+# imagedel
+# maptweak
+# spellcheck (typo fixes)
+# experiment
+#################################
+
+# Your name.
+author: Anewbe
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
+# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - rscadd: "Added a changelog editing system that should cause fewer conflicts and more accurate timestamps."
+ - rscadd: "Added Myelamine, a blood clotting chemical. This is available to antags, and can be ordered from cargo or mixed."
+ - rscadd: "Added Osteodaxon, a bone fixing medicine. This is available to antags, and can be mixed."
+ - rscadd: "Added Carthatoline, a stronger anti-toxin."
+ - rscadd: "Added Spidertoxin, which comes from spiders (duh), and is used in making Myelamine."
+ - rscadd: "Death Alarms can now be ordered from cargo."
+ - rscdel: "Dexalin Plus is half as strong (still pretty strong, though).
+ - rscadd: "Ninja, Merc, and ERT-M Hardsuits can now inject myelamine."
+ - tweak: "Inaprovaline now decreases the effects of bloodloss, and lessens brain damage taken from OxyLoss."
+ - tweak: "Prosthetic organs now take randomised damage when emp'd, this should roughly average out to the old values."
+ - tweak: "The effects of heart damage have been tweaked, should be slightly easier to survive now."
+ - tweak: "Some of the stronger chems have side effects, like confusion or blurry vision. These should be fairly minor, and we may be tweaking them in the future."
+ - tweak: "The suiciding var now wears off, making it possible to save suicide victims if quick action is taken."
+ - tweak: "Death Alarms now announce on General comms again."
\ No newline at end of file
diff --git a/html/changelogs/Anewbe - Tables.yml b/html/changelogs/Anewbe - Tables.yml
new file mode 100644
index 0000000000..b99569b578
--- /dev/null
+++ b/html/changelogs/Anewbe - Tables.yml
@@ -0,0 +1,36 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# bugfix
+# wip (For works in progress)
+# tweak
+# soundadd
+# sounddel
+# rscadd (general adding of nice things)
+# rscdel (general deleting of nice things)
+# imageadd
+# imagedel
+# maptweak
+# spellcheck (typo fixes)
+# experiment
+#################################
+
+# Your name.
+author: Anewbe
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
+# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - rscadd: "Borgs can climb tables."
diff --git a/icons/mob/items/lefthand.dmi b/icons/mob/items/lefthand.dmi
index ad160c7247..975ddbe3b1 100644
Binary files a/icons/mob/items/lefthand.dmi and b/icons/mob/items/lefthand.dmi differ
diff --git a/icons/mob/items/lefthand_material.dmi b/icons/mob/items/lefthand_material.dmi
index 70127575ee..dc642311e5 100644
Binary files a/icons/mob/items/lefthand_material.dmi and b/icons/mob/items/lefthand_material.dmi differ
diff --git a/icons/mob/items/righthand.dmi b/icons/mob/items/righthand.dmi
index 9eb88606ca..b93636d66d 100644
Binary files a/icons/mob/items/righthand.dmi and b/icons/mob/items/righthand.dmi differ
diff --git a/icons/mob/items/righthand_material.dmi b/icons/mob/items/righthand_material.dmi
index db11ee211f..44904c68c7 100644
Binary files a/icons/mob/items/righthand_material.dmi and b/icons/mob/items/righthand_material.dmi differ
diff --git a/icons/obj/items.dmi b/icons/obj/items.dmi
index c0df5a707f..dc3456ba67 100644
Binary files a/icons/obj/items.dmi and b/icons/obj/items.dmi differ
diff --git a/icons/obj/weapons.dmi b/icons/obj/weapons.dmi
index 884b8ac41e..1e7a4ba8a7 100644
Binary files a/icons/obj/weapons.dmi and b/icons/obj/weapons.dmi differ