Expanding the Experimental MODsuit Bepis Node with three new modules. (#75801)

## About The Pull Request
So, I've had this idea to make a contribution to the Bepis feature with
some modsuit stuff. The gimmicky stuff is ok and a good way to even out
the better content since it has game of chance design it has (you can
find those disks in space anyway so...). However, the Experimental
MODsuit node feels very underwhelming right now, compared to how big
that feature is.

This PR adds three MOD modules to the Experimental MODsuit node, plus
two more:
- Magneto Charger: While the Modsuit is activated, each step the user
takes will charge the installed power cell by a tiny bit, enough to
sustain a standard modsuit of generic slow speed with only a few, easy
modules installed. It won't work in zero G, while flying, pulled by
someone else, on a conveyor belt, riding a vehicle or crawling on the
floor, though.
- Recycler: It collects (most) garbage and casings off the ground and
recycles them into material sheets that can be dispensed on an adjacent
location or storage with with Middle Mouse Button. Doesn't clean debris,
and scuffed because most trash doesn't yield material anyway.
- - It also has two subtypes, unbound from the node: one that dispenses
riot foam darts and can be found on the black market, and another that
dispenses the more innocuous foam darts, rarely found in maints.
- Shooting Assistant: A configurable module. On Stormtrooper mode, it
will give the user a faster fire rate (the double tap trait) at the cost
of accuracy. On Sharpshooter mode, it will improve the user accuracy and
make their shots ricochet against walls at least once (if the hit atom
allows that, that is, e.g. lasers don't ricochet against iron walls), at
the cost of movement speed. Both modes also prevent the user from dual
wielding guns.

To make the Stormtrooper mode stackable with the poor aim quirk and
refrain from making a new trait for the sharpshooter mode, the gun
spread code in gun.dm has also received a little refactor and cleanup.
Also, it's been tested.

## Why It's Good For The Game
The Experimental MODsuit node is quite shabby and could use something
extra to make it more appealing to MODsuit enjoyers.

Also doubles down as a small addition to the black market and maint
loot, and code cleanup, since gun code gives off some garbled vibes.

## Changelog

🆑
add: Expanded the Experimental MODsuit Bepis node with three new
modules: Magneto Charger, Recycler and Shooting Assistant.
add: Added a Riot Foam Recycler module to the black market, as well a
more innocuous version as maint loot.
/🆑
This commit is contained in:
Ghom
2023-06-14 23:00:05 -04:00
committed by GitHub
parent ee7b79b1af
commit 2bb4dfe76c
24 changed files with 443 additions and 50 deletions
+178
View File
@@ -596,3 +596,181 @@
/obj/item/mod/module/signlang_radio/on_suit_deactivation(deleting = FALSE)
REMOVE_TRAIT(mod.wearer, TRAIT_CAN_SIGN_ON_COMMS, MOD_TRAIT)
///A module that recharges the suit by an itsy tiny bit whenever the user takes a step. Originally called "magneto module" but the videogame reference sounds cooler.
/obj/item/mod/module/joint_torsion
name = "MOD joint torsion ratchet module"
desc = "A compact, weak AC generator that charges the suit's internal cell through the power of deambulation. It doesn't work in zero G."
icon_state = "joint_torsion"
complexity = 1
incompatible_modules = list(/obj/item/mod/module/joint_torsion)
var/power_per_step = DEFAULT_CHARGE_DRAIN * 0.3
/obj/item/mod/module/joint_torsion/on_suit_activation()
if(!(mod.wearer.movement_type & (FLOATING|FLYING)))
RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
/// This way we don't even bother to call on_moved() while flying/floating
RegisterSignal(mod.wearer, COMSIG_MOVETYPE_FLAG_ENABLED, PROC_REF(on_movetype_flag_enabled))
RegisterSignal(mod.wearer, COMSIG_MOVETYPE_FLAG_DISABLED, PROC_REF(on_movetype_flag_disabled))
/obj/item/mod/module/joint_torsion/on_suit_deactivation(deleting = FALSE)
UnregisterSignal(mod.wearer, list(COMSIG_MOVABLE_MOVED, COMSIG_MOVETYPE_FLAG_ENABLED, COMSIG_MOVETYPE_FLAG_DISABLED))
/obj/item/mod/module/joint_torsion/proc/on_movetype_flag_enabled(datum/source, flag, old_state)
SIGNAL_HANDLER
if(!(old_state & (FLOATING|FLYING)) && flag & (FLOATING|FLYING))
UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
/obj/item/mod/module/joint_torsion/proc/on_movetype_flag_disabled(datum/source, flag, old_state)
SIGNAL_HANDLER
if(old_state & (FLOATING|FLYING) && !(mod.wearer.movement_type & (FLOATING|FLYING)))
RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
/obj/item/mod/module/joint_torsion/proc/on_moved(mob/living/carbon/human/wearer, atom/old_loc, movement_dir, forced)
SIGNAL_HANDLER
//Shouldn't work if the wearer isn't really walking/running around.
if(forced || wearer.throwing || wearer.body_position == LYING_DOWN || wearer.buckled || CHECK_MOVE_LOOP_FLAGS(wearer, MOVEMENT_LOOP_OUTSIDE_CONTROL))
return
mod.core.add_charge(power_per_step)
/// Module that shoves garbage inside its material container when the user crosses it, and eject the recycled material with MMB.
/obj/item/mod/module/recycler
name = "MOD recycler module"
desc = "An innovative garbage collection module that recycles gathered trash into usable material. \
Doesn't work on debris and some items. May recycle live ammunition. \
Activate on a nearby turf or storage to unload stored material."
icon_state = "recycler"
module_type = MODULE_ACTIVE
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
complexity = 2
incompatible_modules = list(/obj/item/mod/module/recycler)
overlay_state_inactive = "module_recycler"
overlay_state_active = "module_recycler"
/// A multiplier of the amount of material extracted from the item
var/efficiency = 1
/// Items that will be collected
var/list/allowed_item_types = list(
/obj/item/trash,
/obj/item/shard,
/obj/item/light,
/obj/item/broken_bottle,
/obj/item/ammo_casing,
/obj/item/cigbutt,
)
/// Materials that will be extracted.
var/list/accepted_mats = list(
/datum/material/iron,
/datum/material/glass,
/datum/material/silver,
/datum/material/plasma,
/datum/material/gold,
/datum/material/diamond,
/datum/material/plastic,
/datum/material/uranium,
/datum/material/bananium,
/datum/material/titanium,
/datum/material/bluespace,
)
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_obj_entered),
COMSIG_ATOM_INITIALIZED_ON = PROC_REF(on_atom_initialized_on),
)
var/datum/component/connect_loc_behalf/connector
/obj/item/mod/module/recycler/Initialize(mapload)
. = ..()
AddComponent(/datum/component/material_container, accepted_mats, 50 * SHEET_MATERIAL_AMOUNT, MATCONTAINER_EXAMINE|MATCONTAINER_NO_INSERT, _after_retrieve=CALLBACK(src, PROC_REF(attempt_insert_storage)))
/obj/item/mod/module/recycler/on_activation()
. = ..()
if(!.)
return
connector = AddComponent(/datum/component/connect_loc_behalf, mod.wearer, loc_connections)
RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, PROC_REF(on_wearer_moved))
/obj/item/mod/module/recycler/on_deactivation(display_message, deleting = FALSE)
. = ..()
if(!.)
return
QDEL_NULL(connector)
UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, PROC_REF(on_wearer_moved))
/obj/item/mod/module/recycler/proc/on_wearer_moved(datum/source, atom/old_loc, dir, forced)
SIGNAL_HANDLER
for(var/obj/item/item in mod.wearer.loc)
if(!is_type_in_list(item, allowed_item_types))
return
insert_trash(item)
/obj/item/mod/module/recycler/proc/on_obj_entered(atom/new_loc, atom/movable/arrived, atom/old_loc)
SIGNAL_HANDLER
if(!is_type_in_list(arrived, allowed_item_types))
return
insert_trash(arrived)
/obj/item/mod/module/recycler/proc/on_atom_initialized_on(atom/loc, atom/new_atom)
SIGNAL_HANDLER
if(!is_type_in_list(new_atom, allowed_item_types))
return
//Give the new atom the time to fully initialize and maybe live if the wearer moves away.
addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/item/mod/module/recycler, insert_trash_if_nearby), new_atom), 0.5 SECONDS)
/obj/item/mod/module/recycler/proc/insert_trash_if_nearby(atom/new_atom)
if(new_atom && mod?.wearer && new_atom.loc == mod.wearer.loc)
insert_trash(new_atom)
/obj/item/mod/module/recycler/proc/insert_trash(obj/item/item)
var/datum/component/material_container/container = GetComponent(/datum/component/material_container)
var/retrieved = container.insert_item(item, multiplier = efficiency, breakdown_flags = BREAKDOWN_FLAGS_RECYCLER)
if(retrieved == MATERIAL_INSERT_ITEM_NO_MATS) //even if it doesn't have any material to give, trash is trash.
qdel(item)
playsound(src, SFX_RUSTLE, 50, TRUE, -5)
/obj/item/mod/module/recycler/on_select_use(atom/target)
. = ..()
if(!.)
return
if(!target?.atom_storage)
target = get_turf(target)
if(!isopenturf(target) || !mod.wearer.Adjacent(target))
return FALSE
dispense(target)
/obj/item/mod/module/recycler/proc/dispense(atom/target)
var/datum/component/material_container/container = GetComponent(/datum/component/material_container)
if(container.retrieve_all(target))
balloon_alert(mod.wearer, "material dispensed")
playsound(src, 'sound/machines/microwave/microwave-end.ogg', 50, TRUE)
return
balloon_alert(mod.wearer, "not enough material")
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
/obj/item/mod/module/recycler/proc/attempt_insert_storage(obj/item/to_drop)
if(!isturf(to_drop.loc) && !to_drop.loc.atom_storage?.attempt_insert(to_drop, mod.wearer, override = TRUE))
to_drop.forceMove(to_drop.loc.drop_location())
///A black market variant of the above that dispenses riot foam dart boxes
/obj/item/mod/module/recycler/donk
name = "MOD riot foam dart recycler module"
desc = "A mod module collects and repackages fired foam darts (and garbage) into half-sized boxes of riot foam darts. \
Activate on a nearby turf or storage to unload stored ammo boxes."
icon_state = "donk_recycler"
overlay_state_inactive = "module_donk_recycler"
overlay_state_active = "module_donk_recycler"
efficiency = 0.7 // Stops getting as many riot foam darts as one consumes.
accepted_mats = list(/datum/material/iron)
///The type of ammo box that it dispenses
var/ammobox_type = /obj/item/ammo_box/foambox/riot/mini
///The cost of each dispensed ammo box
var/required_amount = SHEET_MATERIAL_AMOUNT*12.5
/obj/item/mod/module/recycler/donk/dispense(atom/target)
var/datum/component/material_container/container = GetComponent(/datum/component/material_container)
if(!container.use_amount_mat(required_amount, /datum/material/iron))
balloon_alert(mod.wearer, "not enough material")
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
return
var/obj/item/ammo_box/product = new ammobox_type(target)
attempt_insert_storage(product)
balloon_alert(mod.wearer, "ammo box dispensed.")
playsound(src, 'sound/machines/microwave/microwave-end.ogg', 50, TRUE)