Skip to content

Commit

Permalink
componentizes rechargers
Browse files Browse the repository at this point in the history
  • Loading branch information
Zonespace27 committed Dec 2, 2023
1 parent 750591c commit dc2c1d8
Show file tree
Hide file tree
Showing 15 changed files with 392 additions and 275 deletions.
3 changes: 3 additions & 0 deletions code/__DEFINES/dcs/signals/atom/signals_atom.dm
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,6 @@

/// Called when an atom has emp_act called on it, from /atom/emp_act: (severity)
#define COMSIG_ATOM_EMP_ACT "atom_emp_act"

/// Called when an atom has attack_hand called on it, from /atom/attack_hand: (mob/user)
#define COMSIG_ATOM_ATTACK_HAND "atom_attack_hand"
7 changes: 7 additions & 0 deletions code/__DEFINES/dcs/signals/atom/signals_cell.dm
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@
#define COMSIG_CELL_CHECK_CHARGE "cell_check_charge"
#define COMPONENT_CELL_CHARGE_INSUFFICIENT (1<<0)

/// (check_percent)
#define COMSIG_CELL_CHECK_CHARGE_PERCENT "cell_check_charge_percent"
#define COMPONENT_CELL_CHARGE_PERCENT_INSUFFICIENT (1<<0)

#define COMSIG_CELL_CHECK_FULL_CHARGE "cell_check_full_charge"
#define COMPONENT_CELL_CHARGE_NOT_FULL (1<<0)

#define COMSIG_CELL_TRY_INSERT_CELL "cell_try_insert_cell"
#define COMPONENT_CANCEL_CELL_INSERT (1<<0)

Expand Down
7 changes: 7 additions & 0 deletions code/__DEFINES/dcs/signals/atom/signals_obj.dm
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,10 @@
#define COSMIG_OBJ_AFTER_BUCKLE "signal_obj_after_buckle"

#define COMSIG_STRUCTURE_CRATE_SQUAD_LAUNCHED "structure_crate_squad_launched"

/// from /obj/structure/machinery/power_change(): (new_power_state)
#define COMSIG_MACHINERY_POWER_CHANGE "machinery_power_change"

/// from /datum/element/simple_unwrench(): (obj/item/wrench, mob/living/user)
#define COMSIG_OBJ_TRY_UNWRENCH "obj_try_unwrench"
#define ELEMENT_OBJ_STOP_UNWRENCH (1<<0)
2 changes: 1 addition & 1 deletion code/__DEFINES/dcs/signals/signals_datum.dm
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#define COMSIG_TOPIC "handle_topic"
/// from datum ui_act (usr, action)
#define COMSIG_UI_ACT "COMSIG_UI_ACT"
///from base of atom/attackby(): (/obj/item, /mob/living, params)
///from base of atom/attackby(): (obj/item/weapon, mob/living/user, params)
#define COMSIG_PARENT_ATTACKBY "atom_attackby"
///Return this in response if you don't want afterattack to be called
#define COMPONENT_NO_AFTERATTACK (1<<0)
Expand Down
1 change: 1 addition & 0 deletions code/_onclick/human.dm
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
return HANDLE_CLICK_PASS_THRU

/atom/proc/attack_hand(mob/user)
SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_HAND, user)
return

/mob/living/carbon/human/MouseDrop_T(atom/dropping, mob/living/user)
Expand Down
3 changes: 2 additions & 1 deletion code/_onclick/item_attack.dm
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
return FALSE

/atom/movable/attackby(obj/item/W, mob/living/user)
if(W)
. = ..()
if(!. && W)
if(!(W.flags_item & NOBLUDGEON))
visible_message(SPAN_DANGER("[src] has been hit by [user] with [W]."), null, null, 5, CHAT_TYPE_MELEE_HIT)
user.animation_attack_on(src)
Expand Down
17 changes: 16 additions & 1 deletion code/datums/components/cell.dm
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,14 @@
QDEL_NULL(inserted_cell)
return ..()


