diff --git a/baystation12.dme b/baystation12.dme index d7a1750493..d5d6d41162 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -1174,6 +1174,7 @@ #include "code\modules\mob\mob_cleanup.dm" #include "code\modules\mob\mob_defines.dm" #include "code\modules\mob\mob_grab.dm" +#include "code\modules\mob\mob_grab_specials.dm" #include "code\modules\mob\mob_helpers.dm" #include "code\modules\mob\mob_movement.dm" #include "code\modules\mob\mob_transformation_simple.dm" diff --git a/code/game/machinery/computer/skills.dm b/code/game/machinery/computer/skills.dm index fca6cf4024..8e57548d6e 100644 --- a/code/game/machinery/computer/skills.dm +++ b/code/game/machinery/computer/skills.dm @@ -277,7 +277,11 @@ What a mess.*/ else P.info += "General Record Lost!
" P.info += "" - P.name = "Employment Record ([active1.fields["name"]])" + if(active1) + P.name = "Employment Record ([active1.fields["name"]])" + else + P.name = "Employment Record (Unknown/Invald Entry)" + log_debug("[usr] ([usr.ckey]) attempted to print a null employee record, this should be investigated.") printing = null //RECORD DELETE if ("Delete All Records") diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm index 61be05870b..6a61a082b9 100644 --- a/code/game/machinery/telecomms/broadcaster.dm +++ b/code/game/machinery/telecomms/broadcaster.dm @@ -287,14 +287,18 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept /* --- Loop through the receivers and categorize them --- */ - if (R.client && !(R.client.prefs.toggles & CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios. - continue + if (R.client) + if(R.client.prefs) + if(!(R.client.prefs.toggles & CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios. + continue + else + log_debug("Client prefs found to be null in /proc/Broadcast_Message() for mob [R] and client [R.ckey], this should be investigated.") if(istype(R, /mob/new_player)) // we don't want new players to hear messages. rare but generates runtimes. continue // Ghosts hearing all radio chat don't want to hear syndicate intercepts, they're duplicates - if(data == 3 && istype(R, /mob/dead/observer) && R.client && (R.client.prefs.toggles & CHAT_GHOSTRADIO)) + if(data == 3 && istype(R, /mob/dead/observer) && R.client && R.client.prefs && (R.client.prefs.toggles & CHAT_GHOSTRADIO)) continue // --- Check for compression --- @@ -490,8 +494,12 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept /* --- Loop through the receivers and categorize them --- */ - if (R.client && !(R.client.prefs.toggles & CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios. - continue + if (R.client) + if(R.client.prefs) + if(!(R.client.prefs.toggles & CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios. + continue + else + log_debug("Client prefs found to be null in /proc/Broadcast_SimpleMessage() for mob [R] and client [R.ckey], this should be investigated.") // --- Check for compression --- diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 2bb2c29a39..4f805f0da5 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -8,6 +8,8 @@ anchored = 1.0 flags = ON_BORDER var/maxhealth = 14.0 + var/maximal_heat = T0C + 100 // Maximal heat before this window begins taking damage from fire + var/damage_per_fire_tick = 2.0 // Amount of damage per fire tick. Regular windows are not fireproof so they might as well break quickly. var/health var/ini_dir = null var/state = 2 @@ -398,8 +400,8 @@ return /obj/structure/window/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(exposed_temperature > T0C + 800) - hit(round(exposed_volume / 100), 0) + if(exposed_temperature > maximal_heat) + hit(damage_per_fire_tick, 0) ..() @@ -409,44 +411,46 @@ icon_state = "window" basestate = "window" glasstype = /obj/item/stack/material/glass - + maximal_heat = T0C + 100 + damage_per_fire_tick = 2.0 + maxhealth = 12.0 /obj/structure/window/phoronbasic name = "phoron window" - desc = "A phoron-glass alloy window. It looks insanely tough to break. It appears it's also insanely tough to burn through." + desc = "A borosilicate alloy window. It seems to be quite strong." basestate = "phoronwindow" icon_state = "phoronwindow" shardtype = /obj/item/weapon/material/shard/phoron glasstype = /obj/item/stack/material/glass/phoronglass - maxhealth = 120 - -/obj/structure/window/phoronbasic/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(exposed_temperature > T0C + 32000) - hit(round(exposed_volume / 1000), 0) - ..() + maximal_heat = T0C + 2000 + damage_per_fire_tick = 1.0 + maxhealth = 40.0 /obj/structure/window/phoronreinforced - name = "reinforced phoron window" - desc = "A phoron-glass alloy window, with rods supporting it. It looks hopelessly tough to break. It also looks completely fireproof, considering how basic phoron windows are insanely fireproof." + name = "reinforced borosilicate window" + desc = "A borosilicate alloy window, with rods supporting it. It seems to be very strong." basestate = "phoronrwindow" icon_state = "phoronrwindow" shardtype = /obj/item/weapon/material/shard/phoron glasstype = /obj/item/stack/material/glass/phoronrglass reinf = 1 - maxhealth = 160 + maximal_heat = T0C + 4000 + damage_per_fire_tick = 1.0 // This should last for 80 fire ticks if the window is not damaged at all. The idea is that borosilicate windows have something like ablative layer that protects them for a while. + maxhealth = 80.0 -/obj/structure/window/phoronreinforced/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - return /obj/structure/window/reinforced name = "reinforced window" desc = "It looks rather strong. Might take a few good hits to shatter it." icon_state = "rwindow" basestate = "rwindow" - maxhealth = 40 + maxhealth = 40.0 reinf = 1 + maximal_heat = T0C + 750 + damage_per_fire_tick = 2.0 glasstype = /obj/item/stack/material/glass/reinforced + /obj/structure/window/New(Loc, constructed=0) ..() diff --git a/code/modules/materials/material_sheets.dm b/code/modules/materials/material_sheets.dm index 653879cfb9..fdb20ab119 100644 --- a/code/modules/materials/material_sheets.dm +++ b/code/modules/materials/material_sheets.dm @@ -46,7 +46,7 @@ /obj/item/stack/material/proc/update_strings() // Update from material datum. singular_name = material.sheet_singular_name - + if(amount>1) name = "[material.use_name] [material.sheet_plural_name]" desc = "A stack of [material.use_name] [material.sheet_plural_name]." @@ -196,12 +196,15 @@ default_type = "rglass" /obj/item/stack/material/glass/phoronglass - name = "phoron glass" - singular_name = "phoron glass sheet" + name = "borosilicate glass" + desc = "This sheet is special platinum-glass alloy designed to withstand large temperatures" + singular_name = "borosilicate glass sheet" icon_state = "sheet-phoronglass" - default_type = "phglass" + default_type = "borosilicate glass" /obj/item/stack/material/glass/phoronrglass - name = "reinforced phoron glass" + name = "reinforced borosilicate glass" + desc = "This sheet is special platinum-glass alloy designed to withstand large temperatures. It is reinforced with few rods." + singular_name = "reinforced borosilicate glass sheet" icon_state = "sheet-phoronrglass" - default_type = "rphglass" + default_type = "reinforced borosilicate glass" \ No newline at end of file diff --git a/code/modules/materials/materials.dm b/code/modules/materials/materials.dm index d8f6aeb67c..71a669ab66 100644 --- a/code/modules/materials/materials.dm +++ b/code/modules/materials/materials.dm @@ -476,23 +476,22 @@ var/list/name_to_material rod_product = null /material/glass/phoron - name = "phglass" - display_name = "phoron glass" + name = "borosilicate glass" + display_name = "borosilicate glass" stack_type = /obj/item/stack/material/glass/phoronglass flags = MATERIAL_BRITTLE - ignition_point = PHORON_MINIMUM_BURN_TEMPERATURE+300 - integrity = 200 // idk why but phoron windows are strong, so. + integrity = 100 icon_colour = "#FC2BC5" - stack_origin_tech = list(TECH_MATERIAL = 3, TECH_PHORON = 2) + stack_origin_tech = list(TECH_MATERIAL = 4) created_window = /obj/structure/window/phoronbasic wire_product = null rod_product = /obj/item/stack/material/glass/phoronrglass /material/glass/phoron/reinforced - name = "rphglass" - display_name = "reinforced phoron glass" + name = "reinforced borosilicate glass" + display_name = "reinforced borosilicate glass" stack_type = /obj/item/stack/material/glass/phoronrglass - stack_origin_tech = list(TECH_MATERIAL = 4, TECH_PHORON = 2) + stack_origin_tech = list(TECH_MATERIAL = 5) composite_material = list() //todo created_window = /obj/structure/window/phoronreinforced hardness = 40 diff --git a/code/modules/mining/alloys.dm b/code/modules/mining/alloys.dm index dac05badd0..19d11fc9c0 100644 --- a/code/modules/mining/alloys.dm +++ b/code/modules/mining/alloys.dm @@ -24,4 +24,12 @@ "carbon" = 1, "hematite" = 1 ) - product = /obj/item/stack/material/steel \ No newline at end of file + product = /obj/item/stack/material/steel + +/datum/alloy/borosilicate + metaltag = "borosilicate glass" + requires = list( + "platinum" = 1, + "sand" = 2 + ) + product = /obj/item/stack/material/glass/phoronglass diff --git a/code/modules/mob/living/bot/secbot.dm b/code/modules/mob/living/bot/secbot.dm index ca233881f2..1820ee93ab 100644 --- a/code/modules/mob/living/bot/secbot.dm +++ b/code/modules/mob/living/bot/secbot.dm @@ -336,7 +336,7 @@ path = list() /mob/living/bot/secbot/proc/check_threat(var/mob/living/M) - if(!M || !istype(M) || M.stat) + if(!M || !istype(M) || M.stat || src == M) return 0 if(emagged) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index bfedd85ec0..eac54734db 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -575,12 +575,14 @@ default behaviour is: if(can_resist()) setClickCooldown(20) - process_resist() + resist_grab() + if(!weakened && !restrained()) + process_resist() /mob/living/proc/can_resist() //need to allow !canmove, or otherwise neck grabs can't be resisted - //so just check weakened instead. - if(stat || weakened) + //similar thing with weakened and pinning + if(stat) return 0 if(!canClick()) return 0 @@ -592,10 +594,6 @@ default behaviour is: escape_inventory(src.loc) return - //resisting grabs (as if it helps anyone...) - if (!restrained()) - resist_grab() - //unbuckling yourself if(buckled) spawn() escape_buckle() diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index ed8e122b62..a9d5456a93 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -1,6 +1,13 @@ #define UPGRADE_COOLDOWN 40 #define UPGRADE_KILL_TIMER 100 +///Process_Grab() +///Called by client/Move() +///Checks to see if you are grabbing anything and if moving will affect your grab. +/client/proc/Process_Grab() + for(var/obj/item/weapon/grab/G in list(mob.l_hand, mob.r_hand)) + G.reset_kill_state() //no wandering across the station/asteroid while choking someone + /obj/item/weapon/grab name = "grab" icon = 'icons/mob/screen1.dmi' @@ -69,7 +76,6 @@ else hud.screen_loc = ui_lhand - /obj/item/weapon/grab/process() if(gcDestroyed) // GC is trying to delete us, we'll kill our processing so we can cleanly GC return PROCESS_KILL @@ -116,23 +122,9 @@ affecting.drop_l_hand() affecting.drop_r_hand() - var/hit_zone = assailant.zone_sel.selecting - var/announce = 0 - if(hit_zone != last_hit_zone) - announce = 1 - last_hit_zone = hit_zone - if(ishuman(affecting)) - switch(hit_zone) - if("mouth") - if(announce) - assailant.visible_message("[assailant] covers [affecting]'s mouth!") - if(affecting:silent < 3) - affecting:silent = 3 - if("eyes") - if(announce) - assailant.visible_message("[assailant] covers [affecting]'s eyes!") - if(affecting:eye_blind < 3) - affecting:eye_blind = 3 + if(iscarbon(affecting)) + handle_eye_mouth_covering(affecting, assailant, assailant.zone_sel.selecting) + if(force_down) if(affecting.loc != assailant.loc) force_down = 0 @@ -153,6 +145,21 @@ adjust_position() +/obj/item/weapon/grab/proc/handle_eye_mouth_covering(mob/living/carbon/target, mob/user, var/target_zone) + var/announce = (target_zone != last_hit_zone) //only display messages when switching between different target zones + last_hit_zone = target_zone + + switch(target_zone) + if("mouth") + if(announce) + user.visible_message("\The [user] covers [target]'s mouth!") + if(target.silent < 3) + target.silent = 3 + if("eyes") + if(announce) + assailant.visible_message("[assailant] covers [affecting]'s eyes!") + if(affecting.eye_blind < 3) + affecting.eye_blind = 3 /obj/item/weapon/grab/attack_self() return s_click(hud) @@ -202,8 +209,6 @@ if(EAST) animate(affecting, pixel_x =-shift, pixel_y = 0, 5, 1, LINEAR_EASING) - - /obj/item/weapon/grab/proc/s_click(obj/screen/S) if(!affecting) return @@ -226,11 +231,8 @@ assailant.visible_message("[assailant] has grabbed [affecting] aggressively (now hands)!") else assailant.visible_message("[assailant] pins [affecting] down to the ground (now hands)!") - force_down = 1 - affecting.Weaken(3) - step_to(assailant, affecting) - assailant.set_dir(EAST) //face the victim - affecting.set_dir(SOUTH) //face up + apply_pinning(affecting, assailant) + state = GRAB_AGGRESSIVE icon_state = "grabbed1" hud.icon_state = "reinforce1" @@ -264,7 +266,6 @@ affecting.set_dir(WEST) adjust_position() - //This is used to make sure the victim hasn't managed to yackety sax away before using the grab. /obj/item/weapon/grab/proc/confirm() if(!assailant || !affecting) @@ -278,17 +279,18 @@ return 1 - /obj/item/weapon/grab/attack(mob/M, mob/living/user) if(!affecting) return - if(world.time < (last_action + 20)) return - + + last_action = world.time + reset_kill_state() //using special grab moves will interrupt choking them + + //clicking on the victim while grabbing them if(M == affecting) - if(ishuman(M)) - last_action = world.time + if(ishuman(affecting)) var/hit_zone = assailant.zone_sel.selecting flick(hud.icon_state, hud) switch(assailant.a_intent) @@ -297,174 +299,37 @@ assailant << "You are no longer pinning [affecting] to the ground." force_down = 0 return - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/E = H.get_organ(hit_zone) - if(E && !(E.status & ORGAN_DESTROYED)) - assailant.visible_message("[assailant] starts inspecting [affecting]'s [E.name] carefully.") - if(do_mob(assailant,H, 10)) - if(E.wounds.len) - assailant << "You find [E.get_wounds_desc()]" - else - assailant << "You find no visible wounds." - else - assailant << "You must stand still to inspect [E] for wounds." - assailant << "Checking bones now..." - if(do_mob(assailant, H, 20)) - if(E.status & ORGAN_BROKEN) - assailant << "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.") - else - assailant << "The [E.encased ? E.encased : "bones in the [E.name]"] seem to be fine." - else - assailant << "You must stand still to feel [E] for fractures." - assailant << "Checking skin now..." - if(do_mob(assailant, H, 10)) - var/bad = 0 - if(H.getToxLoss() >= 40) - assailant << "[H] has an unhealthy skin discoloration." - bad = 1 - if(H.getOxyLoss() >= 20) - assailant << "[H]'s skin is unusaly pale." - bad = 1 - if(E.status & ORGAN_DEAD) - assailant << "[E] is decaying!" - bad = 1 - if(!bad) - assailant << "[H]'s skin is normal." - else - assailant << "You must stand still to check [H]'s skin for abnormalities." - else - assailant << "[H] is missing that bodypart." + inspect_organ(affecting, assailant, hit_zone) + if(I_GRAB) - if(state < GRAB_AGGRESSIVE) - assailant << "You require a better grab to do this." - return - var/obj/item/organ/external/organ = affecting:get_organ(check_zone(hit_zone)) - if(!organ || organ.dislocated == -1) - return - assailant.visible_message("[assailant] [pick("bent", "twisted")] [affecting]'s [organ.name] into a jointlock!") - var/armor = affecting:run_armor_check(affecting, "melee") - if(armor < 2) - affecting << "You feel extreme pain!" - affecting.adjustHalLoss(Clamp(0, 40-affecting.halloss, 40)) //up to 40 halloss - return + jointlock(affecting, assailant, hit_zone) + if(I_HURT) - if(hit_zone == "eyes") - var/mob/living/carbon/human/H = affecting - var/datum/unarmed_attack/attack = H.get_unarmed_attack(src, hit_zone) - if(!attack) - return - - if(state < GRAB_NECK) - assailant << "You require a better grab to do this." - return - for(var/slot in list(slot_wear_mask, slot_head, slot_glasses)) - var/obj/item/protection = affecting.get_equipped_item(slot) - if(istype(protection) && (protection.body_parts_covered & EYES)) - assailant << "You're going to need to remove the eye covering first." - return - if(!affecting.has_eyes()) - assailant << "You cannot locate any eyes on [affecting]!" - return - assailant.attack_log += text("\[[time_stamp()]\] Attacked [affecting.name]'s eyes using grab ([affecting.ckey])") - affecting.attack_log += text("\[[time_stamp()]\] Had eyes attacked by [assailant.name]'s grab ([assailant.ckey])") - msg_admin_attack("[key_name(assailant)] attacked [key_name(affecting)]'s eyes using a grab action.") - - attack.handle_eye_attack(assailant, affecting) - else if(hit_zone != "head") - if(state < GRAB_NECK) - assailant << "You require a better grab to do this." - return - if(affecting:grab_joint(assailant)) - playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - return + attack_eye(affecting, assailant) + else if(hit_zone == "head") + headbut(affecting, assailant) else - if(affecting.lying) - return - assailant.visible_message("[assailant] thrusts \his head into [affecting]'s skull!") - var/damage = 20 - var/obj/item/clothing/hat = assailant.head - if(istype(hat)) - damage += hat.force * 10 - var/armor = affecting:run_armor_check(affecting, "melee") - affecting.apply_damage(damage*rand(90, 110)/100, BRUTE, "head", armor) - assailant.apply_damage(10*rand(90, 110)/100, BRUTE, "head", assailant:run_armor_check("head", "melee")) - if(!armor && prob(damage)) - affecting.apply_effect(20, PARALYZE) - affecting.visible_message("[affecting] has been knocked unconscious!") - playsound(assailant.loc, "swing_hit", 25, 1, -1) - assailant.attack_log += text("\[[time_stamp()]\] Headbutted [affecting.name] ([affecting.ckey])") - affecting.attack_log += text("\[[time_stamp()]\] Headbutted by [assailant.name] ([assailant.ckey])") - msg_admin_attack("[key_name(assailant)] has headbutted [key_name(affecting)]") - assailant.drop_from_inventory(src) - src.loc = null - qdel(src) - return + dislocate(affecting, assailant, hit_zone) + if(I_DISARM) - if(state < GRAB_AGGRESSIVE) - assailant << "You require a better grab to do this." - return - assailant << "You start forcing [affecting] to the ground." - if(!force_down) - if(do_after(assailant, 20) && affecting) - assailant.visible_message("[assailant] is forcing [affecting] to the ground!") - force_down = 1 - affecting.Weaken(3) - affecting.lying = 1 - step_to(assailant, affecting) - assailant.set_dir(EAST) //face the victim - affecting.set_dir(SOUTH) //face up - return - else - assailant << "You are already pinning [affecting] to the ground." - return - + pin_down(affecting, assailant) + + //clicking on yourself while grabbing them if(M == assailant && state >= GRAB_AGGRESSIVE) - - var/can_eat - if((FAT in user.mutations) && issmall(affecting)) - can_eat = 1 - else - var/mob/living/carbon/human/H = user - if(istype(H) && H.species.gluttonous) - // Small animals (mice, lizards). - if(affecting.small) - can_eat = 2 - else - if(H.species.gluttonous == 2) - // Diona nymphs, alien larvae. - if(iscarbon(affecting) && !ishuman(affecting)) - can_eat = 2 - // Monkeys. - else if(issmall(affecting)) - can_eat = 1 - else if(H.species.gluttonous == 3) - // Full-sized humans. - if(ishuman(affecting) && !issmall(affecting)) - can_eat = 1 - // Literally everything else. - else - can_eat = 2 - - if(can_eat) - var/mob/living/carbon/attacker = user - user.visible_message("[user] is attempting to devour [affecting]!") - if(can_eat == 2) - if(!do_mob(user, affecting)||!do_after(user, 30)) return - else - if(!do_mob(user, affecting)||!do_after(user, 100)) return - user.visible_message("[user] devours [affecting]!") - affecting.loc = user - attacker.stomach_contents.Add(affecting) - qdel(src) - + devour(affecting, assailant) /obj/item/weapon/grab/dropped() loc = null if(!destroying) qdel(src) +/obj/item/weapon/grab/proc/reset_kill_state() + if(state == GRAB_KILL) + assailant.visible_message("[assailant] lost \his tight grip on [affecting]'s neck!") + hud.icon_state = "kill" + state = GRAB_NECK + /obj/item/weapon/grab var/destroying = 0 diff --git a/code/modules/mob/mob_grab_specials.dm b/code/modules/mob/mob_grab_specials.dm new file mode 100644 index 0000000000..a151b97aae --- /dev/null +++ b/code/modules/mob/mob_grab_specials.dm @@ -0,0 +1,165 @@ + +/obj/item/weapon/grab/proc/inspect_organ(mob/living/carbon/human/H, mob/user, var/target_zone) + + var/obj/item/organ/external/E = H.get_organ(target_zone) + + if(!E || (E.status & ORGAN_DESTROYED)) + user << "[H] is missing that bodypart." + return + + user.visible_message("[user] starts inspecting [affecting]'s [E.name] carefully.") + if(!do_mob(user,H, 10)) + user << "You must stand still to inspect [E] for wounds." + else if(E.wounds.len) + user << "You find [E.get_wounds_desc()]" + else + user << "You find no visible wounds." + + user << "Checking bones now..." + if(!do_mob(user, H, 20)) + 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.") + else + user << "The [E.encased ? E.encased : "bones in the [E.name]"] seem to be fine." + + user << "Checking skin now..." + if(!do_mob(user, H, 10)) + user << "You must stand still to check [H]'s skin for abnormalities." + else + var/bad = 0 + if(H.getToxLoss() >= 40) + user << "[H] has an unhealthy skin discoloration." + bad = 1 + if(H.getOxyLoss() >= 20) + user << "[H]'s skin is unusaly pale." + bad = 1 + if(E.status & ORGAN_DEAD) + user << "[E] is decaying!" + bad = 1 + if(!bad) + user << "[H]'s skin is normal." + +/obj/item/weapon/grab/proc/jointlock(mob/living/carbon/human/target, mob/attacker, var/target_zone) + if(state < GRAB_AGGRESSIVE) + attacker << "You require a better grab to do this." + return + + var/obj/item/organ/external/organ = target.get_organ(check_zone(target_zone)) + if(!organ || organ.dislocated == -1) + return + + attacker.visible_message("[attacker] [pick("bent", "twisted")] [target]'s [organ.name] into a jointlock!") + var/armor = target.run_armor_check(target, "melee") + if(armor < 2) + target << "You feel extreme pain!" + affecting.adjustHalLoss(Clamp(0, 60-affecting.halloss, 30)) //up to 60 halloss + +/obj/item/weapon/grab/proc/attack_eye(mob/living/carbon/human/target, mob/living/carbon/human/attacker) + if(!istype(attacker)) + return + + var/datum/unarmed_attack/attack = attacker.get_unarmed_attack(target, "eyes") + + if(!attack) + return + if(state < GRAB_NECK) + attacker << "You require a better grab to do this." + return + for(var/obj/item/protection in list(target.head, target.wear_mask, target.glasses)) + if(protection && (protection.body_parts_covered & EYES)) + attacker << "You're going to need to remove the eye covering first." + return + if(!target.has_eyes()) + attacker << "You cannot locate any eyes on [target]!" + return + + attacker.attack_log += text("\[[time_stamp()]\] Attacked [target.name]'s eyes using grab ([target.ckey])") + target.attack_log += text("\[[time_stamp()]\] Had eyes attacked by [attacker.name]'s grab ([attacker.ckey])") + msg_admin_attack("[key_name(attacker)] attacked [key_name(target)]'s eyes using a grab action.") + + attack.handle_eye_attack(attacker, target) + +/obj/item/weapon/grab/proc/headbut(mob/living/carbon/human/target, mob/living/carbon/human/attacker) + if(!istype(attacker)) + return + if(target.lying) + return + attacker.visible_message("[attacker] thrusts \his head into [target]'s skull!") + + var/damage = 20 + var/obj/item/clothing/hat = attacker.head + if(istype(hat)) + damage += hat.force * 10 + + var/armor = target.run_armor_check("head", "melee") + target.apply_damage(damage*rand(90, 110)/100, BRUTE, "head", armor) + attacker.apply_damage(10*rand(90, 110)/100, BRUTE, "head", attacker.run_armor_check("head", "melee")) + + if(!armor && prob(damage)) + target.apply_effect(20, PARALYZE) + target.visible_message("[target] has been knocked unconscious!") + + playsound(attacker.loc, "swing_hit", 25, 1, -1) + attacker.attack_log += text("\[[time_stamp()]\] Headbutted [target.name] ([target.ckey])") + target.attack_log += text("\[[time_stamp()]\] Headbutted by [attacker.name] ([attacker.ckey])") + msg_admin_attack("[key_name(attacker)] has headbutted [key_name(target)]") + + attacker.drop_from_inventory(src) + src.loc = null + qdel(src) + return + +/obj/item/weapon/grab/proc/dislocate(mob/living/carbon/human/target, mob/living/attacker, var/target_zone) + if(state < GRAB_NECK) + attacker << "You require a better grab to do this." + return + if(target.grab_joint(attacker, target_zone)) + playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + return + +/obj/item/weapon/grab/proc/pin_down(mob/target, mob/attacker) + if(state < GRAB_AGGRESSIVE) + attacker << "You require a better grab to do this." + return + if(force_down) + attacker << "You are already pinning [target] to the ground." + + attacker.visible_message("[attacker] starts forcing [target] to the ground!") + if(do_after(attacker, 20) && target) + last_action = world.time + attacker.visible_message("[attacker] forces [target] to the ground!") + apply_pinning(target, attacker) + +/obj/item/weapon/grab/proc/apply_pinning(mob/target, mob/attacker) + force_down = 1 + target.Weaken(3) + target.lying = 1 + step_to(attacker, target) + attacker.set_dir(EAST) //face the victim + target.set_dir(SOUTH) //face up + +/obj/item/weapon/grab/proc/devour(mob/target, mob/user) + var/can_eat + if((FAT in user.mutations) && issmall(target)) + can_eat = 1 + else + var/mob/living/carbon/human/H = user + if(istype(H) && H.species.gluttonous) + if(H.species.gluttonous == 2) + can_eat = 2 + else if(!ishuman(target) && !issmall(target) && (target.small || iscarbon(target))) + can_eat = 1 + + if(can_eat) + var/mob/living/carbon/attacker = user + user.visible_message("[user] is attempting to devour [target]!") + if(can_eat == 2) + if(!do_mob(user, target)||!do_after(user, 30)) return + else + if(!do_mob(user, target)||!do_after(user, 100)) return + user.visible_message("[user] devours [target]!") + target.loc = user + attacker.stomach_contents.Add(target) + qdel(src) \ No newline at end of file diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index e0235f6d5f..9e89abf4ca 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -369,16 +369,6 @@ return Move(n, direct) -///Process_Grab() -///Called by client/Move() -///Checks to see if you are grabbing anything and if moving will affect your grab. -/client/proc/Process_Grab() - for(var/obj/item/weapon/grab/G in list(mob.l_hand, mob.r_hand)) - if(G.state == GRAB_KILL) //no wandering across the station/asteroid while choking someone - mob.visible_message("[mob] lost \his tight grip on [G.affecting]'s neck!") - G.hud.icon_state = "kill" - G.state = GRAB_NECK - ///Process_Incorpmove ///Called by client/Move() ///Allows mobs to run though walls diff --git a/html/changelog.html b/html/changelog.html index d7fd8db0e4..dd54c8a5fe 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -56,6 +56,37 @@ -->
+

02 September 2015

+

Atlantis updated:

+ +

Hubblenaut updated:

+ +

Kelenius updated:

+ +

Matthew951 updated:

+ +

Zuhayr updated:

+ +

24 August 2015

HarpyEagle updated: