Adds The Liberator: Doorhickey Corporation's brand new gun (#89495)

## About The Pull Request

The Liberator is a brand-new 3D printable gun, holding a single 10mm
shot in it's barrel. It has a damage multiplier of x0.5, dealing 20
damage per shot. However, shooting someone point blank gives it an x2
modifier, and shooting someone while holding them at gunpoint adds
additional x1.5 multiplier.


https://github.com/user-attachments/assets/b27adf1a-5b02-4817-90ad-2ba922cc6bf9

They can be printed for dirt cheap 3 sheets of plastic and 2 sheets of
iron from an autolathe. However, as a downside, whenever you're firing a
liberator you're risking it blowing up in your hands, potentially
wounding you! Reloading is also a bit tricky, since you need to unscrew
the barrel before placing the bullet in, and screwing it back on.

In order to print them, you'll need to find a (mildly rare) disk in
maintenance and upload the design to an autolathe. You also can find a
whole gun, albeit at a lower chance.

## Why It's Good For The Game

Funny gun that can be used for some gimmicks, not too overpowered but a
nifty tool for assistants. Can be countered by a well timed baton, or a
shove if your opponent is next to a wall.

## Changelog
🆑
add: Added a new 3D printable gun "The Liberator", you can find the gun
itself or its design disc in maintenance tunnels.
/🆑
This commit is contained in:
SmArtKar
2025-02-16 23:46:29 +00:00
committed by GitHub
parent f1ee833823
commit 2b44bffdeb
10 changed files with 231 additions and 59 deletions
@@ -262,6 +262,7 @@ GLOBAL_LIST_INIT(uncommon_loot, list(//uncommon: useful items
/obj/item/computer_disk/maintenance/modsuit_control = 1,
/obj/item/computer_disk/maintenance/spectre_meter = 1,
/obj/item/computer_disk/maintenance/arcade = 1,
/obj/item/disk/design_disk/liberator = 1,
/obj/item/computer_disk/maintenance/theme = 3,
) = 4,
@@ -309,6 +310,7 @@ GLOBAL_LIST_INIT(rarity_loot, list(//rare: really good items
/obj/item/weldingtool/hugetank = 1,
/obj/item/fishing_rod/telescopic/master = 1,
/obj/item/spess_knife = 1,
/obj/item/gun/ballistic/automatic/pistol/doorhickey = 1,
) = 1,
list(//equipment
+7
View File
@@ -20,6 +20,13 @@
icon_state = "s-casing"
embed_type = null
/obj/item/shrapnel/plastic
name = "plastic shard"
custom_materials = list(/datum/material/plastic = SMALL_MATERIAL_AMOUNT * 0.5)
icon_state = "titaniummedium"
sharpness = SHARP_EDGED
embed_type = /datum/embedding/shrapnel
/obj/projectile/bullet/shrapnel
name = "flying shrapnel shard"
damage = 14
@@ -111,3 +111,12 @@
price_max = CARGO_CRATE_VALUE * 5
stock_max = 1
availability_prob = 25
/datum/market_item/weapon/liberator
name = "illegal 3D printer designs"
desc = "Designs for a dirt cheap 3D printable gun, well known for exploding in unfortunate assistants' hands."
item = /obj/item/disk/design_disk/liberator
price_min = CARGO_CRATE_VALUE * 2
price_max = CARGO_CRATE_VALUE * 5
stock_max = 1
availability_prob = 35
@@ -0,0 +1,5 @@
/obj/item/ammo_box/magazine/internal/doorhickey
name = "single-shot barrel"
ammo_type = /obj/item/ammo_casing/c10mm
caliber = CALIBER_10MM
max_ammo = 1
+8 -5
View File
@@ -198,7 +198,7 @@
shake_camera(user, recoil + 1, recoil)
fire_sounds()
if(suppressed || !message)
return
return FALSE
if(tk_firing(user))
visible_message(
span_danger("[src] fires itself[pointblank ? " point blank at [pbtarget]!" : "!"]"),
@@ -226,6 +226,7 @@
if(chambered?.integrity_damage)
take_damage(chambered.integrity_damage, sound_effect = FALSE)
return TRUE
/obj/item/gun/atom_destruction(damage_flag)
if(!isliving(loc))
@@ -496,10 +497,12 @@
else
shoot_with_empty_chamber(user)
return
process_chamber()
update_appearance()
semicd = TRUE
addtimer(CALLBACK(src, PROC_REF(reset_semicd)), modified_delay)
// If gun gets destroyed as a result of firing
if (!QDELETED(src))
process_chamber()
update_appearance()
semicd = TRUE
addtimer(CALLBACK(src, PROC_REF(reset_semicd)), modified_delay)
if(user)
user.update_held_items()
+76 -54
View File
@@ -399,58 +399,74 @@
/obj/item/gun/ballistic/can_shoot()
return chambered?.loaded_projectile
/obj/item/gun/ballistic/attackby(obj/item/A, mob/user, params)
/obj/item/gun/ballistic/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
. = ..()
if (.)
return
if (!internal_magazine && istype(A, /obj/item/ammo_box/magazine))
var/obj/item/ammo_box/magazine/AM = A
if (!internal_magazine && istype(tool, /obj/item/ammo_box/magazine))
if (!magazine)
insert_magazine(user, AM)
else
if (tac_reloads)
eject_magazine(user, FALSE, AM)
else
balloon_alert(user, "already loaded!")
return
if (isammocasing(A) || istype(A, /obj/item/ammo_box))
insert_magazine(user, tool)
return ITEM_INTERACT_SUCCESS
if (tac_reloads)
eject_magazine(user, FALSE, tool)
return ITEM_INTERACT_SUCCESS
balloon_alert(user, "already loaded!")
return ITEM_INTERACT_FAILURE
if (isammocasing(tool) || istype(tool, /obj/item/ammo_box))
if (must_hold_to_load && !check_if_held(user))
return
return NONE
if (bolt_type == BOLT_TYPE_NO_BOLT || internal_magazine)
if (chambered && !chambered.loaded_projectile)
chambered.forceMove(drop_location())
if(chambered != magazine?.stored_ammo[1])
magazine.stored_ammo -= chambered
chambered = null
var/num_loaded = magazine?.attackby(A, user, params, TRUE)
if (num_loaded)
balloon_alert(user, "[num_loaded] [cartridge_wording]\s loaded")
playsound(src, load_sound, load_sound_volume, load_sound_vary)
if (chambered == null && bolt_type == BOLT_TYPE_NO_BOLT)
chamber_round()
A.update_appearance()
update_appearance()
return
if(istype(A, /obj/item/suppressor))
var/obj/item/suppressor/S = A
if (load_gun(tool, user))
return ITEM_INTERACT_SUCCESS
return ITEM_INTERACT_FAILURE
if(istype(tool, /obj/item/suppressor))
if(!can_suppress)
balloon_alert(user, "[S.name] doesn't fit!")
return
balloon_alert(user, "[tool.name] doesn't fit!")
return ITEM_INTERACT_FAILURE
if(!user.is_holding(src))
balloon_alert(user, "not in hand!")
return
return ITEM_INTERACT_FAILURE
if(suppressed)
balloon_alert(user, "already has a supressor!")
return
if(user.transferItemToLoc(A, src))
balloon_alert(user, "[S.name] attached")
install_suppressor(A)
return
if (can_be_sawn_off)
if (sawoff(user, A))
return
return ITEM_INTERACT_FAILURE
return FALSE
if(!user.transferItemToLoc(tool, src))
balloon_alert(user, "cannot attach!")
return ITEM_INTERACT_FAILURE
balloon_alert(user, "[tool.name] attached")
install_suppressor(tool)
return ITEM_INTERACT_SUCCESS
if (can_be_sawn_off && sawoff(user, tool))
return ITEM_INTERACT_SUCCESS
/obj/item/gun/ballistic/proc/load_gun(obj/item/ammo, mob/living/user)
if (chambered && !chambered.loaded_projectile)
chambered.forceMove(drop_location())
if(chambered != magazine?.stored_ammo[1])
magazine.stored_ammo -= chambered
chambered = null
var/num_loaded = magazine?.attackby(ammo, user, silent = TRUE)
if (!num_loaded)
return FALSE
balloon_alert(user, "[num_loaded] [cartridge_wording]\s loaded")
playsound(src, load_sound, load_sound_volume, load_sound_vary)
if (chambered == null && bolt_type == BOLT_TYPE_NO_BOLT)
chamber_round()
ammo.update_appearance()
update_appearance()
return TRUE
/obj/item/gun/ballistic/proc/check_if_held(mob/user)
if(src != user.get_inactive_held_item())
@@ -540,20 +556,7 @@
eject_magazine(user)
return
if(bolt_type == BOLT_TYPE_NO_BOLT)
var/num_unloaded = 0
for(var/obj/item/ammo_casing/casing as anything in get_ammo_list(FALSE))
casing.forceMove(drop_location())
casing.bounce_away(FALSE, NONE)
num_unloaded++
var/turf/T = get_turf(drop_location())
if(T && is_station_level(T.z))
SSblackbox.record_feedback("tally", "station_mess_created", 1, casing.name)
if (num_unloaded)
balloon_alert(user, "[num_unloaded] [cartridge_wording]\s unloaded")
playsound(user, eject_sound, eject_sound_volume, eject_sound_vary)
update_appearance()
else
balloon_alert(user, "it's empty!")
unload_ammo(user)
return
if(bolt_type == BOLT_TYPE_LOCKING && bolt_locked)
drop_bolt(user)
@@ -564,6 +567,25 @@
rack(user)
return
/obj/item/gun/ballistic/proc/unload_ammo(mob/living/user, forced = FALSE)
var/num_unloaded = 0
var/turf/drop_turf = get_turf(drop_location())
for(var/obj/item/ammo_casing/casing as anything in get_ammo_list(FALSE))
casing.forceMove(drop_location())
casing.bounce_away(FALSE, NONE)
num_unloaded++
if(drop_turf && is_station_level(drop_turf.z))
SSblackbox.record_feedback("tally", "station_mess_created", 1, casing.name)
if (!num_unloaded)
if (!forced)
balloon_alert(user, "it's empty!")
return
if (!forced)
balloon_alert(user, "[num_unloaded] [cartridge_wording]\s unloaded")
playsound(user, eject_sound, eject_sound_volume, eject_sound_vary)
update_appearance()
/obj/item/gun/ballistic/examine(mob/user)
. = ..()
@@ -212,3 +212,117 @@
else
to_chat(user, span_notice("..and falls into view. Whew, that was a close one."))
user.dropItemToGround(src)
#define DOORHICKEY_GUN_MIN_DAMAGE 70
#define DOORHICKEY_GUN_MAX_DAMAGE 140
/obj/item/gun/ballistic/automatic/pistol/doorhickey
name = "\improper Liberator"
desc = "A poorly made 3D printed \"gun\", only capable of firing a single shot. Well-known throughout the Spinward Sector \
after an incident where 3 assistants were killed by shrapnel from such a device exploding while attempting to shoot a mouse."
icon_state = "doorhickey"
custom_materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT * 2)
bolt_type = BOLT_TYPE_NO_BOLT
internal_magazine = TRUE
casing_ejector = FALSE
force = 10
max_integrity = 100
accepted_magazine_type = /obj/item/ammo_box/magazine/internal/doorhickey
can_suppress = FALSE
semi_auto = FALSE
show_bolt_icon = FALSE
projectile_damage_multiplier = 0.5
spread = 10
/obj/item/gun/ballistic/automatic/pistol/doorhickey/unload_ammo(mob/living/user, forced = FALSE)
if (forced)
return ..()
balloon_alert(user, "unscrewing the barrel...")
playsound(user, 'sound/items/tools/screwdriver_operating.ogg', 75, FALSE, MEDIUM_RANGE_SOUND_EXTRARANGE)
if (!do_after(user, 2 SECONDS, src))
balloon_alert(user, "interrupted!")
return
. = ..()
/obj/item/gun/ballistic/automatic/pistol/doorhickey/load_gun(obj/item/ammo, mob/living/user)
. = ..()
if (!.)
return
balloon_alert(user, "screwing the barrel on...")
playsound(user, 'sound/items/tools/screwdriver_operating.ogg', 75, FALSE, MEDIUM_RANGE_SOUND_EXTRARANGE)
if (do_after(user, 2 SECONDS, src))
return TRUE
balloon_alert(user, "interrupted!")
unload_ammo(user, forced = TRUE)
return FALSE
/obj/item/gun/ballistic/automatic/pistol/doorhickey/fire_gun(atom/target, mob/living/user, flag, params)
var/dmg_multiplier = 1
if (get_dist(target, user) <= 1)
dmg_multiplier *= 2
if (isliving(target))
var/mob/living/victim = target
var/datum/status_effect/grouped/heldup/gunpoint = victim.has_status_effect(/datum/status_effect/grouped/heldup)
if (gunpoint)
for (var/datum/weakref/pointer_ref as anything in gunpoint.sources)
if (pointer_ref.resolve() == user)
dmg_multiplier *= 1.5 // Caps at 60 damage
break
projectile_damage_multiplier *= dmg_multiplier
. = ..()
projectile_damage_multiplier /= dmg_multiplier
/obj/item/gun/ballistic/automatic/pistol/doorhickey/shoot_live_shot(mob/living/user, pointblank = FALSE, atom/pbtarget = null, message = TRUE)
. = ..()
if (!.)
return
var/damage_to_take = rand(DOORHICKEY_GUN_MIN_DAMAGE, DOORHICKEY_GUN_MAX_DAMAGE)
if (atom_integrity > damage_to_take)
take_damage(damage_to_take)
return
playsound(loc, SFX_SHATTER, 75, TRUE)
if (loc != user)
take_damage(damage_to_take)
return
var/shrapnel_bomb = FALSE
var/obj/item/bodypart/arm/poor_sod = user.get_active_hand()
if (prob(damage_to_take - atom_integrity) && poor_sod)
shrapnel_bomb = TRUE
user.visible_message(span_danger("[src] explodes into small pieces[shrapnel_bomb ? ", chunk of it embedding in [user]'s [user.parse_zone_with_bodypart(poor_sod.body_zone)]" : ""]!"),
span_userdanger("[src] explodes into small pieces[shrapnel_bomb ? ", chunk of it embedding in your [poor_sod]!" : ""]!"),
span_hear("You can hear sound of plastic shattering."))
if (poor_sod)
poor_sod.receive_damage((damage_to_take - atom_integrity) * 0.5, wound_bonus = -10, bare_wound_bonus = 20, sharpness = SHARP_EDGED, damage_source = src)
else
user.take_bodypart_damage((damage_to_take - atom_integrity) * 0.5, wound_bonus = -10, bare_wound_bonus = 20, sharpness = SHARP_EDGED)
if (shrapnel_bomb)
var/obj/item/shrapnel/plastic/shrapnel = new(user.loc)
if (!shrapnel.force_embed(user, poor_sod))
qdel(shrapnel)
else if(!HAS_TRAIT(user, TRAIT_ANALGESIA))
user.emote("scream")
new /obj/effect/decal/cleanable/plastic(get_turf(src))
take_damage(damage_to_take)
/obj/item/disk/design_disk/liberator
name = "illegal 3D printer design disk"
/obj/item/disk/design_disk/liberator/Initialize(mapload)
. = ..()
blueprints += new /datum/design/liberator_gun
#undef DOORHICKEY_GUN_MIN_DAMAGE
#undef DOORHICKEY_GUN_MAX_DAMAGE
@@ -560,6 +560,15 @@
build_path = /obj/item/melee/cleric_mace
category = list(RND_CATEGORY_IMPORTED)
/datum/design/liberator_gun
name = "The Liberator"
desc = "A dirt cheap 3D printed gun. Only holds one bullet, and is infamous for sometimes exploding on it's user."
id = "liberator_gun"
build_type = AUTOLATHE
materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT * 2, /datum/material/iron = SMALL_MATERIAL_AMOUNT * 15)
build_path = /obj/item/gun/ballistic/automatic/pistol/doorhickey
category = list(RND_CATEGORY_IMPORTED)
/datum/design/stun_boomerang
name = "OZtek Boomerang"
desc = "Uses reverse flow gravitodynamics to flip its personal gravity back to the thrower mid-flight. Also functions similar to a stun baton."
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 32 KiB

+1
View File
@@ -5663,6 +5663,7 @@
#include "code\modules\projectiles\boxes_magazines\internal\_internal.dm"
#include "code\modules\projectiles\boxes_magazines\internal\grenade.dm"
#include "code\modules\projectiles\boxes_magazines\internal\meathook.dm"
#include "code\modules\projectiles\boxes_magazines\internal\pistol.dm"
#include "code\modules\projectiles\boxes_magazines\internal\revolver.dm"
#include "code\modules\projectiles\boxes_magazines\internal\rifle.dm"
#include "code\modules\projectiles\boxes_magazines\internal\shotgun.dm"