diff --git a/code/__DEFINES/dcs/signals/atom/signals_atom.dm b/code/__DEFINES/dcs/signals/atom/signals_atom.dm index d9bd1202c159..a15781ba0581 100644 --- a/code/__DEFINES/dcs/signals/atom/signals_atom.dm +++ b/code/__DEFINES/dcs/signals/atom/signals_atom.dm @@ -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" diff --git a/code/__DEFINES/dcs/signals/atom/signals_cell.dm b/code/__DEFINES/dcs/signals/atom/signals_cell.dm index 75e13d8bfdfc..e1b520bb7160 100644 --- a/code/__DEFINES/dcs/signals/atom/signals_cell.dm +++ b/code/__DEFINES/dcs/signals/atom/signals_cell.dm @@ -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) diff --git a/code/__DEFINES/dcs/signals/atom/signals_obj.dm b/code/__DEFINES/dcs/signals/atom/signals_obj.dm index 93579e068ec7..0c13107f68d8 100644 --- a/code/__DEFINES/dcs/signals/atom/signals_obj.dm +++ b/code/__DEFINES/dcs/signals/atom/signals_obj.dm @@ -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) diff --git a/code/__DEFINES/dcs/signals/signals_datum.dm b/code/__DEFINES/dcs/signals/signals_datum.dm index 7696d8ad6037..730b817fe0b3 100644 --- a/code/__DEFINES/dcs/signals/signals_datum.dm +++ b/code/__DEFINES/dcs/signals/signals_datum.dm @@ -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) diff --git a/code/_onclick/human.dm b/code/_onclick/human.dm index b09c26ffb92f..05dce962541d 100644 --- a/code/_onclick/human.dm +++ b/code/_onclick/human.dm @@ -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) diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index c6052da33199..0e470cc06fbf 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -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) diff --git a/code/datums/components/cell.dm b/code/datums/components/cell.dm index 81ef3733e2e2..0d082ba0752b 100644 --- a/code/datums/components/cell.dm +++ b/code/datums/components/cell.dm @@ -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)) @@ -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) diff --git a/code/datums/components/recharger.dm b/code/datums/components/recharger.dm new file mode 100644 index 000000000000..baf2a6056428 --- /dev/null +++ b/code/datums/components/recharger.dm @@ -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) diff --git a/code/datums/elements/simple_unwrench.dm b/code/datums/elements/simple_unwrench.dm new file mode 100644 index 000000000000..73a1c162e314 --- /dev/null +++ b/code/datums/elements/simple_unwrench.dm @@ -0,0 +1,45 @@ +/datum/element/simple_unwrench + element_flags = ELEMENT_DETACH|ELEMENT_BESPOKE + /// If we have a delay or if the wrenching is instant + var/has_delay = TRUE + +/datum/element/simple_unwrench/Attach(datum/target, has_wrench_delay) + . = ..() + if(!isobj(target)) + return ELEMENT_INCOMPATIBLE + RegisterSignal(target, COMaSIG_PARENT_ATTACKBY, PROC_REF(on_attack)) + + if(!isnull(has_wrench_delay)) + has_delay = has_wrench_delay + +/datum/element/simple_unwrench/Detach(datum/source, force) + UnregisterSignal(source, COMSIG_PARENT_ATTACKBY) + return ..() + +/datum/element/simple_unwrench/proc/on_attack(obj/source, obj/item/weapon, mob/living/user, params) + SIGNAL_HANDLER + + if(!HAS_TRAIT(weapon, TRAIT_TOOL_WRENCH)) + return + + if(SEND_SIGNAL(source, COMSIG_OBJ_TRY_UNWRENCH, weapon, user) & ELEMENT_OBJ_STOP_UNWRENCH) + return + + if(has_delay && user.action_busy) + return + + INVOKE_ASYNC(src, PROC_REF(handle_wrench), source, weapon, user) + return COMPONENT_NO_AFTERATTACK + +/datum/element/simple_unwrench/proc/handle_wrench(obj/source, obj/item/weapon, mob/living/user) + // If there's a delay, take 1-4 seconds (dependent on skill) to unwrench/wrench this + if(has_delay && !do_after(user, max(1 SECONDS, (4 SECONDS) - (user.skills.get_skill_level(SKILL_ENGINEER) * (1 SECONDS))), INTERRUPT_ALL, BUSY_ICON_BUILD)) + return + + user.visible_message( + SPAN_NOTICE("[user] [source.anchored ? "unanchors" : "anchors"] [source]."), + SPAN_NOTICE("You [source.anchored ? "unanchor" : "anchor"] [source]."), + SPAN_NOTICE("You hear the sound of bolts being wrenched."), + ) + playsound(source, 'sound/items/Ratchet.ogg', 25, 1) + source.anchored = !source.anchored diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index c5eaa14e05b5..6c458ff29dd6 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -256,6 +256,10 @@ Class Procs: return src.attack_hand(user) /obj/structure/machinery/attack_hand(mob/living/user as mob) + . = ..() + if(.) + return + if(inoperable(MAINT)) return TRUE if(user.is_mob_incapacitated()) diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index 72e311c6d8ff..1b5eb9456701 100644 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -9,256 +9,27 @@ idle_power_usage = 4 active_power_usage = 15000 //15 kW black_market_value = 35 - var/obj/item/charging = null - var/percent_charge_complete = 0 - var/list/allowed_devices = 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) + has_wrench_delay = FALSE - var/charge_amount = 1000 - -/obj/structure/machinery/recharger/attackby(obj/item/G as obj, mob/user as mob) - if(istype(user,/mob/living/silicon)) - return - - var/allowed = 0 - for (var/allowed_type in allowed_devices) - if (istype(G, allowed_type)) allowed = 1 - - if(allowed) - if(charging) - to_chat(user, SPAN_DANGER("\A [charging] is already charging here.")) - return - // Checks to make sure he's not in space doing it, and that the area got proper power. - var/area/a = get_area(src) - if(!isarea(a) || (a.power_equip == 0 && !a.unlimited_power)) - to_chat(user, SPAN_DANGER("\The [name] blinks red as you try to insert the item!")) - return - if(istype(G, /obj/item/device/defibrillator)) - var/obj/item/device/defibrillator/D = G - if(D.ready) - to_chat(user, SPAN_WARNING("It won't fit, put the paddles back into \the [D] first!")) - return - if(istype(G, /obj/item/tool/portadialysis)) - var/obj/item/tool/portadialysis/P = G - if(P.attached) - to_chat(user, SPAN_WARNING("It won't fit, detach it from [P.attached] first!")) - return - if(user.drop_inv_item_to_loc(G, src)) - charging = G - start_processing() - update_icon() - else if(HAS_TRAIT(G, TRAIT_TOOL_WRENCH)) - if(charging) - to_chat(user, SPAN_DANGER("Remove \the [charging] first!")) - return - anchored = !anchored - to_chat(user, "You [anchored ? "attached" : "detached"] the recharger.") - playsound(loc, 'sound/items/Ratchet.ogg', 25, 1) - -/obj/structure/machinery/recharger/attack_hand(mob/user as mob) - if(istype(user,/mob/living/silicon)) - return - - add_fingerprint(user) - - if(charging) - charging.update_icon() - user.put_in_hands(charging) - charging = null - stop_processing() - percent_charge_complete = 0 - update_icon() - -/obj/structure/machinery/recharger/process() - if(inoperable() || !anchored) - update_use_power(USE_POWER_NONE) - update_icon() - return - if(!charging) - update_use_power(USE_POWER_IDLE) - percent_charge_complete = 0 - update_icon() - //This is an awful check. Holy cow. - else - if(istype(charging, /obj/item/weapon/gun/energy)) - var/obj/item/weapon/gun/energy/E = charging - if(!E.works_in_recharger) - return; - if(!E.cell.fully_charged()) - E.cell.give(charge_amount) - percent_charge_complete = E.cell.percent() - update_use_power(USE_POWER_ACTIVE) - update_icon() - else - percent_charge_complete = 100 - update_use_power(USE_POWER_IDLE) - update_icon() - return - - if(istype(charging, /obj/item/weapon/baton)) - var/obj/item/weapon/baton/B = charging - if(B.bcell) - if(!B.bcell.fully_charged()) - B.bcell.give(charge_amount) - percent_charge_complete = B.bcell.percent() - update_use_power(USE_POWER_ACTIVE) - update_icon() - else - percent_charge_complete = 100 - update_use_power(USE_POWER_IDLE) - update_icon() - else - percent_charge_complete = 0 - update_use_power(USE_POWER_IDLE) - update_icon() - return - - if(istype(charging, /obj/item/device/defibrillator)) - var/obj/item/device/defibrillator/D = charging - if(!D.dcell.fully_charged()) - D.dcell.give(active_power_usage*CELLRATE) - percent_charge_complete = D.dcell.percent() - update_use_power(USE_POWER_ACTIVE) - update_icon() - else - percent_charge_complete = 100 - update_use_power(USE_POWER_IDLE) - update_icon() - return - - - - if(istype(charging, /obj/item/clothing/suit/auto_cpr)) - var/obj/item/clothing/suit/auto_cpr/A = charging - if(!A.pdcell.fully_charged()) - A.pdcell.give(active_power_usage*CELLRATE) - percent_charge_complete = A.pdcell.percent() - update_use_power(USE_POWER_ACTIVE) - update_icon() - else - percent_charge_complete = 100 - update_use_power(USE_POWER_IDLE) - update_icon() - return - - if(istype(charging, /obj/item/tool/portadialysis)) - var/obj/item/tool/portadialysis/P = charging - if(!P.pdcell.fully_charged()) - P.pdcell.give(active_power_usage*CELLRATE) - percent_charge_complete = P.pdcell.percent() - update_use_power(USE_POWER_ACTIVE) - update_icon() - else - percent_charge_complete = 100 - update_use_power(USE_POWER_IDLE) - update_icon() - return - - if(istype(charging, /obj/item/cell)) - var/obj/item/cell/C = charging - if(!C.fully_charged()) - C.give(active_power_usage*CELLRATE) - percent_charge_complete = C.percent() - update_use_power(USE_POWER_ACTIVE) - update_icon() - else - percent_charge_complete = 100 - update_use_power(USE_POWER_IDLE) - update_icon() - return - - if(istype(charging, /obj/item/smartgun_battery)) - var/obj/item/smartgun_battery/charging_smartgun_battery = charging - if(charging_smartgun_battery.power_cell) - if(!charging_smartgun_battery.power_cell.fully_charged()) - charging_smartgun_battery.power_cell.give(charge_amount) - percent_charge_complete = charging_smartgun_battery.power_cell.percent() - update_use_power(USE_POWER_ACTIVE) - update_icon() - return - - percent_charge_complete = 100 - update_use_power(USE_POWER_IDLE) - update_icon() - return - - if(istype(charging, /obj/item/device/helmet_visor/night_vision)) - var/obj/item/device/helmet_visor/night_vision/charging_night_vision_visor = charging - 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) - percent_charge_complete = charging_night_vision_visor.power_cell.percent() - update_use_power(USE_POWER_ACTIVE) - update_icon() - return - - percent_charge_complete = 100 - update_use_power(USE_POWER_IDLE) - update_icon() - return - - /* Disable defib recharging - if(istype(charging, /obj/item/device/defibrillator)) - var/obj/item/device/defibrillator/D = charging - if(D.dcell) - if(!D.dcell.fully_charged()) - icon_state = icon_state_charging - D.dcell.give(active_power_usage*CELLRATE) - update_use_power(USE_POWER_ACTIVE) - else - icon_state = icon_state_charged - update_use_power(USE_POWER_IDLE) - else - icon_state = icon_state_idle - update_use_power(USE_POWER_IDLE) - return - */ +/obj/structure/machinery/recharger/Initialize(mapload, ...) + . = ..() + AddComponent(/datum/component/recharger, \ + charge_overlays = list(\ + "recharger-100" = 100,\ + "recharger-75" = 75,\ + "recharger-50" = 50,\ + "recharger-25" = 25,\ + "recharger-10" = 10,\ + "recharger-power" = 0,\ + ),\ + charge_overlay_icon = 'icons/obj/structures/props/stationobjs.dmi',\ + ) + AddElement(/datum/element/simple_unwrench) /obj/structure/machinery/recharger/power_change() ..() update_icon() -/obj/structure/machinery/recharger/emp_act(severity) - . = ..() - if(inoperable() || !anchored) - return - - if(istype(charging, /obj/item/weapon/baton)) - var/obj/item/weapon/baton/B = charging - if(B.bcell) - B.bcell.charge = 0 - -/obj/structure/machinery/recharger/update_icon() //we have an update_icon() in addition to the stuff in process to make it feel a tiny bit snappier. - src.overlays = 0 - if((inoperable())) - return - else if(!charging) - overlays += "recharger-power" - return - - if(percent_charge_complete < 25) - overlays += "recharger-10" - else if(percent_charge_complete >= 25 && percent_charge_complete < 50) - overlays += "recharger-25" - else if(percent_charge_complete >= 50 && percent_charge_complete < 75) - overlays += "recharger-50" - else if(percent_charge_complete >= 75 && percent_charge_complete < 100) - overlays += "recharger-75" - else if(percent_charge_complete >= 100) - overlays += "recharger-100" - - 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" - -/obj/structure/machinery/recharger/get_examine_text(mob/user) - . = ..() - . += "There's [charging ? "[charging]" : "nothing"] in the charger." - if(charging) - if(istype(charging, /obj/item/cell)) - var/obj/item/cell/C = charging - . += "Current charge: [C.charge] ([C.percent()]%)" - /obj/structure/machinery/recharger/unanchored anchored = FALSE diff --git a/code/game/machinery/vending/cm_vending.dm b/code/game/machinery/vending/cm_vending.dm index 6205f24c8e05..2bb328e69e74 100644 --- a/code/game/machinery/vending/cm_vending.dm +++ b/code/game/machinery/vending/cm_vending.dm @@ -69,10 +69,6 @@ IN_USE used for vending/denying . = ..() cm_build_inventory(get_listed_products(), 1, 3) -/obj/structure/machinery/power_change(area/master_area = null) - ..() - update_icon() - /obj/structure/machinery/cm_vending/update_icon() //restoring sprite to initial diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 95998d5de79a..a1e00c92980b 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -10,6 +10,8 @@ var/list/debris var/unslashable = FALSE var/wrenchable = FALSE + /// If this structure takes time to wrench/unwrench + var/has_wrench_delay = TRUE health = 100 anchored = TRUE projectile_coverage = PROJECTILE_COVERAGE_MEDIUM @@ -20,6 +22,9 @@ if(climbable) verbs += /obj/structure/proc/climb_on + if(wrenchable) + AddElement(/datum/element/simple_unwrench, has_wrench_delay) + /obj/structure/Destroy() //before ..() because the parent does loc = null for(var/atom/movable/A in contents_recursive()) @@ -37,14 +42,6 @@ visible_message(SPAN_DANGER("[user] smashes [src] apart!")) deconstruct(FALSE) -/obj/structure/attackby(obj/item/W, mob/user) - if(HAS_TRAIT(W, TRAIT_TOOL_WRENCH)) - if(user.action_busy) - return TRUE - toggle_anchored(W, user) - return TRUE - ..() - /obj/structure/ex_act(severity, direction) if(indestructible) return @@ -207,22 +204,6 @@ return 0 return 1 -/obj/structure/proc/toggle_anchored(obj/item/W, mob/user) - if(!wrenchable) - to_chat(user, SPAN_WARNING("The [src] cannot be [anchored ? "un" : ""]anchored.")) - return FALSE - else - // Wrenching is faster if we are better at engineering - var/timer = max(10, 40 - user.skills.get_skill_level(SKILL_ENGINEER) * 10) - if(do_after(usr, timer, INTERRUPT_ALL, BUSY_ICON_BUILD)) - anchored = !anchored - playsound(loc, 'sound/items/Ratchet.ogg', 25, 1) - if(anchored) - user.visible_message(SPAN_NOTICE("[user] anchors [src] into place."),SPAN_NOTICE("You anchor [src] into place.")) - else - user.visible_message(SPAN_NOTICE("[user] unanchors [src]."),SPAN_NOTICE("You unanchor [src].")) - return TRUE - /obj/structure/get_applying_acid_time() if(unacidable) return -1 diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 00e6b92eab8f..8acdf2c68f31 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -91,6 +91,9 @@ stat |= NOPOWER src.update_use_power(USE_POWER_NONE) + SEND_SIGNAL(src, COMSIG_MACHINERY_POWER_CHANGE, has_power) + update_icon() + // the powernet datum // each contiguous network of cables & nodes diff --git a/colonialmarines.dme b/colonialmarines.dme index d632d6bd4998..639897865e32 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -387,6 +387,7 @@ #include "code\datums\components\label.dm" #include "code\datums\components\orbiter.dm" #include "code\datums\components\overlay_lighting.dm" +#include "code\datums\components\recharger.dm" #include "code\datums\components\rename.dm" #include "code\datums\components\speed_modifier.dm" #include "code\datums\components\toxin_buildup.dm" @@ -461,6 +462,7 @@ #include "code\datums\elements\light_blocking.dm" #include "code\datums\elements\mouth_drop_item.dm" #include "code\datums\elements\poor_eyesight_correction.dm" +#include "code\datums\elements\simple_unwrench.dm" #include "code\datums\elements\suturing.dm" #include "code\datums\elements\yautja_tracked_item.dm" #include "code\datums\elements\bullet_trait\damage_boost.dm"