/datum/component/cell/RegisterWithParent()
..()
RegisterSignal(parent, list(COMSIG_PARENT_ATTACKBY, COMSIG_ITEM_ATTACKED), PROC_REF(on_object_hit))
RegisterSignal(parent, COMSIG_CELL_ADD_CHARGE, PROC_REF(add_charge))
RegisterSignal(parent, COMSIG_CELL_USE_CHARGE, PROC_REF(use_charge))
RegisterSignal(parent, COMSIG_CELL_CHECK_CHARGE, PROC_REF(has_charge))
RegisterSignal(parent, COMSIG_CELL_CHECK_CHARGE_PERCENT, PROC_REF(has_charge_percent))
RegisterSignal(parent, COMSIG_CELL_CHECK_FULL_CHARGE, PROC_REF(has_full_charge))
RegisterSignal(parent, COMSIG_CELL_START_TICK_DRAIN, PROC_REF(start_drain))
RegisterSignal(parent, COMSIG_CELL_STOP_TICK_DRAIN, PROC_REF(stop_drain))
RegisterSignal(parent, COMSIG_CELL_REMOVE_CELL, PROC_REF(remove_cell))
Expand Down Expand Up @@ -194,6 +195,20 @@
if(charge < charge_amount)
return COMPONENT_CELL_CHARGE_INSUFFICIENT

/datum/component/cell/proc/has_charge_percent(datum/source, check_percent = 0)
SIGNAL_HANDLER

var/charge_percent = charge / max_charge

if(check_percent > charge_percent)
return COMPONENT_CELL_CHARGE_PERCENT_INSUFFICIENT

/datum/component/cell/proc/has_full_charge(datum/source)
SIGNAL_HANDLER

if(charge < max_charge)
return COMPONENT_CELL_CHARGE_NOT_FULL

/datum/component/cell/proc/on_charge_empty()
stop_drain()
SEND_SIGNAL(parent, COMSIG_CELL_OUT_OF_CHARGE)
Expand Down
281 changes: 281 additions & 0 deletions code/datums/components/recharger.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
/datum/component/recharger
dupe_mode = COMPONENT_DUPE_UNIQUE
/// Ref to the inserted item, if any
var/obj/item/inserted_item
/// If we're currently recharging anything
var/recharging = FALSE
/// A typecache of what items can be inserted into the recharger
var/list/valid_items
/// A list of the default acceptable items to be inserted into the recharger, in case one isn't passed in during init
var/static/list/default_valid_items = list(
/obj/item/weapon/baton,
/obj/item/cell,
/obj/item/weapon/gun/energy,
/obj/item/device/defibrillator,
/obj/item/tool/portadialysis,
/obj/item/clothing/suit/auto_cpr,
/obj/item/smartgun_battery,
/obj/item/device/helmet_visor/night_vision,
)
/// How much power (later multiplied by CELLRATE, which is 0.006) to recharge by per tick, should the parent not have an active_power_usage
var/charge_amount = 15000
/// If not empty, the parent has unique overlays dependent on how charged the inserted item is.
/// Formatted like so: list("icon_state_name" = charge_upper_bound)
/// Highest charge should be 1st in the list, 2nd highest 2nd in list, etc.
var/list/charge_overlays = list()
/// Icon file for charge_overlays
var/charge_overlay_icon
/// Ref to the mutable appearance that we use as an overlay for the parent if we're using charge_overlays
var/mutable_appearance/charge_overlay

/datum/component/recharger/Initialize(
valid_items,
override_charge_amount,
charge_overlays,
charge_overlay_icon,
)
. = ..()
if(!istype(parent, /obj/structure/machinery))
return COMPONENT_INCOMPATIBLE

if(valid_items)
src.valid_items = typecacheof(valid_items)
else
src.valid_items = typecacheof(default_valid_items)

src.charge_overlay_icon = charge_overlay_icon

if(charge_overlays)
src.charge_overlays = charge_overlays
update_overlay()

var/obj/structure/machinery/machine_parent = parent
if(machine_parent.active_power_usage && !override_charge_amount)
charge_amount = machine_parent.active_power_usage

if(override_charge_amount)
charge_amount = override_charge_amount

/datum/component/recharger/Destroy(force, silent)
QDEL_NULL(inserted_item)

