From 834245ecdb7e238ba0977d15ef8795fd7234f083 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 10 Jan 2026 23:56:19 +0100 Subject: [PATCH] creates more deticated folders for tools and weaponry and move dm files into them (#94804) ## About The Pull Request Hello hello the evil folder mover goat is back, i will destroy years worth of muscle memory. Items is an folder alot of stuff that really didn't get a spefific location. Items/Weaponry Contains Shields.dm Items/Weaponry/Melee Contains (Most of the extra dm files come from me splitting misc/weaponry.dm into its own categories killing offf weaponry.dm in its whole) baseball_bat.dm, baton.dm, chainofcommand.dm, chainsaw.dm, claymore.dm, dualsaber.dm, energy.dm fireaxe.dm, high_frequency_blade.dm, his_grace.dm, katana.dm, knives.dm, misc.dm, powerfist.dm, sabre.dm soulscythe.dm, spear.dm, supermatter_sword.dm. Items/weaponry/ranged Contains flamethrower.dm, pnuematic_cannon.dm, throwing_star.dm items/tools Contains control_wand.dm, extinguisher.dm, inducer.dm, spess_knife.dm, theft_tools.dm items/tools/engineering Contains airlock_painter.dm, decal_painter.dm, paintable_decals.dm, crowbar.dm, screwdriver.dm, weldingtool.dm wirebbrush.dm, wirecutters.dm, wrench.dm items/tools/janitorial Contains broom.dm, janitor_key.dm items/tools/medical Contains bodybag.dm, cane.dm, defib.dm, reflex_hammer.dm boxcutter.dm is merged into knives.dm Kitchen.dm renamed into kitchen utensils and moved into food and drinks module surgery_tray.dm moved into surgery module banhammer/extendo hand moved from weaponry into toys.dm balloon mallet moved into clown_items.dm ectoplasm moved from weaponry.dm into soulstone.dm (since thats the resulting residue when a shade dies, and not really a weapon) ## Why It's Good For The Game So things are way easier to navigate ## Changelog --- code/game/objects/items/boxcutter.dm | 62 - code/game/objects/items/clown_items.dm | 44 + code/game/objects/items/tongs.dm | 102 -- .../objects/items/{ => tools}/control_wand.dm | 0 .../items/tools/{ => engineering}/crowbar.dm | 0 .../painter/airlock_painter.dm | 0 .../painter/decal_painter.dm | 0 .../painter/paintable_decals.dm | 0 .../tools/{ => engineering}/screwdriver.dm | 0 .../tools/{ => engineering}/weldingtool.dm | 0 .../tools/{ => engineering}/wirebrush.dm | 0 .../tools/{ => engineering}/wirecutters.dm | 0 .../items/tools/{ => engineering}/wrench.dm | 0 .../objects/items/{ => tools}/extinguisher.dm | 0 .../game/objects/items/{ => tools}/inducer.dm | 0 .../items/{ => tools/janitorial}/broom.dm | 0 .../{ => tools/janitorial}/janitor_key.dm | 0 .../items/{ => tools/medical}/bodybag.dm | 0 code/game/objects/items/tools/medical/cane.dm | 147 ++ .../items/{ => tools/medical}/defib.dm | 0 .../{ => tools/medical}/reflex_hammer.dm | 0 .../objects/items/{ => tools}/theft_tools.dm | 0 code/game/objects/items/toys.dm | 71 +- code/game/objects/items/weaponry.dm | 1315 ----------------- .../items/weaponry/melee/baseball_bat.dm | 160 ++ .../items/{ => weaponry}/melee/baton.dm | 0 .../{ => weaponry}/melee/chainofcommand.dm | 0 .../items/{ => weaponry/melee}/chainsaw.dm | 0 .../objects/items/weaponry/melee/claymore.dm | 274 ++++ .../items/{ => weaponry/melee}/dualsaber.dm | 0 .../items/{ => weaponry}/melee/energy.dm | 0 .../items/{ => weaponry/melee}/fireaxe.dm | 0 .../weaponry/melee/high_frequency_blade.dm | 152 ++ .../items/{ => weaponry/melee}/his_grace.dm | 0 .../objects/items/weaponry/melee/katana.dm | 50 + .../items/{ => weaponry/melee}/knives.dm | 136 ++ .../items/{ => weaponry}/melee/misc.dm | 551 +++---- .../items/{ => weaponry/melee}/powerfist.dm | 0 .../objects/items/weaponry/melee/sabre.dm | 156 ++ .../items/{ => weaponry/melee}/soulscythe.dm | 0 .../items/{ => weaponry/melee}/spear.dm | 0 .../items/weaponry/melee/supermatter_sword.dm | 115 ++ .../{ => weaponry/ranged}/flamethrower.dm | 0 .../{ => weaponry/ranged}/pneumatic_cannon.dm | 0 .../items/weaponry/ranged/throwing_star.dm | 51 + .../objects/items/{ => weaponry}/shields.dm | 0 .../antagonists/wizard/equipment/soulstone.dm | 21 + .../equipment/kitchen_utensils.dm} | 104 ++ .../food_and_drinks/{ => equipment}/plate.dm | 0 .../items => modules/surgery}/surgery_tray.dm | 0 icons/map_icons/items/_item.dmi | Bin 19849 -> 19853 bytes tgstation.dme | 83 +- 52 files changed, 1802 insertions(+), 1792 deletions(-) delete mode 100644 code/game/objects/items/boxcutter.dm delete mode 100644 code/game/objects/items/tongs.dm rename code/game/objects/items/{ => tools}/control_wand.dm (100%) rename code/game/objects/items/tools/{ => engineering}/crowbar.dm (100%) rename code/game/objects/items/tools/{ => engineering}/painter/airlock_painter.dm (100%) rename code/game/objects/items/tools/{ => engineering}/painter/decal_painter.dm (100%) rename code/game/objects/items/tools/{ => engineering}/painter/paintable_decals.dm (100%) rename code/game/objects/items/tools/{ => engineering}/screwdriver.dm (100%) rename code/game/objects/items/tools/{ => engineering}/weldingtool.dm (100%) rename code/game/objects/items/tools/{ => engineering}/wirebrush.dm (100%) rename code/game/objects/items/tools/{ => engineering}/wirecutters.dm (100%) rename code/game/objects/items/tools/{ => engineering}/wrench.dm (100%) rename code/game/objects/items/{ => tools}/extinguisher.dm (100%) rename code/game/objects/items/{ => tools}/inducer.dm (100%) rename code/game/objects/items/{ => tools/janitorial}/broom.dm (100%) rename code/game/objects/items/{ => tools/janitorial}/janitor_key.dm (100%) rename code/game/objects/items/{ => tools/medical}/bodybag.dm (100%) create mode 100644 code/game/objects/items/tools/medical/cane.dm rename code/game/objects/items/{ => tools/medical}/defib.dm (100%) rename code/game/objects/items/{ => tools/medical}/reflex_hammer.dm (100%) rename code/game/objects/items/{ => tools}/theft_tools.dm (100%) delete mode 100644 code/game/objects/items/weaponry.dm create mode 100644 code/game/objects/items/weaponry/melee/baseball_bat.dm rename code/game/objects/items/{ => weaponry}/melee/baton.dm (100%) rename code/game/objects/items/{ => weaponry}/melee/chainofcommand.dm (100%) rename code/game/objects/items/{ => weaponry/melee}/chainsaw.dm (100%) create mode 100644 code/game/objects/items/weaponry/melee/claymore.dm rename code/game/objects/items/{ => weaponry/melee}/dualsaber.dm (100%) rename code/game/objects/items/{ => weaponry}/melee/energy.dm (100%) rename code/game/objects/items/{ => weaponry/melee}/fireaxe.dm (100%) create mode 100644 code/game/objects/items/weaponry/melee/high_frequency_blade.dm rename code/game/objects/items/{ => weaponry/melee}/his_grace.dm (100%) create mode 100644 code/game/objects/items/weaponry/melee/katana.dm rename code/game/objects/items/{ => weaponry/melee}/knives.dm (72%) rename code/game/objects/items/{ => weaponry}/melee/misc.dm (55%) rename code/game/objects/items/{ => weaponry/melee}/powerfist.dm (100%) create mode 100644 code/game/objects/items/weaponry/melee/sabre.dm rename code/game/objects/items/{ => weaponry/melee}/soulscythe.dm (100%) rename code/game/objects/items/{ => weaponry/melee}/spear.dm (100%) create mode 100644 code/game/objects/items/weaponry/melee/supermatter_sword.dm rename code/game/objects/items/{ => weaponry/ranged}/flamethrower.dm (100%) rename code/game/objects/items/{ => weaponry/ranged}/pneumatic_cannon.dm (100%) create mode 100644 code/game/objects/items/weaponry/ranged/throwing_star.dm rename code/game/objects/items/{ => weaponry}/shields.dm (100%) rename code/{game/objects/items/kitchen.dm => modules/food_and_drinks/equipment/kitchen_utensils.dm} (78%) rename code/modules/food_and_drinks/{ => equipment}/plate.dm (100%) rename code/{game/objects/items => modules/surgery}/surgery_tray.dm (100%) diff --git a/code/game/objects/items/boxcutter.dm b/code/game/objects/items/boxcutter.dm deleted file mode 100644 index 5452cd013fb..00000000000 --- a/code/game/objects/items/boxcutter.dm +++ /dev/null @@ -1,62 +0,0 @@ -/obj/item/boxcutter - name = "boxcutter" - desc = "A tool for cutting boxes, or throats." - icon = 'icons/obj/tools.dmi' - icon_state = "boxcutter" - inhand_icon_state = "boxcutter" - base_icon_state = "boxcutter" - icon_angle = -90 - lefthand_file = 'icons/mob/inhands/equipment/boxcutter_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/boxcutter_righthand.dmi' - inhand_icon_state = null - attack_verb_continuous = list("prods", "pokes") - attack_verb_simple = list("prod", "poke") - w_class = WEIGHT_CLASS_SMALL - resistance_flags = FIRE_PROOF - force = 0 - /// Used on Initialize, how much time to cut cable restraints and zipties. - var/snap_time_weak_handcuffs = 0 SECONDS - /// Used on Initialize, how much time to cut real handcuffs. Null means it can't. - var/snap_time_strong_handcuffs = null - /// Starts open if true - var/start_extended = FALSE - -/obj/item/boxcutter/get_all_tool_behaviours() - return list(TOOL_KNIFE) - -/obj/item/boxcutter/Initialize(mapload) - . = ..() - AddElement(/datum/element/update_icon_updates_onmob) - AddComponent(/datum/component/butchering, \ - speed = 7 SECONDS, \ - effectiveness = 100, \ - ) - - AddComponent( \ - /datum/component/transforming, \ - start_transformed = start_extended, \ - force_on = 10, \ - throwforce_on = 4, \ - throw_speed_on = throw_speed, \ - sharpness_on = SHARP_EDGED, \ - hitsound_on = 'sound/items/weapons/bladeslice.ogg', \ - w_class_on = WEIGHT_CLASS_NORMAL, \ - attack_verb_continuous_on = list("cuts", "stabs", "slashes"), \ - attack_verb_simple_on = list("cut", "stab", "slash"), \ - ) - - RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform)) - -/obj/item/boxcutter/proc/on_transform(obj/item/source, mob/user, active) - SIGNAL_HANDLER - - playsound(src, 'sound/items/boxcutter_activate.ogg', 50) - tool_behaviour = (active ? TOOL_KNIFE : NONE) - if(active) - AddElement(/datum/element/cuffsnapping, snap_time_weak_handcuffs, snap_time_strong_handcuffs) - else - RemoveElement(/datum/element/cuffsnapping, snap_time_weak_handcuffs, snap_time_strong_handcuffs) - return COMPONENT_NO_DEFAULT_MESSAGE - -/obj/item/boxcutter/extended - start_extended = TRUE diff --git a/code/game/objects/items/clown_items.dm b/code/game/objects/items/clown_items.dm index a3d41245399..c0e35ee303a 100644 --- a/code/game/objects/items/clown_items.dm +++ b/code/game/objects/items/clown_items.dm @@ -4,6 +4,7 @@ * Bike Horns * Air Horns * Canned Laughter + * Balloon Mallet */ /* @@ -294,3 +295,46 @@ icon_state = "laughter" volume = 50 list_reagents = list(/datum/reagent/consumable/laughter = 50) + +//balloon mallet +/obj/item/balloon_mallet + name = "balloon mallet" + desc = "It's a mallet, a weapon known for being heavy, but made from notoriously light balloons. Air inside removes any force from the swings. It'd be quite embarrassing to get hit by this." + icon = 'icons/obj/weapons/hammer.dmi' + icon_state = "balloon_mallet" + inhand_icon_state = "balloon_mallet" + icon_angle = -45 + lefthand_file = 'icons/mob/inhands/weapons/hammers_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/hammers_righthand.dmi' + siemens_coefficient = 0 + force = 1 + throw_speed = 1 + throwforce = 1 + throw_range = 1 + w_class = WEIGHT_CLASS_HUGE + attack_verb_continuous = list("mallets", "smoother") + attack_verb_simple = list("mallet", "smoother") + max_integrity = 20 + armor_type = /datum/armor/item_banhammer + resistance_flags = FIRE_PROOF + +/obj/item/balloon_mallet/examine(mob/user) + . = ..() + if(HAS_TRAIT(user,TRAIT_BALLOON_SUTRA)) + . += "A sacred weapon of the higher castes from the clown planet, used to strike fear into the hearts of their foes. Wield it with care." + +/obj/item/balloon_mallet/attack(mob/living/target, mob/living/user) + playsound(loc, 'sound/mobs/non-humanoids/clown/hehe.ogg', 20) + if (!isliving(target)) + return + switch(target.mob_mood.sanity) + if (SANITY_INSANE to SANITY_CRAZY) + force = 8 + if (SANITY_UNSTABLE to SANITY_DISTURBED) + force = 4 + target.add_mood_event("humiliated", /datum/mood_event/mallet_humiliation) + if (SANITY_NEUTRAL to SANITY_GREAT) + target.add_mood_event("humiliated", /datum/mood_event/mallet_humiliation) + + if(user.combat_mode) + return ..(target, user) diff --git a/code/game/objects/items/tongs.dm b/code/game/objects/items/tongs.dm deleted file mode 100644 index a6be04b604f..00000000000 --- a/code/game/objects/items/tongs.dm +++ /dev/null @@ -1,102 +0,0 @@ -/// Tongs, let you pick up and feed people food from further away. -/obj/item/kitchen/tongs - name = "tongs" - desc = "So you never have to touch anything with your dirty, unwashed hands." - reach = 2 - icon_state = "tongs" - base_icon_state = "tongs" - inhand_icon_state = "fork" // close enough - icon_angle = -45 - attack_verb_continuous = list("pinches", "tongs", "nips") - attack_verb_simple = list("pinch", "tong", "nip") - /// What are we holding in our tongs? - var/obj/item/tonged - /// Sound to play when we click our tongs together - var/clack_sound = 'sound/items/handling/component_drop.ogg' - /// Time to wait between clacking sounds - var/clack_delay = 2 SECONDS - /// Have we clacked recently? - COOLDOWN_DECLARE(clack_cooldown) - -/obj/item/kitchen/tongs/Destroy(force) - QDEL_NULL(tonged) - return ..() - -/obj/item/kitchen/tongs/examine(mob/user) - . = ..() - if (!isnull(tonged)) - . += span_notice("It is holding [tonged].") - -/obj/item/kitchen/tongs/dropped(mob/user, silent) - . = ..() - drop_tonged() - -/obj/item/kitchen/tongs/attack_self(mob/user, modifiers) - . = ..() - if(.) - return TRUE - if (!isnull(tonged)) - drop_tonged() - return TRUE - if (!COOLDOWN_FINISHED(src, clack_cooldown)) - return TRUE - user.visible_message(span_notice("[user] clacks [user.p_their()] [name] together like a crab. Click clack!")) - click_clack() - return TRUE - -/// Release the food we are holding -/obj/item/kitchen/tongs/proc/drop_tonged() - if (isnull(tonged)) - return - visible_message(span_notice("[tonged] falls to the ground!")) - var/turf/location = drop_location() - tonged.forceMove(location) - tonged.do_drop_animation(location) - -/// Play a clacking sound and appear closed, then open again -/obj/item/kitchen/tongs/proc/click_clack() - COOLDOWN_START(src, clack_cooldown, clack_delay) - playsound(src, clack_sound, vol = 100, vary = FALSE) - icon_state = "[base_icon_state]_closed" - var/delay = min(0.5 SECONDS, clack_delay / 2) // Just in case someone's been fucking with the cooldown - addtimer(CALLBACK(src, PROC_REF(clack)), delay, TIMER_DELETE_ME) - -/// Plays a clacking sound and appear open -/obj/item/kitchen/tongs/proc/clack() - playsound(src, clack_sound, vol = 100, vary = FALSE) - icon_state = base_icon_state - -/obj/item/kitchen/tongs/Exited(atom/movable/leaving, direction) - . = ..() - if (leaving != tonged) - return - tonged = null - update_appearance(UPDATE_ICON) - -/obj/item/kitchen/tongs/pre_attack(obj/item/attacked, mob/living/user, list/modifiers, list/attack_modifiers) - if (!isnull(tonged) && tonged.force <= 0) // prevents tongs from giving food-weapons extra range - attacked.attackby(tonged, user) - return TRUE - if (isliving(attacked)) - if (COOLDOWN_FINISHED(src, clack_cooldown)) - click_clack() - return ..() - if (!IS_EDIBLE(attacked) || attacked.w_class > WEIGHT_CLASS_NORMAL || !isnull(tonged)) - return ..() - tonged = attacked - attacked.do_pickup_animation(src) - attacked.forceMove(src) - update_appearance(UPDATE_ICON) - return TRUE - -/obj/item/kitchen/tongs/update_overlays() - . = ..() - if (isnull(tonged)) - return - var/mutable_appearance/held_food = new /mutable_appearance(tonged.appearance) - held_food.layer = layer - held_food.plane = plane - held_food.transform = held_food.transform.Scale(0.7, 0.7) - held_food.pixel_w = 6 - held_food.pixel_z = 6 - . += held_food diff --git a/code/game/objects/items/control_wand.dm b/code/game/objects/items/tools/control_wand.dm similarity index 100% rename from code/game/objects/items/control_wand.dm rename to code/game/objects/items/tools/control_wand.dm diff --git a/code/game/objects/items/tools/crowbar.dm b/code/game/objects/items/tools/engineering/crowbar.dm similarity index 100% rename from code/game/objects/items/tools/crowbar.dm rename to code/game/objects/items/tools/engineering/crowbar.dm diff --git a/code/game/objects/items/tools/painter/airlock_painter.dm b/code/game/objects/items/tools/engineering/painter/airlock_painter.dm similarity index 100% rename from code/game/objects/items/tools/painter/airlock_painter.dm rename to code/game/objects/items/tools/engineering/painter/airlock_painter.dm diff --git a/code/game/objects/items/tools/painter/decal_painter.dm b/code/game/objects/items/tools/engineering/painter/decal_painter.dm similarity index 100% rename from code/game/objects/items/tools/painter/decal_painter.dm rename to code/game/objects/items/tools/engineering/painter/decal_painter.dm diff --git a/code/game/objects/items/tools/painter/paintable_decals.dm b/code/game/objects/items/tools/engineering/painter/paintable_decals.dm similarity index 100% rename from code/game/objects/items/tools/painter/paintable_decals.dm rename to code/game/objects/items/tools/engineering/painter/paintable_decals.dm diff --git a/code/game/objects/items/tools/screwdriver.dm b/code/game/objects/items/tools/engineering/screwdriver.dm similarity index 100% rename from code/game/objects/items/tools/screwdriver.dm rename to code/game/objects/items/tools/engineering/screwdriver.dm diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/engineering/weldingtool.dm similarity index 100% rename from code/game/objects/items/tools/weldingtool.dm rename to code/game/objects/items/tools/engineering/weldingtool.dm diff --git a/code/game/objects/items/tools/wirebrush.dm b/code/game/objects/items/tools/engineering/wirebrush.dm similarity index 100% rename from code/game/objects/items/tools/wirebrush.dm rename to code/game/objects/items/tools/engineering/wirebrush.dm diff --git a/code/game/objects/items/tools/wirecutters.dm b/code/game/objects/items/tools/engineering/wirecutters.dm similarity index 100% rename from code/game/objects/items/tools/wirecutters.dm rename to code/game/objects/items/tools/engineering/wirecutters.dm diff --git a/code/game/objects/items/tools/wrench.dm b/code/game/objects/items/tools/engineering/wrench.dm similarity index 100% rename from code/game/objects/items/tools/wrench.dm rename to code/game/objects/items/tools/engineering/wrench.dm diff --git a/code/game/objects/items/extinguisher.dm b/code/game/objects/items/tools/extinguisher.dm similarity index 100% rename from code/game/objects/items/extinguisher.dm rename to code/game/objects/items/tools/extinguisher.dm diff --git a/code/game/objects/items/inducer.dm b/code/game/objects/items/tools/inducer.dm similarity index 100% rename from code/game/objects/items/inducer.dm rename to code/game/objects/items/tools/inducer.dm diff --git a/code/game/objects/items/broom.dm b/code/game/objects/items/tools/janitorial/broom.dm similarity index 100% rename from code/game/objects/items/broom.dm rename to code/game/objects/items/tools/janitorial/broom.dm diff --git a/code/game/objects/items/janitor_key.dm b/code/game/objects/items/tools/janitorial/janitor_key.dm similarity index 100% rename from code/game/objects/items/janitor_key.dm rename to code/game/objects/items/tools/janitorial/janitor_key.dm diff --git a/code/game/objects/items/bodybag.dm b/code/game/objects/items/tools/medical/bodybag.dm similarity index 100% rename from code/game/objects/items/bodybag.dm rename to code/game/objects/items/tools/medical/bodybag.dm diff --git a/code/game/objects/items/tools/medical/cane.dm b/code/game/objects/items/tools/medical/cane.dm new file mode 100644 index 00000000000..132f03a9b37 --- /dev/null +++ b/code/game/objects/items/tools/medical/cane.dm @@ -0,0 +1,147 @@ +/obj/item/cane + name = "cane" + desc = "A cane used by a true gentleman. Or a clown." + icon = 'icons/obj/weapons/staff.dmi' + icon_state = "cane" + inhand_icon_state = "stick" + icon_angle = 135 + lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' + force = 5 + throwforce = 5 + w_class = WEIGHT_CLASS_SMALL + custom_materials = list(/datum/material/iron= SMALL_MATERIAL_AMOUNT * 0.5) + attack_verb_continuous = list("bludgeons", "whacks", "disciplines", "thrashes") + attack_verb_simple = list("bludgeon", "whack", "discipline", "thrash") + +/obj/item/cane/examine(mob/user, thats) + . = ..() + . += span_notice("This item can be used to support your weight, preventing limping from any broken bones on your legs you may have.") + +/obj/item/cane/equipped(mob/living/user, slot, initial) + ..() + if(!(slot & ITEM_SLOT_HANDS)) + return + movement_support_add(user) + +/obj/item/cane/dropped(mob/living/user, silent = FALSE) + . = ..() + movement_support_del(user) + +/obj/item/cane/proc/movement_support_add(mob/living/user) + RegisterSignal(user, COMSIG_CARBON_LIMPING, PROC_REF(handle_limping)) + return TRUE + +/obj/item/cane/proc/movement_support_del(mob/living/user) + UnregisterSignal(user, list(COMSIG_CARBON_LIMPING)) + return TRUE + +/obj/item/cane/proc/handle_limping(mob/living/user) + SIGNAL_HANDLER + return COMPONENT_CANCEL_LIMP + +/obj/item/cane/crutch + name = "medical crutch" + desc = "A medical crutch used by people missing a leg. Not all that useful if you're missing both of them, though." + icon = 'icons/obj/weapons/staff.dmi' + icon_state = "crutch_med" + inhand_icon_state = "crutch_med" + icon_angle = 45 + lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' + force = 12 + throwforce = 8 + w_class = WEIGHT_CLASS_BULKY + custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 0.5) + attack_verb_continuous = list("bludgeons", "whacks", "thrashes") + attack_verb_simple = list("bludgeon", "whack", "thrash") + +/obj/item/cane/crutch/Initialize(mapload) + . = ..() + AddElement(/datum/element/cuffable_item) + +/obj/item/cane/crutch/examine(mob/user, thats) + . = ..() + // tacked on after the cane string + . += span_notice("As a crutch, it can also help lessen the slowdown incurred by missing a leg.") + +/obj/item/cane/crutch/movement_support_add(mob/living/user) + . = ..() + if(!.) + return + RegisterSignal(user, COMSIG_LIVING_LIMBLESS_SLOWDOWN, PROC_REF(handle_slowdown)) + user.update_usable_leg_status() + user.AddElementTrait(TRAIT_WADDLING, REF(src), /datum/element/waddling) + +/obj/item/cane/crutch/movement_support_del(mob/living/user) + . = ..() + if(!.) + return + UnregisterSignal(user, list(COMSIG_LIVING_LIMBLESS_SLOWDOWN, COMSIG_CARBON_LIMPING)) + user.update_usable_leg_status() + REMOVE_TRAIT(user, TRAIT_WADDLING, REF(src)) + +/obj/item/cane/crutch/proc/handle_slowdown(mob/living/user, limbless_slowdown, list/slowdown_mods) + SIGNAL_HANDLER + var/leg_amount = user.usable_legs + // Don't do anything if the number is equal (or higher) to the usual. + if(leg_amount >= user.default_num_legs) + return + // If we have at least one leg and it's less than the default, reduce slowdown by 60%. + if(leg_amount && (leg_amount < user.default_num_legs)) + slowdown_mods += 0.4 + +/obj/item/cane/crutch/wood + name = "wooden crutch" + desc = "A handmade crutch. Also makes a decent bludgeon if you need it." + icon_state = "crutch_wood" + inhand_icon_state = "crutch_wood" + custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 5) + +/obj/item/cane/white + name = "white cane" + desc = "Traditionally used by the blind to help them see. Folds down to be easier to transport." + icon_state = "cane_white" + inhand_icon_state = "cane_white" + icon_angle = 45 + lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' + force = 1 + w_class = WEIGHT_CLASS_SMALL + custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 6) + +/obj/item/cane/white/Initialize(mapload) + . = ..() + AddElement(/datum/element/cuffable_item) + AddComponent( \ + /datum/component/transforming, \ + force_on = 7, \ + hitsound_on = hitsound, \ + w_class_on = WEIGHT_CLASS_BULKY, \ + clumsy_check = FALSE, \ + attack_verb_continuous_on = list("smacks", "strikes", "cracks", "beats"), \ + attack_verb_simple_on = list("smack", "strike", "crack", "beat"), \ + ) + RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform)) + +/obj/item/cane/white/handle_limping(mob/living/user) + return HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE) ? COMPONENT_CANCEL_LIMP : NONE + +/* + * Signal proc for [COMSIG_TRANSFORMING_ON_TRANSFORM]. + * + * Gives feedback to the user and makes it show up inhand. + */ +/obj/item/cane/white/proc/on_transform(obj/item/source, mob/user, active) + SIGNAL_HANDLER + + if(user) + balloon_alert(user, active ? "extended" : "collapsed") + + if(!HAS_TRAIT(src, TRAIT_BLIND_TOOL)) + ADD_TRAIT(src, TRAIT_BLIND_TOOL, INNATE_TRAIT) + else + REMOVE_TRAIT(src, TRAIT_BLIND_TOOL, INNATE_TRAIT) + + playsound(src, 'sound/items/weapons/batonextend.ogg', 50, TRUE) + return COMPONENT_NO_DEFAULT_MESSAGE diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/tools/medical/defib.dm similarity index 100% rename from code/game/objects/items/defib.dm rename to code/game/objects/items/tools/medical/defib.dm diff --git a/code/game/objects/items/reflex_hammer.dm b/code/game/objects/items/tools/medical/reflex_hammer.dm similarity index 100% rename from code/game/objects/items/reflex_hammer.dm rename to code/game/objects/items/tools/medical/reflex_hammer.dm diff --git a/code/game/objects/items/theft_tools.dm b/code/game/objects/items/tools/theft_tools.dm similarity index 100% rename from code/game/objects/items/theft_tools.dm rename to code/game/objects/items/tools/theft_tools.dm diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index c9f04348682..2bd52a0577e 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -16,7 +16,7 @@ * Toy big red button * Beach ball * Toy xeno - * Kitty toys! + * Kitty toys! * Snowballs * Clockwork Watches * Toy Daggers @@ -25,6 +25,8 @@ * Fake heretic codex * Fake Pierced Reality * Intento + * Extendo hand + * Ban hammer */ /obj/item/toy abstract_type = /obj/item/toy @@ -1846,3 +1848,70 @@ GLOBAL_LIST_EMPTY(intento_players) attack_verb_simple = list("smack", "club", "wack", "vendor") w_class = WEIGHT_CLASS_SMALL resistance_flags = FLAMMABLE + +/obj/item/extendohand + name = "extendo-hand" + desc = "Futuristic tech has allowed these classic spring-boxing toys to essentially act as a fully functional hand-operated hand prosthetic." + icon = 'icons/obj/toys/toy.dmi' + icon_state = "extendohand" + inhand_icon_state = "extendohand" + lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' + force = 0 + throwforce = 5 + reach = 2 + var/min_reach = 2 + +/obj/item/extendohand/acme + name = "\improper ACME Extendo-Hand" + desc = "A novelty extendo-hand produced by the ACME corporation. Originally designed to knock out roadrunners." + +/obj/item/extendohand/attack(atom/M, mob/living/carbon/human/user, list/modifiers, list/attack_modifiers) + var/dist = get_dist(M, user) + if(dist < min_reach) + to_chat(user, span_warning("[M] is too close to use [src] on.")) + return + M.attack_hand(user, modifiers) + +/obj/item/banhammer + desc = "A banhammer." + name = "banhammer" + icon = 'icons/obj/weapons/hammer.dmi' + icon_state = "toyhammer" + icon_angle = -45 + slot_flags = ITEM_SLOT_BELT + throwforce = 0 + force = 1 + w_class = WEIGHT_CLASS_TINY + throw_speed = 3 + throw_range = 7 + attack_verb_continuous = list("bans") + attack_verb_simple = list("ban") + max_integrity = 200 + armor_type = /datum/armor/item_banhammer + resistance_flags = FIRE_PROOF + +/datum/armor/item_banhammer + fire = 100 + acid = 70 + +/obj/item/banhammer/Initialize(mapload) + . = ..() + AddElement(/datum/element/kneejerk) + +/obj/item/banhammer/suicide_act(mob/living/user) + user.visible_message(span_suicide("[user] is hitting [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to ban [user.p_them()]self from life.")) + return (BRUTELOSS|FIRELOSS|TOXLOSS|OXYLOSS) +/* +oranges says: This is a meme relating to the english translation of the ss13 russian wiki page on lurkmore. +mrdoombringer sez: and remember kids, if you try and PR a fix for this item's grammar, you are admitting that you are, indeed, a newfriend. +for further reading, please see: https://github.com/tgstation/tgstation/pull/30173 and https://translate.google.com/translate?sl=auto&tl=en&js=y&prev=_t&hl=en&ie=UTF-8&u=%2F%2Flurkmore.to%2FSS13&edit-text=&act=url +*/ +/obj/item/banhammer/attack(mob/M, mob/living/user) + if(user.zone_selected == BODY_ZONE_HEAD) + M.visible_message(span_danger("[user] are stroking the head of [M] with a bangammer."), span_userdanger("[user] are stroking your head with a bangammer."), span_hear("You hear a bangammer stroking a head.")) // see above comment + else + M.visible_message(span_danger("[M] has been banned FOR NO REISIN by [user]!"), span_userdanger("You have been banned FOR NO REISIN by [user]!"), span_hear("You hear a banhammer banning someone.")) + playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much + if(user.combat_mode) + return ..(M, user) diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm deleted file mode 100644 index 5b128b21ab0..00000000000 --- a/code/game/objects/items/weaponry.dm +++ /dev/null @@ -1,1315 +0,0 @@ -/obj/item/banhammer - desc = "A banhammer." - name = "banhammer" - icon = 'icons/obj/weapons/hammer.dmi' - icon_state = "toyhammer" - icon_angle = -45 - slot_flags = ITEM_SLOT_BELT - throwforce = 0 - force = 1 - w_class = WEIGHT_CLASS_TINY - throw_speed = 3 - throw_range = 7 - attack_verb_continuous = list("bans") - attack_verb_simple = list("ban") - max_integrity = 200 - armor_type = /datum/armor/item_banhammer - resistance_flags = FIRE_PROOF - -/datum/armor/item_banhammer - fire = 100 - acid = 70 - -/obj/item/banhammer/Initialize(mapload) - . = ..() - AddElement(/datum/element/kneejerk) - -/obj/item/banhammer/suicide_act(mob/living/user) - user.visible_message(span_suicide("[user] is hitting [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to ban [user.p_them()]self from life.")) - return (BRUTELOSS|FIRELOSS|TOXLOSS|OXYLOSS) -/* -oranges says: This is a meme relating to the english translation of the ss13 russian wiki page on lurkmore. -mrdoombringer sez: and remember kids, if you try and PR a fix for this item's grammar, you are admitting that you are, indeed, a newfriend. -for further reading, please see: https://github.com/tgstation/tgstation/pull/30173 and https://translate.google.com/translate?sl=auto&tl=en&js=y&prev=_t&hl=en&ie=UTF-8&u=%2F%2Flurkmore.to%2FSS13&edit-text=&act=url -*/ -/obj/item/banhammer/attack(mob/M, mob/living/user) - if(user.zone_selected == BODY_ZONE_HEAD) - M.visible_message(span_danger("[user] are stroking the head of [M] with a bangammer."), span_userdanger("[user] are stroking your head with a bangammer."), span_hear("You hear a bangammer stroking a head.")) // see above comment - else - M.visible_message(span_danger("[M] has been banned FOR NO REISIN by [user]!"), span_userdanger("You have been banned FOR NO REISIN by [user]!"), span_hear("You hear a banhammer banning someone.")) - playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much - if(user.combat_mode) - return ..(M, user) - - -/obj/item/balloon_mallet - name = "balloon mallet" - desc = "It's a mallet, a weapon known for being heavy, but made from notoriously light balloons. Air inside removes any force from the swings. It'd be quite embarrassing to get hit by this." - icon = 'icons/obj/weapons/hammer.dmi' - icon_state = "balloon_mallet" - inhand_icon_state = "balloon_mallet" - icon_angle = -45 - lefthand_file = 'icons/mob/inhands/weapons/hammers_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/hammers_righthand.dmi' - siemens_coefficient = 0 - force = 1 - throw_speed = 1 - throwforce = 1 - throw_range = 1 - w_class = WEIGHT_CLASS_HUGE - attack_verb_continuous = list("mallets", "smoother") - attack_verb_simple = list("mallet", "smoother") - max_integrity = 20 - armor_type = /datum/armor/item_banhammer - resistance_flags = FIRE_PROOF - -/obj/item/balloon_mallet/examine(mob/user) - . = ..() - if(HAS_TRAIT(user,TRAIT_BALLOON_SUTRA)) - . += "A sacred weapon of the higher castes from the clown planet, used to strike fear into the hearts of their foes. Wield it with care." - -/obj/item/balloon_mallet/attack(mob/living/target, mob/living/user) - playsound(loc, 'sound/mobs/non-humanoids/clown/hehe.ogg', 20) - if (!isliving(target)) - return - switch(target.mob_mood.sanity) - if (SANITY_INSANE to SANITY_CRAZY) - force = 8 - if (SANITY_UNSTABLE to SANITY_DISTURBED) - force = 4 - target.add_mood_event("humiliated", /datum/mood_event/mallet_humiliation) - if (SANITY_NEUTRAL to SANITY_GREAT) - target.add_mood_event("humiliated", /datum/mood_event/mallet_humiliation) - - if(user.combat_mode) - return ..(target, user) - -/obj/item/sord - name = "\improper SORD" - desc = "This thing is so unspeakably shitty you are having a hard time even holding it." - icon = 'icons/obj/weapons/sword.dmi' - icon_state = "sord" - inhand_icon_state = "sord" - icon_angle = -35 - lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - slot_flags = ITEM_SLOT_BELT - force = 2 - throwforce = 1 - w_class = WEIGHT_CLASS_NORMAL - hitsound = 'sound/items/weapons/bladeslice.ogg' - attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts") - attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut") - -/obj/item/sord/suicide_act(mob/living/user) - user.visible_message(span_suicide("[user] is trying to impale [user.p_them()]self with [src]! It might be a suicide attempt if it weren't so shitty."), \ - span_suicide("You try to impale yourself with [src], but it's USELESS...")) - return SHAME - -/obj/item/claymore - name = "claymore" - desc = "What are you standing around staring at this for? Get to killing!" - icon = 'icons/obj/weapons/sword.dmi' - icon_state = "claymore" - inhand_icon_state = "claymore" - icon_angle = -45 - lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - hitsound = 'sound/items/weapons/bladeslice.ogg' - obj_flags = CONDUCTS_ELECTRICITY - slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK - force = 40 - throwforce = 10 - w_class = WEIGHT_CLASS_NORMAL - attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts") - attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut") - block_chance = 50 - block_sound = 'sound/items/weapons/parry.ogg' - sharpness = SHARP_EDGED - max_integrity = 200 - armor_type = /datum/armor/item_claymore - resistance_flags = FIRE_PROOF - var/list/alt_continuous = list("stabs", "pierces", "impales") - var/list/alt_simple = list("stab", "pierce", "impale") - -/datum/armor/item_claymore - fire = 100 - acid = 50 - -/obj/item/claymore/Initialize(mapload) - . = ..() - alt_continuous = string_list(alt_continuous) - alt_simple = string_list(alt_simple) - make_stabby() - AddComponent(/datum/component/butchering, \ - speed = 4 SECONDS, \ - effectiveness = 105, \ - ) - -// Applies alt sharpness component, for overrides -/obj/item/claymore/proc/make_stabby() - AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -15) - -/obj/item/claymore/suicide_act(mob/living/user) - user.visible_message(span_suicide("[user] is falling on [src]! It looks like [user.p_theyre()] trying to commit suicide!")) - return BRUTELOSS - -/obj/item/claymore/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) - if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK || attack_type == OVERWHELMING_ATTACK) - final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword. Or a road roller, if one happened to hit you. - return ..() - -//statistically similar to e-cutlasses -/obj/item/claymore/cutlass - name = "cutlass" - desc = "A piratey sword used by buckaneers to \"negotiate\" the transfer of treasure." - icon_state = "cutlass" - inhand_icon_state = "cutlass" - worn_icon_state = "cutlass" - slot_flags = ITEM_SLOT_BACK - force = 30 - throwforce = 20 - throw_speed = 3 - throw_range = 5 - armour_penetration = 35 - -/obj/item/claymore/cutlass/Initialize(mapload) - . = ..() - AddElement(/datum/element/cuffable_item) //closed sword guard - -/obj/item/claymore/cutlass/old - name = "old cutlass" - desc = parent_type::desc + " This one seems a tad old." - force = 24 - throwforce = 17 - armour_penetration = 20 - block_chance = 30 - -/obj/item/claymore/carrot - name = "carrot sword" - desc = "A full-sized carrot sword. Definitely not good for the eyes, not anymore." - icon_state = "carrot_sword" - inhand_icon_state = "carrot_sword" - worn_icon_state = "carrot_sword" - flags_1 = NONE - force = 19 - throwforce = 7 - throw_speed = 3 - throw_range = 7 - armour_penetration = 5 - block_chance = 10 - resistance_flags = NONE - -//bootleg claymore -/obj/item/claymore/shortsword - name = "shortsword" - desc = "A mercenary's sword, chipped and worn from battles long gone. You could say it is a swordsman's shortsword short sword." - icon_state = "shortsword" - inhand_icon_state = "shortsword" - worn_icon_state = "shortsword" - slot_flags = ITEM_SLOT_BELT - force = 20 - demolition_mod = 0.75 - block_chance = 30 - -/obj/item/claymore/highlander //ALL COMMENTS MADE REGARDING THIS SWORD MUST BE MADE IN ALL CAPS - desc = "THERE CAN BE ONLY ONE, AND IT WILL BE YOU!!!\nActivate it in your hand to point to the nearest victim." - obj_flags = CONDUCTS_ELECTRICITY - item_flags = DROPDEL //WOW BRO YOU LOST AN ARM, GUESS WHAT YOU DONT GET YOUR SWORD ANYMORE //I CANT BELIEVE SPOOKYDONUT WOULD BREAK THE REQUIREMENTS - slot_flags = null - block_chance = 0 //RNG WON'T HELP YOU NOW, PANSY - light_range = 3 - attack_verb_continuous = list("brutalizes", "eviscerates", "disembowels", "hacks", "carves", "cleaves") //ONLY THE MOST VISCERAL ATTACK VERBS - attack_verb_simple = list("brutalize", "eviscerate", "disembowel", "hack", "carve", "cleave") - var/notches = 0 //HOW MANY PEOPLE HAVE BEEN SLAIN WITH THIS BLADE - var/obj/item/disk/nuclear/nuke_disk //OUR STORED NUKE DISK - -/obj/item/claymore/highlander/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_NODROP, HIGHLANDER_TRAIT) - START_PROCESSING(SSobj, src) - -/obj/item/claymore/highlander/Destroy() - if(nuke_disk) - nuke_disk.forceMove(get_turf(src)) - nuke_disk.visible_message(span_warning("The nuke disk is vulnerable!")) - nuke_disk = null - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/claymore/highlander/process() - if(ishuman(loc)) - var/mob/living/carbon/human/holder = loc - holder.layer = ABOVE_ALL_MOB_LAYER //NO HIDING BEHIND PLANTS FOR YOU, DICKWEED (HA GET IT, BECAUSE WEEDS ARE PLANTS) - ADD_TRAIT(holder, TRAIT_NOBLOOD, HIGHLANDER_TRAIT) //AND WE WON'T BLEED OUT LIKE COWARDS - else - if(!(flags_1 & ADMIN_SPAWNED_1)) - qdel(src) - - -/obj/item/claymore/highlander/pickup(mob/living/user) - . = ..() - to_chat(user, span_notice("The power of Scotland protects you! You are shielded from all stuns and knockdowns.")) - user.ignore_slowdown(HIGHLANDER_TRAIT) - user.add_stun_absorption( - source = HIGHLANDER_TRAIT, - message = span_warning("%EFFECT_OWNER is protected by the power of Scotland!"), - self_message = span_boldwarning("The power of Scotland absorbs the stun!"), - examine_message = span_warning("%EFFECT_OWNER_THEYRE protected by the power of Scotland!"), - ) - -/obj/item/claymore/highlander/dropped(mob/living/user) - . = ..() - user.unignore_slowdown(HIGHLANDER_TRAIT) - user.remove_stun_absorption(HIGHLANDER_TRAIT) - -/obj/item/claymore/highlander/examine(mob/user) - . = ..() - . += "It has [!notches ? "nothing" : "[notches] notches"] scratched into the blade." - if(nuke_disk) - . += span_boldwarning("It's holding the nuke disk!") - -/obj/item/claymore/highlander/attack(mob/living/target, mob/living/user) - . = ..() - if(!QDELETED(target) && target.stat == DEAD && target.mind?.has_antag_datum(/datum/antagonist/highlander)) - user.fully_heal() //STEAL THE LIFE OF OUR FALLEN FOES - add_notch(user) - target.visible_message(span_warning("[target] crumbles to dust beneath [user]'s blows!"), span_userdanger("As you fall, your body crumbles to dust!")) - target.investigate_log("has been dusted by a highlander claymore.", INVESTIGATE_DEATHS) - target.dust() - -/obj/item/claymore/highlander/attack_self(mob/living/user) - var/closest_victim - var/closest_distance = 255 - for(var/mob/living/carbon/human/scot in GLOB.player_list - user) - if(scot.mind?.has_antag_datum(/datum/antagonist/highlander) && (!closest_victim || get_dist(user, closest_victim) < closest_distance)) - closest_victim = scot - for(var/mob/living/silicon/robot/siliscot in GLOB.player_list - user) - if(siliscot.mind?.has_antag_datum(/datum/antagonist/highlander) && (!closest_victim || get_dist(user, closest_victim) < closest_distance)) - closest_victim = siliscot - - if(!closest_victim) - to_chat(user, span_warning("[src] thrums for a moment and falls dark. Perhaps there's nobody nearby.")) - return - to_chat(user, span_danger("[src] thrums and points to the [dir2text(get_dir(user, closest_victim))].")) - -/obj/item/claymore/highlander/IsReflect() - return 1 //YOU THINK YOUR PUNY LASERS CAN STOP ME? - -/obj/item/claymore/highlander/proc/add_notch(mob/living/user) //DYNAMIC CLAYMORE PROGRESSION SYSTEM - THIS IS THE FUTURE - notches++ - force++ - var/new_name = name - switch(notches) - if(1) - to_chat(user, span_notice("Your first kill - hopefully one of many. You scratch a notch into [src]'s blade.")) - to_chat(user, span_warning("You feel your fallen foe's soul entering your blade, restoring your wounds!")) - new_name = "notched claymore" - if(2) - to_chat(user, span_notice("Another falls before you. Another soul fuses with your own. Another notch in the blade.")) - new_name = "double-notched claymore" - add_atom_colour(rgb(255, 235, 235), ADMIN_COLOUR_PRIORITY) - if(3) - to_chat(user, span_notice("You're beginning to relish the thrill of battle.")) - new_name = "triple-notched claymore" - add_atom_colour(rgb(255, 215, 215), ADMIN_COLOUR_PRIORITY) - if(4) - to_chat(user, span_notice("You've lost count of how many you've killed.")) - new_name = "many-notched claymore" - add_atom_colour(rgb(255, 195, 195), ADMIN_COLOUR_PRIORITY) - if(5) - to_chat(user, span_bolddanger("Five voices now echo in your mind, cheering the slaughter.")) - new_name = "battle-tested claymore" - add_atom_colour(rgb(255, 175, 175), ADMIN_COLOUR_PRIORITY) - if(6) - to_chat(user, span_bolddanger("Is this what the vikings felt like? Visions of glory fill your head as you slay your sixth foe.")) - new_name = "battle-scarred claymore" - add_atom_colour(rgb(255, 155, 155), ADMIN_COLOUR_PRIORITY) - if(7) - to_chat(user, span_bolddanger("Kill. Butcher. Conquer.")) - new_name = "vicious claymore" - add_atom_colour(rgb(255, 135, 135), ADMIN_COLOUR_PRIORITY) - if(8) - to_chat(user, span_userdanger("IT NEVER GETS OLD. THE SCREAMING. THE BLOOD AS IT SPRAYS ACROSS YOUR FACE.")) - new_name = "bloodthirsty claymore" - add_atom_colour(rgb(255, 115, 115), ADMIN_COLOUR_PRIORITY) - if(9) - to_chat(user, span_userdanger("ANOTHER ONE FALLS TO YOUR BLOWS. ANOTHER WEAKLING UNFIT TO LIVE.")) - new_name = "gore-stained claymore" - add_atom_colour(rgb(255, 95, 95), ADMIN_COLOUR_PRIORITY) - if(10) - user.visible_message(span_warning("[user]'s eyes light up with a vengeful fire!"), \ - span_userdanger("YOU FEEL THE POWER OF VALHALLA FLOWING THROUGH YOU! THERE CAN BE ONLY ONE!!!")) - new_name = "GORE-DRENCHED CLAYMORE OF [pick("THE WHIMSICAL SLAUGHTER", "A THOUSAND SLAUGHTERED CATTLE", "GLORY AND VALHALLA", "ANNIHILATION", "OBLITERATION")]" - icon_state = "claymore_gold" - inhand_icon_state = "cultblade" - lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi' - righthand_file = 'icons/mob/inhands/64x64_righthand.dmi' - remove_atom_colour(ADMIN_COLOUR_PRIORITY) - user.update_held_items() - - name = new_name - playsound(user, 'sound/items/tools/screwdriver2.ogg', 50, TRUE) - -/obj/item/claymore/highlander/robot //BLOODTHIRSTY BORGS NOW COME IN PLAID - icon = 'icons/obj/items_cyborg.dmi' - icon_state = "claymore_cyborg" - -/obj/item/claymore/highlander/robot/Initialize(mapload) - . = ..() - if(!iscyborg(loc)) - return INITIALIZE_HINT_QDEL - -/obj/item/claymore/highlander/robot/process() - loc.layer = ABOVE_ALL_MOB_LAYER - -/obj/item/claymore/gladius - name = "gladius" - desc = "A short but formidable sword, favored by recently-reanimated ancient warriors." - icon = 'icons/obj/weapons/sword.dmi' - icon_state = "gladius" - inhand_icon_state = "gladius" - lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - sharpness = SHARP_POINTY - attack_verb_continuous = list("stabs", "cuts") - attack_verb_simple = list("stab", "cut") - slot_flags = null - sound_vary = TRUE - block_sound = 'sound/items/weapons/parry.ogg' - hitsound = 'sound/items/weapons/bladeslice.ogg' - pickup_sound = SFX_KNIFE_PICKUP - drop_sound = SFX_KNIFE_DROP - - -/obj/item/katana - name = "katana" - desc = "Woefully underpowered in D20." - icon = 'icons/obj/weapons/sword.dmi' - icon_state = "katana" - inhand_icon_state = "katana" - worn_icon_state = "katana" - icon_angle = -45 - lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - obj_flags = CONDUCTS_ELECTRICITY - slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK - force = 40 - throwforce = 10 - w_class = WEIGHT_CLASS_HUGE - hitsound = 'sound/items/weapons/bladeslice.ogg' - attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts") - attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut") - block_chance = 50 - block_sound = 'sound/items/weapons/parry.ogg' - sharpness = SHARP_EDGED - max_integrity = 200 - armor_type = /datum/armor/item_katana - resistance_flags = FIRE_PROOF - var/list/alt_continuous = list("stabs", "pierces", "impales") - var/list/alt_simple = list("stab", "pierce", "impale") - -/obj/item/katana/Initialize(mapload) - . = ..() - alt_continuous = string_list(alt_continuous) - alt_simple = string_list(alt_simple) - AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -15) - -/obj/item/katana/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) - if(attack_type == OVERWHELMING_ATTACK) - final_block_chance = 0 //Not a high freuqnecy blade, sorry pal - return ..() - -/datum/armor/item_katana - fire = 100 - acid = 50 - -/obj/item/katana/suicide_act(mob/living/user) - user.visible_message(span_suicide("[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit seppuku!")) - return BRUTELOSS - -/obj/item/katana/cursed //used by wizard events, see the tendril_loot.dm file for the miner one - slot_flags = null - -/obj/item/throwing_star - name = "throwing star" - desc = "An ancient weapon still used to this day, due to its ease of lodging itself into its victim's body parts." - icon = 'icons/obj/weapons/thrown.dmi' - icon_state = "throwingstar" - inhand_icon_state = "eshield" - lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi' - force = 2 - throwforce = 10 //10 + 2 (WEIGHT_CLASS_SMALL) * 4 (EMBEDDED_IMPACT_PAIN_MULTIPLIER) = 18 damage on hit due to guaranteed embedding - throw_speed = 4 - embed_type = /datum/embedding/throwing_star - armour_penetration = 40 - mob_throw_hit_sound = 'sound/items/weapons/pierce.ogg' - hitsound = 'sound/items/weapons/bladeslice.ogg' - - w_class = WEIGHT_CLASS_SMALL - sharpness = SHARP_POINTY - custom_materials = list(/datum/material/iron= SMALL_MATERIAL_AMOUNT * 5, /datum/material/glass= SMALL_MATERIAL_AMOUNT * 5) - resistance_flags = FIRE_PROOF - -/datum/embedding/throwing_star - pain_mult = 4 - embed_chance = 100 - fall_chance = 0 - -/obj/item/throwing_star/stamina - name = "shock throwing star" - desc = "An aerodynamic disc designed to cause excruciating pain when stuck inside fleeing targets, hopefully without causing fatal harm." - throwforce = 5 - embed_type = /datum/embedding/throwing_star/stamina - -/datum/embedding/throwing_star/stamina - pain_mult = 5 - jostle_chance = 10 - pain_stam_pct = 0.8 - jostle_pain_mult = 3 - -/obj/item/throwing_star/toy - name = "toy throwing star" - desc = "An aerodynamic disc strapped with adhesive for sticking to people, good for playing pranks and getting yourself killed by security." - sharpness = NONE - force = 0 - throwforce = 0 - embed_type = /datum/embedding/throwing_star/toy - -/datum/embedding/throwing_star/toy - pain_mult = 0 - jostle_pain_mult = 0 - -/obj/item/carpenter_hammer - name = "carpenter hammer" - icon = 'icons/obj/weapons/hammer.dmi' - icon_state = "carpenter_hammer" - inhand_icon_state = "carpenter_hammer" - worn_icon_state = "clawhammer" //plaecholder - icon_angle = -45 - lefthand_file = 'icons/mob/inhands/weapons/hammers_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/hammers_righthand.dmi' - desc = "Uncanny looking hammer." - force = 17 - throwforce = 14 - throw_range = 4 - w_class = WEIGHT_CLASS_NORMAL - wound_bonus = 20 - demolition_mod = 1.15 - slot_flags = ITEM_SLOT_BELT - -/obj/item/carpenter_hammer/Initialize(mapload) - . = ..() - AddElement(/datum/element/kneejerk) - AddComponent(/datum/component/item_killsound, \ - allowed_mobs = list(/mob/living/carbon/human), \ - killsound = 'sound/items/weapons/hammer_death_scream.ogg', \ - replace_default_death_sound = TRUE, \ - ) - -/obj/item/carpenter_hammer/examine(mob/user) - . = ..() - . += "" - . += "Real World Tip:" - . += pick( - "Every building, from hospitals to homes, has a room that serves as the heart of the building \ - and carries blood and nutrients to its extremities. Try to find the heart of your home!", - "All the food you've tried is rotten. You've never eaten fresh food.", - "Viruses do not exist. Illness is simply your body punishing you for what you have done wrong.", - "Space stations must have at least 50 mammalian teeth embedded in the north walls for structural safety reasons.", - "Queen dragonfly sleeps and smiles.", - ) - -/obj/item/switchblade - name = "switchblade" - icon = 'icons/obj/weapons/sword.dmi' - icon_state = "switchblade" - base_icon_state = "switchblade" - icon_angle = -90 - lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - desc = "A sharp, concealable, spring-loaded knife." - obj_flags = CONDUCTS_ELECTRICITY - force = 3 - w_class = WEIGHT_CLASS_SMALL - throwforce = 5 - throw_speed = 3 - throw_range = 6 - custom_materials = list(/datum/material/iron= SHEET_MATERIAL_AMOUNT * 6) - hitsound = 'sound/items/weapons/genhit.ogg' - attack_verb_continuous = list("stubs", "pokes") - attack_verb_simple = list("stub", "poke") - resistance_flags = FIRE_PROOF - /// Whether the switchblade starts extended or not. - var/start_extended = FALSE - var/list/alt_continuous = list("stabs", "pierces", "shanks") - var/list/alt_simple = list("stab", "pierce", "shank") - -/obj/item/switchblade/get_all_tool_behaviours() - return list(TOOL_KNIFE) - -/obj/item/switchblade/Initialize(mapload) - . = ..() - AddElement(/datum/element/update_icon_updates_onmob) - AddComponent(/datum/component/butchering, \ - speed = 7 SECONDS, \ - effectiveness = 100, \ - ) - - AddComponent( \ - /datum/component/transforming, \ - start_transformed = start_extended, \ - force_on = 20, \ - throwforce_on = 23, \ - throw_speed_on = throw_speed, \ - sharpness_on = SHARP_EDGED, \ - hitsound_on = 'sound/items/weapons/bladeslice.ogg', \ - w_class_on = WEIGHT_CLASS_NORMAL, \ - attack_verb_continuous_on = list("slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts"), \ - attack_verb_simple_on = list("slash", "slice", "tear", "lacerate", "rip", "dice", "cut"), \ - ) - - alt_continuous = string_list(alt_continuous) - alt_simple = string_list(alt_simple) - AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -5, TRAIT_TRANSFORM_ACTIVE) - - RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform)) - -/obj/item/switchblade/proc/on_transform(obj/item/source, mob/user, active) - SIGNAL_HANDLER - - tool_behaviour = (active ? TOOL_KNIFE : NONE) - -/obj/item/switchblade/suicide_act(mob/living/user) - user.visible_message(span_suicide("[user] is slitting [user.p_their()] own throat with [src]! It looks like [user.p_theyre()] trying to commit suicide!")) - return BRUTELOSS - -/obj/item/switchblade/extended - start_extended = TRUE - -/obj/item/phone - name = "red phone" - desc = "Should anything ever go wrong..." - icon = 'icons/obj/devices/voice.dmi' - icon_state = "red_phone" - force = 3 - throwforce = 2 - throw_speed = 3 - throw_range = 4 - w_class = WEIGHT_CLASS_SMALL - attack_verb_continuous = list("calls", "rings") - attack_verb_simple = list("call", "ring") - hitsound = 'sound/items/weapons/ring.ogg' - -/obj/item/phone/suicide_act(mob/living/user) - if(locate(/obj/structure/chair/stool) in user.loc) - user.visible_message(span_suicide("[user] begins to tie a noose with [src]'s cord! It looks like [user.p_theyre()] trying to commit suicide!")) - else - user.visible_message(span_suicide("[user] is strangling [user.p_them()]self with [src]'s cord! It looks like [user.p_theyre()] trying to commit suicide!")) - return OXYLOSS - -/obj/item/bambostaff - name = "bamboo staff" - desc = "A long bamboo-made staff with steel-capped ends. It is rumoured that initiates of Spider Clan train with such before getting to learn how to use a katana." - force = 10 - block_chance = 45 - block_sound = 'sound/items/weapons/genhit.ogg' - slot_flags = ITEM_SLOT_BACK - w_class = WEIGHT_CLASS_BULKY - hitsound = SFX_SWING_HIT - attack_verb_continuous = list("smashes", "slams", "whacks", "thwacks") - attack_verb_simple = list("smash", "slam", "whack", "thwack") - icon = 'icons/obj/weapons/staff.dmi' - icon_state = "bambostaff0" - base_icon_state = "bambostaff" - inhand_icon_state = "bambostaff0" - worn_icon_state = "bambostaff0" - icon_angle = -135 - lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' - custom_materials = list(/datum/material/bamboo = SHEET_MATERIAL_AMOUNT * 4) - -/obj/item/bambostaff/Initialize(mapload) - . = ..() - // there are too many puns to choose from. ('Bo' is the 'real' name for this kind of weapon.) - name = pick("bamboo staff", "bambo staff", "bam-Bo staff", "bam boo staff", "bam-boo staff", "bam Bo", "bambo", "bam-Bo", "bamboo-Bo") - AddComponent(/datum/component/two_handed, \ - force_unwielded = 10, \ - force_wielded = 14, \ - ) - -/obj/item/bambostaff/update_icon_state() - icon_state = inhand_icon_state = "[base_icon_state][HAS_TRAIT(src, TRAIT_WIELDED)]" - return ..() - -/obj/item/bambostaff/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) - if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK || attack_type == OVERWHELMING_ATTACK) - final_block_chance = 0 //Don't bring a staff to a gunfight, and also you aren't going to really block someone full body tackling you with a staff. Or a road roller, if one happened to hit you. - return ..() - -/obj/item/cane - name = "cane" - desc = "A cane used by a true gentleman. Or a clown." - icon = 'icons/obj/weapons/staff.dmi' - icon_state = "cane" - inhand_icon_state = "stick" - icon_angle = 135 - lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' - force = 5 - throwforce = 5 - w_class = WEIGHT_CLASS_SMALL - custom_materials = list(/datum/material/iron= SMALL_MATERIAL_AMOUNT * 0.5) - attack_verb_continuous = list("bludgeons", "whacks", "disciplines", "thrashes") - attack_verb_simple = list("bludgeon", "whack", "discipline", "thrash") - -/obj/item/cane/examine(mob/user, thats) - . = ..() - . += span_notice("This item can be used to support your weight, preventing limping from any broken bones on your legs you may have.") - -/obj/item/cane/equipped(mob/living/user, slot, initial) - ..() - if(!(slot & ITEM_SLOT_HANDS)) - return - movement_support_add(user) - -/obj/item/cane/dropped(mob/living/user, silent = FALSE) - . = ..() - movement_support_del(user) - -/obj/item/cane/proc/movement_support_add(mob/living/user) - RegisterSignal(user, COMSIG_CARBON_LIMPING, PROC_REF(handle_limping)) - return TRUE - -/obj/item/cane/proc/movement_support_del(mob/living/user) - UnregisterSignal(user, list(COMSIG_CARBON_LIMPING)) - return TRUE - -/obj/item/cane/proc/handle_limping(mob/living/user) - SIGNAL_HANDLER - return COMPONENT_CANCEL_LIMP - -/obj/item/cane/crutch - name = "medical crutch" - desc = "A medical crutch used by people missing a leg. Not all that useful if you're missing both of them, though." - icon = 'icons/obj/weapons/staff.dmi' - icon_state = "crutch_med" - inhand_icon_state = "crutch_med" - icon_angle = 45 - lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' - force = 12 - throwforce = 8 - w_class = WEIGHT_CLASS_BULKY - custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 0.5) - attack_verb_continuous = list("bludgeons", "whacks", "thrashes") - attack_verb_simple = list("bludgeon", "whack", "thrash") - -/obj/item/cane/crutch/Initialize(mapload) - . = ..() - AddElement(/datum/element/cuffable_item) - -/obj/item/cane/crutch/examine(mob/user, thats) - . = ..() - // tacked on after the cane string - . += span_notice("As a crutch, it can also help lessen the slowdown incurred by missing a leg.") - -/obj/item/cane/crutch/movement_support_add(mob/living/user) - . = ..() - if(!.) - return - RegisterSignal(user, COMSIG_LIVING_LIMBLESS_SLOWDOWN, PROC_REF(handle_slowdown)) - user.update_usable_leg_status() - user.AddElementTrait(TRAIT_WADDLING, REF(src), /datum/element/waddling) - -/obj/item/cane/crutch/movement_support_del(mob/living/user) - . = ..() - if(!.) - return - UnregisterSignal(user, list(COMSIG_LIVING_LIMBLESS_SLOWDOWN, COMSIG_CARBON_LIMPING)) - user.update_usable_leg_status() - REMOVE_TRAIT(user, TRAIT_WADDLING, REF(src)) - -/obj/item/cane/crutch/proc/handle_slowdown(mob/living/user, limbless_slowdown, list/slowdown_mods) - SIGNAL_HANDLER - var/leg_amount = user.usable_legs - // Don't do anything if the number is equal (or higher) to the usual. - if(leg_amount >= user.default_num_legs) - return - // If we have at least one leg and it's less than the default, reduce slowdown by 60%. - if(leg_amount && (leg_amount < user.default_num_legs)) - slowdown_mods += 0.4 - -/obj/item/cane/crutch/wood - name = "wooden crutch" - desc = "A handmade crutch. Also makes a decent bludgeon if you need it." - icon_state = "crutch_wood" - inhand_icon_state = "crutch_wood" - custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 5) - -/obj/item/cane/white - name = "white cane" - desc = "Traditionally used by the blind to help them see. Folds down to be easier to transport." - icon_state = "cane_white" - inhand_icon_state = "cane_white" - icon_angle = 45 - lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' - force = 1 - w_class = WEIGHT_CLASS_SMALL - custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 6) - -/obj/item/cane/white/Initialize(mapload) - . = ..() - AddElement(/datum/element/cuffable_item) - AddComponent( \ - /datum/component/transforming, \ - force_on = 7, \ - hitsound_on = hitsound, \ - w_class_on = WEIGHT_CLASS_BULKY, \ - clumsy_check = FALSE, \ - attack_verb_continuous_on = list("smacks", "strikes", "cracks", "beats"), \ - attack_verb_simple_on = list("smack", "strike", "crack", "beat"), \ - ) - RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform)) - -/obj/item/cane/white/handle_limping(mob/living/user) - return HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE) ? COMPONENT_CANCEL_LIMP : NONE - -/* - * Signal proc for [COMSIG_TRANSFORMING_ON_TRANSFORM]. - * - * Gives feedback to the user and makes it show up inhand. - */ -/obj/item/cane/white/proc/on_transform(obj/item/source, mob/user, active) - SIGNAL_HANDLER - - if(user) - balloon_alert(user, active ? "extended" : "collapsed") - - if(!HAS_TRAIT(src, TRAIT_BLIND_TOOL)) - ADD_TRAIT(src, TRAIT_BLIND_TOOL, INNATE_TRAIT) - else - REMOVE_TRAIT(src, TRAIT_BLIND_TOOL, INNATE_TRAIT) - - playsound(src, 'sound/items/weapons/batonextend.ogg', 50, TRUE) - return COMPONENT_NO_DEFAULT_MESSAGE - -/obj/item/staff - name = "wizard staff" - desc = "Apparently a staff used by the wizard." - icon = 'icons/obj/weapons/guns/magic.dmi' - icon_state = "staff" - inhand_icon_state = "staff" - lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' - force = 3 - throwforce = 5 - throw_speed = 2 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - armour_penetration = 100 - attack_verb_continuous = list("bludgeons", "whacks", "disciplines") - attack_verb_simple = list("bludgeon", "whack", "discipline") - resistance_flags = FLAMMABLE - -/obj/item/staff/broom - name = "broom" - desc = "Used for sweeping, and flying into the night while cackling. Black cat not included." - icon_state = "broom" - inhand_icon_state = "broom" - resistance_flags = FLAMMABLE - -/obj/item/staff/tape - name = "tape staff" - desc = "A roll of tape snugly attached to a stick." - icon_state = "tapestaff" - inhand_icon_state = "tapestaff" - resistance_flags = FLAMMABLE - -/obj/item/staff/stick - name = "stick" - desc = "A great tool to drag someone else's drinks across the bar." - icon = 'icons/obj/weapons/staff.dmi' - icon_state = "cane" - inhand_icon_state = "stick" - icon_angle = 135 - lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' - force = 3 - throwforce = 5 - throw_speed = 2 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - -/obj/item/ectoplasm - name = "ectoplasm" - desc = "Spooky." - gender = PLURAL - icon = 'icons/effects/magic.dmi' - icon_state = "ectoplasm" - -/obj/item/ectoplasm/grind_results() - return list(/datum/reagent/hauntium = 25) - -/obj/item/ectoplasm/suicide_act(mob/living/user) - user.visible_message(span_suicide("[user] is inhaling [src]! It looks like [user.p_theyre()] trying to visit the astral plane!")) - return OXYLOSS - -/obj/item/ectoplasm/angelic - icon = 'icons/effects/magic.dmi' - icon_state = "angelplasm" - -/obj/item/ectoplasm/mystic - icon_state = "mysticplasm" - -/obj/item/tailclub - name = "tail club" - desc = "For the beating to death of lizards with their own tails." - icon = 'icons/obj/weapons/club.dmi' - icon_state = "tailclub" - icon_angle = -25 - force = 14 - throwforce = 1 // why are you throwing a club do you even weapon - throw_speed = 1 - throw_range = 1 - attack_verb_continuous = list("clubs", "bludgeons") - attack_verb_simple = list("club", "bludgeon") - custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT) - -/obj/item/melee/baseball_bat - name = "baseball bat" - desc = "There ain't a skull in the league that can withstand a swatter." - icon = 'icons/obj/weapons/bat.dmi' - icon_state = "baseball_bat" - inhand_icon_state = "baseball_bat" - icon_angle = -45 - lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' - force = 12 - wound_bonus = -10 - throwforce = 12 - demolition_mod = 1.25 - attack_verb_continuous = list("beats", "smacks") - attack_verb_simple = list("beat", "smack") - custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 3.5) - resistance_flags = FLAMMABLE - w_class = WEIGHT_CLASS_HUGE - /// Are we able to do a homerun? - var/homerun_able = FALSE - /// Are we ready to do a homerun? - var/homerun_ready = FALSE - /// Can we launch mobs thrown at us away? - var/mob_thrower = FALSE - /// List of all thrown datums we sent. - var/list/thrown_datums = list() - -/obj/item/melee/baseball_bat/Initialize(mapload) - . = ..() - AddElement(/datum/element/kneecapping) - // No subtypes - if(type != /obj/item/melee/baseball_bat) - return - if(prob(check_holidays(APRIL_FOOLS) ? 50 : 1)) - make_silly() - -/obj/item/melee/baseball_bat/attack_self(mob/user) - if(!homerun_able) - return ..() - if(homerun_ready) - to_chat(user, span_warning("You're already ready to do a home run!")) - return ..() - to_chat(user, span_warning("You begin gathering strength...")) - playsound(get_turf(src), 'sound/effects/magic/lightning_chargeup.ogg', 65, TRUE) - if(do_after(user, 9 SECONDS, target = src)) - to_chat(user, span_userdanger("You gather power! Time for a home run!")) - homerun_ready = TRUE - return ..() - -/obj/item/melee/baseball_bat/attack(mob/living/target, mob/living/user) - // we obtain the relative direction from the bat itself to the target - var/relative_direction = get_cardinal_dir(src, target) - var/atom/throw_target = get_edge_target_turf(target, relative_direction) - . = ..() - if(HAS_TRAIT(user, TRAIT_PACIFISM)) - return - if(homerun_ready) - user.visible_message(span_userdanger("It's a home run!")) - if(!QDELETED(target)) - target.throw_at(throw_target, rand(8,10), 14, user) - SSexplosions.medturf += throw_target - playsound(get_turf(src), 'sound/items/weapons/homerun.ogg', 100, TRUE) - homerun_ready = FALSE - return - else if(!QDELETED(target) && !target.anchored) - var/whack_speed = (prob(60) ? 1 : 4) - target.throw_at(throw_target, rand(1, 2), whack_speed, user, gentle = TRUE) // sorry friends, 7 speed batting caused wounds to absolutely delete whoever you knocked your target into (and said target) - -/obj/item/melee/baseball_bat/Destroy(force) - for(var/target in thrown_datums) - var/datum/thrownthing/throw_datum = thrown_datums[target] - throw_datum.callback.Invoke() - thrown_datums.Cut() - return ..() - -/obj/item/melee/baseball_bat/pre_attack(atom/movable/target, mob/living/user, list/modifiers, list/attack_modifiers) - var/turf/target_turf = get_turf(target) - if(!target_turf) - return ..() - for(var/atom/movable/atom as anything in target_turf) - if(!try_launch(atom, user)) - continue - return TRUE - return ..() - -/obj/item/melee/baseball_bat/proc/try_launch(atom/movable/target, mob/living/user) - if(!target.throwing || (ismob(target) && !mob_thrower)) - return FALSE - var/datum/thrownthing/throw_datum = target.throwing - var/datum_throw_speed = throw_datum.speed - var/angle = 0 - var/target_to_user = get_dir(target, user) - if(target.dir & turn(target_to_user, 90)) - angle = 270 - if(target.dir & turn(target_to_user, 270)) - angle = 90 - if(target.dir & REVERSE_DIR(target_to_user)) - angle = 180 - if(target.dir & target_to_user) - angle = 360 - var/turf/return_to_sender = get_ranged_target_turf_direct(user, throw_datum.starting_turf, max(3, round(target.throw_range * 1.5, 1)), offset = angle + (rand(-1, 1) * 10)) - throw_datum.finalize(hit = FALSE) - target.mouse_opacity = MOUSE_OPACITY_TRANSPARENT //dont mess with our ball - target.color = list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,3) //make them super light - animate(target, 0.5 SECONDS, color = null, flags = ANIMATION_PARALLEL) - user.color = list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,3) - animate(user, 0.5 SECONDS, color = null, flags = ANIMATION_PARALLEL) - playsound(src, 'sound/items/baseballhit.ogg', 100, TRUE) - user.do_attack_animation(target, used_item = src) - ADD_TRAIT(user, TRAIT_IMMOBILIZED, type) - addtimer(CALLBACK(src, PROC_REF(launch_back), target, user, return_to_sender, datum_throw_speed), 0.5 SECONDS) - return TRUE - -/obj/item/melee/baseball_bat/proc/launch_back(atom/movable/target, mob/living/user, turf/target_turf, datum_throw_speed) - playsound(target, 'sound/effects/magic/tail_swing.ogg', 50, TRUE) - REMOVE_TRAIT(user, TRAIT_IMMOBILIZED, type) - target.mouse_opacity = initial(target.mouse_opacity) - target.add_filter("baseball_launch", 3, motion_blur_filter(1, 3)) - target.throwforce *= 2 - target.throw_at(target_turf, get_dist(target, target_turf), datum_throw_speed + 1, user, callback = CALLBACK(src, PROC_REF(on_hit), target)) - thrown_datums[target] = target.throwing - -/obj/item/melee/baseball_bat/proc/make_silly() - name = "cricket bat" - icon_state = "baseball_bat_brit" - inhand_icon_state = "baseball_bat_brit" - desc = pick("You've got red on you.", "You gotta know what a crumpet is to understand cricket.") - -/obj/item/melee/baseball_bat/proc/on_hit(atom/movable/target) - target.remove_filter("baseball_launch") - target.throwforce *= 0.5 - thrown_datums -= target - -/obj/item/melee/baseball_bat/homerun - name = "home run bat" - desc = "This thing looks dangerous... Dangerously good at baseball, that is." - icon_state = "baseball_bat_home" - inhand_icon_state = "baseball_bat_home" - homerun_able = TRUE - mob_thrower = TRUE - -/obj/item/melee/baseball_bat/ablative - name = "metal baseball bat" - desc = "This bat is made of highly reflective, highly armored material." - icon_state = "baseball_bat_metal" - inhand_icon_state = "baseball_bat_metal" - custom_materials = list(/datum/material/titanium = SHEET_MATERIAL_AMOUNT * 3.5) - resistance_flags = NONE - force = 20 - throwforce = 20 - mob_thrower = TRUE - block_sound = 'sound/items/weapons/effects/batreflect.ogg' - -/obj/item/melee/baseball_bat/ablative/IsReflect()//some day this will reflect thrown items instead of lasers - return TRUE - -// In case you ever want to spawn it via map/admin console -/obj/item/melee/baseball_bat/british/Initialize(mapload) - . = ..() - make_silly() - -/obj/item/melee/flyswatter - name = "flyswatter" - desc = "Useful for killing pests of all sizes." - icon = 'icons/obj/service/hydroponics/equipment.dmi' - icon_state = "flyswatter" - inhand_icon_state = "flyswatter" - icon_angle = -45 - lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' - force = 1 - throwforce = 1 - attack_verb_continuous = list("swats", "smacks") - attack_verb_simple = list("swat", "smack") - hitsound = 'sound/effects/snap.ogg' - w_class = WEIGHT_CLASS_SMALL - /// Things in this list will be instantly splatted. Flyman weakness is handled in the flyman species weakness proc. - var/static/list/splattable - -/obj/item/melee/flyswatter/Initialize(mapload) - . = ..() - if (isnull(splattable)) - splattable = typecacheof(list( - /mob/living/basic/ant, - /mob/living/basic/butterfly, - /mob/living/basic/cockroach, - /mob/living/basic/cockroach/bloodroach, - /mob/living/basic/spider/growing/spiderling, - /mob/living/basic/bee, - /obj/effect/decal/cleanable/ants, - /obj/item/queen_bee, - )) - AddElement(/datum/element/bane, mob_biotypes = MOB_BUG, target_type = /mob/living/basic, damage_multiplier = 0, added_damage = 24, requires_combat_mode = FALSE) - -/obj/item/melee/flyswatter/afterattack(atom/target, mob/user, list/modifiers, list/attack_modifiers) - if(is_type_in_typecache(target, splattable)) - to_chat(user, span_warning("You easily splat [target].")) - if(QDELETED(target)) - return - if(isliving(target)) - new /obj/effect/decal/cleanable/insectguts(target.drop_location()) - var/mob/living/bug = target - bug.investigate_log("has been splatted by a flyswatter.", INVESTIGATE_DEATHS) - bug.gib(DROP_ALL_REMAINS) - else - qdel(target) - -/obj/item/proc/can_trigger_gun(mob/living/user, akimbo_usage) - if(!user.can_use_guns(src)) - return FALSE - return TRUE - -/obj/item/extendohand - name = "extendo-hand" - desc = "Futuristic tech has allowed these classic spring-boxing toys to essentially act as a fully functional hand-operated hand prosthetic." - icon = 'icons/obj/toys/toy.dmi' - icon_state = "extendohand" - inhand_icon_state = "extendohand" - lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' - force = 0 - throwforce = 5 - reach = 2 - var/min_reach = 2 - -/obj/item/extendohand/acme - name = "\improper ACME Extendo-Hand" - desc = "A novelty extendo-hand produced by the ACME corporation. Originally designed to knock out roadrunners." - -/obj/item/extendohand/attack(atom/M, mob/living/carbon/human/user, list/modifiers, list/attack_modifiers) - var/dist = get_dist(M, user) - if(dist < min_reach) - to_chat(user, span_warning("[M] is too close to use [src] on.")) - return - M.attack_hand(user, modifiers) - -/obj/item/gohei - name = "gohei" - desc = "A wooden stick with white streamers at the end. Originally used by shrine maidens to purify things. Now used by the station's valued weeaboos." - resistance_flags = FLAMMABLE - force = 5 - throwforce = 5 - hitsound = SFX_SWING_HIT - attack_verb_continuous = list("whacks", "thwacks", "wallops", "socks") - attack_verb_simple = list("whack", "thwack", "wallop", "sock") - icon = 'icons/obj/weapons/club.dmi' - icon_state = "gohei" - inhand_icon_state = "gohei" - icon_angle = -65 - lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' - -/obj/item/melee/moonlight_greatsword - name = "moonlight greatsword" - desc = "Don't tell anyone you put any points into dex, though." - icon = 'icons/obj/weapons/sword.dmi' - icon_state = "swordon" - inhand_icon_state = "swordon" - worn_icon_state = "swordon" - icon_angle = -45 - lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - w_class = WEIGHT_CLASS_BULKY - slot_flags = ITEM_SLOT_BACK|ITEM_SLOT_BELT - block_chance = 20 - block_sound = 'sound/items/weapons/parry.ogg' - sharpness = SHARP_EDGED - force = 14 - throwforce = 12 - hitsound = 'sound/items/weapons/bladeslice.ogg' - attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts") - attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut") - var/list/alt_continuous = list("stabs", "pierces", "impales") - var/list/alt_simple = list("stab", "pierce", "impale") - -/obj/item/melee/moonlight_greatsword/Initialize(mapload) - . = ..() - alt_continuous = string_list(alt_continuous) - alt_simple = string_list(alt_simple) - AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple) - -//High Frequency Blade - -/obj/item/highfrequencyblade - name = "high frequency blade" - desc = "A sword reinforced by a powerful alternating current and resonating at extremely high vibration frequencies. \ - This oscillation weakens the molecular bonds of anything it cuts, thereby increasing its cutting ability." - icon = 'icons/obj/weapons/sword.dmi' - icon_state = "hfrequency0" - worn_icon_state = "hfrequency0" - icon_angle = -45 - lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - force = 10 - wound_bonus = 25 - exposed_wound_bonus = 50 - throwforce = 25 - throw_speed = 4 - attack_speed = CLICK_CD_HYPER_RAPID - embed_type = /datum/embedding/hfr_blade - block_chance = 25 - block_sound = 'sound/items/weapons/parry.ogg' - sharpness = SHARP_EDGED - w_class = WEIGHT_CLASS_BULKY - slot_flags = ITEM_SLOT_BACK - /// The color of the slash we create - var/slash_color = COLOR_BLUE - /// Previous x position of where we clicked on the target's icon - var/previous_x - /// Previous y position of where we clicked on the target's icon - var/previous_y - /// The previous target we attacked - var/datum/weakref/previous_target - -/datum/embedding/hfr_blade - embed_chance = 100 - -/obj/item/highfrequencyblade/Initialize(mapload) - . = ..() - AddComponent(/datum/component/two_handed, \ - wield_callback = CALLBACK(src, PROC_REF(on_wield)), \ - unwield_callback = CALLBACK(src, PROC_REF(on_unwield)), \ - ) - AddElement(/datum/element/update_icon_updates_onmob) - -/obj/item/highfrequencyblade/update_icon_state() - icon_state = "hfrequency[HAS_TRAIT(src, TRAIT_WIELDED)]" - return ..() - -/obj/item/highfrequencyblade/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) - if(attack_type == PROJECTILE_ATTACK) - if(HAS_TRAIT(src, TRAIT_WIELDED) || prob(final_block_chance)) - owner.visible_message(span_danger("[owner] deflects [attack_text] with [src]!")) - playsound(src, SFX_BULLET_MISS, 75, TRUE) - return TRUE - return FALSE - var/stop_that_blade = (final_block_chance + (attack_type == OVERWHELMING_ATTACK ? 25 : 0)) * (HAS_TRAIT(src, TRAIT_WIELDED) ? 2 : 1) - if(prob(stop_that_blade)) - owner.visible_message(span_danger("[owner] parries [attack_text] with [src]!")) - return TRUE - return FALSE - -/obj/item/highfrequencyblade/pre_attack(atom/target, mob/living/user, list/modifiers, list/attack_modifiers) - . = ..() - if(.) - return . - if(!HAS_TRAIT(src, TRAIT_WIELDED)) - return . // Default attack - if(isliving(target) && HAS_TRAIT(src, TRAIT_PACIFISM)) - return . // Default attack (ultimately nothing) - - return slash(target, user, modifiers) - -/// triggered on wield of two handed item -/obj/item/highfrequencyblade/proc/on_wield(obj/item/source, mob/user) - update_icon(UPDATE_ICON_STATE) - -/// triggered on unwield of two handed item -/obj/item/highfrequencyblade/proc/on_unwield(obj/item/source, mob/user) - update_icon(UPDATE_ICON_STATE) - -/obj/item/highfrequencyblade/proc/slash(atom/target, mob/living/user, list/modifiers) - user.do_attack_animation(target, "nothing") - var/damage_mod = 1 - var/x_slashed = text2num(modifiers[ICON_X]) || ICON_SIZE_X/2 //in case we arent called by a client - var/y_slashed = text2num(modifiers[ICON_Y]) || ICON_SIZE_Y/2 //in case we arent called by a client - new /obj/effect/temp_visual/slash(get_turf(target), target, x_slashed, y_slashed, slash_color) - if(target == previous_target?.resolve()) //if the same target, we calculate a damage multiplier if you swing your mouse around - var/x_mod = previous_x - x_slashed - var/y_mod = previous_y - y_slashed - damage_mod = max(1, round((sqrt(x_mod ** 2 + y_mod ** 2) / 10), 0.1)) - previous_target = WEAKREF(target) - previous_x = x_slashed - previous_y = y_slashed - playsound(src, 'sound/items/weapons/bladeslice.ogg', 100, vary = TRUE) - playsound(src, 'sound/items/weapons/zapbang.ogg', 50, vary = TRUE) - if(isliving(target)) - var/mob/living/living_target = target - living_target.apply_damage(force*damage_mod, BRUTE, sharpness = SHARP_EDGED, wound_bonus = wound_bonus, exposed_wound_bonus = exposed_wound_bonus, def_zone = user.zone_selected) - log_combat(user, living_target, "slashed", src) - if(living_target.stat == DEAD && prob(force*damage_mod*0.5)) - living_target.visible_message(span_danger("[living_target] explodes in a shower of gore!"), blind_message = span_hear("You hear organic matter ripping and tearing!")) - living_target.investigate_log("has been gibbed by [src].", INVESTIGATE_DEATHS) - living_target.gib(DROP_ALL_REMAINS) - log_combat(user, living_target, "gibbed", src) - return TRUE - else if(target.uses_integrity) - target.take_damage(force*damage_mod*3, BRUTE, MELEE, FALSE, null, 50) - return TRUE - else if(iswallturf(target) && prob(force*damage_mod*0.5)) - var/turf/closed/wall/wall_target = target - wall_target.dismantle_wall() - return TRUE - else if(ismineralturf(target) && prob(force*damage_mod)) - var/turf/closed/mineral/mineral_target = target - mineral_target.gets_drilled() - return TRUE - return FALSE - -/obj/effect/temp_visual/slash - icon_state = "highfreq_slash" - alpha = 150 - duration = 0.5 SECONDS - layer = ABOVE_ALL_MOB_LAYER - plane = ABOVE_GAME_PLANE - -/obj/effect/temp_visual/slash/Initialize(mapload, atom/target, x_slashed, y_slashed, slash_color) - . = ..() - if(!target) - return - var/matrix/new_transform = matrix() - new_transform.Turn(rand(1, 360)) // Random slash angle - var/datum/decompose_matrix/decomp = target.transform.decompose() - new_transform.Translate((x_slashed - ICON_SIZE_X/2) * decomp.scale_x, (y_slashed - ICON_SIZE_Y/2) * decomp.scale_y) // Move to where we clicked - //Follow target's transform while ignoring scaling - new_transform.Turn(decomp.rotation) - new_transform.Translate(decomp.shift_x, decomp.shift_y) - new_transform.Translate(target.pixel_x, target.pixel_y) // Follow target's pixel offsets - transform = new_transform - //Double the scale of the matrix by doubling the 2x2 part without touching the translation part - var/matrix/scaled_transform = new_transform + matrix(new_transform.a, new_transform.b, 0, new_transform.d, new_transform.e, 0) - animate(src, duration*0.5, color = slash_color, transform = scaled_transform, alpha = 255) - -/obj/item/highfrequencyblade/wizard - desc = "A blade that was mastercrafted by a legendary blacksmith. Its enchantments let it slash through anything." - force = 8 - throwforce = 20 - wound_bonus = 20 - exposed_wound_bonus = 25 - -/obj/item/highfrequencyblade/wizard/attack_self(mob/user, modifiers) - if(!HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED)) - balloon_alert(user, "you're too weak!") - return - return ..() diff --git a/code/game/objects/items/weaponry/melee/baseball_bat.dm b/code/game/objects/items/weaponry/melee/baseball_bat.dm new file mode 100644 index 00000000000..8d12e772436 --- /dev/null +++ b/code/game/objects/items/weaponry/melee/baseball_bat.dm @@ -0,0 +1,160 @@ +/obj/item/melee/baseball_bat + name = "baseball bat" + desc = "There ain't a skull in the league that can withstand a swatter." + icon = 'icons/obj/weapons/bat.dmi' + icon_state = "baseball_bat" + inhand_icon_state = "baseball_bat" + icon_angle = -45 + lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' + force = 12 + wound_bonus = -10 + throwforce = 12 + demolition_mod = 1.25 + attack_verb_continuous = list("beats", "smacks") + attack_verb_simple = list("beat", "smack") + custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 3.5) + resistance_flags = FLAMMABLE + w_class = WEIGHT_CLASS_HUGE + /// Are we able to do a homerun? + var/homerun_able = FALSE + /// Are we ready to do a homerun? + var/homerun_ready = FALSE + /// Can we launch mobs thrown at us away? + var/mob_thrower = FALSE + /// List of all thrown datums we sent. + var/list/thrown_datums = list() + +/obj/item/melee/baseball_bat/Initialize(mapload) + . = ..() + AddElement(/datum/element/kneecapping) + // No subtypes + if(type != /obj/item/melee/baseball_bat) + return + if(prob(check_holidays(APRIL_FOOLS) ? 50 : 1)) + make_silly() + +/obj/item/melee/baseball_bat/attack_self(mob/user) + if(!homerun_able) + return ..() + if(homerun_ready) + to_chat(user, span_warning("You're already ready to do a home run!")) + return ..() + to_chat(user, span_warning("You begin gathering strength...")) + playsound(get_turf(src), 'sound/effects/magic/lightning_chargeup.ogg', 65, TRUE) + if(do_after(user, 9 SECONDS, target = src)) + to_chat(user, span_userdanger("You gather power! Time for a home run!")) + homerun_ready = TRUE + return ..() + +/obj/item/melee/baseball_bat/attack(mob/living/target, mob/living/user) + // we obtain the relative direction from the bat itself to the target + var/relative_direction = get_cardinal_dir(src, target) + var/atom/throw_target = get_edge_target_turf(target, relative_direction) + . = ..() + if(HAS_TRAIT(user, TRAIT_PACIFISM)) + return + if(homerun_ready) + user.visible_message(span_userdanger("It's a home run!")) + if(!QDELETED(target)) + target.throw_at(throw_target, rand(8,10), 14, user) + SSexplosions.medturf += throw_target + playsound(get_turf(src), 'sound/items/weapons/homerun.ogg', 100, TRUE) + homerun_ready = FALSE + return + else if(!QDELETED(target) && !target.anchored) + var/whack_speed = (prob(60) ? 1 : 4) + target.throw_at(throw_target, rand(1, 2), whack_speed, user, gentle = TRUE) // sorry friends, 7 speed batting caused wounds to absolutely delete whoever you knocked your target into (and said target) + +/obj/item/melee/baseball_bat/Destroy(force) + for(var/target in thrown_datums) + var/datum/thrownthing/throw_datum = thrown_datums[target] + throw_datum.callback.Invoke() + thrown_datums.Cut() + return ..() + +/obj/item/melee/baseball_bat/pre_attack(atom/movable/target, mob/living/user, list/modifiers, list/attack_modifiers) + var/turf/target_turf = get_turf(target) + if(!target_turf) + return ..() + for(var/atom/movable/atom as anything in target_turf) + if(!try_launch(atom, user)) + continue + return TRUE + return ..() + +/obj/item/melee/baseball_bat/proc/try_launch(atom/movable/target, mob/living/user) + if(!target.throwing || (ismob(target) && !mob_thrower)) + return FALSE + var/datum/thrownthing/throw_datum = target.throwing + var/datum_throw_speed = throw_datum.speed + var/angle = 0 + var/target_to_user = get_dir(target, user) + if(target.dir & turn(target_to_user, 90)) + angle = 270 + if(target.dir & turn(target_to_user, 270)) + angle = 90 + if(target.dir & REVERSE_DIR(target_to_user)) + angle = 180 + if(target.dir & target_to_user) + angle = 360 + var/turf/return_to_sender = get_ranged_target_turf_direct(user, throw_datum.starting_turf, max(3, round(target.throw_range * 1.5, 1)), offset = angle + (rand(-1, 1) * 10)) + throw_datum.finalize(hit = FALSE) + target.mouse_opacity = MOUSE_OPACITY_TRANSPARENT //dont mess with our ball + target.color = list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,3) //make them super light + animate(target, 0.5 SECONDS, color = null, flags = ANIMATION_PARALLEL) + user.color = list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,3) + animate(user, 0.5 SECONDS, color = null, flags = ANIMATION_PARALLEL) + playsound(src, 'sound/items/baseballhit.ogg', 100, TRUE) + user.do_attack_animation(target, used_item = src) + ADD_TRAIT(user, TRAIT_IMMOBILIZED, type) + addtimer(CALLBACK(src, PROC_REF(launch_back), target, user, return_to_sender, datum_throw_speed), 0.5 SECONDS) + return TRUE + +/obj/item/melee/baseball_bat/proc/launch_back(atom/movable/target, mob/living/user, turf/target_turf, datum_throw_speed) + playsound(target, 'sound/effects/magic/tail_swing.ogg', 50, TRUE) + REMOVE_TRAIT(user, TRAIT_IMMOBILIZED, type) + target.mouse_opacity = initial(target.mouse_opacity) + target.add_filter("baseball_launch", 3, motion_blur_filter(1, 3)) + target.throwforce *= 2 + target.throw_at(target_turf, get_dist(target, target_turf), datum_throw_speed + 1, user, callback = CALLBACK(src, PROC_REF(on_hit), target)) + thrown_datums[target] = target.throwing + +/obj/item/melee/baseball_bat/proc/make_silly() + name = "cricket bat" + icon_state = "baseball_bat_brit" + inhand_icon_state = "baseball_bat_brit" + desc = pick("You've got red on you.", "You gotta know what a crumpet is to understand cricket.") + +/obj/item/melee/baseball_bat/proc/on_hit(atom/movable/target) + target.remove_filter("baseball_launch") + target.throwforce *= 0.5 + thrown_datums -= target + +/obj/item/melee/baseball_bat/homerun + name = "home run bat" + desc = "This thing looks dangerous... Dangerously good at baseball, that is." + icon_state = "baseball_bat_home" + inhand_icon_state = "baseball_bat_home" + homerun_able = TRUE + mob_thrower = TRUE + +/obj/item/melee/baseball_bat/ablative + name = "metal baseball bat" + desc = "This bat is made of highly reflective, highly armored material." + icon_state = "baseball_bat_metal" + inhand_icon_state = "baseball_bat_metal" + custom_materials = list(/datum/material/titanium = SHEET_MATERIAL_AMOUNT * 3.5) + resistance_flags = NONE + force = 20 + throwforce = 20 + mob_thrower = TRUE + block_sound = 'sound/items/weapons/effects/batreflect.ogg' + +/obj/item/melee/baseball_bat/ablative/IsReflect()//some day this will reflect thrown items instead of lasers + return TRUE + +// In case you ever want to spawn it via map/admin console +/obj/item/melee/baseball_bat/british/Initialize(mapload) + . = ..() + make_silly() diff --git a/code/game/objects/items/melee/baton.dm b/code/game/objects/items/weaponry/melee/baton.dm similarity index 100% rename from code/game/objects/items/melee/baton.dm rename to code/game/objects/items/weaponry/melee/baton.dm diff --git a/code/game/objects/items/melee/chainofcommand.dm b/code/game/objects/items/weaponry/melee/chainofcommand.dm similarity index 100% rename from code/game/objects/items/melee/chainofcommand.dm rename to code/game/objects/items/weaponry/melee/chainofcommand.dm diff --git a/code/game/objects/items/chainsaw.dm b/code/game/objects/items/weaponry/melee/chainsaw.dm similarity index 100% rename from code/game/objects/items/chainsaw.dm rename to code/game/objects/items/weaponry/melee/chainsaw.dm diff --git a/code/game/objects/items/weaponry/melee/claymore.dm b/code/game/objects/items/weaponry/melee/claymore.dm new file mode 100644 index 00000000000..2fb5b93fadd --- /dev/null +++ b/code/game/objects/items/weaponry/melee/claymore.dm @@ -0,0 +1,274 @@ +/obj/item/claymore + name = "claymore" + desc = "What are you standing around staring at this for? Get to killing!" + icon = 'icons/obj/weapons/sword.dmi' + icon_state = "claymore" + inhand_icon_state = "claymore" + icon_angle = -45 + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + hitsound = 'sound/items/weapons/bladeslice.ogg' + obj_flags = CONDUCTS_ELECTRICITY + slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK + force = 40 + throwforce = 10 + w_class = WEIGHT_CLASS_NORMAL + attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts") + attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut") + block_chance = 50 + block_sound = 'sound/items/weapons/parry.ogg' + sharpness = SHARP_EDGED + max_integrity = 200 + armor_type = /datum/armor/item_claymore + resistance_flags = FIRE_PROOF + var/list/alt_continuous = list("stabs", "pierces", "impales") + var/list/alt_simple = list("stab", "pierce", "impale") + +/datum/armor/item_claymore + fire = 100 + acid = 50 + +/obj/item/claymore/Initialize(mapload) + . = ..() + alt_continuous = string_list(alt_continuous) + alt_simple = string_list(alt_simple) + make_stabby() + AddComponent(/datum/component/butchering, \ + speed = 4 SECONDS, \ + effectiveness = 105, \ + ) + +// Applies alt sharpness component, for overrides +/obj/item/claymore/proc/make_stabby() + AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -15) + +/obj/item/claymore/suicide_act(mob/living/user) + user.visible_message(span_suicide("[user] is falling on [src]! It looks like [user.p_theyre()] trying to commit suicide!")) + return BRUTELOSS + +/obj/item/claymore/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) + if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK || attack_type == OVERWHELMING_ATTACK) + final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword. Or a road roller, if one happened to hit you. + return ..() + +//statistically similar to e-cutlasses +/obj/item/claymore/cutlass + name = "cutlass" + desc = "A piratey sword used by buckaneers to \"negotiate\" the transfer of treasure." + icon_state = "cutlass" + inhand_icon_state = "cutlass" + worn_icon_state = "cutlass" + slot_flags = ITEM_SLOT_BACK + force = 30 + throwforce = 20 + throw_speed = 3 + throw_range = 5 + armour_penetration = 35 + +/obj/item/claymore/cutlass/Initialize(mapload) + . = ..() + AddElement(/datum/element/cuffable_item) //closed sword guard + +/obj/item/claymore/cutlass/old + name = "old cutlass" + desc = parent_type::desc + " This one seems a tad old." + force = 24 + throwforce = 17 + armour_penetration = 20 + block_chance = 30 + +/obj/item/claymore/carrot + name = "carrot sword" + desc = "A full-sized carrot sword. Definitely not good for the eyes, not anymore." + icon_state = "carrot_sword" + inhand_icon_state = "carrot_sword" + worn_icon_state = "carrot_sword" + flags_1 = NONE + force = 19 + throwforce = 7 + throw_speed = 3 + throw_range = 7 + armour_penetration = 5 + block_chance = 10 + resistance_flags = NONE + +//bootleg claymore +/obj/item/claymore/shortsword + name = "shortsword" + desc = "A mercenary's sword, chipped and worn from battles long gone. You could say it is a swordsman's shortsword short sword." + icon_state = "shortsword" + inhand_icon_state = "shortsword" + worn_icon_state = "shortsword" + slot_flags = ITEM_SLOT_BELT + force = 20 + demolition_mod = 0.75 + block_chance = 30 + +/obj/item/claymore/highlander //ALL COMMENTS MADE REGARDING THIS SWORD MUST BE MADE IN ALL CAPS + desc = "THERE CAN BE ONLY ONE, AND IT WILL BE YOU!!!\nActivate it in your hand to point to the nearest victim." + obj_flags = CONDUCTS_ELECTRICITY + item_flags = DROPDEL //WOW BRO YOU LOST AN ARM, GUESS WHAT YOU DONT GET YOUR SWORD ANYMORE //I CANT BELIEVE SPOOKYDONUT WOULD BREAK THE REQUIREMENTS + slot_flags = null + block_chance = 0 //RNG WON'T HELP YOU NOW, PANSY + light_range = 3 + attack_verb_continuous = list("brutalizes", "eviscerates", "disembowels", "hacks", "carves", "cleaves") //ONLY THE MOST VISCERAL ATTACK VERBS + attack_verb_simple = list("brutalize", "eviscerate", "disembowel", "hack", "carve", "cleave") + var/notches = 0 //HOW MANY PEOPLE HAVE BEEN SLAIN WITH THIS BLADE + var/obj/item/disk/nuclear/nuke_disk //OUR STORED NUKE DISK + +/obj/item/claymore/highlander/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_NODROP, HIGHLANDER_TRAIT) + START_PROCESSING(SSobj, src) + +/obj/item/claymore/highlander/Destroy() + if(nuke_disk) + nuke_disk.forceMove(get_turf(src)) + nuke_disk.visible_message(span_warning("The nuke disk is vulnerable!")) + nuke_disk = null + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/claymore/highlander/process() + if(ishuman(loc)) + var/mob/living/carbon/human/holder = loc + holder.layer = ABOVE_ALL_MOB_LAYER //NO HIDING BEHIND PLANTS FOR YOU, DICKWEED (HA GET IT, BECAUSE WEEDS ARE PLANTS) + ADD_TRAIT(holder, TRAIT_NOBLOOD, HIGHLANDER_TRAIT) //AND WE WON'T BLEED OUT LIKE COWARDS + else + if(!(flags_1 & ADMIN_SPAWNED_1)) + qdel(src) + + +/obj/item/claymore/highlander/pickup(mob/living/user) + . = ..() + to_chat(user, span_notice("The power of Scotland protects you! You are shielded from all stuns and knockdowns.")) + user.ignore_slowdown(HIGHLANDER_TRAIT) + user.add_stun_absorption( + source = HIGHLANDER_TRAIT, + message = span_warning("%EFFECT_OWNER is protected by the power of Scotland!"), + self_message = span_boldwarning("The power of Scotland absorbs the stun!"), + examine_message = span_warning("%EFFECT_OWNER_THEYRE protected by the power of Scotland!"), + ) + +/obj/item/claymore/highlander/dropped(mob/living/user) + . = ..() + user.unignore_slowdown(HIGHLANDER_TRAIT) + user.remove_stun_absorption(HIGHLANDER_TRAIT) + +/obj/item/claymore/highlander/examine(mob/user) + . = ..() + . += "It has [!notches ? "nothing" : "[notches] notches"] scratched into the blade." + if(nuke_disk) + . += span_boldwarning("It's holding the nuke disk!") + +/obj/item/claymore/highlander/attack(mob/living/target, mob/living/user) + . = ..() + if(!QDELETED(target) && target.stat == DEAD && target.mind?.has_antag_datum(/datum/antagonist/highlander)) + user.fully_heal() //STEAL THE LIFE OF OUR FALLEN FOES + add_notch(user) + target.visible_message(span_warning("[target] crumbles to dust beneath [user]'s blows!"), span_userdanger("As you fall, your body crumbles to dust!")) + target.investigate_log("has been dusted by a highlander claymore.", INVESTIGATE_DEATHS) + target.dust() + +/obj/item/claymore/highlander/attack_self(mob/living/user) + var/closest_victim + var/closest_distance = 255 + for(var/mob/living/carbon/human/scot in GLOB.player_list - user) + if(scot.mind?.has_antag_datum(/datum/antagonist/highlander) && (!closest_victim || get_dist(user, closest_victim) < closest_distance)) + closest_victim = scot + for(var/mob/living/silicon/robot/siliscot in GLOB.player_list - user) + if(siliscot.mind?.has_antag_datum(/datum/antagonist/highlander) && (!closest_victim || get_dist(user, closest_victim) < closest_distance)) + closest_victim = siliscot + + if(!closest_victim) + to_chat(user, span_warning("[src] thrums for a moment and falls dark. Perhaps there's nobody nearby.")) + return + to_chat(user, span_danger("[src] thrums and points to the [dir2text(get_dir(user, closest_victim))].")) + +/obj/item/claymore/highlander/IsReflect() + return 1 //YOU THINK YOUR PUNY LASERS CAN STOP ME? + +/obj/item/claymore/highlander/proc/add_notch(mob/living/user) //DYNAMIC CLAYMORE PROGRESSION SYSTEM - THIS IS THE FUTURE + notches++ + force++ + var/new_name = name + switch(notches) + if(1) + to_chat(user, span_notice("Your first kill - hopefully one of many. You scratch a notch into [src]'s blade.")) + to_chat(user, span_warning("You feel your fallen foe's soul entering your blade, restoring your wounds!")) + new_name = "notched claymore" + if(2) + to_chat(user, span_notice("Another falls before you. Another soul fuses with your own. Another notch in the blade.")) + new_name = "double-notched claymore" + add_atom_colour(rgb(255, 235, 235), ADMIN_COLOUR_PRIORITY) + if(3) + to_chat(user, span_notice("You're beginning to relish the thrill of battle.")) + new_name = "triple-notched claymore" + add_atom_colour(rgb(255, 215, 215), ADMIN_COLOUR_PRIORITY) + if(4) + to_chat(user, span_notice("You've lost count of how many you've killed.")) + new_name = "many-notched claymore" + add_atom_colour(rgb(255, 195, 195), ADMIN_COLOUR_PRIORITY) + if(5) + to_chat(user, span_bolddanger("Five voices now echo in your mind, cheering the slaughter.")) + new_name = "battle-tested claymore" + add_atom_colour(rgb(255, 175, 175), ADMIN_COLOUR_PRIORITY) + if(6) + to_chat(user, span_bolddanger("Is this what the vikings felt like? Visions of glory fill your head as you slay your sixth foe.")) + new_name = "battle-scarred claymore" + add_atom_colour(rgb(255, 155, 155), ADMIN_COLOUR_PRIORITY) + if(7) + to_chat(user, span_bolddanger("Kill. Butcher. Conquer.")) + new_name = "vicious claymore" + add_atom_colour(rgb(255, 135, 135), ADMIN_COLOUR_PRIORITY) + if(8) + to_chat(user, span_userdanger("IT NEVER GETS OLD. THE SCREAMING. THE BLOOD AS IT SPRAYS ACROSS YOUR FACE.")) + new_name = "bloodthirsty claymore" + add_atom_colour(rgb(255, 115, 115), ADMIN_COLOUR_PRIORITY) + if(9) + to_chat(user, span_userdanger("ANOTHER ONE FALLS TO YOUR BLOWS. ANOTHER WEAKLING UNFIT TO LIVE.")) + new_name = "gore-stained claymore" + add_atom_colour(rgb(255, 95, 95), ADMIN_COLOUR_PRIORITY) + if(10) + user.visible_message(span_warning("[user]'s eyes light up with a vengeful fire!"), \ + span_userdanger("YOU FEEL THE POWER OF VALHALLA FLOWING THROUGH YOU! THERE CAN BE ONLY ONE!!!")) + new_name = "GORE-DRENCHED CLAYMORE OF [pick("THE WHIMSICAL SLAUGHTER", "A THOUSAND SLAUGHTERED CATTLE", "GLORY AND VALHALLA", "ANNIHILATION", "OBLITERATION")]" + icon_state = "claymore_gold" + inhand_icon_state = "cultblade" + lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi' + righthand_file = 'icons/mob/inhands/64x64_righthand.dmi' + remove_atom_colour(ADMIN_COLOUR_PRIORITY) + user.update_held_items() + + name = new_name + playsound(user, 'sound/items/tools/screwdriver2.ogg', 50, TRUE) + +/obj/item/claymore/highlander/robot //BLOODTHIRSTY BORGS NOW COME IN PLAID + icon = 'icons/obj/items_cyborg.dmi' + icon_state = "claymore_cyborg" + +/obj/item/claymore/highlander/robot/Initialize(mapload) + . = ..() + if(!iscyborg(loc)) + return INITIALIZE_HINT_QDEL + +/obj/item/claymore/highlander/robot/process() + loc.layer = ABOVE_ALL_MOB_LAYER + +/obj/item/claymore/gladius + name = "gladius" + desc = "A short but formidable sword, favored by recently-reanimated ancient warriors." + icon = 'icons/obj/weapons/sword.dmi' + icon_state = "gladius" + inhand_icon_state = "gladius" + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + sharpness = SHARP_POINTY + attack_verb_continuous = list("stabs", "cuts") + attack_verb_simple = list("stab", "cut") + slot_flags = null + sound_vary = TRUE + block_sound = 'sound/items/weapons/parry.ogg' + hitsound = 'sound/items/weapons/bladeslice.ogg' + pickup_sound = SFX_KNIFE_PICKUP + drop_sound = SFX_KNIFE_DROP diff --git a/code/game/objects/items/dualsaber.dm b/code/game/objects/items/weaponry/melee/dualsaber.dm similarity index 100% rename from code/game/objects/items/dualsaber.dm rename to code/game/objects/items/weaponry/melee/dualsaber.dm diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/weaponry/melee/energy.dm similarity index 100% rename from code/game/objects/items/melee/energy.dm rename to code/game/objects/items/weaponry/melee/energy.dm diff --git a/code/game/objects/items/fireaxe.dm b/code/game/objects/items/weaponry/melee/fireaxe.dm similarity index 100% rename from code/game/objects/items/fireaxe.dm rename to code/game/objects/items/weaponry/melee/fireaxe.dm diff --git a/code/game/objects/items/weaponry/melee/high_frequency_blade.dm b/code/game/objects/items/weaponry/melee/high_frequency_blade.dm new file mode 100644 index 00000000000..618c5d24cf9 --- /dev/null +++ b/code/game/objects/items/weaponry/melee/high_frequency_blade.dm @@ -0,0 +1,152 @@ +/obj/item/highfrequencyblade + name = "high frequency blade" + desc = "A sword reinforced by a powerful alternating current and resonating at extremely high vibration frequencies. \ + This oscillation weakens the molecular bonds of anything it cuts, thereby increasing its cutting ability." + icon = 'icons/obj/weapons/sword.dmi' + icon_state = "hfrequency0" + worn_icon_state = "hfrequency0" + icon_angle = -45 + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + force = 10 + wound_bonus = 25 + exposed_wound_bonus = 50 + throwforce = 25 + throw_speed = 4 + attack_speed = CLICK_CD_HYPER_RAPID + embed_type = /datum/embedding/hfr_blade + block_chance = 25 + block_sound = 'sound/items/weapons/parry.ogg' + sharpness = SHARP_EDGED + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK + /// The color of the slash we create + var/slash_color = COLOR_BLUE + /// Previous x position of where we clicked on the target's icon + var/previous_x + /// Previous y position of where we clicked on the target's icon + var/previous_y + /// The previous target we attacked + var/datum/weakref/previous_target + +/datum/embedding/hfr_blade + embed_chance = 100 + +/obj/item/highfrequencyblade/Initialize(mapload) + . = ..() + AddComponent(/datum/component/two_handed, \ + wield_callback = CALLBACK(src, PROC_REF(on_wield)), \ + unwield_callback = CALLBACK(src, PROC_REF(on_unwield)), \ + ) + AddElement(/datum/element/update_icon_updates_onmob) + +/obj/item/highfrequencyblade/update_icon_state() + icon_state = "hfrequency[HAS_TRAIT(src, TRAIT_WIELDED)]" + return ..() + +/obj/item/highfrequencyblade/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) + if(attack_type == PROJECTILE_ATTACK) + if(HAS_TRAIT(src, TRAIT_WIELDED) || prob(final_block_chance)) + owner.visible_message(span_danger("[owner] deflects [attack_text] with [src]!")) + playsound(src, SFX_BULLET_MISS, 75, TRUE) + return TRUE + return FALSE + var/stop_that_blade = (final_block_chance + (attack_type == OVERWHELMING_ATTACK ? 25 : 0)) * (HAS_TRAIT(src, TRAIT_WIELDED) ? 2 : 1) + if(prob(stop_that_blade)) + owner.visible_message(span_danger("[owner] parries [attack_text] with [src]!")) + return TRUE + return FALSE + +/obj/item/highfrequencyblade/pre_attack(atom/target, mob/living/user, list/modifiers, list/attack_modifiers) + . = ..() + if(.) + return . + if(!HAS_TRAIT(src, TRAIT_WIELDED)) + return . // Default attack + if(isliving(target) && HAS_TRAIT(src, TRAIT_PACIFISM)) + return . // Default attack (ultimately nothing) + + return slash(target, user, modifiers) + +/// triggered on wield of two handed item +/obj/item/highfrequencyblade/proc/on_wield(obj/item/source, mob/user) + update_icon(UPDATE_ICON_STATE) + +/// triggered on unwield of two handed item +/obj/item/highfrequencyblade/proc/on_unwield(obj/item/source, mob/user) + update_icon(UPDATE_ICON_STATE) + +/obj/item/highfrequencyblade/proc/slash(atom/target, mob/living/user, list/modifiers) + user.do_attack_animation(target, "nothing") + var/damage_mod = 1 + var/x_slashed = text2num(modifiers[ICON_X]) || ICON_SIZE_X/2 //in case we arent called by a client + var/y_slashed = text2num(modifiers[ICON_Y]) || ICON_SIZE_Y/2 //in case we arent called by a client + new /obj/effect/temp_visual/slash(get_turf(target), target, x_slashed, y_slashed, slash_color) + if(target == previous_target?.resolve()) //if the same target, we calculate a damage multiplier if you swing your mouse around + var/x_mod = previous_x - x_slashed + var/y_mod = previous_y - y_slashed + damage_mod = max(1, round((sqrt(x_mod ** 2 + y_mod ** 2) / 10), 0.1)) + previous_target = WEAKREF(target) + previous_x = x_slashed + previous_y = y_slashed + playsound(src, 'sound/items/weapons/bladeslice.ogg', 100, vary = TRUE) + playsound(src, 'sound/items/weapons/zapbang.ogg', 50, vary = TRUE) + if(isliving(target)) + var/mob/living/living_target = target + living_target.apply_damage(force*damage_mod, BRUTE, sharpness = SHARP_EDGED, wound_bonus = wound_bonus, exposed_wound_bonus = exposed_wound_bonus, def_zone = user.zone_selected) + log_combat(user, living_target, "slashed", src) + if(living_target.stat == DEAD && prob(force*damage_mod*0.5)) + living_target.visible_message(span_danger("[living_target] explodes in a shower of gore!"), blind_message = span_hear("You hear organic matter ripping and tearing!")) + living_target.investigate_log("has been gibbed by [src].", INVESTIGATE_DEATHS) + living_target.gib(DROP_ALL_REMAINS) + log_combat(user, living_target, "gibbed", src) + return TRUE + else if(target.uses_integrity) + target.take_damage(force*damage_mod*3, BRUTE, MELEE, FALSE, null, 50) + return TRUE + else if(iswallturf(target) && prob(force*damage_mod*0.5)) + var/turf/closed/wall/wall_target = target + wall_target.dismantle_wall() + return TRUE + else if(ismineralturf(target) && prob(force*damage_mod)) + var/turf/closed/mineral/mineral_target = target + mineral_target.gets_drilled() + return TRUE + return FALSE + +/obj/effect/temp_visual/slash + icon_state = "highfreq_slash" + alpha = 150 + duration = 0.5 SECONDS + layer = ABOVE_ALL_MOB_LAYER + plane = ABOVE_GAME_PLANE + +/obj/effect/temp_visual/slash/Initialize(mapload, atom/target, x_slashed, y_slashed, slash_color) + . = ..() + if(!target) + return + var/matrix/new_transform = matrix() + new_transform.Turn(rand(1, 360)) // Random slash angle + var/datum/decompose_matrix/decomp = target.transform.decompose() + new_transform.Translate((x_slashed - ICON_SIZE_X/2) * decomp.scale_x, (y_slashed - ICON_SIZE_Y/2) * decomp.scale_y) // Move to where we clicked + //Follow target's transform while ignoring scaling + new_transform.Turn(decomp.rotation) + new_transform.Translate(decomp.shift_x, decomp.shift_y) + new_transform.Translate(target.pixel_x, target.pixel_y) // Follow target's pixel offsets + transform = new_transform + //Double the scale of the matrix by doubling the 2x2 part without touching the translation part + var/matrix/scaled_transform = new_transform + matrix(new_transform.a, new_transform.b, 0, new_transform.d, new_transform.e, 0) + animate(src, duration*0.5, color = slash_color, transform = scaled_transform, alpha = 255) + +/obj/item/highfrequencyblade/wizard + desc = "A blade that was mastercrafted by a legendary blacksmith. Its enchantments let it slash through anything." + force = 8 + throwforce = 20 + wound_bonus = 20 + exposed_wound_bonus = 25 + +/obj/item/highfrequencyblade/wizard/attack_self(mob/user, modifiers) + if(!HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED)) + balloon_alert(user, "you're too weak!") + return + return ..() diff --git a/code/game/objects/items/his_grace.dm b/code/game/objects/items/weaponry/melee/his_grace.dm similarity index 100% rename from code/game/objects/items/his_grace.dm rename to code/game/objects/items/weaponry/melee/his_grace.dm diff --git a/code/game/objects/items/weaponry/melee/katana.dm b/code/game/objects/items/weaponry/melee/katana.dm new file mode 100644 index 00000000000..28747b8a0ef --- /dev/null +++ b/code/game/objects/items/weaponry/melee/katana.dm @@ -0,0 +1,50 @@ +/obj/item/katana + name = "katana" + desc = "Woefully underpowered in D20." + icon = 'icons/obj/weapons/sword.dmi' + icon_state = "katana" + inhand_icon_state = "katana" + worn_icon_state = "katana" + icon_angle = -45 + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + obj_flags = CONDUCTS_ELECTRICITY + slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK + force = 40 + throwforce = 10 + w_class = WEIGHT_CLASS_HUGE + hitsound = 'sound/items/weapons/bladeslice.ogg' + attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts") + attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut") + block_chance = 50 + block_sound = 'sound/items/weapons/parry.ogg' + sharpness = SHARP_EDGED + max_integrity = 200 + armor_type = /datum/armor/item_katana + resistance_flags = FIRE_PROOF + var/list/alt_continuous = list("stabs", "pierces", "impales") + var/list/alt_simple = list("stab", "pierce", "impale") + +/obj/item/katana/Initialize(mapload) + . = ..() + alt_continuous = string_list(alt_continuous) + alt_simple = string_list(alt_simple) + AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -15) + +/obj/item/katana/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) + if(attack_type == OVERWHELMING_ATTACK) + final_block_chance = 0 //Not a high freuqnecy blade, sorry pal + return ..() + +/datum/armor/item_katana + fire = 100 + acid = 50 + +/obj/item/katana/suicide_act(mob/living/user) + user.visible_message(span_suicide("[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit seppuku!")) + return BRUTELOSS + +/obj/item/katana/cursed //used by wizard events, see the tendril_loot.dm file for the miner one + slot_flags = null + +//Ninja energy katana is located in the antagonist module. diff --git a/code/game/objects/items/knives.dm b/code/game/objects/items/weaponry/melee/knives.dm similarity index 72% rename from code/game/objects/items/knives.dm rename to code/game/objects/items/weaponry/melee/knives.dm index b94a91e3456..c26f460871b 100644 --- a/code/game/objects/items/knives.dm +++ b/code/game/objects/items/weaponry/melee/knives.dm @@ -1,3 +1,9 @@ +///Includes +//*Knives +//*Boxcutter +//*Switchblades +/// + // Knife Template, should not appear in game normaly // /obj/item/knife name = "knife" @@ -339,3 +345,133 @@ inhand_icon_state = "rootshiv" icon_angle = -45 custom_materials = null + +/obj/item/switchblade + name = "switchblade" + icon = 'icons/obj/weapons/sword.dmi' + icon_state = "switchblade" + base_icon_state = "switchblade" + icon_angle = -90 + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + desc = "A sharp, concealable, spring-loaded knife." + obj_flags = CONDUCTS_ELECTRICITY + force = 3 + w_class = WEIGHT_CLASS_SMALL + throwforce = 5 + throw_speed = 3 + throw_range = 6 + custom_materials = list(/datum/material/iron= SHEET_MATERIAL_AMOUNT * 6) + hitsound = 'sound/items/weapons/genhit.ogg' + attack_verb_continuous = list("stubs", "pokes") + attack_verb_simple = list("stub", "poke") + resistance_flags = FIRE_PROOF + /// Whether the switchblade starts extended or not. + var/start_extended = FALSE + var/list/alt_continuous = list("stabs", "pierces", "shanks") + var/list/alt_simple = list("stab", "pierce", "shank") + +/obj/item/switchblade/get_all_tool_behaviours() + return list(TOOL_KNIFE) + +/obj/item/switchblade/Initialize(mapload) + . = ..() + AddElement(/datum/element/update_icon_updates_onmob) + AddComponent(/datum/component/butchering, \ + speed = 7 SECONDS, \ + effectiveness = 100, \ + ) + + AddComponent( \ + /datum/component/transforming, \ + start_transformed = start_extended, \ + force_on = 20, \ + throwforce_on = 23, \ + throw_speed_on = throw_speed, \ + sharpness_on = SHARP_EDGED, \ + hitsound_on = 'sound/items/weapons/bladeslice.ogg', \ + w_class_on = WEIGHT_CLASS_NORMAL, \ + attack_verb_continuous_on = list("slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts"), \ + attack_verb_simple_on = list("slash", "slice", "tear", "lacerate", "rip", "dice", "cut"), \ + ) + + alt_continuous = string_list(alt_continuous) + alt_simple = string_list(alt_simple) + AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -5, TRAIT_TRANSFORM_ACTIVE) + + RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform)) + +/obj/item/switchblade/proc/on_transform(obj/item/source, mob/user, active) + SIGNAL_HANDLER + + tool_behaviour = (active ? TOOL_KNIFE : NONE) + +/obj/item/switchblade/suicide_act(mob/living/user) + user.visible_message(span_suicide("[user] is slitting [user.p_their()] own throat with [src]! It looks like [user.p_theyre()] trying to commit suicide!")) + return BRUTELOSS + +/obj/item/switchblade/extended + start_extended = TRUE + +/obj/item/boxcutter + name = "boxcutter" + desc = "A tool for cutting boxes, or throats." + icon = 'icons/obj/tools.dmi' + icon_state = "boxcutter" + inhand_icon_state = "boxcutter" + base_icon_state = "boxcutter" + icon_angle = -90 + lefthand_file = 'icons/mob/inhands/equipment/boxcutter_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/boxcutter_righthand.dmi' + inhand_icon_state = null + attack_verb_continuous = list("prods", "pokes") + attack_verb_simple = list("prod", "poke") + w_class = WEIGHT_CLASS_SMALL + resistance_flags = FIRE_PROOF + force = 0 + /// Used on Initialize, how much time to cut cable restraints and zipties. + var/snap_time_weak_handcuffs = 0 SECONDS + /// Used on Initialize, how much time to cut real handcuffs. Null means it can't. + var/snap_time_strong_handcuffs = null + /// Starts open if true + var/start_extended = FALSE + +/obj/item/boxcutter/get_all_tool_behaviours() + return list(TOOL_KNIFE) + +/obj/item/boxcutter/Initialize(mapload) + . = ..() + AddElement(/datum/element/update_icon_updates_onmob) + AddComponent(/datum/component/butchering, \ + speed = 7 SECONDS, \ + effectiveness = 100, \ + ) + + AddComponent( \ + /datum/component/transforming, \ + start_transformed = start_extended, \ + force_on = 10, \ + throwforce_on = 4, \ + throw_speed_on = throw_speed, \ + sharpness_on = SHARP_EDGED, \ + hitsound_on = 'sound/items/weapons/bladeslice.ogg', \ + w_class_on = WEIGHT_CLASS_NORMAL, \ + attack_verb_continuous_on = list("cuts", "stabs", "slashes"), \ + attack_verb_simple_on = list("cut", "stab", "slash"), \ + ) + + RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform)) + +/obj/item/boxcutter/proc/on_transform(obj/item/source, mob/user, active) + SIGNAL_HANDLER + + playsound(src, 'sound/items/boxcutter_activate.ogg', 50) + tool_behaviour = (active ? TOOL_KNIFE : NONE) + if(active) + AddElement(/datum/element/cuffsnapping, snap_time_weak_handcuffs, snap_time_strong_handcuffs) + else + RemoveElement(/datum/element/cuffsnapping, snap_time_weak_handcuffs, snap_time_strong_handcuffs) + return COMPONENT_NO_DEFAULT_MESSAGE + +/obj/item/boxcutter/extended + start_extended = TRUE diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/weaponry/melee/misc.dm similarity index 55% rename from code/game/objects/items/melee/misc.dm rename to code/game/objects/items/weaponry/melee/misc.dm index 2f4defafa2c..8bb68ee396e 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/weaponry/melee/misc.dm @@ -33,163 +33,6 @@ ) //very imprecise -/obj/item/melee/sabre - name = "officer's sabre" - desc = "An elegant weapon, its monomolecular edge is capable of cutting through flesh and bone with ease." - icon = 'icons/obj/weapons/sword.dmi' - icon_state = "sabre" - inhand_icon_state = "sabre" - icon_angle = -45 - lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - obj_flags = CONDUCTS_ELECTRICITY | UNIQUE_RENAME - force = 20 - throwforce = 10 - demolition_mod = 0.75 //but not metal - w_class = WEIGHT_CLASS_BULKY - block_chance = 50 - armour_penetration = 75 - sharpness = SHARP_EDGED - attack_verb_continuous = list("slashes", "cuts") - attack_verb_simple = list("slash", "cut") - block_sound = 'sound/items/weapons/parry.ogg' - hitsound = 'sound/items/weapons/rapierhit.ogg' - custom_materials = list(/datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT) - wound_bonus = 10 - exposed_wound_bonus = 25 - -/obj/item/melee/sabre/Initialize(mapload) - . = ..() - AddElement(/datum/element/cuffable_item) //closed sword guard - AddComponent(/datum/component/jousting) - //fast and effective, but as a sword, it might damage the results. - AddComponent(/datum/component/butchering, \ - speed = 3 SECONDS, \ - effectiveness = 95, \ - bonus_modifier = 5, \ - ) - // The weight of authority comes down on the tider's crimes. - AddElement(/datum/element/bane, target_type = /mob/living/carbon/human, damage_multiplier = 0.35) - RegisterSignal(src, COMSIG_OBJECT_PRE_BANING, PROC_REF(attempt_bane)) - RegisterSignal(src, COMSIG_OBJECT_ON_BANING, PROC_REF(bane_effects)) - -/** - * If the target reeks of maintenance, the blade can tear through their body with a total of 20 damage. - */ -/obj/item/melee/sabre/proc/attempt_bane(element_owner, mob/living/carbon/criminal) - SIGNAL_HANDLER - var/obj/item/organ/liver/liver = criminal.get_organ_slot(ORGAN_SLOT_LIVER) - if(isnull(liver) || !HAS_TRAIT(liver, TRAIT_MAINTENANCE_METABOLISM)) - return COMPONENT_CANCEL_BANING - -/** - * Assistants should fear this weapon. - */ -/obj/item/melee/sabre/proc/bane_effects(element_owner, mob/living/carbon/human/baned_target) - SIGNAL_HANDLER - baned_target.visible_message( - span_warning("[src] tears through [baned_target] with unnatural ease!"), - span_userdanger("As [src] tears into your body, you feel the weight of authority collapse into your wounds!"), - ) - INVOKE_ASYNC(baned_target, TYPE_PROC_REF(/mob/living/carbon/human, emote), "scream") - -/obj/item/melee/sabre/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) - if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK || attack_type == OVERWHELMING_ATTACK) - final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword. Or a road roller, if one happened to hit you. - return ..() - -/obj/item/melee/sabre/on_exit_storage(datum/storage/container) - playsound(container.parent, 'sound/items/unsheath.ogg', 25, TRUE) - -/obj/item/melee/sabre/on_enter_storage(datum/storage/container) - playsound(container.parent, 'sound/items/sheath.ogg', 25, TRUE) - -/obj/item/melee/sabre/suicide_act(mob/living/user) - user.visible_message(span_suicide("[user] is trying to cut off all [user.p_their()] limbs with [src]! it looks like [user.p_theyre()] trying to commit suicide!")) - var/i = 0 - ADD_TRAIT(src, TRAIT_NODROP, SABRE_SUICIDE_TRAIT) - if(iscarbon(user)) - var/mob/living/carbon/Cuser = user - var/obj/item/bodypart/holding_bodypart = Cuser.get_holding_bodypart_of_item(src) - var/list/limbs_to_dismember - var/list/arms = list() - var/list/legs = list() - var/obj/item/bodypart/bodypart - - for(bodypart in Cuser.bodyparts) - if(bodypart == holding_bodypart) - continue - if(bodypart.body_part & ARMS) - arms += bodypart - else if (bodypart.body_part & LEGS) - legs += bodypart - - limbs_to_dismember = arms + legs - if(holding_bodypart) - limbs_to_dismember += holding_bodypart - - var/speedbase = abs((4 SECONDS) / limbs_to_dismember.len) - for(bodypart in limbs_to_dismember) - i++ - addtimer(CALLBACK(src, PROC_REF(suicide_dismember), user, bodypart), speedbase * i) - addtimer(CALLBACK(src, PROC_REF(manual_suicide), user), (5 SECONDS) * i) - return MANUAL_SUICIDE - -/obj/item/melee/sabre/proc/suicide_dismember(mob/living/user, obj/item/bodypart/affecting) - if(!QDELETED(affecting) && !(affecting.bodypart_flags & BODYPART_UNREMOVABLE) && affecting.owner == user && !QDELETED(user)) - playsound(user, hitsound, 25, TRUE) - affecting.dismember(BRUTE) - user.adjust_brute_loss(force * 1.25) - -/obj/item/melee/sabre/proc/manual_suicide(mob/living/user, originally_nodropped) - if(!QDELETED(user)) - user.adjust_brute_loss(200) - user.death(FALSE) - REMOVE_TRAIT(src, TRAIT_NODROP, SABRE_SUICIDE_TRAIT) - - -/obj/item/melee/parsnip_sabre - name = "parsnip sabre" - desc = "A weird, yet elegant weapon. Surprisingly sharp for something made from a parsnip." - icon = 'icons/obj/weapons/sword.dmi' - icon_state = "parsnip_sabre" - inhand_icon_state = "parsnip_sabre" - icon_angle = -45 - lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - force = 15 - throwforce = 10 - demolition_mod = 0.3 - w_class = WEIGHT_CLASS_BULKY - block_chance = 40 - armour_penetration = 40 - sharpness = SHARP_EDGED - attack_verb_continuous = list("slashes", "cuts") - attack_verb_simple = list("slash", "cut") - block_sound = 'sound/items/weapons/parry.ogg' - hitsound = 'sound/items/weapons/rapierhit.ogg' - custom_materials = null - wound_bonus = 5 - exposed_wound_bonus = 15 - -/obj/item/melee/parsnip_sabre/Initialize(mapload) - . = ..() - AddElement(/datum/element/cuffable_item) //closed sword guard - AddComponent(/datum/component/jousting) - -/obj/item/melee/parsnip_sabre/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) - if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK || attack_type == OVERWHELMING_ATTACK) - final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword. Or a road roller, if one happened to hit you. - return ..() - -/obj/item/melee/parsnip_sabre/on_exit_storage(datum/storage/container) - . = ..() - playsound(container.parent, 'sound/items/unsheath.ogg', 25, TRUE) - -/obj/item/melee/parsnip_sabre/on_enter_storage(datum/storage/container) - . = ..() - playsound(container.parent, 'sound/items/sheath.ogg', 25, TRUE) - /obj/item/melee/beesword name = "The Stinger" desc = "Taken from a giant bee and folded over one thousand times in pure honey. Can sting through anything." @@ -228,122 +71,6 @@ playsound(get_turf(src), hitsound, 75, TRUE, -1) return TOXLOSS -/obj/item/melee/supermatter_sword - name = "supermatter sword" - desc = "In a station full of bad ideas, this might just be the worst." - icon = 'icons/obj/weapons/sword.dmi' - icon_state = "supermatter_sword_balanced" - inhand_icon_state = "supermatter_sword" - icon_angle = -90 - lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - slot_flags = null - w_class = WEIGHT_CLASS_BULKY - force = 0.001 - armour_penetration = 1000 - force_string = "INFINITE" - item_flags = NEEDS_PERMIT|NO_BLOOD_ON_ITEM - custom_materials = list(/datum/material/adamantine = SHEET_MATERIAL_AMOUNT * 20, /datum/material/iron = SHEET_MATERIAL_AMOUNT) - var/obj/machinery/power/supermatter_crystal/shard - var/balanced = 1 - -/obj/item/melee/supermatter_sword/Initialize(mapload) - . = ..() - shard = new /obj/machinery/power/supermatter_crystal(src) - qdel(shard.countdown) - shard.countdown = null - START_PROCESSING(SSobj, src) - visible_message(span_warning("[src] appears, balanced ever so perfectly on its hilt. This isn't ominous at all.")) - RegisterSignal(src, COMSIG_ATOM_PRE_BULLET_ACT, PROC_REF(eat_bullets)) - -/obj/item/melee/supermatter_sword/process() - if(balanced || throwing || ismob(src.loc) || isnull(src.loc)) - return - if(!isturf(src.loc)) - var/atom/target = src.loc - forceMove(target.loc) - consume_everything(target) - else - var/turf/turf = get_turf(src) - if(!isspaceturf(turf)) - consume_turf(turf) - -/obj/item/melee/supermatter_sword/pre_attack(atom/target, mob/living/user, list/modifiers, list/attack_modifiers) - . = ..() - if(.) - return . - - if(target == user) - user.dropItemToGround(src, TRUE) - else - user.do_attack_animation(target) - consume_everything(target) - return TRUE - -/obj/item/melee/supermatter_sword/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) - ..() - if(ismob(hit_atom)) - var/mob/mob = hit_atom - if(src.loc == mob) - mob.dropItemToGround(src, TRUE) - consume_everything(hit_atom) - -/obj/item/melee/supermatter_sword/pickup(user) - ..() - balanced = 0 - icon_state = "supermatter_sword" - icon_angle = -45 - -/obj/item/melee/supermatter_sword/ex_act(severity, target) - visible_message( - span_danger("The blast wave smacks into [src] and rapidly flashes to ash."), - span_hear("You hear a loud crack as you are washed with a wave of heat.") - ) - consume_everything() - return TRUE - -/obj/item/melee/supermatter_sword/acid_act() - visible_message(span_danger("The acid smacks into [src] and rapidly flashes to ash."),\ - span_hear("You hear a loud crack as you are washed with a wave of heat.")) - consume_everything() - return TRUE - -/obj/item/melee/supermatter_sword/proc/eat_bullets(datum/source, obj/projectile/hitting_projectile) - SIGNAL_HANDLER - - visible_message( - span_danger("[hitting_projectile] smacks into [source] and rapidly flashes to ash."), - null, - span_hear("You hear a loud crack as you are washed with a wave of heat."), - ) - consume_everything(hitting_projectile) - return COMPONENT_BULLET_BLOCKED - -/obj/item/melee/supermatter_sword/suicide_act(mob/living/user) - user.visible_message(span_suicide("[user] touches [src]'s blade. It looks like [user.p_theyre()] tired of waiting for the radiation to kill [user.p_them()]!")) - user.dropItemToGround(src, TRUE) - shard.Bumped(user) - -/obj/item/melee/supermatter_sword/proc/consume_everything(target) - if(isnull(target)) - shard.Bump(target) - else if(!isturf(target)) - shard.Bumped(target) - else - consume_turf(target) - -/obj/item/melee/supermatter_sword/proc/consume_turf(turf/turf) - var/oldtype = turf.type - var/turf/newT = turf.ScrapeAway(flags = CHANGETURF_INHERIT_AIR) - if(newT.type == oldtype) - return - playsound(turf, 'sound/effects/supermatter.ogg', 50, TRUE) - turf.visible_message( - span_danger("[turf] smacks into [src] and rapidly flashes to ash."), - span_hear("You hear a loud crack as you are washed with a wave of heat."), - ) - shard.Bump(turf) - /obj/item/melee/curator_whip name = "curator's whip" desc = "Somewhat eccentric and outdated, it still stings like hell to be hit by." @@ -548,3 +275,281 @@ final_block_chance = 0 //Don't bring a...mace to a gunfight, and also you aren't going to really block someone full body tackling you with a mace. Or a road roller, if one happened to hit you. return ..() +/obj/item/sord + name = "\improper SORD" + desc = "This thing is so unspeakably shitty you are having a hard time even holding it." + icon = 'icons/obj/weapons/sword.dmi' + icon_state = "sord" + inhand_icon_state = "sord" + icon_angle = -35 + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + slot_flags = ITEM_SLOT_BELT + force = 2 + throwforce = 1 + w_class = WEIGHT_CLASS_NORMAL + hitsound = 'sound/items/weapons/bladeslice.ogg' + attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts") + attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut") + +/obj/item/sord/suicide_act(mob/living/user) + user.visible_message(span_suicide("[user] is trying to impale [user.p_them()]self with [src]! It might be a suicide attempt if it weren't so shitty."), \ + span_suicide("You try to impale yourself with [src], but it's USELESS...")) + return SHAME + +/obj/item/carpenter_hammer + name = "carpenter hammer" + icon = 'icons/obj/weapons/hammer.dmi' + icon_state = "carpenter_hammer" + inhand_icon_state = "carpenter_hammer" + worn_icon_state = "clawhammer" //plaecholder + icon_angle = -45 + lefthand_file = 'icons/mob/inhands/weapons/hammers_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/hammers_righthand.dmi' + desc = "Uncanny looking hammer." + force = 17 + throwforce = 14 + throw_range = 4 + w_class = WEIGHT_CLASS_NORMAL + wound_bonus = 20 + demolition_mod = 1.15 + slot_flags = ITEM_SLOT_BELT + +/obj/item/carpenter_hammer/Initialize(mapload) + . = ..() + AddElement(/datum/element/kneejerk) + AddComponent(/datum/component/item_killsound, \ + allowed_mobs = list(/mob/living/carbon/human), \ + killsound = 'sound/items/weapons/hammer_death_scream.ogg', \ + replace_default_death_sound = TRUE, \ + ) + +/obj/item/carpenter_hammer/examine(mob/user) + . = ..() + . += "" + . += "Real World Tip:" + . += pick( + "Every building, from hospitals to homes, has a room that serves as the heart of the building \ + and carries blood and nutrients to its extremities. Try to find the heart of your home!", + "All the food you've tried is rotten. You've never eaten fresh food.", + "Viruses do not exist. Illness is simply your body punishing you for what you have done wrong.", + "Space stations must have at least 50 mammalian teeth embedded in the north walls for structural safety reasons.", + "Queen dragonfly sleeps and smiles.", + ) + +/obj/item/phone + name = "red phone" + desc = "Should anything ever go wrong..." + icon = 'icons/obj/devices/voice.dmi' + icon_state = "red_phone" + force = 3 + throwforce = 2 + throw_speed = 3 + throw_range = 4 + w_class = WEIGHT_CLASS_SMALL + attack_verb_continuous = list("calls", "rings") + attack_verb_simple = list("call", "ring") + hitsound = 'sound/items/weapons/ring.ogg' + +/obj/item/phone/suicide_act(mob/living/user) + if(locate(/obj/structure/chair/stool) in user.loc) + user.visible_message(span_suicide("[user] begins to tie a noose with [src]'s cord! It looks like [user.p_theyre()] trying to commit suicide!")) + else + user.visible_message(span_suicide("[user] is strangling [user.p_them()]self with [src]'s cord! It looks like [user.p_theyre()] trying to commit suicide!")) + return OXYLOSS + +/obj/item/bambostaff + name = "bamboo staff" + desc = "A long bamboo-made staff with steel-capped ends. It is rumoured that initiates of Spider Clan train with such before getting to learn how to use a katana." + force = 10 + block_chance = 45 + block_sound = 'sound/items/weapons/genhit.ogg' + slot_flags = ITEM_SLOT_BACK + w_class = WEIGHT_CLASS_BULKY + hitsound = SFX_SWING_HIT + attack_verb_continuous = list("smashes", "slams", "whacks", "thwacks") + attack_verb_simple = list("smash", "slam", "whack", "thwack") + icon = 'icons/obj/weapons/staff.dmi' + icon_state = "bambostaff0" + base_icon_state = "bambostaff" + inhand_icon_state = "bambostaff0" + worn_icon_state = "bambostaff0" + icon_angle = -135 + lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' + custom_materials = list(/datum/material/bamboo = SHEET_MATERIAL_AMOUNT * 4) + +/obj/item/bambostaff/Initialize(mapload) + . = ..() + // there are too many puns to choose from. ('Bo' is the 'real' name for this kind of weapon.) + name = pick("bamboo staff", "bambo staff", "bam-Bo staff", "bam boo staff", "bam-boo staff", "bam Bo", "bambo", "bam-Bo", "bamboo-Bo") + AddComponent(/datum/component/two_handed, \ + force_unwielded = 10, \ + force_wielded = 14, \ + ) + +/obj/item/bambostaff/update_icon_state() + icon_state = inhand_icon_state = "[base_icon_state][HAS_TRAIT(src, TRAIT_WIELDED)]" + return ..() + +/obj/item/bambostaff/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) + if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK || attack_type == OVERWHELMING_ATTACK) + final_block_chance = 0 //Don't bring a staff to a gunfight, and also you aren't going to really block someone full body tackling you with a staff. Or a road roller, if one happened to hit you. + return ..() + +/obj/item/staff + name = "wizard staff" + desc = "Apparently a staff used by the wizard." + icon = 'icons/obj/weapons/guns/magic.dmi' + icon_state = "staff" + inhand_icon_state = "staff" + lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' + force = 3 + throwforce = 5 + throw_speed = 2 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + armour_penetration = 100 + attack_verb_continuous = list("bludgeons", "whacks", "disciplines") + attack_verb_simple = list("bludgeon", "whack", "discipline") + resistance_flags = FLAMMABLE + +/obj/item/staff/broom + name = "broom" + desc = "Used for sweeping, and flying into the night while cackling. Black cat not included." + icon_state = "broom" + inhand_icon_state = "broom" + resistance_flags = FLAMMABLE + +/obj/item/staff/tape + name = "tape staff" + desc = "A roll of tape snugly attached to a stick." + icon_state = "tapestaff" + inhand_icon_state = "tapestaff" + resistance_flags = FLAMMABLE + +/obj/item/staff/stick + name = "stick" + desc = "A great tool to drag someone else's drinks across the bar." + icon = 'icons/obj/weapons/staff.dmi' + icon_state = "cane" + inhand_icon_state = "stick" + icon_angle = 135 + lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' + force = 3 + throwforce = 5 + throw_speed = 2 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + +/obj/item/tailclub + name = "tail club" + desc = "For the beating to death of lizards with their own tails." + icon = 'icons/obj/weapons/club.dmi' + icon_state = "tailclub" + icon_angle = -25 + force = 14 + throwforce = 1 // why are you throwing a club do you even weapon + throw_speed = 1 + throw_range = 1 + attack_verb_continuous = list("clubs", "bludgeons") + attack_verb_simple = list("club", "bludgeon") + custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT) + +/obj/item/melee/flyswatter + name = "flyswatter" + desc = "Useful for killing pests of all sizes." + icon = 'icons/obj/service/hydroponics/equipment.dmi' + icon_state = "flyswatter" + inhand_icon_state = "flyswatter" + icon_angle = -45 + lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' + force = 1 + throwforce = 1 + attack_verb_continuous = list("swats", "smacks") + attack_verb_simple = list("swat", "smack") + hitsound = 'sound/effects/snap.ogg' + w_class = WEIGHT_CLASS_SMALL + /// Things in this list will be instantly splatted. Flyman weakness is handled in the flyman species weakness proc. + var/static/list/splattable + +/obj/item/melee/flyswatter/Initialize(mapload) + . = ..() + if (isnull(splattable)) + splattable = typecacheof(list( + /mob/living/basic/ant, + /mob/living/basic/butterfly, + /mob/living/basic/cockroach, + /mob/living/basic/cockroach/bloodroach, + /mob/living/basic/spider/growing/spiderling, + /mob/living/basic/bee, + /obj/effect/decal/cleanable/ants, + /obj/item/queen_bee, + )) + AddElement(/datum/element/bane, mob_biotypes = MOB_BUG, target_type = /mob/living/basic, damage_multiplier = 0, added_damage = 24, requires_combat_mode = FALSE) + +/obj/item/melee/flyswatter/afterattack(atom/target, mob/user, list/modifiers, list/attack_modifiers) + if(is_type_in_typecache(target, splattable)) + to_chat(user, span_warning("You easily splat [target].")) + if(QDELETED(target)) + return + if(isliving(target)) + new /obj/effect/decal/cleanable/insectguts(target.drop_location()) + var/mob/living/bug = target + bug.investigate_log("has been splatted by a flyswatter.", INVESTIGATE_DEATHS) + bug.gib(DROP_ALL_REMAINS) + else + qdel(target) + +/obj/item/proc/can_trigger_gun(mob/living/user, akimbo_usage) + if(!user.can_use_guns(src)) + return FALSE + return TRUE + +/obj/item/gohei + name = "gohei" + desc = "A wooden stick with white streamers at the end. Originally used by shrine maidens to purify things. Now used by the station's valued weeaboos." + resistance_flags = FLAMMABLE + force = 5 + throwforce = 5 + hitsound = SFX_SWING_HIT + attack_verb_continuous = list("whacks", "thwacks", "wallops", "socks") + attack_verb_simple = list("whack", "thwack", "wallop", "sock") + icon = 'icons/obj/weapons/club.dmi' + icon_state = "gohei" + inhand_icon_state = "gohei" + icon_angle = -65 + lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' + +/obj/item/melee/moonlight_greatsword + name = "moonlight greatsword" + desc = "Don't tell anyone you put any points into dex, though." + icon = 'icons/obj/weapons/sword.dmi' + icon_state = "swordon" + inhand_icon_state = "swordon" + worn_icon_state = "swordon" + icon_angle = -45 + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK|ITEM_SLOT_BELT + block_chance = 20 + block_sound = 'sound/items/weapons/parry.ogg' + sharpness = SHARP_EDGED + force = 14 + throwforce = 12 + hitsound = 'sound/items/weapons/bladeslice.ogg' + attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts") + attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut") + var/list/alt_continuous = list("stabs", "pierces", "impales") + var/list/alt_simple = list("stab", "pierce", "impale") + +/obj/item/melee/moonlight_greatsword/Initialize(mapload) + . = ..() + alt_continuous = string_list(alt_continuous) + alt_simple = string_list(alt_simple) + AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple) diff --git a/code/game/objects/items/powerfist.dm b/code/game/objects/items/weaponry/melee/powerfist.dm similarity index 100% rename from code/game/objects/items/powerfist.dm rename to code/game/objects/items/weaponry/melee/powerfist.dm diff --git a/code/game/objects/items/weaponry/melee/sabre.dm b/code/game/objects/items/weaponry/melee/sabre.dm new file mode 100644 index 00000000000..770f5b53a9c --- /dev/null +++ b/code/game/objects/items/weaponry/melee/sabre.dm @@ -0,0 +1,156 @@ +/obj/item/melee/sabre + name = "officer's sabre" + desc = "An elegant weapon, its monomolecular edge is capable of cutting through flesh and bone with ease." + icon = 'icons/obj/weapons/sword.dmi' + icon_state = "sabre" + inhand_icon_state = "sabre" + icon_angle = -45 + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + obj_flags = CONDUCTS_ELECTRICITY | UNIQUE_RENAME + force = 20 + throwforce = 10 + demolition_mod = 0.75 //but not metal + w_class = WEIGHT_CLASS_BULKY + block_chance = 50 + armour_penetration = 75 + sharpness = SHARP_EDGED + attack_verb_continuous = list("slashes", "cuts") + attack_verb_simple = list("slash", "cut") + block_sound = 'sound/items/weapons/parry.ogg' + hitsound = 'sound/items/weapons/rapierhit.ogg' + custom_materials = list(/datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT) + wound_bonus = 10 + exposed_wound_bonus = 25 + +/obj/item/melee/sabre/Initialize(mapload) + . = ..() + AddElement(/datum/element/cuffable_item) //closed sword guard + AddComponent(/datum/component/jousting) + //fast and effective, but as a sword, it might damage the results. + AddComponent(/datum/component/butchering, \ + speed = 3 SECONDS, \ + effectiveness = 95, \ + bonus_modifier = 5, \ + ) + // The weight of authority comes down on the tider's crimes. + AddElement(/datum/element/bane, target_type = /mob/living/carbon/human, damage_multiplier = 0.35) + RegisterSignal(src, COMSIG_OBJECT_PRE_BANING, PROC_REF(attempt_bane)) + RegisterSignal(src, COMSIG_OBJECT_ON_BANING, PROC_REF(bane_effects)) + +/** + * If the target reeks of maintenance, the blade can tear through their body with a total of 20 damage. + */ +/obj/item/melee/sabre/proc/attempt_bane(element_owner, mob/living/carbon/criminal) + SIGNAL_HANDLER + var/obj/item/organ/liver/liver = criminal.get_organ_slot(ORGAN_SLOT_LIVER) + if(isnull(liver) || !HAS_TRAIT(liver, TRAIT_MAINTENANCE_METABOLISM)) + return COMPONENT_CANCEL_BANING + +/** + * Assistants should fear this weapon. + */ +/obj/item/melee/sabre/proc/bane_effects(element_owner, mob/living/carbon/human/baned_target) + SIGNAL_HANDLER + baned_target.visible_message( + span_warning("[src] tears through [baned_target] with unnatural ease!"), + span_userdanger("As [src] tears into your body, you feel the weight of authority collapse into your wounds!"), + ) + INVOKE_ASYNC(baned_target, TYPE_PROC_REF(/mob/living/carbon/human, emote), "scream") + +/obj/item/melee/sabre/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) + if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK || attack_type == OVERWHELMING_ATTACK) + final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword. Or a road roller, if one happened to hit you. + return ..() + +/obj/item/melee/sabre/on_exit_storage(datum/storage/container) + playsound(container.parent, 'sound/items/unsheath.ogg', 25, TRUE) + +/obj/item/melee/sabre/on_enter_storage(datum/storage/container) + playsound(container.parent, 'sound/items/sheath.ogg', 25, TRUE) + +/obj/item/melee/sabre/suicide_act(mob/living/user) + user.visible_message(span_suicide("[user] is trying to cut off all [user.p_their()] limbs with [src]! it looks like [user.p_theyre()] trying to commit suicide!")) + var/i = 0 + ADD_TRAIT(src, TRAIT_NODROP, SABRE_SUICIDE_TRAIT) + if(iscarbon(user)) + var/mob/living/carbon/Cuser = user + var/obj/item/bodypart/holding_bodypart = Cuser.get_holding_bodypart_of_item(src) + var/list/limbs_to_dismember + var/list/arms = list() + var/list/legs = list() + var/obj/item/bodypart/bodypart + + for(bodypart in Cuser.bodyparts) + if(bodypart == holding_bodypart) + continue + if(bodypart.body_part & ARMS) + arms += bodypart + else if (bodypart.body_part & LEGS) + legs += bodypart + + limbs_to_dismember = arms + legs + if(holding_bodypart) + limbs_to_dismember += holding_bodypart + + var/speedbase = abs((4 SECONDS) / limbs_to_dismember.len) + for(bodypart in limbs_to_dismember) + i++ + addtimer(CALLBACK(src, PROC_REF(suicide_dismember), user, bodypart), speedbase * i) + addtimer(CALLBACK(src, PROC_REF(manual_suicide), user), (5 SECONDS) * i) + return MANUAL_SUICIDE + +/obj/item/melee/sabre/proc/suicide_dismember(mob/living/user, obj/item/bodypart/affecting) + if(!QDELETED(affecting) && !(affecting.bodypart_flags & BODYPART_UNREMOVABLE) && affecting.owner == user && !QDELETED(user)) + playsound(user, hitsound, 25, TRUE) + affecting.dismember(BRUTE) + user.adjust_brute_loss(force * 1.25) + +/obj/item/melee/sabre/proc/manual_suicide(mob/living/user, originally_nodropped) + if(!QDELETED(user)) + user.adjust_brute_loss(200) + user.death(FALSE) + REMOVE_TRAIT(src, TRAIT_NODROP, SABRE_SUICIDE_TRAIT) + + +/obj/item/melee/parsnip_sabre + name = "parsnip sabre" + desc = "A weird, yet elegant weapon. Surprisingly sharp for something made from a parsnip." + icon = 'icons/obj/weapons/sword.dmi' + icon_state = "parsnip_sabre" + inhand_icon_state = "parsnip_sabre" + icon_angle = -45 + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + force = 15 + throwforce = 10 + demolition_mod = 0.3 + w_class = WEIGHT_CLASS_BULKY + block_chance = 40 + armour_penetration = 40 + sharpness = SHARP_EDGED + attack_verb_continuous = list("slashes", "cuts") + attack_verb_simple = list("slash", "cut") + block_sound = 'sound/items/weapons/parry.ogg' + hitsound = 'sound/items/weapons/rapierhit.ogg' + custom_materials = null + wound_bonus = 5 + exposed_wound_bonus = 15 + +/obj/item/melee/parsnip_sabre/Initialize(mapload) + . = ..() + AddElement(/datum/element/cuffable_item) //closed sword guard + AddComponent(/datum/component/jousting) + +/obj/item/melee/parsnip_sabre/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) + if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK || attack_type == OVERWHELMING_ATTACK) + final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword. Or a road roller, if one happened to hit you. + return ..() + +/obj/item/melee/parsnip_sabre/on_exit_storage(datum/storage/container) + . = ..() + playsound(container.parent, 'sound/items/unsheath.ogg', 25, TRUE) + +/obj/item/melee/parsnip_sabre/on_enter_storage(datum/storage/container) + . = ..() + playsound(container.parent, 'sound/items/sheath.ogg', 25, TRUE) diff --git a/code/game/objects/items/soulscythe.dm b/code/game/objects/items/weaponry/melee/soulscythe.dm similarity index 100% rename from code/game/objects/items/soulscythe.dm rename to code/game/objects/items/weaponry/melee/soulscythe.dm diff --git a/code/game/objects/items/spear.dm b/code/game/objects/items/weaponry/melee/spear.dm similarity index 100% rename from code/game/objects/items/spear.dm rename to code/game/objects/items/weaponry/melee/spear.dm diff --git a/code/game/objects/items/weaponry/melee/supermatter_sword.dm b/code/game/objects/items/weaponry/melee/supermatter_sword.dm new file mode 100644 index 00000000000..cbfff58e9cd --- /dev/null +++ b/code/game/objects/items/weaponry/melee/supermatter_sword.dm @@ -0,0 +1,115 @@ +/obj/item/melee/supermatter_sword + name = "supermatter sword" + desc = "In a station full of bad ideas, this might just be the worst." + icon = 'icons/obj/weapons/sword.dmi' + icon_state = "supermatter_sword_balanced" + inhand_icon_state = "supermatter_sword" + icon_angle = -90 + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + slot_flags = null + w_class = WEIGHT_CLASS_BULKY + force = 0.001 + armour_penetration = 1000 + force_string = "INFINITE" + item_flags = NEEDS_PERMIT|NO_BLOOD_ON_ITEM + custom_materials = list(/datum/material/adamantine = SHEET_MATERIAL_AMOUNT * 20, /datum/material/iron = SHEET_MATERIAL_AMOUNT) + var/obj/machinery/power/supermatter_crystal/shard + var/balanced = 1 + +/obj/item/melee/supermatter_sword/Initialize(mapload) + . = ..() + shard = new /obj/machinery/power/supermatter_crystal(src) + qdel(shard.countdown) + shard.countdown = null + START_PROCESSING(SSobj, src) + visible_message(span_warning("[src] appears, balanced ever so perfectly on its hilt. This isn't ominous at all.")) + RegisterSignal(src, COMSIG_ATOM_PRE_BULLET_ACT, PROC_REF(eat_bullets)) + +/obj/item/melee/supermatter_sword/process() + if(balanced || throwing || ismob(src.loc) || isnull(src.loc)) + return + if(!isturf(src.loc)) + var/atom/target = src.loc + forceMove(target.loc) + consume_everything(target) + else + var/turf/turf = get_turf(src) + if(!isspaceturf(turf)) + consume_turf(turf) + +/obj/item/melee/supermatter_sword/pre_attack(atom/target, mob/living/user, list/modifiers, list/attack_modifiers) + . = ..() + if(.) + return . + + if(target == user) + user.dropItemToGround(src, TRUE) + else + user.do_attack_animation(target) + consume_everything(target) + return TRUE + +/obj/item/melee/supermatter_sword/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) + ..() + if(ismob(hit_atom)) + var/mob/mob = hit_atom + if(src.loc == mob) + mob.dropItemToGround(src, TRUE) + consume_everything(hit_atom) + +/obj/item/melee/supermatter_sword/pickup(user) + ..() + balanced = 0 + icon_state = "supermatter_sword" + icon_angle = -45 + +/obj/item/melee/supermatter_sword/ex_act(severity, target) + visible_message( + span_danger("The blast wave smacks into [src] and rapidly flashes to ash."), + span_hear("You hear a loud crack as you are washed with a wave of heat.") + ) + consume_everything() + return TRUE + +/obj/item/melee/supermatter_sword/acid_act() + visible_message(span_danger("The acid smacks into [src] and rapidly flashes to ash."),\ + span_hear("You hear a loud crack as you are washed with a wave of heat.")) + consume_everything() + return TRUE + +/obj/item/melee/supermatter_sword/proc/eat_bullets(datum/source, obj/projectile/hitting_projectile) + SIGNAL_HANDLER + + visible_message( + span_danger("[hitting_projectile] smacks into [source] and rapidly flashes to ash."), + null, + span_hear("You hear a loud crack as you are washed with a wave of heat."), + ) + consume_everything(hitting_projectile) + return COMPONENT_BULLET_BLOCKED + +/obj/item/melee/supermatter_sword/suicide_act(mob/living/user) + user.visible_message(span_suicide("[user] touches [src]'s blade. It looks like [user.p_theyre()] tired of waiting for the radiation to kill [user.p_them()]!")) + user.dropItemToGround(src, TRUE) + shard.Bumped(user) + +/obj/item/melee/supermatter_sword/proc/consume_everything(target) + if(isnull(target)) + shard.Bump(target) + else if(!isturf(target)) + shard.Bumped(target) + else + consume_turf(target) + +/obj/item/melee/supermatter_sword/proc/consume_turf(turf/turf) + var/oldtype = turf.type + var/turf/newT = turf.ScrapeAway(flags = CHANGETURF_INHERIT_AIR) + if(newT.type == oldtype) + return + playsound(turf, 'sound/effects/supermatter.ogg', 50, TRUE) + turf.visible_message( + span_danger("[turf] smacks into [src] and rapidly flashes to ash."), + span_hear("You hear a loud crack as you are washed with a wave of heat."), + ) + shard.Bump(turf) diff --git a/code/game/objects/items/flamethrower.dm b/code/game/objects/items/weaponry/ranged/flamethrower.dm similarity index 100% rename from code/game/objects/items/flamethrower.dm rename to code/game/objects/items/weaponry/ranged/flamethrower.dm diff --git a/code/game/objects/items/pneumatic_cannon.dm b/code/game/objects/items/weaponry/ranged/pneumatic_cannon.dm similarity index 100% rename from code/game/objects/items/pneumatic_cannon.dm rename to code/game/objects/items/weaponry/ranged/pneumatic_cannon.dm diff --git a/code/game/objects/items/weaponry/ranged/throwing_star.dm b/code/game/objects/items/weaponry/ranged/throwing_star.dm new file mode 100644 index 00000000000..ec86607ab00 --- /dev/null +++ b/code/game/objects/items/weaponry/ranged/throwing_star.dm @@ -0,0 +1,51 @@ +/obj/item/throwing_star + name = "throwing star" + desc = "An ancient weapon still used to this day, due to its ease of lodging itself into its victim's body parts." + icon = 'icons/obj/weapons/thrown.dmi' + icon_state = "throwingstar" + inhand_icon_state = "eshield" + lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi' + force = 2 + throwforce = 10 //10 + 2 (WEIGHT_CLASS_SMALL) * 4 (EMBEDDED_IMPACT_PAIN_MULTIPLIER) = 18 damage on hit due to guaranteed embedding + throw_speed = 4 + embed_type = /datum/embedding/throwing_star + armour_penetration = 40 + mob_throw_hit_sound = 'sound/items/weapons/pierce.ogg' + hitsound = 'sound/items/weapons/bladeslice.ogg' + + w_class = WEIGHT_CLASS_SMALL + sharpness = SHARP_POINTY + custom_materials = list(/datum/material/iron= SMALL_MATERIAL_AMOUNT * 5, /datum/material/glass= SMALL_MATERIAL_AMOUNT * 5) + resistance_flags = FIRE_PROOF + +/datum/embedding/throwing_star + pain_mult = 4 + embed_chance = 100 + fall_chance = 0 + +/obj/item/throwing_star/stamina + name = "shock throwing star" + desc = "An aerodynamic disc designed to cause excruciating pain when stuck inside fleeing targets, hopefully without causing fatal harm." + throwforce = 5 + embed_type = /datum/embedding/throwing_star/stamina + +/datum/embedding/throwing_star/stamina + pain_mult = 5 + jostle_chance = 10 + pain_stam_pct = 0.8 + jostle_pain_mult = 3 + +/obj/item/throwing_star/toy + name = "toy throwing star" + desc = "An aerodynamic disc strapped with adhesive for sticking to people, good for playing pranks and getting yourself killed by security." + sharpness = NONE + force = 0 + throwforce = 0 + embed_type = /datum/embedding/throwing_star/toy + +/datum/embedding/throwing_star/toy + pain_mult = 0 + jostle_pain_mult = 0 + +//Ninja throwing stars are located in the ninja mod modules diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/weaponry/shields.dm similarity index 100% rename from code/game/objects/items/shields.dm rename to code/game/objects/items/weaponry/shields.dm diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index 0b8f2259855..0d587f2343d 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -614,11 +614,28 @@ /obj/item/soulstone/anybody required_role = null +/obj/item/ectoplasm + name = "ectoplasm" + desc = "Spooky." + gender = PLURAL + icon = 'icons/effects/magic.dmi' + icon_state = "ectoplasm" + +/obj/item/ectoplasm/grind_results() + return list(/datum/reagent/hauntium = 25) + +/obj/item/ectoplasm/suicide_act(mob/living/user) + user.visible_message(span_suicide("[user] is inhaling [src]! It looks like [user.p_theyre()] trying to visit the astral plane!")) + return OXYLOSS + /obj/item/soulstone/mystic icon_state = "mystic_soulstone" theme = THEME_WIZARD required_role = /datum/antagonist/wizard +/obj/item/ectoplasm/mystic + icon_state = "mysticplasm" + /obj/item/soulstone/anybody/revolver one_use = TRUE grab_sleeping = FALSE @@ -627,6 +644,10 @@ icon_state = "purified_soulstone" theme = THEME_HOLY +/obj/item/ectoplasm/angelic + icon = 'icons/effects/magic.dmi' + icon_state = "angelplasm" + /obj/item/soulstone/anybody/chaplain name = "mysterious old shard" one_use = TRUE diff --git a/code/game/objects/items/kitchen.dm b/code/modules/food_and_drinks/equipment/kitchen_utensils.dm similarity index 78% rename from code/game/objects/items/kitchen.dm rename to code/modules/food_and_drinks/equipment/kitchen_utensils.dm index 95412ef4088..ca5a338fa4c 100644 --- a/code/game/objects/items/kitchen.dm +++ b/code/modules/food_and_drinks/equipment/kitchen_utensils.dm @@ -4,6 +4,7 @@ * Kitchen knives * Rolling Pins * Plastic Utensils + * Tongs */ #define PLASTIC_BREAK_PROBABILITY 25 @@ -354,4 +355,107 @@ . = ..() create_reagents(SOUP_SERVING_SIZE + 5, INJECTABLE|OPENCONTAINER) +/// Tongs, let you pick up and feed people food from further away. +/obj/item/kitchen/tongs + name = "tongs" + desc = "So you never have to touch anything with your dirty, unwashed hands." + reach = 2 + icon_state = "tongs" + base_icon_state = "tongs" + inhand_icon_state = "fork" // close enough + icon_angle = -45 + attack_verb_continuous = list("pinches", "tongs", "nips") + attack_verb_simple = list("pinch", "tong", "nip") + /// What are we holding in our tongs? + var/obj/item/tonged + /// Sound to play when we click our tongs together + var/clack_sound = 'sound/items/handling/component_drop.ogg' + /// Time to wait between clacking sounds + var/clack_delay = 2 SECONDS + /// Have we clacked recently? + COOLDOWN_DECLARE(clack_cooldown) + +/obj/item/kitchen/tongs/Destroy(force) + QDEL_NULL(tonged) + return ..() + +/obj/item/kitchen/tongs/examine(mob/user) + . = ..() + if (!isnull(tonged)) + . += span_notice("It is holding [tonged].") + +/obj/item/kitchen/tongs/dropped(mob/user, silent) + . = ..() + drop_tonged() + +/obj/item/kitchen/tongs/attack_self(mob/user, modifiers) + . = ..() + if(.) + return TRUE + if (!isnull(tonged)) + drop_tonged() + return TRUE + if (!COOLDOWN_FINISHED(src, clack_cooldown)) + return TRUE + user.visible_message(span_notice("[user] clacks [user.p_their()] [name] together like a crab. Click clack!")) + click_clack() + return TRUE + +/// Release the food we are holding +/obj/item/kitchen/tongs/proc/drop_tonged() + if (isnull(tonged)) + return + visible_message(span_notice("[tonged] falls to the ground!")) + var/turf/location = drop_location() + tonged.forceMove(location) + tonged.do_drop_animation(location) + +/// Play a clacking sound and appear closed, then open again +/obj/item/kitchen/tongs/proc/click_clack() + COOLDOWN_START(src, clack_cooldown, clack_delay) + playsound(src, clack_sound, vol = 100, vary = FALSE) + icon_state = "[base_icon_state]_closed" + var/delay = min(0.5 SECONDS, clack_delay / 2) // Just in case someone's been fucking with the cooldown + addtimer(CALLBACK(src, PROC_REF(clack)), delay, TIMER_DELETE_ME) + +/// Plays a clacking sound and appear open +/obj/item/kitchen/tongs/proc/clack() + playsound(src, clack_sound, vol = 100, vary = FALSE) + icon_state = base_icon_state + +/obj/item/kitchen/tongs/Exited(atom/movable/leaving, direction) + . = ..() + if (leaving != tonged) + return + tonged = null + update_appearance(UPDATE_ICON) + +/obj/item/kitchen/tongs/pre_attack(obj/item/attacked, mob/living/user, list/modifiers, list/attack_modifiers) + if (!isnull(tonged) && tonged.force <= 0) // prevents tongs from giving food-weapons extra range + attacked.attackby(tonged, user) + return TRUE + if (isliving(attacked)) + if (COOLDOWN_FINISHED(src, clack_cooldown)) + click_clack() + return ..() + if (!IS_EDIBLE(attacked) || attacked.w_class > WEIGHT_CLASS_NORMAL || !isnull(tonged)) + return ..() + tonged = attacked + attacked.do_pickup_animation(src) + attacked.forceMove(src) + update_appearance(UPDATE_ICON) + return TRUE + +/obj/item/kitchen/tongs/update_overlays() + . = ..() + if (isnull(tonged)) + return + var/mutable_appearance/held_food = new /mutable_appearance(tonged.appearance) + held_food.layer = layer + held_food.plane = plane + held_food.transform = held_food.transform.Scale(0.7, 0.7) + held_food.pixel_w = 6 + held_food.pixel_z = 6 + . += held_food + #undef PLASTIC_BREAK_PROBABILITY diff --git a/code/modules/food_and_drinks/plate.dm b/code/modules/food_and_drinks/equipment/plate.dm similarity index 100% rename from code/modules/food_and_drinks/plate.dm rename to code/modules/food_and_drinks/equipment/plate.dm diff --git a/code/game/objects/items/surgery_tray.dm b/code/modules/surgery/surgery_tray.dm similarity index 100% rename from code/game/objects/items/surgery_tray.dm rename to code/modules/surgery/surgery_tray.dm diff --git a/icons/map_icons/items/_item.dmi b/icons/map_icons/items/_item.dmi index f4aec3e61db93487fd3f8e6ec5568700e7c39785..f1e1a9f5f02887f6994272774e0a615b77696f67 100644 GIT binary patch delta 19396 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

YQN29A*gC4!J&$^EcZ-{@( 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