mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-25 17:12:12 +00:00
## About The Pull Request Melee attack chain now has a list passed along with it, `attack_modifiers`, which you can stick force modifiers to change the resulting attack This is basically a soft implementation of damage packets until a more definitive pr, but one that only applies to item attack chain, and not unarmed attacks. This change was done to facilitate a baton refactor - batons no longer hack together their own attack chain, and are now integrated straight into the real attack chain. This refactor itself was done because batons don't send any attack signals, which has been annoying in the past (for swing combat). ## Changelog 🆑 Melbert refactor: Batons have been refactored again. Baton stuns now properly count as an attack, when before it was a nothing. Report any oddities, particularly in regards to harmbatonning vs normal batonning. refactor: The method of adjusting item damage mid-attack has been refactored - some affected items include the Nullblade and knives. Report any strange happenings with damage numbers. refactor: A few objects have been moved to the new interaction chain - records consoles, mawed crucible, alien weeds and space vines, hedges, restaurant portals, and some mobs - to name a few. fix: Spears only deal bonus damage against secure lockers, not all closet types (including crates) /🆑
311 lines
10 KiB
Plaintext
311 lines
10 KiB
Plaintext
#define SKILLCHIP_IMPLANT_TIME (15 SECONDS)
|
|
#define SKILLCHIP_REMOVAL_TIME (15 SECONDS)
|
|
|
|
/obj/machinery/skill_station
|
|
name = "\improper Skillsoft station"
|
|
desc = "Learn skills with only minimal chance for brain damage."
|
|
|
|
icon = 'icons/obj/machines/implant_chair.dmi'
|
|
icon_state = "implantchair"
|
|
occupant_typecache = list(/mob/living/carbon) //todo make occupant_typecache per type
|
|
state_open = TRUE
|
|
// Only opens UI when inside; also, you can use the machine while lying down (for paraplegics and the like)
|
|
interaction_flags_atom = parent_type::interaction_flags_atom | INTERACT_ATOM_IGNORE_MOBILITY
|
|
circuit = /obj/item/circuitboard/machine/skill_station
|
|
/// Currently implanting/removing
|
|
var/working = FALSE
|
|
/// Timer until implanting/removing finishes.
|
|
var/work_timer
|
|
/// What we're implanting
|
|
var/obj/item/skillchip/inserted_skillchip
|
|
|
|
/obj/machinery/skill_station/Initialize(mapload)
|
|
. = ..()
|
|
update_appearance()
|
|
|
|
//Only usable by the person inside
|
|
/obj/machinery/skill_station/ui_state(mob/user)
|
|
return GLOB.contained_state
|
|
|
|
/obj/machinery/skill_station/ui_interact(mob/user, datum/tgui/ui)
|
|
ui = SStgui.try_update_ui(user, src, ui)
|
|
if(!ui)
|
|
ui = new(user, src, "SkillStation", name)
|
|
ui.open()
|
|
|
|
/obj/machinery/skill_station/update_icon_state()
|
|
icon_state = initial(icon_state)
|
|
if(state_open)
|
|
icon_state += "_open"
|
|
if(occupant)
|
|
icon_state += "_occupied"
|
|
return ..()
|
|
|
|
/obj/machinery/skill_station/update_overlays()
|
|
. = ..()
|
|
if(working)
|
|
. += "working"
|
|
|
|
/obj/machinery/skill_station/relaymove(mob/living/user, direction)
|
|
open_machine()
|
|
|
|
/obj/machinery/skill_station/open_machine(drop = TRUE, density_to_set = FALSE)
|
|
. = ..()
|
|
interrupt_operation()
|
|
|
|
/obj/machinery/skill_station/Exited(atom/movable/gone, direction)
|
|
. = ..()
|
|
if(gone == inserted_skillchip)
|
|
inserted_skillchip = null
|
|
interrupt_operation()
|
|
|
|
/obj/machinery/skill_station/power_change()
|
|
. = ..()
|
|
if(working)
|
|
interrupt_operation()
|
|
|
|
/obj/machinery/skill_station/close_machine(atom/movable/target, density_to_set = TRUE)
|
|
. = ..()
|
|
if(occupant)
|
|
ui_interact(occupant)
|
|
|
|
/obj/machinery/skill_station/proc/interrupt_operation()
|
|
working = FALSE
|
|
if(work_timer)
|
|
deltimer(work_timer)
|
|
work_timer = null
|
|
update_appearance()
|
|
|
|
/obj/machinery/skill_station/interact(mob/user)
|
|
. = ..()
|
|
if(user == occupant)
|
|
ui_interact(user)
|
|
else
|
|
toggle_open()
|
|
|
|
/obj/machinery/skill_station/attackby(obj/item/I, mob/living/user, list/modifiers, list/attack_modifiers)
|
|
if(istype(I,/obj/item/skillchip))
|
|
if(inserted_skillchip)
|
|
to_chat(user,span_notice("There's already a skillchip inside."))
|
|
return
|
|
if(!user.transferItemToLoc(I, src))
|
|
return
|
|
inserted_skillchip = I
|
|
return
|
|
return ..()
|
|
|
|
/obj/machinery/skill_station/dump_contents()
|
|
. = ..()
|
|
inserted_skillchip = null
|
|
|
|
/obj/machinery/skill_station/dump_inventory_contents(list/subset = null)
|
|
// Don't drop the skillchip, it's directly inserted into the machine.
|
|
// dump_contents() will drop everything including the skillchip as an alternative to this.
|
|
return ..(contents - inserted_skillchip)
|
|
|
|
/obj/machinery/skill_station/proc/toggle_open(mob/user)
|
|
state_open ? close_machine() : open_machine()
|
|
|
|
// Functions below do not validate occupant exists - should be handled outer wrappers.
|
|
/// Start implanting.
|
|
/obj/machinery/skill_station/proc/start_implanting()
|
|
var/mob/living/carbon/carbon_occupant = occupant
|
|
|
|
if(inserted_skillchip.has_mob_incompatibility(carbon_occupant))
|
|
CRASH("Unusual error - [usr] attempted to start implanting of [inserted_skillchip] when the interface state should not have allowed it.")
|
|
|
|
working = TRUE
|
|
work_timer = addtimer(CALLBACK(src, PROC_REF(implant)),SKILLCHIP_IMPLANT_TIME,TIMER_STOPPABLE)
|
|
update_appearance()
|
|
|
|
/// Finish implanting.
|
|
/obj/machinery/skill_station/proc/implant()
|
|
working = FALSE
|
|
work_timer = null
|
|
var/mob/living/carbon/carbon_occupant = occupant
|
|
var/implant_msg = carbon_occupant.implant_skillchip(inserted_skillchip, FALSE)
|
|
if(implant_msg)
|
|
to_chat(carbon_occupant,span_notice("Operation failed! [implant_msg]"))
|
|
else
|
|
to_chat(carbon_occupant,span_notice("Operation complete!"))
|
|
inserted_skillchip = null
|
|
|
|
update_appearance()
|
|
|
|
/// Start removal.
|
|
/obj/machinery/skill_station/proc/start_removal(obj/item/skillchip/to_be_removed)
|
|
if(!to_be_removed)
|
|
return
|
|
|
|
if(to_be_removed.is_on_cooldown())
|
|
to_chat(occupant, span_notice("DANGER! Operation cannot be completed, removal is unsafe."))
|
|
CRASH("Unusual error - [usr] attempted to start removal of [to_be_removed] when the interface state should not have allowed it.")
|
|
|
|
working = TRUE
|
|
work_timer = addtimer(CALLBACK(src, PROC_REF(remove_skillchip),to_be_removed),SKILLCHIP_REMOVAL_TIME,TIMER_STOPPABLE)
|
|
update_appearance()
|
|
|
|
/// Finish removal.
|
|
/obj/machinery/skill_station/proc/remove_skillchip(obj/item/skillchip/to_be_removed)
|
|
working = FALSE
|
|
work_timer = null
|
|
update_appearance()
|
|
|
|
var/mob/living/carbon/carbon_occupant = occupant
|
|
|
|
if(to_be_removed.is_on_cooldown())
|
|
to_chat(carbon_occupant,span_notice("Safety mechanisms activated! Skillchip cannot be safely removed."))
|
|
return
|
|
|
|
if(!istype(carbon_occupant))
|
|
to_chat(carbon_occupant,span_notice("Occupant does not appear to be a carbon-based lifeform!"))
|
|
return
|
|
|
|
if(!carbon_occupant.remove_skillchip(to_be_removed))
|
|
to_chat(carbon_occupant,span_notice("Failed to remove skillchip!"))
|
|
return
|
|
|
|
if(to_be_removed.removable)
|
|
carbon_occupant.put_in_hands(to_be_removed)
|
|
else
|
|
qdel(to_be_removed)
|
|
|
|
to_chat(carbon_occupant, span_notice("Operation complete!"))
|
|
|
|
/obj/machinery/skill_station/proc/toggle_chip_active(obj/item/skillchip/to_be_toggled)
|
|
var/mob/living/carbon/carbon_occupant = occupant
|
|
|
|
if(to_be_toggled.is_on_cooldown())
|
|
to_chat(carbon_occupant,span_notice("Safety mechanisms activated! Skillchip cannot be safely modified."))
|
|
return
|
|
|
|
if(!istype(carbon_occupant))
|
|
to_chat(carbon_occupant,span_notice("Occupant does not appear to be a carbon-based lifeform!"))
|
|
return
|
|
|
|
if(to_be_toggled.is_active())
|
|
var/active_msg = to_be_toggled.try_deactivate_skillchip(FALSE, FALSE)
|
|
if(active_msg)
|
|
to_chat(carbon_occupant,span_notice("Failed to deactivate skillchip! [active_msg]"))
|
|
return
|
|
|
|
// This code will fire when to_be_toggled.active is FALSE
|
|
var/active_msg = to_be_toggled.try_activate_skillchip(FALSE, FALSE)
|
|
if(active_msg)
|
|
to_chat(carbon_occupant,span_notice("Failed to activate skillchip! [active_msg]"))
|
|
|
|
/obj/machinery/skill_station/ui_data(mob/user)
|
|
. = ..()
|
|
.["working"] = working
|
|
.["timeleft"] = work_timer ? timeleft(work_timer) : null
|
|
var/mob/living/carbon/carbon_occupant = occupant
|
|
|
|
.["skillchip_ready"] = inserted_skillchip ? TRUE : FALSE
|
|
if(inserted_skillchip)
|
|
// This is safe, incompatibility check can accept a null or invalid mob.
|
|
var/incompatibility_check = inserted_skillchip.has_mob_incompatibility(carbon_occupant)
|
|
// Grab chip data. We do this because of special chips like Chameleon that may want to
|
|
// spoof their information.
|
|
var/list/inserted_chip_data = inserted_skillchip.get_chip_data()
|
|
if(incompatibility_check)
|
|
.["implantable"] = FALSE
|
|
.["implantable_reason"] = incompatibility_check
|
|
else
|
|
.["implantable"] = TRUE
|
|
.["implantable_reason"] = null
|
|
.["skill_name"] = inserted_chip_data["name"]
|
|
.["skill_desc"] = inserted_chip_data["desc"]
|
|
.["skill_icon"] = inserted_chip_data["icon"]
|
|
.["complexity"] = inserted_chip_data["complexity"]
|
|
.["slot_use"] = inserted_chip_data["slot_use"]
|
|
|
|
// If there's no occupant, we don't need to worry about what skillchips are in their brain.
|
|
if(!carbon_occupant)
|
|
.["error"] = "No valid occupant detected. Please consult nearest medical practitioner."
|
|
.["current"] = null
|
|
.["complexity_used"] = null
|
|
.["complexity_max"] = null
|
|
.["slots_used"] = null
|
|
.["slots_max"] = null
|
|
return
|
|
|
|
var/obj/item/organ/brain/occupant_brain = carbon_occupant.get_organ_slot(ORGAN_SLOT_BRAIN)
|
|
|
|
// If there's no brain, we don't need to worry either.
|
|
if(QDELETED(occupant_brain))
|
|
.["error"] = "Brain not detected. Please consult nearest medical practitioner."
|
|
.["current"] = null
|
|
.["complexity_used"] = null
|
|
.["complexity_max"] = null
|
|
.["slots_used"] = null
|
|
.["slots_max"] = null
|
|
return
|
|
|
|
.["complexity_used"] = occupant_brain.get_used_skillchip_complexity()
|
|
.["complexity_max"] = occupant_brain.get_max_skillchip_complexity()
|
|
.["slots_used"] = occupant_brain.get_used_skillchip_slots()
|
|
.["slots_max"] = occupant_brain.get_max_skillchip_slots()
|
|
|
|
// If we got here, we have both an occupant and it has a brain, so we can check for skillchips.
|
|
var/list/current_skills = list()
|
|
for(var/obj/item/skillchip/skill_chip in occupant_brain.skillchips)
|
|
current_skills += list(skill_chip.get_chip_data())
|
|
.["current"] = current_skills
|
|
|
|
/obj/machinery/skill_station/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
if(usr != occupant)
|
|
return
|
|
switch(action)
|
|
if("implant")
|
|
if(working)
|
|
stack_trace("[usr] tried to start skillchip implanting when [src] was in an invalid state.")
|
|
return TRUE
|
|
if(occupant && inserted_skillchip)
|
|
start_implanting()
|
|
return TRUE
|
|
if("remove")
|
|
if(working)
|
|
stack_trace("[usr] tried to start skillchip removal when [src] was in an invalid state.")
|
|
return TRUE
|
|
var/chipref = params["ref"]
|
|
var/mob/living/carbon/carbon_occupant = occupant
|
|
var/obj/item/organ/brain/occupant_brain = carbon_occupant.get_organ_slot(ORGAN_SLOT_BRAIN)
|
|
if(QDELETED(carbon_occupant) || QDELETED(occupant_brain))
|
|
return TRUE
|
|
var/obj/item/skillchip/to_be_removed = locate(chipref) in occupant_brain.skillchips
|
|
if(!to_be_removed)
|
|
return TRUE
|
|
start_removal(to_be_removed)
|
|
return TRUE
|
|
if("eject")
|
|
if(working)
|
|
stack_trace("[usr] tried to toggle skillchip activation when [src] was in an invalid state.")
|
|
return TRUE
|
|
if(inserted_skillchip)
|
|
to_chat(occupant,span_notice("You eject the skillchip."))
|
|
var/mob/living/carbon/human/H = occupant
|
|
H.put_in_hands(inserted_skillchip)
|
|
inserted_skillchip = null
|
|
return TRUE
|
|
if("toggle_activate")
|
|
var/chipref = params["ref"]
|
|
// Check if the machine is already working. If it is, this act should not have sent.
|
|
if(working)
|
|
stack_trace("[usr] tried to toggle skillchip activation when [src] was in an invalid state.")
|
|
return TRUE
|
|
var/mob/living/carbon/carbon_occupant = occupant
|
|
var/obj/item/organ/brain/occupant_brain = carbon_occupant.get_organ_slot(ORGAN_SLOT_BRAIN)
|
|
if(QDELETED(carbon_occupant) || QDELETED(occupant_brain))
|
|
return TRUE
|
|
var/obj/item/skillchip/to_be_removed = locate(chipref) in occupant_brain.skillchips
|
|
if(!to_be_removed)
|
|
return TRUE
|
|
toggle_chip_active(to_be_removed)
|
|
return TRUE
|
|
|
|
#undef SKILLCHIP_IMPLANT_TIME
|
|
#undef SKILLCHIP_REMOVAL_TIME
|