if(charge_overlay)
var/obj/structure/machinery/machine_parent = parent
machine_parent.overlays -= charge_overlay
QDEL_NULL(charge_overlay)

return ..()

/datum/component/recharger/RegisterWithParent()
..()
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, PROC_REF(on_attack))
RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_attack_hand))
RegisterSignal(parent, COMSIG_MACHINERY_POWER_CHANGE, PROC_REF(update_overlay))
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine))
RegisterSignal(parent, COMSIG_OBJ_TRY_UNWRENCH, PROC_REF(on_unwrench))

/datum/component/recharger/proc/on_unwrench(datum/source, obj/item/wrench, mob/living/user)
if(recharging || inserted_item)
to_chat(user, SPAN_WARNING("Remove [inserted_item] from [parent] before trying to move it!"))
return ELEMENT_OBJ_STOP_UNWRENCH

/datum/component/recharger/proc/on_attack(datum/source, obj/item/weapon, mob/living/user, params)
SIGNAL_HANDLER

if(inserted_item)
to_chat(user, SPAN_WARNING("There's already something recharging inside [parent]."))
return

var/area/parent_area = get_area(parent)
if(!isarea(parent_area) || (!parent_area.power_equip && !parent_area.unlimited_power))
to_chat(user, SPAN_WARNING("[parent] blinks red as you try to insert [weapon]!"))
return

if(!is_type_in_typecache(weapon, valid_items))
return

insert_item(weapon, user)
return COMPONENT_NO_AFTERATTACK

/datum/component/recharger/proc/on_attack_hand(datum/source, mob/user)
SIGNAL_HANDLER

if(!inserted_item)
return

user.put_in_hands(inserted_item)
inserted_item = null
stop_charging()
update_overlay()

/datum/component/recharger/proc/insert_item(obj/item/item, mob/living/user)
if(user.drop_inv_item_to_loc(item, parent))
inserted_item = item
start_charging()

/datum/component/recharger/proc/start_charging()
START_PROCESSING(SSobj, src)
recharging = TRUE

/datum/component/recharger/proc/stop_charging()
STOP_PROCESSING(SSobj, src)
recharging = FALSE

/datum/component/recharger/proc/on_examine(datum/source, mob/examiner, list/examine_text)
examine_text += "There's [inserted_item ? "[inserted_item.name]" : "nothing"] in the charger."
if(recharging)
if(istype(inserted_item, /obj/item/cell))
var/obj/item/cell/powercell = inserted_item
examine_text += "Current charge: [powercell.charge] ([powercell.percent()]%)"

/datum/component/recharger/proc/on_emp(datum/source, severity)
SIGNAL_HANDLER

if(inserted_item)
// Async called because there's a subtype of /atom/emp_act() that sleeps
// And since we use SIGNAL_HANDLER above, the linter's unhappy
INVOKE_ASYNC(inserted_item, TYPE_PROC_REF(/atom, emp_act), severity)

/datum/component/recharger/proc/update_power_use(new_power_use)
if(isnull(new_power_use))
return

var/obj/structure/machinery/machine_parent = parent
machine_parent.update_use_power(new_power_use)
update_overlay()

/datum/component/recharger/proc/update_overlay(datum/source)
SIGNAL_HANDLER

if(!length(charge_overlays) || !charge_overlay_icon)
return

var/obj/structure/machinery/machine_parent = parent

if(!charge_overlay)
charge_overlay = mutable_appearance(charge_overlay_icon)
machine_parent.overlays += charge_overlay

if(!inserted_item || machine_parent.inoperable())
charge_overlay.icon_state = ""
return

for(var/overlay_state in charge_overlays)
var/charge_percent = charge_overlays[overlay_state]
// We check if the % needed for the icon state is less the cell
// If it's more than the cell, we continue down and go to the next (lower) overlay iconstate
if(SEND_SIGNAL(inserted_item, COMSIG_CELL_CHECK_CHARGE_PERCENT, charge_percent) & COMPONENT_CELL_CHARGE_PERCENT_INSUFFICIENT)
continue

charge_overlay.icon_state = overlay_state
return

