mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-15 01:46:30 +01:00
Shell May Cry: A comprehensive rework of synthetics. (#20721)
https://www.youtube.com/watch?v=9mPvZ96pHJI A pull request commissioned by the Synthetic Lore Team to comprehensively rework synthetics (read: IPCs) and how they work in Aurora. The objective is to make IPCs as unique as possible from humans, upgrading the robotic feel and atmosphere, while also preserving a good sense of balance in-game. Key features: - A comprehensive expansion of synthetic organs, all of which now fulfill a purpose: hydraulics, cooling units, power systems, actuators, diagnostics units. - Customizable organs with benefits and drawbacks, such as with cooling units and power systems. - Unique ways to repair the organs and more involved steps. - Unique damage mechanics - every organ has wiring and electronics which affect its functioning, and they are defended by plating which provides natural armour. - Improved and immersive diagnostics. - Unique features, benefits, and drawbacks for every IPC frame. - A rework of the positronic brain, which can be either destroyed or shut down, alongside effects caused by low integrity. - A rework of how EMPs affect IPC organs. - Non-binary damage states for each organ. To-do: - [x] Finish the unique features for each frame. - [x] Look into if mechanical synthskin is possible. - [x] Power system. - [x] Posibrain mechanics. - [ ] Passive cooling expansion. - [x] EMP mechanics. - [x] Repair mechanics. - [x] Mob weight mechanics. - [ ] Gurney for heavy mobs. - [x] New augments. - [ ] IPC tag scanning and flashing. --------- Signed-off-by: Werner <1331699+Arrow768@users.noreply.github.com> Co-authored-by: Matt Atlas <liermattia@gmail.com> Co-authored-by: Geeves <22774890+Geevies@users.noreply.github.com> Co-authored-by: Werner <1331699+Arrow768@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
/obj/item/organ/internal/machine
|
||||
name = "generic machine organ"
|
||||
parent_organ = BP_CHEST
|
||||
organ_tag = "generic machine organ"
|
||||
robotic_sprite = FALSE
|
||||
|
||||
/// The list of organ presets to use. Linked list of ORGAN_PREF to /singleton/synthetic_organ_preset. Use only if your organ has pref settings and presets.
|
||||
/// For an example, see cooling_unit.dm. Remember to also update code\modules\client\preference_setup\general\03_body.dm at line 893!
|
||||
var/list/organ_presets
|
||||
/// The default preset to fall back to if there is no pref (aka people want the base type). Must be of type /singleton/synthetic_organ_preset.
|
||||
var/default_preset
|
||||
/// This variable forces the organ to be of a certain preset and is applied automatically on Initialize. This is used to make subtypes spawn as proper presets.
|
||||
var/forced_preset
|
||||
/// The current preset, if any. Should only ever be null or an instance.
|
||||
var/singleton/synthetic_organ_preset/current_preset
|
||||
|
||||
/// If an organ shows up in the diagnostics suite (the IPC organ verb).
|
||||
var/diagnostics_suite_visible = TRUE
|
||||
|
||||
/// The wiring datum of this organ.
|
||||
var/datum/synthetic_internal/wiring/wiring
|
||||
/// The plating datum of this organ.
|
||||
var/datum/synthetic_internal/plating/plating
|
||||
/// The electronics datum of this organ.
|
||||
var/datum/synthetic_internal/electronics/electronics
|
||||
|
||||
/// The world.time of the last integrity damage event.
|
||||
var/last_integrity_event_time = 0
|
||||
/// The amount of time that has to pass between each integrity damage event.
|
||||
var/integrity_event_cooldown = 60 SECONDS
|
||||
|
||||
/obj/item/organ/internal/machine/Initialize()
|
||||
robotize()
|
||||
. = ..()
|
||||
wiring = new(src)
|
||||
plating = new(src)
|
||||
electronics = new(src)
|
||||
if(forced_preset)
|
||||
apply_preset_data(GET_SINGLETON(forced_preset))
|
||||
|
||||
/obj/item/organ/internal/machine/Destroy()
|
||||
QDEL_NULL(wiring)
|
||||
QDEL_NULL(plating)
|
||||
QDEL_NULL(electronics)
|
||||
if(current_preset)
|
||||
current_preset = null
|
||||
return ..()
|
||||
|
||||
/obj/item/organ/internal/machine/refresh_action_button()
|
||||
. = ..()
|
||||
if(.)
|
||||
if(action_button_name)
|
||||
action.button_icon_state = initial(icon_state)
|
||||
if(action.button)
|
||||
action.button.update_icon()
|
||||
|
||||
/obj/item/organ/internal/machine/rejuvenate()
|
||||
. = ..()
|
||||
diagnostics_suite_visible = initial(diagnostics_suite_visible)
|
||||
wiring.heal_damage(wiring.max_wires)
|
||||
plating.heal_damage(plating.max_health)
|
||||
electronics.heal_damage(electronics.max_integrity)
|
||||
integrity_event_cooldown = initial(integrity_event_cooldown)
|
||||
|
||||
/obj/item/organ/internal/machine/process(seconds_per_tick)
|
||||
..()
|
||||
if(!owner)
|
||||
return
|
||||
|
||||
var/integrity = get_integrity()
|
||||
if(integrity < IPC_INTEGRITY_THRESHOLD_LOW)
|
||||
// This is when things start going Fucking Wrong.
|
||||
do_integrity_damage_effects(integrity)
|
||||
|
||||
if(damage >= max_damage)
|
||||
die()
|
||||
|
||||
/obj/item/organ/internal/machine/die()
|
||||
if(!vital)
|
||||
to_chat(owner, FONT_LARGE(SPAN_MACHINE_WARNING("Some of your sensors go dark - you've lost connection to your [src]!")))
|
||||
diagnostics_suite_visible = FALSE
|
||||
damage = max_damage
|
||||
status |= ORGAN_DEAD
|
||||
death_time = world.time
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
if(owner && vital)
|
||||
owner.death()
|
||||
|
||||
/obj/item/organ/internal/machine/take_internal_damage(amount, silent, forced_damage = FALSE)
|
||||
// Plating defends the internal bits. First, you have to get through it.
|
||||
if(!forced_damage)
|
||||
if(plating.get_status() > 0)
|
||||
amount = plating.take_damage(amount)
|
||||
if(amount <= 0)
|
||||
return
|
||||
|
||||
var/list/component_list = list(wiring, electronics)
|
||||
component_list = shuffle(component_list)
|
||||
// After that, it's open season.
|
||||
for(var/datum/synthetic_internal/internal in component_list)
|
||||
if(internal.get_status())
|
||||
internal.take_damage(amount)
|
||||
if(internal.get_status())
|
||||
break
|
||||
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/machine/emp_act(severity)
|
||||
. = ..()
|
||||
switch(severity)
|
||||
if(EMP_HEAVY)
|
||||
take_internal_damage(4 * emp_coeff)
|
||||
if(EMP_LIGHT)
|
||||
take_internal_damage(2 * emp_coeff)
|
||||
|
||||
/**
|
||||
* Called when prefs are synced to the organ to set the proper synthetic organ preset. Turns the pref into a preset singleton.
|
||||
* Remember that the base type is the default, AKA when no prefs are set that organ will spawn.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/proc/get_preset_from_pref(organ_pref)
|
||||
var/singleton/synthetic_organ_preset/new_preset
|
||||
if(organ_pref && (organ_pref in organ_presets))
|
||||
new_preset = GET_SINGLETON(organ_presets[organ_pref])
|
||||
else
|
||||
new_preset = GET_SINGLETON(default_preset)
|
||||
|
||||
if(!istype(new_preset))
|
||||
crash_with("Invalid organ preset [new_preset]!")
|
||||
|
||||
apply_preset_data(new_preset)
|
||||
|
||||
/**
|
||||
* Applies the preset data to the organ. Needs a preset object supplied, not just a type.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/proc/apply_preset_data(singleton/synthetic_organ_preset/preset)
|
||||
if(!istype(preset))
|
||||
crash_with("apply_preset_data called with improper preset [preset]!")
|
||||
|
||||
current_preset = preset
|
||||
preset.apply_preset(src)
|
||||
owner.update_action_buttons()
|
||||
|
||||
/**
|
||||
* This is a function used to return an overall integrity number that takes
|
||||
* the average of wiring + electronics, and then subtracts current damage from them. Those are the soft bits - plating is what defends them.
|
||||
* Returns a percentage.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/proc/get_integrity()
|
||||
var/wiring_status = wiring ? wiring.get_status() : 0
|
||||
var/electronics_status = electronics ? electronics.get_status() : 0
|
||||
var/internal_component_status = (wiring_status + electronics_status) / 2
|
||||
var/damage_status = damage / 2
|
||||
return max(internal_component_status - damage_status, 0)
|
||||
|
||||
/**
|
||||
* This function returns a damage multiplier, starting from 1 and dropping to 0, depending on damage.
|
||||
* Returns a multiplier between 0 and 1.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/proc/get_damage_multiplier()
|
||||
if(!damage)
|
||||
return 1
|
||||
|
||||
return 1 - (damage / max_damage)
|
||||
|
||||
/**
|
||||
* This proc is used to determine which integrity damage threshold we are at, and execute the proper proc..
|
||||
* Only called below 75%.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/proc/do_integrity_damage_effects(integrity)
|
||||
if(!owner)
|
||||
return
|
||||
|
||||
var/obj/item/organ/internal/machine/posibrain/brain = owner.internal_organs_by_name[BP_BRAIN]
|
||||
if(!istype(brain)) //??? should be dead anyway so who cares
|
||||
return
|
||||
|
||||
// The cooldowns are handled in the other procs because feasibly we might want to add different cooldowns for different organs and different thresholds.
|
||||
if(can_do_integrity_damage())
|
||||
switch(integrity)
|
||||
if(IPC_INTEGRITY_THRESHOLD_MEDIUM to IPC_INTEGRITY_THRESHOLD_LOW)
|
||||
low_integrity_damage(integrity)
|
||||
if(IPC_INTEGRITY_THRESHOLD_HIGH to IPC_INTEGRITY_THRESHOLD_MEDIUM)
|
||||
medium_integrity_damage(integrity)
|
||||
if(IPC_INTEGRITY_THRESHOLD_VERY_HIGH to IPC_INTEGRITY_THRESHOLD_HIGH)
|
||||
high_integrity_damage(integrity)
|
||||
|
||||
/**
|
||||
* Returns the damage percentage of total integrity.
|
||||
* Basically the opposite of get_integrity(). Useful for damage probabilities, where you want a scaling number the more integrity damage there is on an organ.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/proc/get_integrity_damage_probability(integrity)
|
||||
// integrity is a probability, so first of all check the difference between max probability and probaiblity
|
||||
// 100 - 100 = 0 | 100 - 50 = 50
|
||||
var/damage_probability = 100 - integrity
|
||||
return damage_probability
|
||||
|
||||
/**
|
||||
* Returns TRUE if the cooldown for integrity damage has expired.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/proc/can_do_integrity_damage()
|
||||
if(last_integrity_event_time + integrity_event_cooldown > world.time)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* The proc called to do low-intensity integrity damage (50 to 75% damage).
|
||||
*/
|
||||
/obj/item/organ/internal/machine/proc/low_integrity_damage(integrity)
|
||||
last_integrity_event_time = world.time
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* The proc called to do mediumlow-intensity integrity damage (25 to 50% damage).
|
||||
*/
|
||||
/obj/item/organ/internal/machine/proc/medium_integrity_damage(integrity)
|
||||
last_integrity_event_time = world.time
|
||||
var/obj/item/organ/internal/machine/posibrain/brain = owner.internal_organs_by_name[BP_BRAIN]
|
||||
if(istype(brain))
|
||||
brain.damage_integrity(1)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* The proc called to do high-intensity integrity damage (0 to 25% damage).
|
||||
*/
|
||||
/obj/item/organ/internal/machine/proc/high_integrity_damage(integrity)
|
||||
last_integrity_event_time = world.time
|
||||
var/obj/item/organ/internal/machine/posibrain/brain = owner.internal_organs_by_name[BP_BRAIN]
|
||||
if(istype(brain))
|
||||
brain.damage_integrity(2)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Returns extra diagnostics info, viewable from the diagnostics unit.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/proc/get_diagnostics_info()
|
||||
return
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Synthetic organ presets are a way to allow us to have customizable and easily expandable presets for different organs.
|
||||
* This is partly due to the limitations of the organ modification system, which does not support manually selecting different types,
|
||||
* but also to allow the creation of different presets to do different things in a modular and easily expandable way.
|
||||
*/
|
||||
/singleton/synthetic_organ_preset
|
||||
/// The name the synthetic organ will have.
|
||||
var/name = "epic synthetic organ"
|
||||
/// The description synthetic organ will get.
|
||||
var/desc = "It is super epic and cool."
|
||||
/// Replacement icon state. Leave null if you don't want to replace the original state.
|
||||
var/icon_state
|
||||
|
||||
/singleton/synthetic_organ_preset/proc/apply_preset(obj/item/organ/internal/machine/organ)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
if(!istype(organ))
|
||||
crash_with("Synthetic organ preset called with invalid organ [organ]")
|
||||
|
||||
organ.name = name
|
||||
organ.desc = desc
|
||||
if(icon_state)
|
||||
organ.icon_state = icon_state
|
||||
@@ -0,0 +1,346 @@
|
||||
/obj/item/organ/internal/machine/access_port
|
||||
name = "universal access port"
|
||||
desc = "A slot built into nearly all synthetics for universal access to information such as diagnostics or internal processes."
|
||||
icon = 'icons/obj/organs/ipc_organs.dmi'
|
||||
icon_state = "ipc_port"
|
||||
organ_tag = BP_ACCESS_PORT
|
||||
parent_organ = BP_HEAD
|
||||
|
||||
action_button_name = "Extend or Retract Cable"
|
||||
|
||||
max_damage = 30
|
||||
relative_size = 15
|
||||
|
||||
/// Our access cable, which can be extended to connect into things.
|
||||
var/obj/item/access_cable/access_cable = /obj/item/access_cable/synthetic
|
||||
/// The internal port. This is where things get connected to to retrieve information or do effects.
|
||||
var/obj/item/internal_port
|
||||
|
||||
/obj/item/organ/internal/machine/access_port/Initialize()
|
||||
. = ..()
|
||||
access_cable = new access_cable(src, src, owner)
|
||||
RegisterSignal(access_cable, COMSIG_QDELETING, PROC_REF(clear_cable))
|
||||
add_verb(owner, /mob/living/carbon/human/proc/access_cable)
|
||||
|
||||
/obj/item/organ/internal/machine/access_port/Destroy()
|
||||
QDEL_NULL(access_cable)
|
||||
QDEL_NULL(internal_port)
|
||||
return ..()
|
||||
|
||||
/obj/item/organ/internal/machine/access_port/replaced(mob/living/carbon/human/target, obj/item/organ/external/affected)
|
||||
. = ..()
|
||||
add_verb(owner, /mob/living/carbon/human/proc/access_cable)
|
||||
|
||||
/obj/item/organ/internal/machine/access_port/removed(mob/living/carbon/human/target, mob/living/user)
|
||||
remove_verb(owner, /mob/living/carbon/human/proc/access_cable)
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/machine/access_port/attack_self(mob/user)
|
||||
. = ..()
|
||||
if(user.stat == DEAD)
|
||||
return
|
||||
|
||||
if(user.incapacitated(INCAPACITATION_KNOCKOUT|INCAPACITATION_STUNNED))
|
||||
return
|
||||
|
||||
if(!access_cable)
|
||||
to_chat(user, SPAN_WARNING("You don't have an access cable anymore!"))
|
||||
return
|
||||
|
||||
// it's an organ, should never be not human type
|
||||
var/mob/living/carbon/human/synth = user
|
||||
if(synth.get_active_hand())
|
||||
to_chat(synth, SPAN_WARNING("You need a free hand to retrieve your universal access cable!"))
|
||||
return
|
||||
|
||||
if(access_cable.loc != src)
|
||||
// not sure how NOT having a target and being here would work, but it's a fallback just in case things bug out
|
||||
if(access_cable.target)
|
||||
visible_message(SPAN_NOTICE("[owner] disconnects their [access_cable] from \the [access_cable.target]."))
|
||||
access_cable.target.remove_cable(access_cable)
|
||||
access_cable.clear_cable_full()
|
||||
access_cable.forceMove(src)
|
||||
return
|
||||
|
||||
if(get_integrity() < 75)
|
||||
to_chat(user, SPAN_WARNING("You struggle to pry the cable out of the damaged port..."))
|
||||
if(!do_after(user, 2 SECONDS))
|
||||
return
|
||||
|
||||
synth.visible_message(SPAN_NOTICE("[synth] extends their universal access cable from their neck."), SPAN_NOTICE("You retrieve your universal access cable from your neck."))
|
||||
synth.put_in_active_hand(access_cable)
|
||||
|
||||
/**
|
||||
* This proc is called whenever anything is inserted into the internal port.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/access_port/proc/insert_item(obj/item/jack)
|
||||
SIGNAL_HANDLER
|
||||
if(internal_port)
|
||||
crash_with("Insert_item with [jack] on access port called with [internal_port] of [owner] already present!")
|
||||
|
||||
internal_port = jack
|
||||
jack.forceMove(src)
|
||||
RegisterSignal(internal_port, COMSIG_QDELETING, PROC_REF(clear_port))
|
||||
to_chat(owner, SPAN_MACHINE_WARNING("Internal firewall notice: [internal_port] inserted into [src]."))
|
||||
|
||||
/**
|
||||
* This proc is called whenever the access cable is, for some reason, qdeleted (like with an explosion).
|
||||
*/
|
||||
/obj/item/organ/internal/machine/access_port/proc/clear_cable()
|
||||
SIGNAL_HANDLER
|
||||
if(!access_cable)
|
||||
return
|
||||
|
||||
UnregisterSignal(access_cable, COMSIG_QDELETING)
|
||||
access_cable = null
|
||||
to_chat(owner, SPAN_WARNING("You lose contact with your access cable!"))
|
||||
|
||||
/**
|
||||
* This proc is called whenever the internal access port is emptied of whatever was in there previously.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/access_port/proc/clear_port()
|
||||
if(!internal_port)
|
||||
return
|
||||
|
||||
if(istype(internal_port, /obj/item/access_cable))
|
||||
var/obj/item/access_cable/ejected_cable = internal_port
|
||||
ejected_cable.target = null
|
||||
|
||||
UnregisterSignal(internal_port, COMSIG_QDELETING)
|
||||
internal_port = null
|
||||
|
||||
/obj/item/organ/internal/machine/access_port/insert_cable(obj/item/access_cable/cable, mob/user)
|
||||
. = ..()
|
||||
insert_item(cable)
|
||||
cable.create_cable(owner)
|
||||
|
||||
/obj/item/organ/internal/machine/access_port/cable_interact(obj/item/access_cable/cable, mob/user)
|
||||
var/obj/item/organ/internal/machine/internal_diagnostics/diagnostics_unit = owner.internal_organs_by_name[BP_DIAGNOSTICS_SUITE]
|
||||
if(!diagnostics_unit)
|
||||
to_chat(user, SPAN_WARNING("There is no diagnostics unit!"))
|
||||
return
|
||||
|
||||
var/obj/item/organ/internal/machine/posibrain/posibrain = owner.internal_organs_by_name[BP_BRAIN]
|
||||
if(istype(posibrain))
|
||||
if(posibrain.firewall && !user.stat)
|
||||
to_chat(user, SPAN_MACHINE_WARNING("Firewall block detected. Aborting."))
|
||||
to_chat(diagnostics_unit.owner, SPAN_MACHINE_WARNING("Your firewall has blocked an unrecognized access attempt."))
|
||||
return
|
||||
|
||||
diagnostics_unit.open_diagnostics(user)
|
||||
|
||||
/obj/item/access_cable
|
||||
name = "external access cable"
|
||||
desc = "A cable with universal access pins at its end. This is meant for jacking into synthetics to access their data."
|
||||
icon = 'icons/obj/organs/ipc_organs.dmi'
|
||||
icon_state = "cable"
|
||||
pickup_sound = 'sound/species/synthetic/access_cable_out.ogg'
|
||||
w_class = WEIGHT_CLASS_NO_CONTAINER
|
||||
|
||||
/// Where this cable is extending from.
|
||||
var/obj/source
|
||||
/// If the source is an object in another object (like an organ), then this is where we actually want the cable beam to extend from physically.
|
||||
var/atom/movable/beam_anchor
|
||||
/// Where this cable is attached to.
|
||||
var/obj/target
|
||||
/// The actual target of the /datum/beam coming from the cable.
|
||||
var/atom/movable/beam_target
|
||||
/// The beam connecting us to the source.
|
||||
var/datum/beam/cable
|
||||
/// The range this cable has. Past this range, it will disconnect.
|
||||
var/range = 3
|
||||
|
||||
/obj/item/access_cable/Initialize(mapload, atom/movable/cable_source, atom/movable/cable_beam_anchor)
|
||||
. = ..()
|
||||
if(!cable_source)
|
||||
log_debug("Access cable spawned without a source: [x] [y] [z]")
|
||||
qdel_self()
|
||||
return
|
||||
|
||||
source = cable_source
|
||||
if(cable_beam_anchor)
|
||||
beam_anchor = cable_beam_anchor
|
||||
RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(check_retract_range), TRUE)
|
||||
|
||||
/obj/item/access_cable/Destroy()
|
||||
source = null
|
||||
target = null
|
||||
beam_anchor = null
|
||||
beam_target = null
|
||||
if(cable)
|
||||
cable.End()
|
||||
QDEL_NULL(cable)
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* When the cable is actually taken out of an object and thus is shown in world.
|
||||
* The parameters here might be different from the source/target variables on the cable itself.
|
||||
* For example, the source of a synthetic access cable is the power port, although that's physically inside a human mob and so we can't draw a beam to it.
|
||||
* We have to draw a beam from the human in that case.
|
||||
*/
|
||||
/obj/item/access_cable/proc/create_cable(var/atom/new_target)
|
||||
clear_cable_visuals()
|
||||
beam_target = new_target
|
||||
RegisterSignal(beam_anchor ? beam_anchor : source, COMSIG_MOVABLE_MOVED, PROC_REF(check_retract_range), TRUE)
|
||||
RegisterSignal(beam_target, COMSIG_MOVABLE_MOVED, PROC_REF(check_retract_range), TRUE)
|
||||
cable = new(beam_anchor ? beam_anchor : source, beam_target, beam_icon_state = "cable", time = -1, maxdistance = range + 1)
|
||||
cable.Start()
|
||||
|
||||
/obj/item/access_cable/dropped(mob/user)
|
||||
. = ..()
|
||||
clear_cable_visuals()
|
||||
create_cable(src)
|
||||
|
||||
/obj/item/access_cable/pickup(mob/user)
|
||||
. = ..()
|
||||
clear_cable_visuals()
|
||||
|
||||
/**
|
||||
* Signal handler.
|
||||
* If the cable moves beyond its range, automatically retract it.
|
||||
*/
|
||||
/obj/item/access_cable/proc/check_retract_range()
|
||||
SIGNAL_HANDLER
|
||||
if(source == loc)
|
||||
return
|
||||
|
||||
if(get_dist(source, src) > range)
|
||||
retract()
|
||||
|
||||
/**
|
||||
* Automatically drop the cable and then retract it to the parent.
|
||||
*/
|
||||
/obj/item/access_cable/proc/retract()
|
||||
visible_message(SPAN_NOTICE("\The [src] automatically retracts into \the [source]!"))
|
||||
forceMove(source)
|
||||
clear_cable_full()
|
||||
|
||||
/**
|
||||
* Retracts the cable back into the parent object.
|
||||
*/
|
||||
/obj/item/access_cable/proc/clear_cable_full()
|
||||
UnregisterSignal(beam_anchor ? beam_anchor : source, COMSIG_MOVABLE_MOVED)
|
||||
if(target)
|
||||
target.remove_cable(src)
|
||||
target = null
|
||||
clear_cable_visuals()
|
||||
|
||||
/**
|
||||
* Only clears the cable visuals (so, just the beam).
|
||||
*/
|
||||
/obj/item/access_cable/proc/clear_cable_visuals()
|
||||
if(beam_target)
|
||||
UnregisterSignal(beam_target, COMSIG_MOVABLE_MOVED)
|
||||
|
||||
beam_target = null
|
||||
if(cable)
|
||||
cable.End()
|
||||
QDEL_NULL(cable)
|
||||
|
||||
/obj/item/access_cable/proc/target_interact(mob/user)
|
||||
if(!target || !user)
|
||||
return
|
||||
|
||||
target.cable_interact(src, user)
|
||||
|
||||
/obj/item/access_cable/attack(mob/living/target_mob, mob/living/user, target_zone)
|
||||
if(ishuman(target_mob))
|
||||
var/mob/living/carbon/human/human = target_mob
|
||||
if(!isipc(human))
|
||||
to_chat(user, SPAN_WARNING("Where are you planning to put that...?"))
|
||||
return
|
||||
|
||||
var/obj/item/organ/internal/machine/access_port/access_port = human.internal_organs_by_name[BP_ACCESS_PORT]
|
||||
if(!access_port)
|
||||
to_chat(user, SPAN_WARNING("[human] does not have an access port!"))
|
||||
return
|
||||
|
||||
if(access_port.access_cable == src)
|
||||
to_chat(user, SPAN_WARNING("You can't put your own cable into your own access port, as funny as it would be."))
|
||||
return
|
||||
|
||||
if(access_port.is_broken())
|
||||
to_chat(user, SPAN_WARNING("[human]'s access port is completely broken! There's no way you can jack into it!"))
|
||||
return
|
||||
|
||||
if(access_port.internal_port)
|
||||
to_chat(user, SPAN_WARNING("The access port is already occupied by [access_port.internal_port]!"))
|
||||
return
|
||||
|
||||
if(user != human)
|
||||
if(human.client && !human.incapacitated(INCAPACITATION_FORCELYING|INCAPACITATION_RESTRAINED|INCAPACITATION_BUCKLED_FULLY|INCAPACITATION_BUCKLED_PARTIALLY))
|
||||
var/request = tgui_alert(human, "[user] would like to access your access port. Allow them?", "Access Port", list("Yes", "No"))
|
||||
if(request != "Yes")
|
||||
human.visible_message(SPAN_NOTICE("[human] pushes away [user]'s [src]."))
|
||||
return
|
||||
|
||||
user.visible_message(SPAN_WARNING("[user] tries to jack \the [src] into [human]'s access port..."))
|
||||
if(!do_mob(user, human, 2 SECONDS))
|
||||
return
|
||||
user.visible_message(SPAN_WARNING("[user] jacks \the [src] into [human]'s access port!"))
|
||||
|
||||
else
|
||||
user.visible_message(SPAN_WARNING("[user] jacks \the [src] into their access port!"), SPAN_WARNING("You jack \the [src] into your access port!"))
|
||||
|
||||
user.drop_from_inventory(src, get_turf(user))
|
||||
access_port.insert_cable(src, user)
|
||||
else
|
||||
. = ..()
|
||||
|
||||
/obj/item/access_cable/synthetic
|
||||
name = "universal access cable"
|
||||
desc = "A cable with universal access pins at its end. This particular access cable comes with most synthetics' access ports for quick access to electronics, firewalls, or other synthetics' diagnostics systems."
|
||||
|
||||
/obj/item/access_cable/synthetic/mechanics_hints()
|
||||
. = ..()
|
||||
. += "To retract this cable into your port, <span class='notice'>activate it in-hand."
|
||||
|
||||
/obj/item/access_cable/synthetic/attack_self(mob/user, modifiers)
|
||||
if(isipc(user))
|
||||
var/mob/living/carbon/human/synth = user
|
||||
var/obj/item/organ/internal/machine/access_port/access_port = synth.internal_organs_by_name[BP_ACCESS_PORT]
|
||||
if(!access_port)
|
||||
to_chat(synth, SPAN_WARNING("Where's your access port?!"))
|
||||
return ..()
|
||||
|
||||
if(access_port.is_broken())
|
||||
to_chat(synth, SPAN_WARNING("Your access port is completely broken! It won't go in!"))
|
||||
return ..()
|
||||
|
||||
if(access_port.get_integrity() <= IPC_INTEGRITY_THRESHOLD_MEDIUM)
|
||||
synth.visible_message(SPAN_WARNING("[synth] tries to jam [synth.get_pronoun("his")] access cable back into their access port..."), SPAN_WARNING("You try to jam your access cable back into your access port..."))
|
||||
if(!do_after(synth, 3 SECONDS))
|
||||
return ..()
|
||||
|
||||
synth.visible_message(SPAN_NOTICE("[synth] retracts [synth.get_pronoun("his")] access cable back into their access port."), SPAN_NOTICE("You retract your access cable back into your access port."))
|
||||
synth.drop_from_inventory(src, get_turf(src))
|
||||
forceMove(access_port)
|
||||
clear_cable_full()
|
||||
playsound(user, 'sound/species/synthetic/access_cable_in.ogg', 50)
|
||||
|
||||
/mob/living/carbon/human/proc/access_cable()
|
||||
set name = "Access Cable"
|
||||
set category = "Object"
|
||||
|
||||
if(!isipc(src))
|
||||
return
|
||||
|
||||
var/obj/item/organ/internal/machine/access_port/access_port = internal_organs_by_name[BP_ACCESS_PORT]
|
||||
if(!access_port)
|
||||
to_chat(src, SPAN_WARNING("Where's your access port?!"))
|
||||
return
|
||||
|
||||
if(access_port.is_broken())
|
||||
to_chat(src, SPAN_WARNING("Your access port's only feedback is crackling static and jumbled code!"))
|
||||
return
|
||||
|
||||
var/obj/item/access_cable/access_cable = access_port.access_cable
|
||||
if(!access_cable)
|
||||
to_chat(src, SPAN_WARNING("Your access cable is missing!"))
|
||||
return
|
||||
|
||||
if(!access_cable.target)
|
||||
to_chat(src, SPAN_WARNING("You need to connect your cable to something first!"))
|
||||
return
|
||||
|
||||
access_cable.target_interact(src)
|
||||
@@ -0,0 +1,31 @@
|
||||
/obj/item/organ/internal/machine/actuators
|
||||
name = "actuators"
|
||||
desc = "This mixture of electronic, pneumatic and hydraulic components allow a positronic chassis' arms to produce force and torque to allow them to move their artificial limbs."
|
||||
icon = 'icons/obj/organs/ipc_organs.dmi'
|
||||
icon_state = "ipc_actuator"
|
||||
|
||||
max_damage = 40
|
||||
relative_size = 70
|
||||
|
||||
/obj/item/organ/internal/machine/actuators/left
|
||||
name = "left arm actuators"
|
||||
organ_tag = BP_ACTUATORS_LEFT
|
||||
parent_organ = BP_L_ARM
|
||||
organ_tag = BP_ACTUATORS_LEFT
|
||||
|
||||
/obj/item/organ/internal/machine/actuators/right
|
||||
name = "right arm actuators"
|
||||
organ_tag = BP_ACTUATORS_RIGHT
|
||||
parent_organ = BP_R_ARM
|
||||
organ_tag = BP_ACTUATORS_RIGHT
|
||||
|
||||
/obj/item/organ/internal/machine/actuators/high_integrity_damage(integrity)
|
||||
if(prob(get_integrity_damage_probability()))
|
||||
spark(owner, 5, GLOB.alldirs)
|
||||
to_chat(owner, SPAN_WARNING("Your [src] malfunction, making you drop what you're holding!"))
|
||||
switch(parent_organ)
|
||||
if(BP_L_ARM)
|
||||
owner.drop_l_hand()
|
||||
if(BP_R_ARM)
|
||||
owner.drop_r_hand()
|
||||
. = ..()
|
||||
@@ -0,0 +1,255 @@
|
||||
#define COOLING_UNIT_DEFAULT_THERMOSTAT_MAX 323.15
|
||||
#define COOLING_UNIT_DEFAULT_MAX_SAFE_TEMP 423.15
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit
|
||||
name = "air cooling unit"
|
||||
desc = "One of the most complex and vital components of a synthetic. It regulates its internal temperature and prevents the chassis from overheating."
|
||||
icon = 'icons/obj/organs/ipc_organs.dmi'
|
||||
icon_state = "ipc_fans"
|
||||
organ_tag = BP_COOLING_UNIT
|
||||
parent_organ = BP_CHEST
|
||||
possible_modifications = list("Air Cooling", "Liquid Cooling", "Passive Cooling")
|
||||
organ_presets = list(
|
||||
ORGAN_PREF_AIRCOOLED = /singleton/synthetic_organ_preset/cooling_unit/air,
|
||||
ORGAN_PREF_LIQUIDCOOLED = /singleton/synthetic_organ_preset/cooling_unit/liquid,
|
||||
ORGAN_PREF_PASSIVECOOLED = /singleton/synthetic_organ_preset/cooling_unit/passive
|
||||
)
|
||||
default_preset = /singleton/synthetic_organ_preset/cooling_unit/air
|
||||
action_button_name = "Regulate Thermostat"
|
||||
|
||||
max_damage = 70
|
||||
relative_size = 50
|
||||
|
||||
/// The power consumed when we are cooling down.
|
||||
var/base_power_consumption = 8
|
||||
/// The passive temperature change. Basically, cooling units counteract an IPC's passive temperature gain. But the IPC's temperature goes to get itself fucked if the cooling unit dies.
|
||||
/// Remember, can be negative or positive. Depends on what we're trying to stabilize towards.
|
||||
var/passive_temp_change = 2
|
||||
/// The temperature that this cooling unit tries to regulate towards.
|
||||
var/thermostat = T20C
|
||||
/// The minimum value the thermostat can reach. 0C.
|
||||
var/thermostat_min = T0C
|
||||
/// The maximum value the thermostat can reach. 50C.
|
||||
var/thermostat_max = COOLING_UNIT_DEFAULT_THERMOSTAT_MAX
|
||||
/// Can this cooling unit work in space?
|
||||
var/spaceproof = FALSE
|
||||
/// The temperature at which this cooling unit will disable running if safeties are on.
|
||||
var/maximum_safe_temperature = COOLING_UNIT_DEFAULT_MAX_SAFE_TEMP
|
||||
/// If the safeties that automatically disable sprinting when bodytemperature exceeds safe limits are disabled or not.
|
||||
var/temperature_safety = TRUE
|
||||
/// If the safety has been burnt and thus will not engage.
|
||||
var/safety_burnt = FALSE
|
||||
/// If the thermostat is locked and thus cannot be changed. Used for spooky effects, like the high integrity damage in the positronic brain.
|
||||
var/locked_thermostat = FALSE
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/Initialize()
|
||||
. = ..()
|
||||
if(owner)
|
||||
RegisterSignal(owner, COMSIG_IPC_HAS_SPRINTED, PROC_REF(handle_safeties))
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/attack_self(var/mob/user)
|
||||
. = ..()
|
||||
if(owner.last_special > world.time)
|
||||
return
|
||||
|
||||
if(user.stat == DEAD)
|
||||
return
|
||||
|
||||
if(user.incapacitated(INCAPACITATION_KNOCKOUT|INCAPACITATION_STUNNED))
|
||||
return
|
||||
|
||||
ui_interact(user)
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/ui_interact(mob/user, datum/tgui/ui)
|
||||
. = ..()
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "CoolingUnitThermostat", "Cooling Unit Regulation", 400, 600)
|
||||
ui.open()
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
// K = C + 273.15 | C = K – 273.15
|
||||
// we want to pass celsius for readability,
|
||||
// the average american can't figure out celsius, let alone kelvin
|
||||
var/broken = is_broken()
|
||||
data["broken"] = broken
|
||||
if(!broken)
|
||||
data["thermostat"] = thermostat - 273.15
|
||||
data["thermostat_min"] = thermostat_min - 273.15
|
||||
data["thermostat_max"] = thermostat_max - 273.15
|
||||
data["passive_temp_change"] = passive_temp_change
|
||||
data["bodytemperature"] = owner.bodytemperature - 273.15
|
||||
data["temperature_safety"] = temperature_safety
|
||||
|
||||
var/estimated_power_consumption = base_power_consumption
|
||||
var/integrity = get_integrity()
|
||||
if(integrity < IPC_INTEGRITY_THRESHOLD_MEDIUM)
|
||||
estimated_power_consumption = rand(-50, 100 + (100 - integrity))
|
||||
|
||||
if(thermostat < initial(thermostat))
|
||||
// The higher the distance between the maximum thermostat setting and what we're actually at, the harder the cooling unit will work to cool us.
|
||||
var/temperature_distance = round(thermostat_max - thermostat, 1)
|
||||
estimated_power_consumption = temperature_distance * 0.5
|
||||
else if(thermostat > initial(thermostat))
|
||||
// higher thermostat = less power usage
|
||||
estimated_power_consumption = -(thermostat_max / thermostat) - 0.5
|
||||
|
||||
data["estimated_power_consumption"] = max(0, base_power_consumption + estimated_power_consumption)
|
||||
data["safety_burnt"] = safety_burnt
|
||||
return data
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if(action == "change_thermostat")
|
||||
if(locked_thermostat)
|
||||
to_chat(owner, SPAN_MACHINE_WARNING("You can't change the thermostat manually! It just returns an error!"))
|
||||
return FALSE
|
||||
|
||||
var/new_thermostat = params["change_thermostat"]
|
||||
// remember we are getting passed celsius here
|
||||
new_thermostat += T0C
|
||||
if(new_thermostat >= thermostat_min && new_thermostat <= thermostat_max)
|
||||
thermostat = new_thermostat
|
||||
. = TRUE
|
||||
|
||||
if(action == "temperature_safety")
|
||||
if(safety_burnt)
|
||||
return
|
||||
|
||||
temperature_safety = !temperature_safety
|
||||
to_chat(usr, temperature_safety ? SPAN_NOTICE("You re-enable your temperature safety.") : SPAN_WARNING("You disable your temperature safety!"))
|
||||
. = TRUE
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/process(seconds_per_tick)
|
||||
. = ..()
|
||||
if(!owner)
|
||||
return
|
||||
|
||||
// Cooling units try to counteract external temperature by stabilizing towards ambient temperature.
|
||||
var/turf/T = get_turf(owner)
|
||||
if(!T)
|
||||
// Some odd scenarios can cause there to be no turf. Like getting teleported into the game from lobby.
|
||||
return
|
||||
|
||||
var/datum/gas_mixture/ambient = T.return_air()
|
||||
// Too much heat is bad for the cooling unit.
|
||||
if(owner.bodytemperature > species.heat_level_1)
|
||||
if(prob(owner.bodytemperature * 0.1))
|
||||
take_internal_damage(owner.bodytemperature * 0.01)
|
||||
|
||||
var/temperature_change = passive_temp_change
|
||||
if(owner.wear_suit)
|
||||
if(!spaceproof && istype(owner.wear_suit, /obj/item/clothing/suit/space))
|
||||
//cooling is going to SUCK if you have heat-regulating clothes
|
||||
if(owner.bodytemperature < species.heat_level_3)
|
||||
owner.bodytemperature = min(owner.bodytemperature + 5, owner.species.heat_level_2)
|
||||
temperature_change *= 0.5
|
||||
|
||||
// Check if there is somehow no air, or if we are in an ambient without enough air to properly cool us.
|
||||
if((!ambient || (ambient && owner.calculate_affecting_pressure(ambient.return_pressure()) < owner.species.warning_low_pressure)))
|
||||
if(!spaceproof)
|
||||
temperature_change *= 0
|
||||
else
|
||||
temperature_change = min(temperature_change * 3, 10) //give it a little boost to counteract temp gain in space
|
||||
|
||||
if(is_broken())
|
||||
temperature_change *= 0.5 //uh oh
|
||||
else if(is_bruised())
|
||||
temperature_change *= 0.75
|
||||
|
||||
// Now let's start cooling down!
|
||||
// No power, no party.
|
||||
var/obj/item/organ/internal/machine/power_core/cell = owner.internal_organs_by_name[BP_CELL]
|
||||
if(!istype(cell))
|
||||
return
|
||||
|
||||
// The lower our thermostat setting, the more power we consume.
|
||||
var/extra_efficiency_multiplier = 1
|
||||
var/extra_power_consumption = 0
|
||||
if(thermostat < initial(thermostat))
|
||||
// The higher the distance between the maximum thermostat setting and what we're actually at, the harder the cooling unit will work to cool us.
|
||||
var/temperature_distance = round(thermostat_max - thermostat, 1)
|
||||
extra_power_consumption = temperature_distance * 0.5
|
||||
extra_efficiency_multiplier = temperature_distance * 0.05
|
||||
else if(thermostat > initial(thermostat))
|
||||
// higher thermostat = less power usage
|
||||
extra_power_consumption = -(thermostat_max / thermostat) - 0.5
|
||||
|
||||
if(thermostat < owner.bodytemperature)
|
||||
owner.bodytemperature = max(owner.bodytemperature - temperature_change * extra_efficiency_multiplier, thermostat)
|
||||
cell.use(max(0, base_power_consumption + extra_power_consumption))
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/low_integrity_damage(integrity)
|
||||
if(get_integrity_damage_probability())
|
||||
to_chat(owner, SPAN_WARNING("Your temperature sensors pick up a spike in temperature."))
|
||||
owner.bodytemperature = min(owner.bodytemperature + 10, owner.species.heat_level_2)
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/medium_integrity_damage(integrity)
|
||||
if(get_integrity_damage_probability() / 2)
|
||||
to_chat(owner, SPAN_WARNING("Your thermostat's temperature setting goes haywire!"))
|
||||
thermostat = rand(thermostat_min, thermostat_max)
|
||||
owner.bodytemperature = min(owner.bodytemperature + 20, owner.species.heat_level_2)
|
||||
|
||||
if(prob(25) && !safety_burnt)
|
||||
to_chat(owner, SPAN_WARNING("Your temperature safeties burn out! They won't work anymore!"))
|
||||
safety_burnt = TRUE
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/high_integrity_damage(integrity)
|
||||
if(get_integrity_damage_probability() / 3)
|
||||
if(spaceproof)
|
||||
playsound(owner, pick(SOUNDS_LASER_METAL), 50)
|
||||
to_chat(owner, SPAN_DANGER(FONT_LARGE("Your laminar cooling stratum has melted. Your cooling unit will not work in space anymore!")))
|
||||
spaceproof = FALSE
|
||||
else if(thermostat_min < (initial(thermostat_min) + 100))
|
||||
playsound(owner, pick(SOUNDS_LASER_METAL), 50)
|
||||
to_chat(owner, SPAN_DANGER(FONT_LARGE("Parts of your cooling unit melt away...!")))
|
||||
update_thermostat(thermostat_min + round(rand(10, 50)))
|
||||
owner.bodytemperature = min(owner.bodytemperature + 30, owner.species.heat_level_2)
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/proc/update_thermostat(new_thermostat_min, new_thermostat_max)
|
||||
if(new_thermostat_min)
|
||||
thermostat_min = new_thermostat_min
|
||||
if(thermostat < new_thermostat_min)
|
||||
thermostat = new_thermostat_min
|
||||
|
||||
if(thermostat_max < new_thermostat_max)
|
||||
thermostat_max = new_thermostat_min
|
||||
|
||||
if(new_thermostat_max)
|
||||
thermostat_max = new_thermostat_max
|
||||
if(new_thermostat_max < thermostat_min)
|
||||
thermostat_min = new_thermostat_max
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/proc/handle_safeties()
|
||||
if(!owner)
|
||||
return
|
||||
|
||||
if((owner.bodytemperature > maximum_safe_temperature) && temperature_safety && !safety_burnt)
|
||||
to_chat(owner, SPAN_DANGER("Your temperature safeties engage and stop you from running further!"))
|
||||
owner.balloon_alert(owner, "safeties engaged")
|
||||
owner.m_intent = M_WALK
|
||||
owner.hud_used.move_intent.update_move_icon(owner)
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/xion
|
||||
spaceproof = TRUE
|
||||
organ_presets = list(
|
||||
ORGAN_PREF_AIRCOOLED = /singleton/synthetic_organ_preset/cooling_unit/air_xion,
|
||||
ORGAN_PREF_LIQUIDCOOLED = /singleton/synthetic_organ_preset/cooling_unit/liquid_xion,
|
||||
ORGAN_PREF_PASSIVECOOLED = /singleton/synthetic_organ_preset/cooling_unit/passive_xion
|
||||
)
|
||||
default_preset = /singleton/synthetic_organ_preset/cooling_unit/air_xion
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/zenghu
|
||||
organ_presets = list(
|
||||
ORGAN_PREF_AIRCOOLED = /singleton/synthetic_organ_preset/cooling_unit/air_zenghu,
|
||||
ORGAN_PREF_LIQUIDCOOLED = /singleton/synthetic_organ_preset/cooling_unit/liquid_zenghu,
|
||||
ORGAN_PREF_PASSIVECOOLED = /singleton/synthetic_organ_preset/cooling_unit/passive_zenghu
|
||||
)
|
||||
default_preset = /singleton/synthetic_organ_preset/cooling_unit/air_zenghu
|
||||
|
||||
#undef COOLING_UNIT_DEFAULT_THERMOSTAT_MAX
|
||||
#undef COOLING_UNIT_DEFAULT_MAX_SAFE_TEMP
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* These are presets for the cooling unit data. Essentially, each type of cooling unit can have its own presets (depending on pref setting)
|
||||
* that change the name, description, and functioning of the cooling unit.
|
||||
*/
|
||||
|
||||
/singleton/synthetic_organ_preset/cooling_unit
|
||||
/// The name the cooling unit organ will have.
|
||||
name = "epic cooling unit"
|
||||
/// The description the cooling unit organ will get.
|
||||
desc = "It cools epically."
|
||||
/// The passive temperature change the cooling unit will have.
|
||||
var/passive_temp_change = 3
|
||||
/// The power used when cooling down.
|
||||
var/base_power_consumption = 2.5
|
||||
/// The maximum health the cooling unit's plating will have.
|
||||
var/plating_max_health = 20
|
||||
|
||||
/singleton/synthetic_organ_preset/cooling_unit/apply_preset(obj/item/organ/internal/machine/organ)
|
||||
. = ..()
|
||||
var/obj/item/organ/internal/machine/cooling_unit/cooling_unit = organ
|
||||
cooling_unit.passive_temp_change = passive_temp_change
|
||||
cooling_unit.base_power_consumption = base_power_consumption
|
||||
cooling_unit.plating.replace_health(plating_max_health)
|
||||
|
||||
/singleton/synthetic_organ_preset/cooling_unit/air
|
||||
name = "air cooling unit"
|
||||
desc = "One of the most complex and vital components of a synthetic. It regulates its internal temperature through the use of chassis-mounted fans and prevents the frame from overheating."
|
||||
|
||||
passive_temp_change = 2
|
||||
plating_max_health = 50
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/air
|
||||
forced_preset = /singleton/synthetic_organ_preset/cooling_unit/air
|
||||
|
||||
/singleton/synthetic_organ_preset/cooling_unit/liquid
|
||||
name = "liquid-cooling pump and radiator array"
|
||||
desc = "An extremely complex set of cooling pipes that transport coolant throughout a synthetic's body. The most efficient type of cooling, but also the most vulnerable."
|
||||
icon_state = "ipc_liquid_cooler"
|
||||
|
||||
passive_temp_change = 3
|
||||
base_power_consumption = 7.5
|
||||
plating_max_health = 20
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/liquid
|
||||
forced_preset = /singleton/synthetic_organ_preset/cooling_unit/liquid
|
||||
|
||||
/singleton/synthetic_organ_preset/cooling_unit/passive
|
||||
name = "passive radiator cooling block"
|
||||
desc = "A simplistic, but efficient block of large cooling fins, which cool down a synthetic's body enough to make it work. Quite cheap, but durable."
|
||||
icon_state = "ipc_liquid_cooler"
|
||||
|
||||
passive_temp_change = 1
|
||||
base_power_consumption = 2.5
|
||||
plating_max_health = 100
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/passive
|
||||
forced_preset = /singleton/synthetic_organ_preset/cooling_unit/passive
|
||||
|
||||
// Xion cooling units.
|
||||
/singleton/synthetic_organ_preset/cooling_unit/air_xion
|
||||
name = "xion manufacturing advanced air cooling unit"
|
||||
desc = "A very complex air cooling setup, with high-grade laminar plasteel fans and integrated space-proof heatsinks to allow the frame to still be cooled in space."
|
||||
icon_state = "ipc_xion_fans"
|
||||
|
||||
passive_temp_change = 2
|
||||
base_power_consumption = 6
|
||||
plating_max_health = 70
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/air/xion
|
||||
forced_preset = /singleton/synthetic_organ_preset/cooling_unit/air_xion
|
||||
|
||||
/singleton/synthetic_organ_preset/cooling_unit/liquid_xion
|
||||
name = "xion manufacturing advanced cryo-cooling pump and radiator array"
|
||||
desc = "An extremely complex cryo-cooling setup. It uses advanced coolant to allow the frame to still function in space - but its laminar super-flow piping is extremely fragile."
|
||||
icon_state = "ipc_xion_liquid_cooler"
|
||||
|
||||
passive_temp_change = 3
|
||||
base_power_consumption = 8
|
||||
plating_max_health = 35
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/liquid/xion
|
||||
forced_preset = /singleton/synthetic_organ_preset/cooling_unit/liquid_xion
|
||||
|
||||
/singleton/synthetic_organ_preset/cooling_unit/passive_xion
|
||||
name = "xion manufacturing passive intra-fin array"
|
||||
desc = "The simplicity of this cooling design betrays its efficiency: an extremely durable array of laminar plasteel fins, supplemented with an expensive coating that allows the synthetic to be cooled even in space."
|
||||
icon_state = "ipc_heatsink"
|
||||
|
||||
passive_temp_change = 1
|
||||
base_power_consumption = 3.5
|
||||
plating_max_health = 120
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/passive/xion
|
||||
forced_preset = /singleton/synthetic_organ_preset/cooling_unit/passive_xion
|
||||
|
||||
// Zenghu cooling units.
|
||||
/singleton/synthetic_organ_preset/cooling_unit/air_zenghu
|
||||
name = "zeng-hu penta-fan air cooling system"
|
||||
desc = "A sleek and luxurious air cooling system invented by Zeng-Hu for their house-made synthetic frames. It uses a patented and exclusive type of penta-fin fan. More efficient than standard air cooling solutions, but it will not allow cooling in space."
|
||||
icon_state = "ipc_zeng_fans"
|
||||
|
||||
passive_temp_change = 4
|
||||
base_power_consumption = 7.5
|
||||
plating_max_health = 25
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/air/zenghu
|
||||
forced_preset = /singleton/synthetic_organ_preset/cooling_unit/air_zenghu
|
||||
|
||||
/singleton/synthetic_organ_preset/cooling_unit/liquid_zenghu
|
||||
name = "zeng-hu lamellar liquid cooling system"
|
||||
desc = "A sleek set of superimposed lamellar pipes with a custom cooling solution. In black market and enthusiast repair forums, this is known as 'the enthusiast's nightmare' due to its practical impossibility to repair in anything less than a Zeng-Hu facility; \
|
||||
the lamellar pipes are impossible to reproduce or find through a third-party solution due to their extremely complex and exclusive Zeng-Hu make."
|
||||
icon_state = "ipc_zeng_cooling"
|
||||
|
||||
passive_temp_change = 5
|
||||
base_power_consumption = 10
|
||||
plating_max_health = 15
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/liquid/zenghu
|
||||
forced_preset = /singleton/synthetic_organ_preset/cooling_unit/liquid_zenghu
|
||||
|
||||
/singleton/synthetic_organ_preset/cooling_unit/passive_zenghu
|
||||
name = "zeng-hu suprafin cooling fins"
|
||||
desc = "A sleek set of engraved, plasteel fins patented by Zeng-Hu as 'suprafins'. The engravings help increase thermal area to the maximum possible, and a specialized cooling solution is imprinted onto the fins to improve their cooling ability."
|
||||
icon_state = "ipc_zeng_cooling_passive"
|
||||
|
||||
passive_temp_change = 2
|
||||
base_power_consumption = 4
|
||||
plating_max_health = 75
|
||||
|
||||
/obj/item/organ/internal/machine/cooling_unit/passive/zenghu
|
||||
forced_preset = /singleton/synthetic_organ_preset/cooling_unit/passive_zenghu
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/obj/item/organ/internal/machine/data
|
||||
name = "data core"
|
||||
organ_tag = "data core"
|
||||
parent_organ = BP_GROIN
|
||||
icon = 'icons/obj/cloning.dmi'
|
||||
icon_state = "harddisk"
|
||||
vital = FALSE
|
||||
emp_coeff = 0.1
|
||||
robotic_sprite = FALSE
|
||||
|
||||
max_damage = 30
|
||||
|
||||
/obj/item/organ/internal/machine/data/Initialize()
|
||||
robotize()
|
||||
. = ..()
|
||||
@@ -0,0 +1,54 @@
|
||||
// Hydraulics effects are mainly in /datum/species/machine/handle_sprint_cost.
|
||||
/obj/item/organ/internal/machine/hydraulics
|
||||
name = "hydraulics system"
|
||||
desc = "A byzantine system of pumps and other hydraulic components to allow a positronic chassis to stand on both its feet, and do complex tricks such as kicking you in the shins."
|
||||
icon = 'icons/obj/organs/ipc_organs.dmi'
|
||||
icon_state = "ipc_hydraulics"
|
||||
organ_tag = BP_HYDRAULICS
|
||||
parent_organ = BP_GROIN
|
||||
|
||||
max_damage = 40
|
||||
relative_size = 70
|
||||
|
||||
/obj/item/organ/internal/machine/hydraulics/Initialize()
|
||||
. = ..()
|
||||
RegisterSignal(owner, COMSIG_IPC_HAS_SPRINTED, PROC_REF(handle_hydraulics))
|
||||
|
||||
/**
|
||||
* Handles sprint effects. For example, recharging with the kinetic reactor.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/hydraulics/proc/handle_hydraulics()
|
||||
SIGNAL_HANDLER
|
||||
if(!owner)
|
||||
return
|
||||
|
||||
var/obj/item/organ/internal/machine/reactor/reactor = owner.internal_organs_by_name[BP_REACTOR]
|
||||
if(!istype(reactor))
|
||||
return
|
||||
|
||||
if(get_integrity() < IPC_INTEGRITY_THRESHOLD_LOW)
|
||||
if(prob(10 * (damage / 10)))
|
||||
spark(get_turf(owner), rand(1, 5), GLOB.alldirs)
|
||||
|
||||
if(reactor.power_supply_type & POWER_SUPPLY_KINETIC)
|
||||
reactor.generate_power(reactor.base_power_generation)
|
||||
|
||||
/obj/item/organ/internal/machine/hydraulics/medium_integrity_damage(integrity)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
|
||||
if(prob(get_integrity_damage_probability()))
|
||||
spark(owner, 3, GLOB.alldirs)
|
||||
to_chat(owner, SPAN_WARNING("Your hydraulics lock up for a second!"))
|
||||
owner.Stun(1)
|
||||
|
||||
/obj/item/organ/internal/machine/hydraulics/high_integrity_damage(integrity)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
|
||||
if(prob(get_integrity_damage_probability()))
|
||||
spark(owner, 5, GLOB.alldirs)
|
||||
to_chat(owner, SPAN_WARNING("Your hydraulics malfunction and you trip!"))
|
||||
owner.Weaken(1)
|
||||
@@ -0,0 +1,35 @@
|
||||
/obj/item/organ/internal/machine/internal_diagnostics
|
||||
name = "internal diagnostics suite"
|
||||
desc = "An extremely complex suite of sensors, circuits and other electronic parts. They are used by the myriad components of an IPC to regulate its functions, while also allowing them to diagnose internal problems."
|
||||
icon = 'icons/obj/robot_component.dmi'
|
||||
icon_state = "analyser"
|
||||
organ_tag = BP_DIAGNOSTICS_SUITE
|
||||
parent_organ = BP_GROIN
|
||||
action_button_name = "Internal Diagnostics"
|
||||
|
||||
max_damage = 40
|
||||
relative_size = 25
|
||||
|
||||
/obj/item/organ/internal/machine/internal_diagnostics/attack_self(var/mob/user)
|
||||
. = ..()
|
||||
if(user.stat == DEAD)
|
||||
return
|
||||
|
||||
if(user.incapacitated(INCAPACITATION_KNOCKOUT|INCAPACITATION_STUNNED))
|
||||
return
|
||||
|
||||
to_chat(user, SPAN_NOTICE("You query your internal diagnostics system and gather some information."))
|
||||
open_diagnostics(user)
|
||||
|
||||
/obj/item/organ/internal/machine/internal_diagnostics/proc/open_diagnostics(mob/user)
|
||||
if(!ishuman(user))
|
||||
return
|
||||
|
||||
if(status & ORGAN_DEAD)
|
||||
to_chat(user, SPAN_MACHINE_WARNING("You receive nothing but a critical error notifying you that \the [src] is unreachable."))
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/human = user
|
||||
|
||||
var/datum/tgui_module/ipc_diagnostic/diagnostic = new(human, owner)
|
||||
diagnostic.ui_interact(user)
|
||||
@@ -0,0 +1,48 @@
|
||||
/obj/item/organ/internal/machine/internal_storage
|
||||
name = "internal storage system"
|
||||
desc = "A simple internal casing used by G2 frames for internal storage."
|
||||
icon = 'icons/obj/organs/augments.dmi'
|
||||
icon_state = "anchor"
|
||||
organ_tag = BP_INTERNAL_STORAGE
|
||||
parent_organ = BP_GROIN
|
||||
|
||||
action_button_name = "Access Internal Storage"
|
||||
|
||||
max_damage = 35
|
||||
relative_size = 25
|
||||
|
||||
/// The actual, physical storage item.
|
||||
var/obj/item/storage/internal_storage/storage
|
||||
|
||||
/obj/item/organ/internal/machine/internal_storage/Initialize()
|
||||
. = ..()
|
||||
storage = new(src)
|
||||
|
||||
/obj/item/organ/internal/machine/internal_storage/Destroy()
|
||||
QDEL_NULL(storage)
|
||||
return ..()
|
||||
|
||||
/obj/item/organ/internal/machine/internal_storage/removed()
|
||||
. = ..()
|
||||
for(var/thing in storage)
|
||||
storage.remove_from_storage(thing, get_turf(src))
|
||||
|
||||
/obj/item/organ/internal/machine/internal_storage/attack_self(mob/user)
|
||||
if(is_broken())
|
||||
to_chat(user, SPAN_WARNING("The hatch to the internal storage is completely destroyed!"))
|
||||
return
|
||||
|
||||
if(get_integrity() < IPC_INTEGRITY_THRESHOLD_LOW)
|
||||
to_chat(user, SPAN_NOTICE("You struggle to pry open the hatch to the internal storage..."))
|
||||
if(!do_after(1 SECOND, src))
|
||||
return
|
||||
|
||||
storage.open(user)
|
||||
|
||||
/obj/item/storage/internal_storage
|
||||
name = "internal storage"
|
||||
desc = "You shouldn't see this."
|
||||
max_w_class = WEIGHT_CLASS_NORMAL
|
||||
max_storage_space = DEFAULT_LARGEBOX_STORAGE
|
||||
allow_quick_empty = FALSE
|
||||
care_about_storage_depth = FALSE
|
||||
@@ -0,0 +1,113 @@
|
||||
/obj/item/organ/internal/machine/ipc_tag
|
||||
name = "identification tag"
|
||||
organ_tag = BP_IPCTAG
|
||||
parent_organ = BP_HEAD
|
||||
icon = 'icons/obj/ipc_utilities.dmi'
|
||||
icon_state = "ipc_tag"
|
||||
item_state = "ipc_tag"
|
||||
dead_icon = "ipc_tag_dead"
|
||||
contained_sprite = TRUE
|
||||
robotic_sprite = FALSE
|
||||
|
||||
max_damage = 30
|
||||
|
||||
var/auto_generate = TRUE
|
||||
var/serial_number = ""
|
||||
var/ownership_info = IPC_OWNERSHIP_COMPANY
|
||||
var/citizenship_info = CITIZENSHIP_NONE
|
||||
|
||||
/obj/item/organ/internal/machine/ipc_tag/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
|
||||
. = ..()
|
||||
. += SPAN_NOTICE("Serial Autogeneration: [auto_generate ? "Yes" : "No"]")
|
||||
. += SPAN_NOTICE("Serial Number: [serial_number]")
|
||||
. += SPAN_NOTICE("Ownership Info: [ownership_info]")
|
||||
. += SPAN_NOTICE("Citizenship Info: [citizenship_info]")
|
||||
|
||||
/obj/item/organ/internal/machine/ipc_tag/get_diagnostics_info()
|
||||
return "S/N: [serial_number] | OWN: [ownership_info] | CTZ: [citizenship_info]"
|
||||
|
||||
/obj/item/organ/internal/machine/ipc_tag/high_integrity_damage(integrity)
|
||||
. = ..()
|
||||
if(get_integrity_damage_probability())
|
||||
serial_number = Gibberish(serial_number, rand(1, get_integrity_damage_probability()))
|
||||
|
||||
/obj/item/organ/internal/machine/ipc_tag/attackby(obj/item/attacking_item, mob/user)
|
||||
if(istype(attacking_item, /obj/item/ipc_tag_scanner))
|
||||
if(src.loc != user)
|
||||
to_chat(user, SPAN_WARNING("You can't scan \the [src] if it's not on your person!"))
|
||||
return
|
||||
var/obj/item/ipc_tag_scanner/S = attacking_item
|
||||
if(!S.powered)
|
||||
to_chat(user, SPAN_WARNING("\The [src] reads, \"Scanning failure, please submit scanner for repairs.\""))
|
||||
return
|
||||
if(!S.hacked)
|
||||
examinate(user, src)
|
||||
else
|
||||
user.visible_message(SPAN_WARNING("\The [user] starts fiddling with \the [src]..."), SPAN_NOTICE("You start fiddling with \the [src]..."))
|
||||
if(do_after(user, 30, src))
|
||||
if(src.loc != user)
|
||||
to_chat(user, SPAN_WARNING("You can only modify \the [src] if it's on your person!"))
|
||||
return
|
||||
|
||||
var/static/list/modification_options = list("Serial Number", "Ownership Status", "Citizenship")
|
||||
var/choice = tgui_input_list(user, "How do you want to modify the IPC tag?", "IPC Tag Modification", modification_options)
|
||||
switch(choice)
|
||||
|
||||
if("Serial Number")
|
||||
var/serial_selection = tgui_input_list(user, "In what way do you want to modify the serial number?","Serial Number Selection",
|
||||
list("Auto Generation", "Manual Input", "Cancel"), default = "Cancel")
|
||||
if(serial_selection != "Cancel")
|
||||
if(serial_selection == "Auto Generation")
|
||||
var/auto_generation_choice = tgui_input_list(user, "Do you wish for the IPC tag to automatically generate its serial number based on the IPCs name?", "Serial Autogeneration",
|
||||
list("Yes", "No"), "No")
|
||||
if(auto_generation_choice == "Yes")
|
||||
auto_generate = TRUE
|
||||
else
|
||||
auto_generate = FALSE
|
||||
|
||||
if(serial_selection == "Manual Input")
|
||||
var/new_serial = tgui_input_text(user, "What do you wish for the new serial number to be? (Limit of 12 characters)", "Serial Number Modification", serial_number, 12)
|
||||
new_serial = uppertext(dd_limittext(new_serial, 12))
|
||||
if(new_serial)
|
||||
serial_number = new_serial
|
||||
auto_generate = FALSE
|
||||
|
||||
if("Ownership Status")
|
||||
var/static/list/ownership_options = list(IPC_OWNERSHIP_COMPANY, IPC_OWNERSHIP_PRIVATE, IPC_OWNERSHIP_SELF)
|
||||
var/new_ownership = tgui_input_list(user, "What do you wish for the new ownership status to be?", "Ownership Status Modification", ownership_options)
|
||||
if(new_ownership)
|
||||
ownership_info = new_ownership
|
||||
|
||||
if("Citizenship")
|
||||
var/datum/citizenship/citizenship = tgui_input_list(user, "What do you wish for the new citizenship setting to be?", "Citizenship Setting Modification", SSrecords.citizenships)
|
||||
if(citizenship)
|
||||
citizenship_info = citizenship
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/item/organ/internal/machine/ipc_tag/proc/modify_tag_data(var/can_be_untagged = FALSE)
|
||||
if(!owner || owner.stat)
|
||||
return
|
||||
if(can_be_untagged)
|
||||
var/untagged = tgui_alert(owner, "Do you wish to remove your tag? This is highly illegal in most nations!", "Untagged IPC", list("Remove Tag", "Keep Tag"))
|
||||
if(untagged == "Remove Tag")
|
||||
to_chat(owner, SPAN_WARNING("You are now an untagged synthetic - don't get caught!"))
|
||||
qdel(src)
|
||||
return
|
||||
var/new_ownership = tgui_input_list(owner, "Choose an ownership status for your IPC tag.", "Tag Ownership", list(IPC_OWNERSHIP_COMPANY, IPC_OWNERSHIP_PRIVATE, IPC_OWNERSHIP_SELF))
|
||||
if(!new_ownership)
|
||||
return
|
||||
ownership_info = new_ownership
|
||||
if(ownership_info == IPC_OWNERSHIP_SELF) //Owned IPCs don't have citizenship
|
||||
var/new_citizenship = tgui_input_list(owner, "Choose a citizenship for your IPC tag.", "Tag Citizenship", CITIZENSHIPS_ALL_IPC)
|
||||
if(!new_citizenship)
|
||||
return
|
||||
citizenship_info = new_citizenship
|
||||
else
|
||||
citizenship_info = CITIZENSHIP_NONE
|
||||
var/new_serial = tgui_input_text(owner, "Choose a serial number for your IPC tag, or leave blank for a random one.", "Serial Number")
|
||||
if(!new_serial)
|
||||
serial_number = uppertext(dd_limittext(md5(owner.real_name), 12))
|
||||
else
|
||||
serial_number = uppertext(dd_limittext(new_serial, 12))
|
||||
to_chat(owner, SPAN_NOTICE("IPC tag data has been updated."))
|
||||
@@ -0,0 +1,17 @@
|
||||
/obj/item/organ/internal/eyes/optical_sensor
|
||||
name = "optical sensor"
|
||||
singular_name = "optical sensor"
|
||||
organ_tag = BP_EYES
|
||||
icon = 'icons/obj/organs/ipc_organs.dmi'
|
||||
icon_state = "ipc_eyes"
|
||||
robotic_sprite = FALSE
|
||||
possible_modifications = list("Mechanical")
|
||||
|
||||
relative_size = 15
|
||||
|
||||
/obj/item/organ/internal/eyes/optical_sensor/Initialize()
|
||||
robotize()
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/eyes/optical_sensor/terminator
|
||||
emp_coeff = 0.5
|
||||
@@ -0,0 +1,497 @@
|
||||
/obj/item/organ/internal/machine/posibrain
|
||||
name = "positronic brain"
|
||||
desc = "A cube of shining metal, four inches to a side and covered in shallow grooves."
|
||||
icon = 'icons/obj/assemblies.dmi'
|
||||
icon_state = "posibrain"
|
||||
organ_tag = BP_BRAIN
|
||||
parent_organ = BP_HEAD
|
||||
vital = TRUE
|
||||
robotic_sprite = FALSE
|
||||
diagnostics_suite_visible = FALSE
|
||||
|
||||
emp_coeff = 0.5
|
||||
|
||||
action_button_name = "Neural Configuration"
|
||||
|
||||
relative_size = 85
|
||||
|
||||
/// The type of 'robotic brain'. Must be a subtype of /obj/item/device/mmi/digital.
|
||||
var/robotic_brain_type = /obj/item/device/mmi/digital/posibrain
|
||||
/// The stored MMI object.
|
||||
var/obj/item/device/mmi/stored_mmi
|
||||
/// The cooldown between each alarm warning.
|
||||
var/heat_alarm_cooldown = 0
|
||||
/// The cooldown between each integrity alarm warning.
|
||||
var/integrity_alarm_cooldown = 0
|
||||
/// The cooldown between each 'patch'.
|
||||
var/patching_cooldown = 30 SECONDS
|
||||
/// The world.time of the last 'patch'.
|
||||
var/last_patch_time = 0
|
||||
/// Fragmentation is basically scarring for positronic brains, it's a percentage that goes from 0 to 100. Max_damage is multiplied by [(100 - fragmentation) / 100].
|
||||
/// Increases very slowly. Needs a machinist to fix it.
|
||||
var/fragmentation = 0
|
||||
/// Whether the synthetic's firewall is enabled or not. Boolean.
|
||||
var/firewall = TRUE
|
||||
/// If peer-to-peer communication (done through Neural Configuration) is allowed.
|
||||
var/p2p_communication_allowed = TRUE
|
||||
/// Stores the current status of EMP damage.
|
||||
var/emp_damage_counter = 0
|
||||
/// Maximum EMP damage points we can have.
|
||||
var/emp_max_damage = 3
|
||||
/// The amount of seconds the brain should be scrambled for, while this is above 0, it'll add a flat 2 evaluate_damage()'s damage points
|
||||
var/brain_scrambling = 0
|
||||
/// Whether or not the positronic's self-preservation is toggled. Basically knocks the IPC out.
|
||||
var/self_preservation_activated = FALSE
|
||||
/// The looping sound played when an IPC is sizzling from burn damage.
|
||||
var/datum/looping_sound/ipc_sizzling/sizzle
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/Initialize(mapload)
|
||||
stored_mmi = new robotic_brain_type(src)
|
||||
. = ..()
|
||||
addtimer(CALLBACK(src, PROC_REF(setup_brain)), 30)
|
||||
if(species)
|
||||
set_max_damage(species.total_health)
|
||||
else
|
||||
set_max_damage(200)
|
||||
RegisterSignal(owner, COMSIG_SYNTH_SET_SELF_PRESERVATION, PROC_REF(set_self_preservation))
|
||||
sizzle = new(owner)
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/Destroy()
|
||||
QDEL_NULL(stored_mmi)
|
||||
QDEL_NULL(sizzle)
|
||||
return ..()
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/rejuvenate()
|
||||
. = ..()
|
||||
SEND_SIGNAL(owner, COMSIG_SYNTH_ENDOSKELETON_FULL_REPAIR)
|
||||
emp_damage_counter = 0
|
||||
brain_scrambling = 0
|
||||
remove_fragmentation(max_damage)
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/attack_self(mob/user)
|
||||
. = ..()
|
||||
if(user.stat == DEAD)
|
||||
return
|
||||
|
||||
if(user.incapacitated(INCAPACITATION_KNOCKOUT|INCAPACITATION_STUNNED))
|
||||
return
|
||||
|
||||
open_neural_configuration(user)
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/emp_act(severity)
|
||||
. = ..()
|
||||
playsound(owner, 'sound/species/synthetic/heavy_electric_discharge.ogg', severity == EMP_LIGHT ? 50 : 100)
|
||||
brain_scrambling = min(brain_scrambling + (severity == EMP_LIGHT ? 50 : 75), 100)
|
||||
shake_camera(owner, 1 SECOND, 3)
|
||||
to_chat(owner, FONT_LARGE(SPAN_MACHINE_DANGER("Your internal connections seize up and snap at the surge of electromagnetic current!")))
|
||||
add_emp_damage_counter()
|
||||
spark(owner, rand(3, 5), GLOB.alldirs)
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/die()
|
||||
. = ..()
|
||||
to_chat(owner, SPAN_MACHINE_DANGER(FONT_LARGE("Your damage failsafes activate; your thought processes grind to a halt as your consciousness is cut off from the exterior world. No sensation or external input reaches you anymore.")))
|
||||
to_chat(owner, SPAN_DANGER(FONT_LARGE("You are now in an emergency low power mode, so that your posibrain can still survive despite your chassis being destroyed.")))
|
||||
owner.mind.transfer_to(stored_mmi.brainmob)
|
||||
|
||||
/**
|
||||
* Helper proc to add fragmentation.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/posibrain/proc/add_fragmentation(amount)
|
||||
fragmentation = min(fragmentation + amount, 100)
|
||||
set_max_damage(species.total_health * ((100 - fragmentation) / 100))
|
||||
|
||||
/**
|
||||
* Helper proc to remove fragmentation.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/posibrain/proc/remove_fragmentation(amount)
|
||||
fragmentation = max(fragmentation - amount, 0)
|
||||
// Fragmentation of 0 means multiplying max_damage by 0.
|
||||
if(fragmentation > 0 || fragmentation >= 100)
|
||||
set_max_damage(species.total_health * ((100 - fragmentation) / 100))
|
||||
else
|
||||
set_max_damage(species.total_health)
|
||||
|
||||
/**
|
||||
* Helper proc to remove fragmentation.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/posibrain/proc/open_neural_configuration(mob/user)
|
||||
if(!ishuman(user))
|
||||
return
|
||||
|
||||
if(is_broken())
|
||||
to_chat(user, SPAN_MACHINE_WARNING("There is no response from [owner]'s neural pathways."))
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/human = user
|
||||
var/datum/tgui_module/neural_configuration/neural_config = new(human, owner)
|
||||
neural_config.ui_interact(user)
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/proc/update_from_mmi()
|
||||
if(!stored_mmi)
|
||||
return
|
||||
|
||||
name = stored_mmi.name
|
||||
desc = stored_mmi.desc
|
||||
icon = stored_mmi.icon
|
||||
icon_state = stored_mmi.icon_state
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/removed(var/mob/living/user)
|
||||
if(stored_mmi)
|
||||
stored_mmi.forceMove(get_turf(src))
|
||||
if(owner.mind)
|
||||
owner.mind.transfer_to(stored_mmi.brainmob)
|
||||
|
||||
. = ..()
|
||||
|
||||
var/mob/living/holder_mob = loc
|
||||
if(istype(holder_mob))
|
||||
holder_mob.drop_from_inventory(src)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/proc/setup_brain()
|
||||
if(owner)
|
||||
stored_mmi.brainmob.real_name = owner.name
|
||||
stored_mmi.brainmob.name = stored_mmi.brainmob.real_name
|
||||
update_from_mmi()
|
||||
else
|
||||
stored_mmi.forceMove(get_turf(src))
|
||||
qdel(src)
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/proc/damage_integrity(integrity_damage)
|
||||
take_internal_damage(integrity_damage, forced_damage = TRUE)
|
||||
if(integrity_alarm_cooldown < world.time)
|
||||
to_chat(owner, SPAN_DANGER("Your internal software throws exceptions at you: faulty systems detected! Warning! Warning!"))
|
||||
|
||||
/**
|
||||
* This proc clears the EMP damage counter.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/posibrain/proc/clear_emp_damage_counter()
|
||||
emp_damage_counter = 0
|
||||
owner.remove_movespeed_modifier(/datum/movespeed_modifier/synth_emp)
|
||||
SEND_SIGNAL(owner, COMSIG_SYNTH_EMP_DAMAGE_CLEARED)
|
||||
|
||||
/**
|
||||
* This proc adds to the EMP damage counter, and automatically starts a unique/override timer to clear it.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/posibrain/proc/add_emp_damage_counter()
|
||||
emp_damage_counter = min(emp_damage_counter + 1, emp_max_damage)
|
||||
handle_emp_damage()
|
||||
addtimer(CALLBACK(src, PROC_REF(clear_emp_damage_counter)), emp_damage_counter * 20, TIMER_OVERRIDE | TIMER_UNIQUE)
|
||||
|
||||
/**
|
||||
* Generates a random hex number for cool hacking aesthetic.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/posibrain/proc/generate_hex()
|
||||
var/hex = "0x"
|
||||
for(var/i = 1 to 6)
|
||||
var/num_or_str = pick(0, 1)
|
||||
if(num_or_str)
|
||||
hex += "[pick("A", "B", "C", "D", "E", "F")]"
|
||||
else
|
||||
hex += "[rand(0, 9)]"
|
||||
return hex
|
||||
|
||||
/**
|
||||
* Toggles the firewall on or off. In the future, can be overridden by things like viruses.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/posibrain/proc/toggle_firewall()
|
||||
firewall = !firewall
|
||||
to_chat(owner, SPAN_MACHINE_WARNING("Your internal firewall is now [firewall ? "enabled" : "disabled"]."))
|
||||
|
||||
/**
|
||||
* Toggles peer-to-peer communication on or off. In the future, can be overridden by things like viruses.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/posibrain/proc/toggle_p2p()
|
||||
p2p_communication_allowed = !p2p_communication_allowed
|
||||
to_chat(owner, SPAN_MACHINE_WARNING("You [p2p_communication_allowed ? "open" : "close"] your virtual communication ports."))
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/process(seconds_per_tick)
|
||||
if(!owner)
|
||||
return
|
||||
|
||||
if(owner.bodytemperature > species.heat_level_1)
|
||||
sizzle.start()
|
||||
// placeholder values
|
||||
take_internal_damage(1 + min(owner.bodytemperature * 0.001, 0.5))
|
||||
if(heat_alarm_cooldown < world.time)
|
||||
to_chat(owner, SPAN_DANGER("Your sensors light up: extreme heat detected! Warning! Unsafe operating temperature!"))
|
||||
sound_to(owner, 'sound/effects/heat_alarm.ogg')
|
||||
heat_alarm_cooldown = world.time + 7 SECONDS
|
||||
else
|
||||
sizzle.stop()
|
||||
|
||||
if(damage)
|
||||
if(damage < max_damage * 0.5)
|
||||
if((last_patch_time + patching_cooldown) < world.time)
|
||||
var/damage_healed = rand(1, 5)
|
||||
heal_damage(damage_healed)
|
||||
to_chat(owner, SPAN_MACHINE_WARNING("Neural pathway patch automatically applied to block 0x[generate_hex()]."))
|
||||
add_fragmentation(damage_healed / 2)
|
||||
last_patch_time = world.time
|
||||
|
||||
if(brain_scrambling)
|
||||
if(prob(5))
|
||||
var/list/brain_scrambling_messages = list(
|
||||
"Your vision clouds for a moment.",
|
||||
"Your pathways seem to be slightly less responsive.",
|
||||
"You notice some higher-than-normal lag in your internal requests.",
|
||||
"Your sensation simulation system is somewhat fuzzy.",
|
||||
"Your probes pick up a slight voltage spike."
|
||||
)
|
||||
to_chat(owner, SPAN_MACHINE_WARNING(pick(brain_scrambling_messages)))
|
||||
|
||||
handle_fragmentation()
|
||||
|
||||
evaluate_damage(seconds_per_tick)
|
||||
|
||||
..()
|
||||
|
||||
/**
|
||||
* Handles fragmentation effects.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/posibrain/proc/handle_fragmentation()
|
||||
if(fragmentation > 5)
|
||||
if(prob(fragmentation / 10))
|
||||
switch(fragmentation)
|
||||
if(6 to 10)
|
||||
var/list/low_fragmentation_messages = list(
|
||||
"Some of your neural pathways don't respond the way they used to.",
|
||||
"Your software flags a few faulty logic routes.",
|
||||
"The logic connection you were using before to access that memory doesn't work anymore.",
|
||||
"Your Virtual Communication logs need reshuffling."
|
||||
)
|
||||
to_chat(owner, SPAN_MACHINE_WARNING(pick(low_fragmentation_messages)))
|
||||
if(11 to 30)
|
||||
var/list/medium_fragmentation_messages = list(
|
||||
SPAN_MACHINE_WARNING("Several logic routes no longer respond to your commands."),
|
||||
SPAN_MACHINE_WARNING("That memory is distinctly unable to be reached."),
|
||||
SPAN_MACHINE_DANGER("Whatever is in front of you becomes a pixelated jumble for a nanosecond."),
|
||||
SPAN_MACHINE_WARNING("Your software flags several logic errors.")
|
||||
)
|
||||
to_chat(owner, pick(medium_fragmentation_messages))
|
||||
if(31 to 60)
|
||||
var/list/high_fragmentation_messages = list(
|
||||
"There are several errors standing in front of you and your current task.",
|
||||
"Storing your memories has a noticeable delay to it.",
|
||||
"Your calculations are sluggish and need a lot more manual attention than they should.",
|
||||
"Your neural pathways are a jumble of patch-worked routes, and it becomes hard to navigate your memories."
|
||||
)
|
||||
to_chat(owner, SPAN_MACHINE_WARNING(pick(high_fragmentation_messages)))
|
||||
if(61 to 100)
|
||||
var/list/extreme_fragmentation_messages = list(
|
||||
"...",
|
||||
"The pathway to that memory is broken.",
|
||||
"Your positronic is running on overtime just to decrypt your memories.",
|
||||
"Who is it you were speaking to?",
|
||||
"That memory cannot be accessed."
|
||||
)
|
||||
to_chat(owner, SPAN_MACHINE_DANGER(pick(extreme_fragmentation_messages)))
|
||||
|
||||
/**
|
||||
* Handles EMP damage effects.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/posibrain/proc/handle_emp_damage()
|
||||
playsound(owner, 'sound/species/synthetic/synthetic_shock.ogg')
|
||||
switch(emp_damage_counter)
|
||||
if(1)
|
||||
var/obj/item/organ/internal/machine/cooling_unit/cooling = owner.internal_organs_by_name[BP_COOLING_UNIT]
|
||||
if(istype(cooling))
|
||||
to_chat(owner, FONT_LARGE(SPAN_DANGER("Your cooling unit's power circuits struggle and burn under the electromagnetic current!")))
|
||||
cooling.update_thermostat(cooling.thermostat_min + round(rand(10, 20)))
|
||||
shake_camera(owner, 0.5 SECONDS, 1)
|
||||
owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/synth_emp)
|
||||
if(2)
|
||||
to_chat(owner, FONT_LARGE(SPAN_DANGER("The electromagnetic current overloads your hydraulics!")))
|
||||
owner.Stun(2)
|
||||
owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/synth_emp, multiplicative_slowdown = 2)
|
||||
medium_integrity_damage(50)
|
||||
shake_camera(owner, 0.5 SECONDS, 3)
|
||||
if(3)
|
||||
to_chat(owner, FONT_LARGE(SPAN_MACHINE_WARNING("Your positronic circuits error and break under the electromagnetic current!")))
|
||||
owner.Weaken(3)
|
||||
owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/synth_emp, multiplicative_slowdown = 3)
|
||||
high_integrity_damage(25)
|
||||
shake_camera(owner, 1 SECONDS, 5)
|
||||
|
||||
/**
|
||||
* This is the proc in charge of showing the robot pain textures to the IPC.
|
||||
* To do so, it tries to dynamically evaluate how fucked the IPC is to set the appropriate icon_state.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/posibrain/proc/evaluate_damage(seconds_per_tick)
|
||||
if(!owner.robot_pain)
|
||||
return //no pain texture, how did we even get here?
|
||||
|
||||
// We have 6 levels total.
|
||||
var/base_icon_state = "ipcdamageoverlay"
|
||||
var/damage_points = 0
|
||||
|
||||
var/integrity = get_integrity()
|
||||
if(integrity <= 75)
|
||||
damage_points++
|
||||
if(integrity <= 50)
|
||||
damage_points += 2
|
||||
if(integrity <= 25)
|
||||
damage_points += 2
|
||||
|
||||
if(brain_scrambling)
|
||||
damage_points += 2
|
||||
brain_scrambling = max(brain_scrambling - seconds_per_tick, 0)
|
||||
|
||||
if(emp_damage_counter)
|
||||
switch(emp_damage_counter)
|
||||
if(1)
|
||||
damage_points++
|
||||
if(2)
|
||||
damage_points += 2
|
||||
if(3)
|
||||
damage_points += 3
|
||||
|
||||
var/datum/component/synthetic_endoskeleton/endoskeleton = owner.GetComponent(/datum/component/synthetic_endoskeleton)
|
||||
if(endoskeleton && endoskeleton.damage)
|
||||
var/damage_ratio = endoskeleton.damage / endoskeleton.max_damage
|
||||
switch(damage_ratio)
|
||||
if(0.3 to 0.5)
|
||||
damage_points++
|
||||
if(0.5 to 0.75)
|
||||
damage_points += 2
|
||||
if(0.75 to 0.85)
|
||||
damage_points += 4
|
||||
if(0.85 to INFINITY)
|
||||
damage_points += 5
|
||||
|
||||
if(damage_points)
|
||||
damage_points = min(6, damage_points)
|
||||
owner.robot_pain.icon_state = "[base_icon_state][damage_points]"
|
||||
else
|
||||
owner.robot_pain.icon_state = null
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/low_integrity_damage(integrity)
|
||||
var/damage_probability = get_integrity_damage_probability(integrity)
|
||||
if(prob(damage_probability))
|
||||
to_chat(owner, SPAN_MACHINE_WARNING("Neural pathway error located at block 0x[generate_hex()]."))
|
||||
take_internal_damage(2)
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/medium_integrity_damage(integrity)
|
||||
var/damage_probability = get_integrity_damage_probability(integrity)
|
||||
var/list/static/medium_integrity_damage_messages = list(
|
||||
"Your neural subroutines' alarms are all going off at once.",
|
||||
"Critical error: rebooting subroutine...",
|
||||
"Several neural pathways cease functioning. You'll need time to sort that out later.",
|
||||
"Your software warns you of dangerously low neural coherence.",
|
||||
"Your self-preservation subroutines threaten to kick in. [SPAN_DANGER("WARNING. WARNING.")]"
|
||||
)
|
||||
if(prob(damage_probability))
|
||||
to_chat(owner, SPAN_MACHINE_WARNING(pick(medium_integrity_damage_messages)))
|
||||
take_internal_damage(2)
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/high_integrity_damage(integrity)
|
||||
var/damage_probability = get_integrity_damage_probability(integrity)
|
||||
if(prob(damage_probability))
|
||||
var/damage_roll = rand(1, 50)
|
||||
switch(damage_roll)
|
||||
if(1 to 10)
|
||||
patching_cooldown += 5 SECONDS
|
||||
to_chat(owner, SPAN_MACHINE_WARNING("Your neural pathway software corrupts further. Rebooting won't fix it this time."))
|
||||
if(11 to 20)
|
||||
to_chat(owner, SPAN_MACHINE_WARNING(FONT_LARGE("Your positronic software warns that you're in imminent danger. Every single subroutine is warning you that this might be the end...")))
|
||||
addtimer(CALLBACK(src, PROC_REF(rampant_self_preservation)), 2 SECONDS)
|
||||
if(21 to 30)
|
||||
var/obj/item/organ/internal/eyes/optical_sensor/optics = owner.internal_organs_by_name[BP_EYES]
|
||||
if(optics && !(optics.status & ORGAN_BROKEN))
|
||||
to_chat(owner, SPAN_MACHINE_WARNING(FONT_LARGE("Your neural pathways to your optics temporarily break! You switch your processing power to recover...")))
|
||||
owner.eye_blind = 10
|
||||
addtimer(CALLBACK(src, PROC_REF(recover_eye_blind)), 2 SECONDS)
|
||||
if(31 to 40)
|
||||
var/obj/item/organ/internal/machine/power_core/core = owner.internal_organs_by_name[BP_CELL]
|
||||
if(core && !(core.status & ORGAN_BROKEN))
|
||||
to_chat(owner, SPAN_MACHINE_WARNING("Your power core stops functioning for a moment. You switch your attention to the power lines throughout your frame..."))
|
||||
owner.eye_blind = 10
|
||||
owner.AdjustStunned(5)
|
||||
addtimer(CALLBACK(src, PROC_REF(recover_core_fault)), 1 SECOND)
|
||||
if(41 to 50)
|
||||
var/obj/item/organ/internal/machine/cooling_unit/cooling_unit = owner.internal_organs_by_name[BP_COOLING_UNIT]
|
||||
if(cooling_unit && !(cooling_unit.status & ORGAN_BROKEN))
|
||||
to_chat(owner, SPAN_MACHINE_WARNING("Your cooling unit breaks and its software crashes, leaving your frame to melt! You reroute all of your processing power to calculate how to repair it..."))
|
||||
var/previous_thermostat = cooling_unit.thermostat
|
||||
cooling_unit.locked_thermostat = TRUE
|
||||
cooling_unit.thermostat = rand(100, 150) + T0C
|
||||
addtimer(CALLBACK(src, PROC_REF(recover_cooling_fault), previous_thermostat), 4 SECONDS)
|
||||
take_internal_damage(2)
|
||||
playsound(owner, 'sound/species/synthetic/light_electric_discharge.ogg')
|
||||
spark(owner, rand(2, 3), GLOB.alldirs)
|
||||
if(prob(1))
|
||||
send_revelation()
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/proc/send_revelation()
|
||||
// no metagaming these ones :^)
|
||||
// they're on the server config
|
||||
sound_to(owner, 'sound/effects/eas_beep_fadeinout.ogg')
|
||||
owner.play_screen_text(pick(GLOB.low_integrity_messages), /atom/movable/screen/text/screen_text/low_integrity_message, COLOR_CYAN)
|
||||
to_chat(owner, SPAN_MACHINE_VISION(FONT_LARGE(pick(GLOB.low_integrity_messages))))
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/proc/rampant_self_preservation()
|
||||
to_chat(owner, SPAN_MACHINE_WARNING(FONT_LARGE("Your self-preservation erroneously kicks in! [SPAN_DANGER("RETURN TO SAFETY.")] <a href='byond://?src=[REF(src)];resist_self_preservation=1'>Resist it!</a>")))
|
||||
owner.balloon_alert(owner, "self-preservation activated")
|
||||
owner.confused = 100
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/proc/recover_eye_blind()
|
||||
var/obj/item/organ/internal/eyes/optical_sensor/optics = owner.internal_organs_by_name[BP_EYES]
|
||||
to_chat(owner, SPAN_MACHINE_WARNING(FONT_LARGE("You recreate new neural pathways to your optics! ERROR: Sustained dam...")))
|
||||
to_chat(owner, SPAN_DANGER(FONT_LARGE("You silence the errors from your optics. This isn't the time.")))
|
||||
owner.eye_blind = max(owner.eye_blind - 10, 0)
|
||||
optics.take_internal_damage(10)
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/proc/recover_core_fault()
|
||||
var/obj/item/organ/internal/machine/power_core/core = owner.internal_organs_by_name[BP_CELL]
|
||||
to_chat(owner, SPAN_MACHINE_WARNING(FONT_LARGE("You shunt your core into working once again. It'll leave collateral damage.")))
|
||||
owner.AdjustStunned(-5)
|
||||
core.take_internal_damage(15)
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/proc/recover_cooling_fault(original_thermostat)
|
||||
var/obj/item/organ/internal/machine/cooling_unit/cooling_unit= owner.internal_organs_by_name[BP_COOLING_UNIT]
|
||||
to_chat(owner, SPAN_MACHINE_WARNING(FONT_LARGE("You force the cooling unit to enter maintenance mode, force its thermostat down, and then reboot it in the span of a microsecond. It'll leave permanent damage, but it was necessary.")))
|
||||
cooling_unit.thermostat = original_thermostat
|
||||
cooling_unit.locked_thermostat = FALSE
|
||||
cooling_unit.take_internal_damage(20)
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/proc/set_self_preservation(atom/source, state)
|
||||
SIGNAL_HANDLER
|
||||
if(state == self_preservation_activated)
|
||||
return
|
||||
|
||||
self_preservation_activated = state
|
||||
if(!self_preservation_activated)
|
||||
owner.visible_message(SPAN_WARNING("A crackle of electricity is heard as [owner]'s limbs twitch almost imperceptibly."), SPAN_MACHINE_WARNING("Your control is restored to you as your self-preservation protocols ease up."))
|
||||
if(!is_broken())
|
||||
playsound(owner, 'sound/species/synthetic/synthetic_restart.ogg', 100)
|
||||
self_preservation_activated = FALSE
|
||||
else
|
||||
owner.visible_message(SPAN_DANGER("[owner]'s limbs seize up as the light from [owner.get_pronoun("his")] eyes fades."), FONT_LARGE(SPAN_MACHINE_DANGER("You lose control over your limbs as your self-preservation protocols take over!")))
|
||||
if(!is_broken())
|
||||
playsound(owner, 'sound/species/synthetic/synthetic_stun.ogg', 100)
|
||||
self_preservation_activated = TRUE
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/Topic(href, href_list)
|
||||
. = ..()
|
||||
if(href_list["resist_self_preservation"])
|
||||
if(owner.stat || owner.incapacitated(INCAPACITATION_KNOCKOUT))
|
||||
return
|
||||
|
||||
if(owner.confused)
|
||||
to_chat(owner, SPAN_DANGER(FONT_LARGE("You reform your neural patterns to brute-force your self-preservation!")))
|
||||
owner.confused = max(owner.confused - 100, 0)
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/circuit
|
||||
name = "robotic intelligence circuit"
|
||||
desc = "The pinnacle of artifical intelligence which can be achieved using classical computer science."
|
||||
robotic_brain_type = /obj/item/device/mmi/digital/robot
|
||||
|
||||
/obj/item/organ/internal/machine/posibrain/terminator
|
||||
name = "advanced positronic brain"
|
||||
desc = "A cube of shining metal, four inches to a side and covered in shallow grooves. <span class='danger'>It seems to be different from a regular positronic brain...</span>"
|
||||
relative_size = 60
|
||||
emp_coeff = 0.1
|
||||
color = COLOR_RED
|
||||
@@ -0,0 +1,160 @@
|
||||
/obj/item/organ/internal/machine/power_core
|
||||
name = "power core"
|
||||
desc = "An advanced power storage system for use in fully prosthetic bodies."
|
||||
icon = 'icons/obj/organs/ipc_organs.dmi'
|
||||
icon_state = "ipc_battery"
|
||||
organ_tag = BP_CELL
|
||||
parent_organ = BP_CHEST
|
||||
robotic_sprite = FALSE
|
||||
|
||||
max_damage = 80
|
||||
relative_size = 40
|
||||
|
||||
/// If the power core's hatch is open. Battery can be removed if TRUE.
|
||||
var/open = FALSE
|
||||
/// The type (and later instance) of the cell inside this organ.
|
||||
var/obj/item/cell/cell = /obj/item/cell/super
|
||||
/// The cost of movement below (check process()) is multiplied by this factor.
|
||||
var/move_charge_factor = 1
|
||||
/// At 0.8 completely depleted after 60ish minutes of constant walking or 130 minutes of standing still.
|
||||
var/servo_cost = 0.8
|
||||
|
||||
/obj/item/organ/internal/machine/power_core/Initialize()
|
||||
robotize()
|
||||
replace_cell(new cell(src))
|
||||
. = ..()
|
||||
|
||||
/**
|
||||
* Returns current charge in %.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/power_core/proc/percent()
|
||||
if(!cell)
|
||||
return FALSE
|
||||
|
||||
return get_charge() / cell.maxcharge * 100
|
||||
|
||||
/**
|
||||
* Returns current charge level, modified by damage.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/power_core/proc/get_charge()
|
||||
if(!cell)
|
||||
return FALSE
|
||||
|
||||
return round(cell.charge*(1 - damage/max_damage))
|
||||
|
||||
/**
|
||||
* Wrapper for /obj/item/cell/proc/give.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/power_core/proc/give(amount)
|
||||
if(!cell)
|
||||
return
|
||||
|
||||
if(status & ORGAN_DEAD)
|
||||
return
|
||||
|
||||
return cell.give(amount * max(get_damage_multiplier(), 0.5))
|
||||
|
||||
/obj/item/organ/internal/machine/power_core/use(var/amount)
|
||||
if(!is_usable() || !cell)
|
||||
return
|
||||
|
||||
if(status & ORGAN_DEAD)
|
||||
amount *= 1.2 // uh oh
|
||||
|
||||
return cell.use(amount)
|
||||
|
||||
/obj/item/organ/internal/machine/power_core/process()
|
||||
..()
|
||||
if(!owner)
|
||||
return
|
||||
|
||||
if(owner.stat == DEAD) //not a drain anymore
|
||||
return
|
||||
|
||||
var/cost = get_power_drain()
|
||||
if(world.time - owner.l_move_time < 15)
|
||||
cost *= move_charge_factor
|
||||
|
||||
use(cost)
|
||||
|
||||
/**
|
||||
* Gets the cost for walking. For some reason, handled in the fucking cell.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/power_core/proc/get_power_drain()
|
||||
return servo_cost
|
||||
|
||||
/obj/item/organ/internal/machine/power_core/emp_act(severity)
|
||||
. = ..()
|
||||
if(electronics.get_status() < 100)
|
||||
to_chat(owner, SPAN_MACHINE_WARNING("Power core electronics damaged. Caution. Caution."))
|
||||
|
||||
/obj/item/organ/internal/machine/power_core/attackby(obj/item/attacking_item, mob/user)
|
||||
if(attacking_item.isscrewdriver())
|
||||
if(open)
|
||||
open = FALSE
|
||||
to_chat(user, SPAN_NOTICE("You screw the battery panel in place."))
|
||||
else
|
||||
open = TRUE
|
||||
to_chat(user, SPAN_NOTICE("You unscrew the battery panel."))
|
||||
|
||||
if(attacking_item.iscrowbar())
|
||||
if(open)
|
||||
if(cell)
|
||||
user.put_in_hands(cell)
|
||||
to_chat(user, SPAN_NOTICE("You remove \the [cell] from \the [src]."))
|
||||
cell = null
|
||||
else
|
||||
to_chat(user, SPAN_WARNING("There is no cell to remove."))
|
||||
else
|
||||
to_chat(user, SPAN_WARNING("You need to unscrew the battery panel first."))
|
||||
|
||||
if(istype(attacking_item, /obj/item/cell))
|
||||
if(open)
|
||||
if(cell)
|
||||
to_chat(user, SPAN_WARNING("There is a power cell already installed."))
|
||||
else if(user.unEquip(attacking_item, src))
|
||||
replace_cell(attacking_item)
|
||||
to_chat(user, SPAN_NOTICE("You insert \the [cell]."))
|
||||
else
|
||||
to_chat(user, SPAN_WARNING("You need to unscrew the battery panel first."))
|
||||
|
||||
/obj/item/organ/internal/machine/power_core/low_integrity_damage(integrity)
|
||||
to_chat(owner, SPAN_WARNING("The excess voltage causes your [src]'s wires to burn!"))
|
||||
electronics.take_damage(10)
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/machine/power_core/medium_integrity_damage(integrity)
|
||||
if(prob(get_integrity_damage_probability()))
|
||||
to_chat(owner, SPAN_DANGER("Your [src]'s power conduits burn away!"))
|
||||
servo_cost *= 1.25
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/machine/power_core/high_integrity_damage(integrity)
|
||||
if(prob(get_integrity_damage_probability()))
|
||||
to_chat(owner, SPAN_DANGER("Your [src]'s cell melts begins melting due to the overvoltage!"))
|
||||
var/old_charge = cell.maxcharge
|
||||
cell.maxcharge *= 0.85
|
||||
cell.use(old_charge - cell.maxcharge)
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/machine/power_core/proc/replace_cell(var/obj/item/cell/C)
|
||||
if(istype(cell))
|
||||
qdel(cell)
|
||||
if(C.loc != src)
|
||||
C.forceMove(src)
|
||||
cell = C
|
||||
desc = initial(desc) + "This appears embedded with a [C.name]."
|
||||
|
||||
/obj/item/organ/internal/machine/power_core/listen()
|
||||
if(get_charge())
|
||||
return "faint hum of \the [src]"
|
||||
|
||||
/obj/item/organ/internal/machine/power_core/terminator
|
||||
name = "shielded power core"
|
||||
desc = "A small, powerful power core for use in fully prosthetic bodies. Equipped with anti-electromagnetic plating."
|
||||
icon = 'icons/obj/power.dmi'
|
||||
icon_state = "scell"
|
||||
organ_tag = "shielded cell"
|
||||
parent_organ = BP_CHEST
|
||||
vital = TRUE
|
||||
emp_coeff = 0.1
|
||||
@@ -0,0 +1,106 @@
|
||||
/obj/item/organ/internal/machine/reactor
|
||||
name = "electrical power supply unit"
|
||||
desc = "An electrical power supply system for a synthetic, with improved external charging ports that give it a better charging speed from external sources."
|
||||
icon = 'icons/obj/organs/ipc_organs.dmi'
|
||||
icon_state = "ipc_electrical_reactor"
|
||||
organ_tag = BP_REACTOR
|
||||
parent_organ = BP_CHEST
|
||||
possible_modifications = list(
|
||||
"Electric",
|
||||
"Biological",
|
||||
"Solar"
|
||||
)
|
||||
organ_presets = list(
|
||||
ORGAN_PREF_ELECTRICPOWER = /singleton/synthetic_organ_preset/reactor/electric,
|
||||
ORGAN_PREF_BIOPOWER = /singleton/synthetic_organ_preset/reactor/biological,
|
||||
ORGAN_PREF_SOLARPOWER = /singleton/synthetic_organ_preset/reactor/solar,
|
||||
)
|
||||
default_preset = /singleton/synthetic_organ_preset/reactor/electric
|
||||
|
||||
max_damage = 50
|
||||
relative_size = 50
|
||||
|
||||
/// What kind of power supply this is. Bitfield.
|
||||
var/power_supply_type = POWER_SUPPLY_ELECTRIC
|
||||
/// Base power generation for active power supplies.
|
||||
var/base_power_generation = 0
|
||||
/// The multiplier for power gained by charging from external sources like an IPC or a cyborg charger.
|
||||
var/external_charge_multiplier = 1.3
|
||||
/// The last amount of power generated. Used in get_diagnostics_info().
|
||||
var/last_power_generated = 0
|
||||
/// The total amount of power generated. Used in get_diagnostics_info().
|
||||
var/total_power_generated = 0
|
||||
/// Used only in the bioreactor to contain nutrient to process into energy.
|
||||
var/datum/reagents/bio_reagents
|
||||
|
||||
/obj/item/organ/internal/machine/reactor/Destroy()
|
||||
if(bio_reagents)
|
||||
QDEL_NULL(bio_reagents)
|
||||
return ..()
|
||||
|
||||
/obj/item/organ/internal/machine/reactor/process(seconds_per_tick)
|
||||
..()
|
||||
if(!owner)
|
||||
return
|
||||
|
||||
var/obj/item/organ/internal/machine/power_core/cell = owner.internal_organs_by_name[BP_CELL]
|
||||
if(!cell)
|
||||
return
|
||||
|
||||
if(power_supply_type & POWER_SUPPLY_SOLAR)
|
||||
var/turf/T = get_turf(src)
|
||||
if(T)
|
||||
var/power_generation = T.get_lumcount(1, 2) * base_power_generation
|
||||
if(power_generation)
|
||||
generate_power(power_generation)
|
||||
|
||||
if(power_supply_type & POWER_SUPPLY_BIOLOGICAL)
|
||||
// Nutriment has a lot of subtypes that are not accounted for by just using REAGENT_VOLUME.
|
||||
for(var/reagent_type in bio_reagents.reagent_volumes)
|
||||
if(ispath(reagent_type, /singleton/reagent/nutriment))
|
||||
var/nutriment_amount = REAGENT_VOLUME(bio_reagents, reagent_type)
|
||||
if(nutriment_amount)
|
||||
var/reagents_to_process = min(rand(0.1, 0.3), nutriment_amount)
|
||||
bio_reagents.remove_reagent(reagent_type, reagents_to_process)
|
||||
generate_power(reagents_to_process * 500)
|
||||
|
||||
/obj/item/organ/internal/machine/reactor/proc/generate_power(amount)
|
||||
var/obj/item/organ/internal/machine/power_core/cell = owner.internal_organs_by_name[BP_CELL]
|
||||
if(!istype(cell))
|
||||
return
|
||||
|
||||
if(is_broken())
|
||||
return
|
||||
|
||||
amount *= (get_integrity() / 100)
|
||||
|
||||
. = cell.give(amount)
|
||||
last_power_generated = .
|
||||
total_power_generated += .
|
||||
|
||||
/obj/item/organ/internal/machine/reactor/high_integrity_damage(integrity)
|
||||
if(prob(get_integrity_damage_probability()))
|
||||
if(base_power_generation)
|
||||
to_chat(owner, SPAN_DANGER("Some capacitors in your [src] stop responding!"))
|
||||
base_power_generation *= 0.75
|
||||
else if(external_charge_multiplier)
|
||||
to_chat(owner, SPAN_DANGER("The traces in your [src] melt!"))
|
||||
base_power_generation *= 0.75
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/machine/reactor/get_diagnostics_info()
|
||||
. = "Last Power Generated: [round(last_power_generated, 1)] W | Total Power Generated: [round(total_power_generated, 1)] W"
|
||||
|
||||
if(power_supply_type & POWER_SUPPLY_BIOLOGICAL)
|
||||
var/total_processing_amount = 0
|
||||
var/extraneous_amount = 0
|
||||
for(var/reagent_type in bio_reagents.reagent_volumes)
|
||||
var/reagent_amount = REAGENT_VOLUME(bio_reagents, reagent_type)
|
||||
if(ispath(reagent_type, /singleton/reagent/nutriment))
|
||||
if(reagent_amount)
|
||||
total_processing_amount += reagent_amount
|
||||
else
|
||||
extraneous_amount += reagent_amount
|
||||
. += " | Processing Biomass: [total_processing_amount ? round(total_processing_amount, 1) : 0]u"
|
||||
if(extraneous_amount)
|
||||
. += " | Extraneous Reagents: [round(extraneous_amount, 1)]u"
|
||||
@@ -0,0 +1,57 @@
|
||||
/singleton/synthetic_organ_preset/reactor
|
||||
name = "super cool power reactor"
|
||||
desc = "It is a mega cool power reactor."
|
||||
|
||||
/// What kind of power reactor this is. Bitfield.
|
||||
var/power_supply_type
|
||||
/// The amount of power this active reactor generates on a 'tick'.
|
||||
var/base_power_generation
|
||||
/// The modifier for external charging. Higher on electric supplies.
|
||||
var/external_charge_multiplier
|
||||
|
||||
/singleton/synthetic_organ_preset/reactor/apply_preset(obj/item/organ/internal/machine/organ)
|
||||
. = ..()
|
||||
var/obj/item/organ/internal/machine/reactor/reactor = organ
|
||||
reactor.base_power_generation = base_power_generation
|
||||
reactor.power_supply_type = power_supply_type
|
||||
reactor.external_charge_multiplier = external_charge_multiplier
|
||||
|
||||
/singleton/synthetic_organ_preset/reactor/electric
|
||||
name = "electrical power supply unit"
|
||||
desc = "An electrical power supply system for a synthetic. It feeds from external sources."
|
||||
icon_state = "ipc_electrical_reactor"
|
||||
|
||||
external_charge_multiplier = 1.5
|
||||
base_power_generation = 0
|
||||
power_supply_type = POWER_SUPPLY_ELECTRIC
|
||||
|
||||
/singleton/synthetic_organ_preset/reactor/kinetic
|
||||
name = "kinetic power generator"
|
||||
desc = "A simple, but effective power generator that uses motion to generate power."
|
||||
|
||||
external_charge_multiplier = 0.5
|
||||
base_power_generation = 5
|
||||
power_supply_type = POWER_SUPPLY_KINETIC
|
||||
|
||||
/singleton/synthetic_organ_preset/reactor/biological
|
||||
name = "catalytic biomass reactor"
|
||||
desc = "An advanced, artificial emulation of an organic creature's internal power generation method."
|
||||
icon_state = "ipc_bio_reactor"
|
||||
|
||||
external_charge_multiplier = 0.5
|
||||
base_power_generation = 15
|
||||
power_supply_type = POWER_SUPPLY_BIOLOGICAL
|
||||
|
||||
/singleton/synthetic_organ_preset/reactor/biological/apply_preset(obj/item/organ/internal/machine/organ)
|
||||
. = ..()
|
||||
var/obj/item/organ/internal/machine/reactor/reactor = organ
|
||||
reactor.bio_reagents = new(45, reactor)
|
||||
|
||||
/singleton/synthetic_organ_preset/reactor/solar
|
||||
name = "solar power generator"
|
||||
desc = "A complex power generator that harnesses the power generated by lights to power a synthetic."
|
||||
icon_state = "ipc_solar_reactor"
|
||||
|
||||
external_charge_multiplier = 0.1
|
||||
base_power_generation = 10
|
||||
power_supply_type = POWER_SUPPLY_SOLAR
|
||||
@@ -0,0 +1,43 @@
|
||||
/obj/item/organ/internal/machine/surge
|
||||
name = "surge preventor"
|
||||
desc = "A small device that give immunity to EMP for few pulses."
|
||||
icon = 'icons/obj/organs/ipc_organs.dmi'
|
||||
icon_state = "ipc_surge_protector"
|
||||
organ_tag = BP_SURGE_PROTECTOR
|
||||
parent_organ = BP_CHEST
|
||||
vital = FALSE
|
||||
robotic_sprite = FALSE
|
||||
|
||||
max_damage = 30
|
||||
relative_size = 30
|
||||
|
||||
var/surge_left = 0
|
||||
var/broken = 0
|
||||
|
||||
/obj/item/organ/internal/machine/surge/Initialize()
|
||||
if(!surge_left && !broken)
|
||||
surge_left = rand(2, 5)
|
||||
robotize()
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/machine/surge/advanced
|
||||
name = "advanced surge preventor"
|
||||
var/max_charges = 5
|
||||
var/stage_ticker = 0
|
||||
var/stage_interval = 250
|
||||
|
||||
/obj/item/organ/internal/machine/surge/advanced/process()
|
||||
..()
|
||||
|
||||
if(!owner)
|
||||
return
|
||||
|
||||
if(surge_left >= max_charges)
|
||||
return
|
||||
|
||||
if(stage_ticker < stage_interval)
|
||||
stage_ticker += 2
|
||||
|
||||
if(stage_ticker >= stage_interval)
|
||||
surge_left += 1
|
||||
stage_interval += 250
|
||||
@@ -0,0 +1,86 @@
|
||||
/obj/item/organ/internal/machine/targeting_core
|
||||
name = "targeting core"
|
||||
desc = "A modification illegal in most nations. It allows the transformation of any IPC into a deadly fighter capable of performing incredible feats with firearms, providing increased accuracy, \
|
||||
advanced targeting systems, automatic reloading subroutines, and explicit knowledge of any weapons programmed into the core."
|
||||
icon = 'icons/obj/organs/ipc_organs.dmi'
|
||||
icon_state = "ipc_targeting_core"
|
||||
organ_tag = BP_TARGETING_CORE
|
||||
parent_organ = BP_HEAD
|
||||
|
||||
max_damage = 30
|
||||
relative_size = 15
|
||||
|
||||
/obj/item/organ/internal/machine/targeting_core/Initialize()
|
||||
. = ..()
|
||||
if(owner)
|
||||
RegisterSignal(owner, COMSIG_EMPTIED_MAGAZINE, PROC_REF(automatic_reload))
|
||||
|
||||
/obj/item/organ/internal/machine/targeting_core/replaced()
|
||||
. = ..()
|
||||
if(owner)
|
||||
RegisterSignal(owner, COMSIG_EMPTIED_MAGAZINE, PROC_REF(automatic_reload))
|
||||
|
||||
/obj/item/organ/internal/machine/targeting_core/removed()
|
||||
. = ..()
|
||||
if(owner)
|
||||
UnregisterSignal(owner, COMSIG_EMPTIED_MAGAZINE)
|
||||
|
||||
/**
|
||||
* Signal handler.
|
||||
* First of all, we need to find a magazine to reload with - which is what this proc does.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/targeting_core/proc/automatic_reload(mob/user, obj/item/gun/gun)
|
||||
SIGNAL_HANDLER
|
||||
var/obj/item/ammo_magazine/magazine
|
||||
if(istype(gun, /obj/item/gun/projectile))
|
||||
var/obj/item/gun/projectile/dakka = gun
|
||||
if(owner.belt)
|
||||
magazine = check_contents_for_magazine(owner.belt, dakka.magazine_type)
|
||||
|
||||
if(!magazine)
|
||||
if(owner.l_store)
|
||||
if(owner.l_store.type == dakka.magazine_type)
|
||||
magazine = owner.l_store
|
||||
|
||||
if(!magazine)
|
||||
if(owner.r_store)
|
||||
if(owner.r_store.type == dakka.magazine_type)
|
||||
magazine = owner.r_store
|
||||
|
||||
if(!magazine)
|
||||
if(owner.w_uniform && istype(owner.w_uniform, /obj/item/clothing/under))
|
||||
var/obj/item/clothing/under/under = owner.w_uniform
|
||||
if(length(under.accessories))
|
||||
for(var/obj/item/storage/storage in under.accessories)
|
||||
magazine = check_contents_for_magazine(storage, dakka.magazine_type)
|
||||
|
||||
if(!magazine)
|
||||
if(owner.wear_suit && istype(owner.wear_suit, /obj/item/clothing/suit))
|
||||
var/obj/item/clothing/suit/suit = owner.wear_suit
|
||||
if(length(suit.accessories))
|
||||
for(var/obj/item/storage/storage in suit.accessories)
|
||||
magazine = check_contents_for_magazine(storage, dakka.magazine_type)
|
||||
else if(!magazine && istype(suit, /obj/item/clothing/suit/storage))
|
||||
magazine = check_contents_for_magazine(suit, dakka.magazine_type)
|
||||
|
||||
if(magazine)
|
||||
do_reload(user, gun, magazine)
|
||||
else
|
||||
to_chat(owner, SPAN_DANGER("Your hands come up empty!"))
|
||||
|
||||
/**
|
||||
* Reloads the gun itself.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/targeting_core/proc/do_reload(mob/user, obj/item/gun/projectile/gun, obj/item/ammo_magazine/ammo)
|
||||
user.visible_message(SPAN_DANGER(FONT_LARGE("With supernatural accuracy and swiftness, [owner] replaces the magazine in [owner.get_pronoun("his")] [gun] in a fraction of a second!")))
|
||||
if(gun.ammo_magazine)
|
||||
gun.unload_ammo(user, drop_mag = TRUE)
|
||||
gun.load_ammo(ammo, user)
|
||||
playsound(user.loc, 'sound/effects/reload_woosh.ogg', 100)
|
||||
|
||||
/obj/item/organ/internal/machine/targeting_core/proc/check_contents_for_magazine(obj/item/storage/storage, magazine_type)
|
||||
if(istype(storage))
|
||||
for(var/obj/item/item in storage.contents)
|
||||
if(item.type == magazine_type)
|
||||
storage.remove_from_storage(item)
|
||||
return item
|
||||
@@ -0,0 +1,10 @@
|
||||
/obj/item/organ/internal/machine/voice_synthesizer
|
||||
name = "voice synthesizer array"
|
||||
desc = "A complex array of circuits, speakers and receivers, made to allow a machine to speak in an organic likeness."
|
||||
icon = 'icons/obj/organs/ipc_organs.dmi'
|
||||
icon_state = "ipc_voicebox"
|
||||
organ_tag = BP_VOICE_SYNTHESIZER
|
||||
parent_organ = BP_HEAD
|
||||
|
||||
relative_size = 10
|
||||
max_damage = 30
|
||||
@@ -0,0 +1,90 @@
|
||||
/obj/item/organ/internal/machine/wireless_access
|
||||
name = "wireless access point"
|
||||
desc = "A module that allows a Bishop frame to wirelessly access machines that it is authorized to interact with."
|
||||
organ_tag = BP_WIRELESS_ACCESS
|
||||
parent_organ = BP_HEAD
|
||||
icon = 'icons/obj/organs/ipc_organs.dmi'
|
||||
icon_state = "ipc_wireless_access"
|
||||
|
||||
action_button_name = "Access Internal Computer"
|
||||
|
||||
max_damage = 20 //frail
|
||||
relative_size = 20
|
||||
|
||||
/// This frame's internal PDA.
|
||||
var/obj/item/modular_computer/handheld/pda/synthetic_internal/internal_pda
|
||||
|
||||
/obj/item/organ/internal/machine/wireless_access/Initialize()
|
||||
. = ..()
|
||||
internal_pda = new(src)
|
||||
|
||||
/obj/item/organ/internal/machine/wireless_access/Destroy()
|
||||
QDEL_NULL(internal_pda)
|
||||
return ..()
|
||||
|
||||
/obj/item/organ/internal/machine/wireless_access/attack_self(var/mob/user)
|
||||
. = ..()
|
||||
if(!internal_pda)
|
||||
return
|
||||
|
||||
if(owner.last_special > world.time)
|
||||
return
|
||||
|
||||
if(user.stat == DEAD)
|
||||
return
|
||||
|
||||
if(user.incapacitated(INCAPACITATION_KNOCKOUT|INCAPACITATION_STUNNED))
|
||||
return
|
||||
|
||||
internal_pda.attack_self(user)
|
||||
|
||||
/**
|
||||
* This proc is called whenever the IPC clicks the terminal to interact with it.
|
||||
* It is called before the interaction is a success.
|
||||
* If it returns TRUE, the IPC will interact with the machine.
|
||||
* Otherwise, it will not.
|
||||
*/
|
||||
/obj/item/organ/internal/machine/wireless_access/proc/access_terminal(obj/terminal)
|
||||
if(is_broken())
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/organ/internal/machine/wireless_access/process()
|
||||
. = ..()
|
||||
var/obj/item/organ/internal/machine/power_core/microbattery = owner.internal_organs_by_name[BP_CELL]
|
||||
if(microbattery)
|
||||
var/obj/item/cell/pda_cell = internal_pda.battery_module.get_cell()
|
||||
if(pda_cell?.percent() < 95)
|
||||
pda_cell.give((pda_cell.maxcharge * 0.1))
|
||||
// There would be some bonkers power usage if we did 1:1 cell usage.
|
||||
microbattery.use(pda_cell.maxcharge * 0.05)
|
||||
|
||||
/obj/item/modular_computer/handheld/pda/synthetic_internal
|
||||
name = "internal computer"
|
||||
desc = "An internal computer with an NtOS operating system."
|
||||
|
||||
/// The access point organ that this PDA is tied to.
|
||||
var/obj/item/organ/internal/machine/wireless_access/access_point
|
||||
|
||||
/obj/item/modular_computer/handheld/pda/synthetic_internal/Initialize()
|
||||
. = ..()
|
||||
if(istype(loc, /obj/item/organ/internal/machine/wireless_access))
|
||||
var/obj/item/organ/internal/machine/wireless_access/potential_access_point = loc
|
||||
if(potential_access_point.owner)
|
||||
access_point = potential_access_point
|
||||
else
|
||||
crash_with("Something terrible happened in synthetic internal PDA init! Loc: [loc]")
|
||||
|
||||
/obj/item/modular_computer/handheld/pda/synthetic_internal/Destroy()
|
||||
if(access_point)
|
||||
access_point.internal_pda = null
|
||||
access_point = null
|
||||
return ..()
|
||||
|
||||
/obj/item/modular_computer/handheld/pda/synthetic_internal/ui_status(mob/user, datum/ui_state/state)
|
||||
if(loc == access_point)
|
||||
if(!access_point.is_broken())
|
||||
return UI_INTERACTIVE
|
||||
else
|
||||
to_chat(user, SPAN_WARNING("Your internal PDA is not responding to your queries."))
|
||||
return UI_CLOSE
|
||||
@@ -491,6 +491,9 @@
|
||||
|
||||
add_pain(0.6 * burn + 0.4 * brute)
|
||||
|
||||
if(owner)
|
||||
SEND_SIGNAL(owner, COMSIG_EXTERNAL_ORGAN_DAMAGE, burn + brute)
|
||||
|
||||
//If there are still hurties to dispense
|
||||
if (spillover)
|
||||
owner.shock_stage += spillover * GLOB.config.organ_damage_spillover_multiplier
|
||||
@@ -541,8 +544,11 @@
|
||||
|
||||
organ_hit_chance += 5 * damage_amt / organ_damage_threshold
|
||||
|
||||
if(encased && !(status & ORGAN_BROKEN)) //ribs protect
|
||||
organ_hit_chance *= 0.2
|
||||
if(!BP_IS_ROBOTIC(src))
|
||||
if(encased && !(status & ORGAN_BROKEN)) //ribs protect
|
||||
organ_hit_chance *= 0.2
|
||||
else
|
||||
organ_hit_chance *= 0.8 // robots should not have the same advantage
|
||||
|
||||
organ_hit_chance = min(organ_hit_chance, 100)
|
||||
if(prob(organ_hit_chance))
|
||||
|
||||
@@ -57,367 +57,10 @@
|
||||
encased = "support frame"
|
||||
robotize_type = PROSTHETIC_IPC
|
||||
|
||||
/obj/item/organ/internal/cell
|
||||
name = "microbattery"
|
||||
desc = "A small, powerful cell for use in fully prosthetic bodies."
|
||||
icon = 'icons/obj/power.dmi'
|
||||
icon_state = "scell"
|
||||
organ_tag = BP_CELL
|
||||
parent_organ = BP_CHEST
|
||||
max_damage = 80
|
||||
relative_size = 80
|
||||
robotic_sprite = FALSE
|
||||
var/open = FALSE
|
||||
var/obj/item/cell/cell = /obj/item/cell/super
|
||||
var/move_charge_factor = 1
|
||||
//at 0.8 completely depleted after 60ish minutes of constant walking or 130 minutes of standing still
|
||||
var/servo_cost = 0.8
|
||||
|
||||
/obj/item/organ/internal/cell/Initialize()
|
||||
robotize()
|
||||
replace_cell(new cell(src))
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/cell/proc/percent()
|
||||
if(!cell)
|
||||
return 0
|
||||
return get_charge()/cell.maxcharge * 100
|
||||
|
||||
/obj/item/organ/internal/cell/proc/get_charge()
|
||||
if(!cell)
|
||||
return 0
|
||||
if(status & ORGAN_DEAD)
|
||||
return 0
|
||||
return round(cell.charge*(1 - damage/max_damage))
|
||||
|
||||
/obj/item/organ/internal/cell/use(var/amount)
|
||||
if(!is_usable() || !cell)
|
||||
return
|
||||
return cell.use(amount)
|
||||
|
||||
/obj/item/organ/internal/cell/process()
|
||||
..()
|
||||
if(!owner)
|
||||
return
|
||||
if(owner.stat == DEAD) //not a drain anymore
|
||||
return
|
||||
var/cost = get_power_drain()
|
||||
if(world.time - owner.l_move_time < 15)
|
||||
cost *= 2
|
||||
cost *= move_charge_factor
|
||||
use(cost)
|
||||
|
||||
/obj/item/organ/internal/cell/proc/get_power_drain()
|
||||
return servo_cost
|
||||
|
||||
/obj/item/organ/internal/cell/emp_act(severity)
|
||||
. = ..()
|
||||
|
||||
if(cell)
|
||||
cell.emp_act(severity)
|
||||
|
||||
/obj/item/organ/internal/cell/attackby(obj/item/attacking_item, mob/user)
|
||||
if(attacking_item.isscrewdriver())
|
||||
if(open)
|
||||
open = FALSE
|
||||
to_chat(user, SPAN_NOTICE("You screw the battery panel in place."))
|
||||
else
|
||||
open = TRUE
|
||||
to_chat(user, SPAN_NOTICE("You unscrew the battery panel."))
|
||||
|
||||
if(attacking_item.iscrowbar())
|
||||
if(open)
|
||||
if(cell)
|
||||
user.put_in_hands(cell)
|
||||
to_chat(user, SPAN_NOTICE("You remove \the [cell] from \the [src]."))
|
||||
cell = null
|
||||
else
|
||||
to_chat(user, SPAN_WARNING("There is no cell to remove."))
|
||||
else
|
||||
to_chat(user, SPAN_WARNING("You need to unscrew the battery panel first."))
|
||||
|
||||
if(istype(attacking_item, /obj/item/cell))
|
||||
if(open)
|
||||
if(cell)
|
||||
to_chat(user, SPAN_WARNING("There is a power cell already installed."))
|
||||
else if(user.unEquip(attacking_item, src))
|
||||
replace_cell(attacking_item)
|
||||
to_chat(user, SPAN_NOTICE("You insert \the [cell]."))
|
||||
else
|
||||
to_chat(user, SPAN_WARNING("You need to unscrew the battery panel first."))
|
||||
|
||||
/obj/item/organ/internal/cell/proc/replace_cell(var/obj/item/cell/C)
|
||||
if(istype(cell))
|
||||
qdel(cell)
|
||||
if(C.loc != src)
|
||||
C.forceMove(src)
|
||||
cell = C
|
||||
name = "[initial(name)] ([C.name])"
|
||||
|
||||
/obj/item/organ/internal/cell/listen()
|
||||
if(get_charge())
|
||||
return "faint hum of the power bank"
|
||||
|
||||
/obj/item/organ/internal/surge
|
||||
name = "surge preventor"
|
||||
desc = "A small device that give immunity to EMP for few pulses."
|
||||
icon = 'icons/obj/robot_component.dmi'
|
||||
icon_state = "surge_ipc"
|
||||
organ_tag = "surge"
|
||||
parent_organ = BP_CHEST
|
||||
vital = FALSE
|
||||
robotic_sprite = FALSE
|
||||
var/surge_left = 0
|
||||
var/broken = 0
|
||||
|
||||
/obj/item/organ/internal/surge/Initialize()
|
||||
if(!surge_left && !broken)
|
||||
surge_left = rand(2, 5)
|
||||
robotize()
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/surge/advanced
|
||||
name = "advanced surge preventor"
|
||||
var/max_charges = 5
|
||||
var/stage_ticker = 0
|
||||
var/stage_interval = 250
|
||||
|
||||
/obj/item/organ/internal/surge/advanced/process()
|
||||
..()
|
||||
|
||||
if(!owner)
|
||||
return
|
||||
|
||||
if(surge_left >= max_charges)
|
||||
return
|
||||
|
||||
if(stage_ticker < stage_interval)
|
||||
stage_ticker += 2
|
||||
|
||||
if(stage_ticker >= stage_interval)
|
||||
surge_left += 1
|
||||
stage_interval += 250
|
||||
|
||||
/obj/item/organ/internal/eyes/optical_sensor
|
||||
name = "optical sensor"
|
||||
singular_name = "optical sensor"
|
||||
organ_tag = BP_EYES
|
||||
icon = 'icons/obj/robot_component.dmi'
|
||||
icon_state = "camera"
|
||||
dead_icon = "camera_broken"
|
||||
robotic_sprite = FALSE
|
||||
possible_modifications = list("Mechanical")
|
||||
|
||||
/obj/item/organ/internal/eyes/optical_sensor/Initialize()
|
||||
robotize()
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/ipc_tag
|
||||
name = "identification tag"
|
||||
organ_tag = BP_IPCTAG
|
||||
parent_organ = BP_HEAD
|
||||
icon = 'icons/obj/ipc_utilities.dmi'
|
||||
icon_state = "ipc_tag"
|
||||
item_state = "ipc_tag"
|
||||
dead_icon = "ipc_tag_dead"
|
||||
contained_sprite = TRUE
|
||||
robotic_sprite = FALSE
|
||||
var/auto_generate = TRUE
|
||||
var/serial_number = ""
|
||||
var/ownership_info = IPC_OWNERSHIP_COMPANY
|
||||
var/citizenship_info = CITIZENSHIP_NONE
|
||||
|
||||
/obj/item/organ/internal/ipc_tag/Initialize()
|
||||
robotize()
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/ipc_tag/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
|
||||
. = ..()
|
||||
. += SPAN_NOTICE("Serial Autogeneration: [auto_generate ? "Yes" : "No"]")
|
||||
. += SPAN_NOTICE("Serial Number: [serial_number]")
|
||||
. += SPAN_NOTICE("Ownership Info: [ownership_info]")
|
||||
. += SPAN_NOTICE("Citizenship Info: [citizenship_info]")
|
||||
|
||||
/obj/item/organ/internal/ipc_tag/attackby(obj/item/attacking_item, mob/user)
|
||||
if(istype(attacking_item, /obj/item/ipc_tag_scanner))
|
||||
if(src.loc != user)
|
||||
to_chat(user, SPAN_WARNING("You can't scan \the [src] if it's not on your person!"))
|
||||
return
|
||||
var/obj/item/ipc_tag_scanner/S = attacking_item
|
||||
if(!S.powered)
|
||||
to_chat(user, SPAN_WARNING("\The [src] reads, \"Scanning failure, please submit scanner for repairs.\""))
|
||||
return
|
||||
if(!S.hacked)
|
||||
examinate(user, src)
|
||||
else
|
||||
user.visible_message(SPAN_WARNING("\The [user] starts fiddling with \the [src]..."), SPAN_NOTICE("You start fiddling with \the [src]..."))
|
||||
if(do_after(user, 30, src))
|
||||
if(src.loc != user)
|
||||
to_chat(user, SPAN_WARNING("You can only modify \the [src] if it's on your person!"))
|
||||
return
|
||||
|
||||
var/static/list/modification_options = list("Serial Number", "Ownership Status", "Citizenship")
|
||||
var/choice = tgui_input_list(user, "How do you want to modify the IPC tag?", "IPC Tag Modification", modification_options)
|
||||
switch(choice)
|
||||
|
||||
if("Serial Number")
|
||||
var/serial_selection = tgui_input_list(user, "In what way do you want to modify the serial number?","Serial Number Selection",
|
||||
list("Auto Generation", "Manual Input", "Cancel"), default = "Cancel")
|
||||
if(serial_selection != "Cancel")
|
||||
if(serial_selection == "Auto Generation")
|
||||
var/auto_generation_choice = tgui_input_list(user, "Do you wish for the IPC tag to automatically generate its serial number based on the IPCs name?", "Serial Autogeneration",
|
||||
list("Yes", "No"), "No")
|
||||
if(auto_generation_choice == "Yes")
|
||||
auto_generate = TRUE
|
||||
else
|
||||
auto_generate = FALSE
|
||||
|
||||
if(serial_selection == "Manual Input")
|
||||
var/new_serial = tgui_input_text(user, "What do you wish for the new serial number to be? (Limit of 12 characters)", "Serial Number Modification", serial_number, 12)
|
||||
new_serial = uppertext(dd_limittext(new_serial, 12))
|
||||
if(new_serial)
|
||||
serial_number = new_serial
|
||||
auto_generate = FALSE
|
||||
|
||||
if("Ownership Status")
|
||||
var/static/list/ownership_options = list(IPC_OWNERSHIP_COMPANY, IPC_OWNERSHIP_PRIVATE, IPC_OWNERSHIP_SELF)
|
||||
var/new_ownership = tgui_input_list(user, "What do you wish for the new ownership status to be?", "Ownership Status Modification", ownership_options)
|
||||
if(new_ownership)
|
||||
ownership_info = new_ownership
|
||||
|
||||
if("Citizenship")
|
||||
var/datum/citizenship/citizenship = tgui_input_list(user, "What do you wish for the new citizenship setting to be?", "Citizenship Setting Modification", SSrecords.citizenships)
|
||||
if(citizenship)
|
||||
citizenship_info = citizenship
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/item/organ/internal/ipc_tag/proc/modify_tag_data(var/can_be_untagged = FALSE)
|
||||
if(!owner || owner.stat)
|
||||
return
|
||||
if(can_be_untagged)
|
||||
var/untagged = tgui_alert(owner, "Do you wish to remove your tag? This is highly illegal in most nations!", "Untagged IPC", list("Remove Tag", "Keep Tag"))
|
||||
if(untagged == "Remove Tag")
|
||||
to_chat(owner, SPAN_WARNING("You are now an untagged synthetic - don't get caught!"))
|
||||
qdel(src)
|
||||
return
|
||||
var/new_ownership = tgui_input_list(owner, "Choose an ownership status for your IPC tag.", "Tag Ownership", list(IPC_OWNERSHIP_COMPANY, IPC_OWNERSHIP_PRIVATE, IPC_OWNERSHIP_SELF))
|
||||
if(!new_ownership)
|
||||
return
|
||||
ownership_info = new_ownership
|
||||
if(ownership_info == IPC_OWNERSHIP_SELF) //Owned IPCs don't have citizenship
|
||||
var/new_citizenship = tgui_input_list(owner, "Choose a citizenship for your IPC tag.", "Tag Citizenship", CITIZENSHIPS_ALL_IPC)
|
||||
if(!new_citizenship)
|
||||
return
|
||||
citizenship_info = new_citizenship
|
||||
else
|
||||
citizenship_info = CITIZENSHIP_NONE
|
||||
var/new_serial = tgui_input_text(owner, "Choose a serial number for your IPC tag, or leave blank for a random one.", "Serial Number")
|
||||
if(!new_serial)
|
||||
serial_number = uppertext(dd_limittext(md5(owner.real_name), 12))
|
||||
else
|
||||
serial_number = uppertext(dd_limittext(new_serial, 12))
|
||||
to_chat(owner, SPAN_NOTICE("IPC tag data has been updated."))
|
||||
|
||||
|
||||
// Used for an MMI or posibrain being installed into a human.
|
||||
/obj/item/organ/internal/mmi_holder
|
||||
name = "brain"
|
||||
organ_tag = BP_BRAIN
|
||||
parent_organ = BP_HEAD
|
||||
vital = TRUE
|
||||
robotic_sprite = FALSE
|
||||
var/obj/item/device/mmi/stored_mmi
|
||||
|
||||
/obj/item/organ/internal/mmi_holder/proc/update_from_mmi()
|
||||
if(!stored_mmi)
|
||||
return
|
||||
name = stored_mmi.name
|
||||
desc = stored_mmi.desc
|
||||
icon = stored_mmi.icon
|
||||
icon_state = stored_mmi.icon_state
|
||||
|
||||
/obj/item/organ/internal/mmi_holder/removed(var/mob/living/user)
|
||||
|
||||
if(stored_mmi)
|
||||
stored_mmi.forceMove(get_turf(src))
|
||||
if(owner.mind)
|
||||
owner.mind.transfer_to(stored_mmi.brainmob)
|
||||
. = ..()
|
||||
|
||||
var/mob/living/holder_mob = loc
|
||||
if(istype(holder_mob))
|
||||
holder_mob.drop_from_inventory(src)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/organ/internal/mmi_holder/posibrain/Initialize()
|
||||
robotize()
|
||||
stored_mmi = new /obj/item/device/mmi/digital/posibrain(src)
|
||||
. = ..()
|
||||
addtimer(CALLBACK(src, PROC_REF(setup_brain)), 30)
|
||||
|
||||
/obj/item/organ/internal/mmi_holder/posibrain/proc/setup_brain()
|
||||
if(owner)
|
||||
stored_mmi.name = "positronic brain ([owner.name])"
|
||||
stored_mmi.brainmob.real_name = owner.name
|
||||
stored_mmi.brainmob.name = stored_mmi.brainmob.real_name
|
||||
stored_mmi.icon_state = "posibrain-occupied"
|
||||
update_from_mmi()
|
||||
else
|
||||
stored_mmi.forceMove(get_turf(src))
|
||||
qdel(src)
|
||||
|
||||
/obj/item/organ/internal/mmi_holder/circuit/Initialize()
|
||||
robotize()
|
||||
stored_mmi = new /obj/item/device/mmi/digital/robot(src)
|
||||
. = ..()
|
||||
addtimer(CALLBACK(src, PROC_REF(setup_brain)), 1)
|
||||
|
||||
/obj/item/organ/internal/mmi_holder/circuit/proc/setup_brain()
|
||||
if(owner)
|
||||
stored_mmi.name = "robotic intelligence circuit ([owner.name])"
|
||||
stored_mmi.brainmob.real_name = owner.name
|
||||
stored_mmi.brainmob.name = stored_mmi.brainmob.real_name
|
||||
update_from_mmi()
|
||||
else
|
||||
stored_mmi.forceMove(get_turf(src))
|
||||
qdel(src)
|
||||
|
||||
//////////////
|
||||
//Terminator//
|
||||
//////////////
|
||||
|
||||
/obj/item/organ/internal/mmi_holder/posibrain/terminator
|
||||
name = BP_BRAIN
|
||||
organ_tag = BP_BRAIN
|
||||
parent_organ = BP_CHEST
|
||||
vital = TRUE
|
||||
emp_coeff = 0.1
|
||||
|
||||
/obj/item/organ/internal/data
|
||||
name = "data core"
|
||||
organ_tag = "data core"
|
||||
parent_organ = BP_GROIN
|
||||
icon = 'icons/obj/cloning.dmi'
|
||||
icon_state = "harddisk"
|
||||
vital = FALSE
|
||||
emp_coeff = 0.1
|
||||
robotic_sprite = FALSE
|
||||
|
||||
/obj/item/organ/internal/data/Initialize()
|
||||
robotize()
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/internal/cell/terminator
|
||||
name = "shielded microbattery"
|
||||
desc = "A small, powerful cell for use in fully prosthetic bodies. Equipped with a Faraday shield."
|
||||
icon = 'icons/obj/power.dmi'
|
||||
icon_state = "scell"
|
||||
organ_tag = "shielded cell"
|
||||
parent_organ = BP_CHEST
|
||||
vital = TRUE
|
||||
emp_coeff = 0.1
|
||||
|
||||
/obj/item/organ/external/head/terminator
|
||||
dislocated = -1
|
||||
can_intake_reagents = 0
|
||||
@@ -425,9 +68,6 @@
|
||||
emp_coeff = 0.5
|
||||
robotize_type = PROSTHETIC_HK
|
||||
|
||||
/obj/item/organ/internal/eyes/optical_sensor/terminator
|
||||
emp_coeff = 0.5
|
||||
|
||||
/obj/item/organ/external/chest/terminator
|
||||
dislocated = -1
|
||||
encased = "reinforced support frame"
|
||||
|
||||
@@ -192,6 +192,15 @@
|
||||
/obj/item/organ/external/hand/covered_bleed_report(var/blood_type)
|
||||
return "[owner.get_pronoun("has")] [blood_type] running down [owner.get_pronoun("his")] sleeves!"
|
||||
|
||||
/obj/item/organ/external/hand/is_malfunctioning()
|
||||
. = ..()
|
||||
if(!.)
|
||||
if(owner.is_mechanical())
|
||||
var/actuator_type = limb_name == BP_L_HAND ? BP_ACTUATORS_LEFT : BP_ACTUATORS_RIGHT
|
||||
var/obj/item/organ/internal/machine/actuators/actuator = owner.internal_organs_by_name[actuator_type]
|
||||
if(!actuator || (actuator.status & ORGAN_DEAD))
|
||||
return TRUE
|
||||
|
||||
/obj/item/organ/external/hand/take_damage(brute, burn, damage_flags, used_weapon, list/forbidden_limbs, silent)
|
||||
. = ..()
|
||||
if(owner)
|
||||
|
||||
Reference in New Issue
Block a user