/* Can't think of a good way to add these. Not sure if I even should
if(istype(charging, /obj/item/weapon/gun/energy))
overlays += "recharger-taser"//todo make more generic I guess. It works for now -trii
else if(istype(charging, /obj/item/weapon/baton))
overlays += "recharger-baton"
*/

/datum/component/recharger/process()
var/obj/structure/machinery/machine_parent = parent
if(machine_parent.inoperable() || !machine_parent.anchored)
update_power_use(USE_POWER_NONE)
return

if(!recharging)
update_power_use(USE_POWER_IDLE)
return

// This code is awful.
// I plan to cut this down by converting more objects to have cell components over time
// But you always know how //TODO goes
if(istype(inserted_item, /obj/item/weapon/gun/energy))
var/obj/item/weapon/gun/energy/E = inserted_item
if(!E.works_in_recharger)
return
if(!E.cell.fully_charged())
E.cell.give(charge_amount * CELLRATE)
update_power_use(USE_POWER_ACTIVE)
else
update_power_use(USE_POWER_IDLE)
return

else if(istype(inserted_item, /obj/item/weapon/baton))
var/obj/item/weapon/baton/B = inserted_item
if(B.bcell)
if(!B.bcell.fully_charged())
B.bcell.give(charge_amount * CELLRATE)
update_power_use(USE_POWER_ACTIVE)
else
update_power_use(USE_POWER_IDLE)
else
update_power_use(USE_POWER_IDLE)
return

else if(istype(inserted_item, /obj/item/device/defibrillator))
var/obj/item/device/defibrillator/D = inserted_item
if(!D.dcell.fully_charged())
D.dcell.give(charge_amount * CELLRATE)
update_power_use(USE_POWER_ACTIVE)
else
update_power_use(USE_POWER_IDLE)
return



else if(istype(inserted_item, /obj/item/clothing/suit/auto_cpr))
var/obj/item/clothing/suit/auto_cpr/A = inserted_item
if(!A.pdcell.fully_charged())
A.pdcell.give(charge_amount * CELLRATE)
update_power_use(USE_POWER_ACTIVE)
else
update_power_use(USE_POWER_IDLE)
return

else if(istype(inserted_item, /obj/item/tool/portadialysis))
var/obj/item/tool/portadialysis/P = inserted_item
if(!P.pdcell.fully_charged())
P.pdcell.give(charge_amount * CELLRATE)
update_power_use(USE_POWER_ACTIVE)
else
update_power_use(USE_POWER_IDLE)
return

else if(istype(inserted_item, /obj/item/cell))
var/obj/item/cell/C = inserted_item
if(!C.fully_charged())
C.give(charge_amount * CELLRATE)
update_power_use(USE_POWER_ACTIVE)
else
update_power_use(USE_POWER_IDLE)
return

else if(istype(inserted_item, /obj/item/smartgun_battery))
var/obj/item/smartgun_battery/charging_smartgun_battery = inserted_item
if(charging_smartgun_battery.power_cell)
if(!charging_smartgun_battery.power_cell.fully_charged())
charging_smartgun_battery.power_cell.give(charge_amount * CELLRATE)
update_power_use(USE_POWER_ACTIVE)
return

update_power_use(USE_POWER_IDLE)
return

else if(istype(inserted_item, /obj/item/device/helmet_visor/night_vision))
var/obj/item/device/helmet_visor/night_vision/charging_night_vision_visor = inserted_item
if(charging_night_vision_visor.power_cell)
if(!charging_night_vision_visor.power_cell.fully_charged())
charging_night_vision_visor.power_cell.give(charge_amount * CELLRATE)
update_power_use(USE_POWER_ACTIVE)
return

update_power_use(USE_POWER_IDLE)
return

else
if(SEND_SIGNAL(inserted_item, COMSIG_CELL_CHECK_FULL_CHARGE) & COMPONENT_CELL_CHARGE_NOT_FULL)
SEND_SIGNAL(inserted_item, COMSIG_CELL_ADD_CHARGE, charge_amount * CELLRATE)
update_power_use(USE_POWER_ACTIVE)
else
update_power_use(USE_POWER_IDLE)
Loading

0 comments on commit dc2c1d8

Please sign in to comment.