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:
Matt Atlas
2025-12-21 17:26:23 +01:00
committed by GitHub
parent 1898ad3417
commit e0aa218843
201 changed files with 5962 additions and 896 deletions
+36 -1
View File
@@ -187,6 +187,7 @@
#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movement.dm"
#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_x_act.dm"
#include "code\__DEFINES\dcs\signals\signals_atom\signals_power_cell.dm"
#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_human.dm"
#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_living.dm"
#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_main.dm"
#include "code\__DEFINES\dcs\signals\signals_object\signals_object.dm"
@@ -476,7 +477,8 @@
#include "code\datums\components\local_network.dm"
#include "code\datums\components\orbiter.dm"
#include "code\datums\components\overlay_lighting.dm"
#include "code\datums\components\armor\armor.dm"
#include "code\datums\components\armor\_armor.dm"
#include "code\datums\components\armor\synthetic_armor.dm"
#include "code\datums\components\base_name\base_name.dm"
#include "code\datums\components\eye\_eye.dm"
#include "code\datums\components\eye\base_planner.dm"
@@ -487,6 +489,7 @@
#include "code\datums\components\multitool\circuitboards\circuitboards.dm"
#include "code\datums\components\overhead_emote\overhead_emote_datum.dm"
#include "code\datums\components\overhead_emote\overhead_emote_singleton.dm"
#include "code\datums\components\synthetic_endoskeleton\synthetic_endoskeleton.dm"
#include "code\datums\components\turf_click\turf_hand.dm"
#include "code\datums\elements\_element.dm"
#include "code\datums\elements\connect_loc.dm"
@@ -505,6 +508,7 @@
#include "code\datums\langchat\langchat.dm"
#include "code\datums\looping_sounds\_looping_sound.dm"
#include "code\datums\looping_sounds\machinery_sounds.dm"
#include "code\datums\looping_sounds\mobs.dm"
#include "code\datums\looping_sounds\revenant_rift.dm"
#include "code\datums\looping_sounds\thermal_drill.dm"
#include "code\datums\observation\_debug.dm"
@@ -573,6 +577,10 @@
#include "code\datums\scenarios\scenario.dm"
#include "code\datums\state_machine\state.dm"
#include "code\datums\state_machine\transition.dm"
#include "code\datums\synthetic_internals\_synthetic_internals.dm"
#include "code\datums\synthetic_internals\electronics.dm"
#include "code\datums\synthetic_internals\plating.dm"
#include "code\datums\synthetic_internals\wiring.dm"
#include "code\datums\tips\tips.dm"
#include "code\datums\trading\_trading_defines.dm"
#include "code\datums\trading\ai.dm"
@@ -3008,6 +3016,7 @@
#include "code\modules\modular_computers\file_system\programs\research\ai_restorer.dm"
#include "code\modules\modular_computers\file_system\programs\research\chemistry_codex.dm"
#include "code\modules\modular_computers\file_system\programs\research\ntmonitor.dm"
#include "code\modules\modular_computers\file_system\programs\research\robotics.dm"
#include "code\modules\modular_computers\file_system\programs\security\camera.dm"
#include "code\modules\modular_computers\file_system\programs\security\digitalwarrant.dm"
#include "code\modules\modular_computers\file_system\programs\security\guntracker.dm"
@@ -3017,6 +3026,7 @@
#include "code\modules\modular_computers\file_system\programs\system\configurator.dm"
#include "code\modules\modular_computers\file_system\programs\system\file_browser.dm"
#include "code\modules\modular_computers\file_system\programs\system\ntdownloader.dm"
#include "code\modules\modular_computers\hardware\access_cable_dongle.dm"
#include "code\modules\modular_computers\hardware\ai_slot.dm"
#include "code\modules\modular_computers\hardware\battery_module.dm"
#include "code\modules\modular_computers\hardware\card_slot.dm"
@@ -3029,6 +3039,7 @@
#include "code\modules\modular_computers\hardware\portable_hard_drive_presets.dm"
#include "code\modules\modular_computers\hardware\processor_unit.dm"
#include "code\modules\modular_computers\hardware\tesla_link.dm"
#include "code\modules\modular_computers\hardware\universal_port.dm"
#include "code\modules\modular_computers\items\paper_scanner.dm"
#include "code\modules\modular_computers\NTNet\NTNet.dm"
#include "code\modules\modular_computers\NTNet\NTNet_relay.dm"
@@ -3040,6 +3051,7 @@
#include "code\modules\movespeed\modifiers\mobs.dm"
#include "code\modules\movespeed\modifiers\reagents.dm"
#include "code\modules\movespeed\modifiers\spells.dm"
#include "code\modules\movespeed\modifiers\synthetics.dm"
#include "code\modules\multiz\_stubs.dm"
#include "code\modules\multiz\basic.dm"
#include "code\modules\multiz\hoist.dm"
@@ -3083,6 +3095,26 @@
#include "code\modules\organs\internal\species\tajara.dm"
#include "code\modules\organs\internal\species\unathi.dm"
#include "code\modules\organs\internal\species\vaurca.dm"
#include "code\modules\organs\internal\species\machine\_generic.dm"
#include "code\modules\organs\internal\species\machine\_presets.dm"
#include "code\modules\organs\internal\species\machine\access_port.dm"
#include "code\modules\organs\internal\species\machine\actuators.dm"
#include "code\modules\organs\internal\species\machine\cooling_unit.dm"
#include "code\modules\organs\internal\species\machine\cooling_unit_presets.dm"
#include "code\modules\organs\internal\species\machine\data_core.dm"
#include "code\modules\organs\internal\species\machine\hydraulics.dm"
#include "code\modules\organs\internal\species\machine\internal_diagnostics.dm"
#include "code\modules\organs\internal\species\machine\internal_storage.dm"
#include "code\modules\organs\internal\species\machine\ipc_tag.dm"
#include "code\modules\organs\internal\species\machine\optical_sensor.dm"
#include "code\modules\organs\internal\species\machine\posibrain.dm"
#include "code\modules\organs\internal\species\machine\power_core.dm"
#include "code\modules\organs\internal\species\machine\reactor.dm"
#include "code\modules\organs\internal\species\machine\reactor_presets.dm"
#include "code\modules\organs\internal\species\machine\surge_protector.dm"
#include "code\modules\organs\internal\species\machine\targeting_core.dm"
#include "code\modules\organs\internal\species\machine\voice_synthesizer.dm"
#include "code\modules\organs\internal\species\machine\wireless_access.dm"
#include "code\modules\organs\subtypes\autakh.dm"
#include "code\modules\organs\subtypes\diona.dm"
#include "code\modules\organs\subtypes\industrial.dm"
@@ -3600,6 +3632,7 @@
#include "code\modules\research\designs\mechfab\prosthetics\internal.dm"
#include "code\modules\research\designs\mechfab\robot\robot.dm"
#include "code\modules\research\designs\mechfab\robot\robot_upgrades.dm"
#include "code\modules\research\designs\mechfab\synthetic\synthetic_organ_parts.dm"
#include "code\modules\research\designs\protolathe\deployable_kits.dm"
#include "code\modules\research\designs\protolathe\disk_designs.dm"
#include "code\modules\research\designs\protolathe\hud_glasses_designs.dm"
@@ -3799,6 +3832,8 @@
#include "code\modules\tgui\modules\armor_values.dm"
#include "code\modules\tgui\modules\flavor_text.dm"
#include "code\modules\tgui\modules\hivenet_manifest.dm"
#include "code\modules\tgui\modules\ipc_diagnostic.dm"
#include "code\modules\tgui\modules\neural_configuration.dm"
#include "code\modules\tgui\states\admin.dm"
#include "code\modules\tgui\states\always.dm"
#include "code\modules\tgui\states\conscious.dm"
+2
View File
@@ -66,6 +66,8 @@
#define COMSIG_ITEM_UPDATE_STATE "update_state"
/// updates sprites
#define COMSIG_ITEM_ICON_UPDATE "icon_update"
/// when a gun mag is emptied
#define COMSIG_EMPTIED_MAGAZINE "emptied_magazine"
// tgui signals
#define COMSIG_TGUI_CLOSE "tgui_close"
@@ -0,0 +1,2 @@
///from datum/species/machine/handle_sprint_cost()
#define COMSIG_IPC_HAS_SPRINTED "ipc_sprinted"
+57
View File
@@ -141,6 +141,23 @@
#define BP_CELL "cell"
#define BP_OPTICS "optics"
#define BP_IPCTAG "ipc tag"
#define BP_REACTOR "reactor"
#define BP_VOICE_SYNTHESIZER "voice synthesizer"
#define BP_DIAGNOSTICS_SUITE "internal diagnostics suite"
#define BP_HYDRAULICS "hydraulics system"
#define BP_COOLING_UNIT "cooling unit"
#define BP_ACCESS_PORT "universal access port"
#define BP_ACTUATORS_LEFT "left arm actuators"
#define BP_ACTUATORS_RIGHT "right arm actuators"
#define BP_SURGE_PROTECTOR "surge"
#define BP_DATACORE "data core"
#define BP_TARGETING_CORE "targeting core"
// Bishop frame only.
#define BP_WIRELESS_ACCESS "wireless access point"
// G2 frame only.
#define BP_INTERNAL_STORAGE "internal storage system"
// Parasite organs
#define BP_ZOMBIE_PARASITE "black tumour"
@@ -303,6 +320,24 @@
#define MOB_TINY 4
#define MOB_MINISCULE 1
// Mob weight, separate from mob size. Affects how easy a mob is to lift.
/// Light mobs, can be lifted by anyone.
#define MOB_WEIGHT_LIGHT 1
/// Medium weight mobs, the default for humanoids. Can be lifted by medium+.
#define MOB_WEIGHT_MEDIUM 2
/// Heavy mobs. They need speecial equipment or a heavy+ mob to lift.
#define MOB_WEIGHT_HEAVY 3
/// Superheavy mobs. Gonna need a lot of help with those.
#define MOB_WEIGHT_SUPERHEAVY 4
// Mob strength. This allows us to represent stronger mobs with a bit more granularity. Robots should be stronger than normal people. A bonus to the checks on lifting/throwing mobs, essentially.
/// A normal mob, basically. Does not get us any buffs.
#define MOB_STRENGTH_NORMAL 0
/// A strong mob. Small bonus.
#define MOB_STRENGTH_STRONG 1
/// A very strong mob. Big bonus.
#define MOB_STRENGTH_VERY_STRONG 2
#define BASE_MAX_NUTRITION 600
#define HUNGER_FACTOR 0.04 // Factor of how fast mob nutrition decreases over time.
@@ -573,3 +608,25 @@
#define DO_MISSING_TARGET (-2)
#define DO_INCAPACITATED (-3)
#define DO_EXTRA_CHECKS (-4)
// Threshold defines for integrity damage. Used for when you want to check for a specific threshold to do damage effects.
#define IPC_INTEGRITY_THRESHOLD_LOW 75
#define IPC_INTEGRITY_THRESHOLD_MEDIUM 50
#define IPC_INTEGRITY_THRESHOLD_HIGH 25
#define IPC_INTEGRITY_THRESHOLD_VERY_HIGH 10
// These are the power supply organ defines.
// They are bitflags because it should be technically possible to have a power supply do multiple things at once if you wish.
// Just be wary of balancing issues.
/// The default functioning of a power reactor. Externally powered only. Allows recharging from APCs. Has a multiplier for faster recharging from power stations.
#define POWER_SUPPLY_ELECTRIC 1
/// Recharges by walking/running. Far slower at charging from external sources.
#define POWER_SUPPLY_KINETIC 2
/// Recharges through consuming nutrients/food. Slower at recharging from external sources.
#define POWER_SUPPLY_BIOLOGICAL 4
/// Recharges from external lighting. Slower at recharging from external sources.
#define POWER_SUPPLY_SOLAR 8
+15
View File
@@ -48,3 +48,18 @@
* Set the canceled variable to true in order to negate said weakness.
*/
#define COMSIG_GRAVITY_WEAKNESS_EVENT "gravity_weakness_event"
/// Raised external organ (a limb) takes damage. Used for the synthetic endoskeleton at the moment. Must supply damage.
#define COMSIG_EXTERNAL_ORGAN_DAMAGE "machine_internal_damage"
/// Sent when the burst damage is cleared by the posibrain.
#define COMSIG_SYNTH_EMP_DAMAGE_CLEARED "emp_damage_cleared"
/// Sent when the synthetic enters self-preservation mode. Must supply a TRUE/FALSE state.
#define COMSIG_SYNTH_SET_SELF_PRESERVATION "synth_self_preservation_set"
/// Sent when endoskeleton repair is done. Must supply a number of damage healed.
#define COMSIG_SYNTH_ENDOSKELETON_REPAIR "synth_endoskeleton_repair"
/// Sent when a full endoskeleton repair is done. Unlike the normal one, also restores max_damage to initial state. No extra arguments.
#define COMSIG_SYNTH_ENDOSKELETON_FULL_REPAIR "synth_endoskeleton_full_repair"
+16
View File
@@ -35,3 +35,19 @@
/// Internal organ. Removed, used for appendixes.
#define ORGAN_PREF_REMOVED "removed"
/// Note that a "normal" limb or organ has no pref, so there's no define for it.
/// Air cooling. Technically not used, but we need this for the linked lists in cooling_unit.dm.
#define ORGAN_PREF_AIRCOOLED "air"
/// Liquid cooling. For IPC cooling units. Default is air cooling.
#define ORGAN_PREF_LIQUIDCOOLED "liquid"
/// Passive cooling. For IPC cooling units. Default is air cooling.
#define ORGAN_PREF_PASSIVECOOLED "passive"
/// Externally powered only. Technically not used, but we need this for the linked lists in reactor.dm.
#define ORGAN_PREF_ELECTRICPOWER "electric"
/// Biological power reactor. Nutrients, food, etc.
#define ORGAN_PREF_BIOPOWER "biological"
/// Kinetic power reactor. Walking, running.
#define ORGAN_PREF_KINETICPOWER "kinetic"
/// Solar powered. Stand in the sun. Or LED lights. It's the future, fuck you.
#define ORGAN_PREF_SOLARPOWER "solar"
+4
View File
@@ -25,6 +25,10 @@
#define SPAN_CAUTION(str) ("<span class='caution'>" + str + "</span>")
#define SPAN_STORYTELLER(str) ("<span class='storyteller'>" + str + "</span>")
#define SPAN_MACHINE_WARNING(str) ("<span class='machine-warning'>" + str + "</span>")
#define SPAN_MACHINE_DANGER(str) ("<span class='machine-danger'>" + str + "</span>")
#define SPAN_MACHINE_VISION(str) ("<span class='machine-vision'>" + str + "</span>")
#define SPAN_RED(str) "<span style='color:[COLOR_RED]'>[str]</span>"
#define SPAN_YELLOW(str) "<span style='color:[COLOR_YELLOW]'>[str]</span>"
#define SPAN_GREEN(str) "<span style='color:[COLOR_GREEN]'>[str]</span>"
-2
View File
@@ -140,5 +140,3 @@
SPECIES_IPC_UNBRANDED, SPECIES_IPC_UNBRANDED_REMOTE, SPECIES_IPC_XION, SPECIES_IPC_ZENGHU, \
SPECIES_IPC_SHELL_ROGUE, SPECIES_IPC_XION_REMOTE, SPECIES_IPC_PURPOSE_HK, \
)
+1
View File
@@ -1 +1,2 @@
GLOBAL_LIST_EMPTY(mob_config_movespeed_type_lookup)
GLOBAL_LIST_INIT(low_integrity_messages, world.file2list("config/synthetic_messages/low_integrity_messages.txt"))
+4
View File
@@ -114,6 +114,10 @@
icon_state = "brutedamageoverlay6"
alpha = 0
/atom/movable/screen/fullscreen/robot_pain
icon = 'icons/mob/screen/robot_pain.dmi'
alpha = 255
/atom/movable/screen/fullscreen/flash
icon = 'icons/mob/screen/effects.dmi'
screen_loc = "WEST,SOUTH to EAST,NORTH"
+4
View File
@@ -324,6 +324,10 @@
mymob.up_hint.screen_loc = ui_up_hint
hud_elements |= mymob.up_hint
if(hud_data.has_robot_pain)
mymob.robot_pain = new /atom/movable/screen/fullscreen/robot_pain()
hud_elements |= mymob.robot_pain
mymob.pain = new /atom/movable/screen/fullscreen/pain(null)
hud_elements |= mymob.pain
@@ -109,6 +109,11 @@
key = RAD
return key
// Used for natural armor for species.
/datum/component/armor/natural
full_block_message = "Your natural armor blocks the blow!"
partial_block_message = "Your natural armor softens the blow!"
/datum/component/armor/toggle
var/active = TRUE
@@ -0,0 +1,61 @@
// An edit of ablative armor, taken from Baystation12.
/datum/component/armor/synthetic
full_block_message = "Your external plating absorbs the blow!"
partial_block_message = "Your external plating dulls the blow!"
/// Maximum armor values.
var/list/max_armor_values
/// How fast armor degrades with blocked damage, with armor value reduced by [coef * damage taken]
var/armor_degradation_coef = 0.8
/// Wearer feedback.
var/list/last_reported_damage
/datum/component/armor/synthetic/Initialize(list/armor, armor_type, _armor_degradation_speed)
. = ..()
max_armor_values = armor_values.Copy()
if(!isnull(_armor_degradation_speed))
armor_degradation_coef = _armor_degradation_speed
/datum/component/armor/synthetic/on_blocking(damage, damage_type, damage_flags, armor_pen, blocked)
if (!(damage_type == DAMAGE_BRUTE || damage_type == DAMAGE_BURN))
return
if(armor_degradation_coef)
var/key = get_armor_key(damage_type, damage_flags)
var/damage_taken
if(blocked)
damage_taken = round(damage * blocked)
else
damage_taken = damage
var/new_armor = max(0, get_value(key) - armor_degradation_coef * damage_taken)
set_value(key, new_armor)
var/mob/M = parent
if(istype(M))
var/list/visible = get_visible_damage()
for(var/k in visible)
if(LAZYACCESS(last_reported_damage, k) != visible[k])
LAZYSET(last_reported_damage, k, visible[k])
to_chat(M, SPAN_WARNING("Your external plating has suffered [visible[k]] damage!"))
playsound(M, 'sound/effects/synth_armor_break.ogg', 50)
/datum/component/armor/synthetic/proc/get_damage()
for(var/key in armor_values)
var/damage = max_armor_values[key] - armor_values[key]
if(damage > 0)
LAZYSET(., key, damage)
/datum/component/armor/synthetic/proc/get_visible_damage()
var/list/damages = get_damage()
if(!LAZYLEN(damages))
return
var/result = list()
for(var/key in damages)
switch(round(100 * damages[key]/max_armor_values[key]))
if(5 to 10)
result[key] = "minor"
if(11 to 25)
result[key] = "moderate"
if(26 to 50)
result[key] = "serious"
if(51 to 100)
result[key] = "catastrophic"
return result
@@ -0,0 +1,120 @@
/datum/component/synthetic_endoskeleton
/// The synthetic that owns this component. Equivalent to `parent`, we just use this for ease of use.
var/mob/living/carbon/human/owner
/**
* The synthetic endoskeleton is our answer to the eternal problem of IPCs being unable to be brainmed complaint because
* of a lack of mechanics to represent pain, organ breaking, or bleeding/respiration, which is how humans in brainmed are
* taken down nonlethally, or before they die.
*/
var/damage = 0
/// The maximum damage the endoskeleton can take before triggering the safety shutdown.
var/max_damage = 200
/// Time until next message.
var/message_cooldown = 10 SECONDS
/// World.time of last message sent.
var/last_sent_message = 0
/datum/component/synthetic_endoskeleton/Initialize(...)
. = ..()
if(isipc(parent))
owner = parent
else
log_debug("Synthetic endoskeleton component spawned on non-IPC. Deleting.")
qdel_self()
RegisterSignal(owner, COMSIG_EXTERNAL_ORGAN_DAMAGE, PROC_REF(receive_damage))
RegisterSignal(owner, COMSIG_SYNTH_ENDOSKELETON_REPAIR, PROC_REF(heal_damage))
RegisterSignal(owner, COMSIG_SYNTH_ENDOSKELETON_FULL_REPAIR, PROC_REF(full_repair))
/datum/component/synthetic_endoskeleton/Destroy(force)
owner = null
return ..()
/**
* Signal handler called when an external synthetic limb receives damage.
*/
/datum/component/synthetic_endoskeleton/proc/receive_damage(atom/source, amount)
SIGNAL_HANDLER
damage = min(damage + amount, max_damage)
handle_exoskeleton_damage()
/**
* Signal handler called when the endoskeleton is repaired.
*/
/datum/component/synthetic_endoskeleton/proc/heal_damage(atom/source, amount)
SIGNAL_HANDLER
damage = max(damage - amount, 0)
var/damage_ratio = damage / max_damage
switch(damage_ratio)
if(0 to 0.3)
owner.remove_movespeed_modifier(/datum/movespeed_modifier/endoskeleton)
if(0.3 to 0.5)
owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/endoskeleton, multiplicative_slowdown = 1)
if(0.5 to 0.75)
owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/endoskeleton, multiplicative_slowdown = 2)
if(0.75 to 1)
owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/endoskeleton, multiplicative_slowdown = 3)
if(!damage)
STOP_PROCESSING(SSprocessing, src)
/**
* Does a full repair of the endoskeleton, also restoring max_damage to initial state.
*/
/datum/component/synthetic_endoskeleton/proc/full_repair(atom/source)
max_damage = initial(max_damage)
heal_damage(source, max_damage)
/**
* The function that handles exoskeleton damage effects.
*/
/datum/component/synthetic_endoskeleton/proc/handle_exoskeleton_damage()
if(damage)
START_PROCESSING(SSprocessing, src)
var/damage_ratio = damage / max_damage
switch(damage_ratio)
if(0.3 to 0.5)
notify_owner(owner, SPAN_WARNING("Your self-preservation warning system notifies you of moderate damage to your endoskeleton's supports!"))
owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/endoskeleton, multiplicative_slowdown = 1)
if(0.5 to 0.75)
notify_owner(owner, SPAN_WARNING("Your self-preservation warning system notifies you of major damage to your endoskeleton!"))
spark(owner, 2, GLOB.alldirs)
owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/endoskeleton, multiplicative_slowdown = 2)
if(0.75 to 0.99)
notify_owner(owner, SPAN_DANGER("Your self-preservation routines are starting to kick in! Your endoskeleton is falling apart!"))
spark(owner, 3, GLOB.alldirs)
owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/endoskeleton, multiplicative_slowdown = 3)
if(1 to INFINITY)
SEND_SIGNAL(owner, COMSIG_SYNTH_SET_SELF_PRESERVATION, TRUE)
/datum/component/synthetic_endoskeleton/process()
if(owner.stat)
return
var/damage_ratio = damage / max_damage
switch(damage_ratio)
if(0.3 to 0.5)
if(prob(5))
notify_owner(owner, SPAN_MACHINE_WARNING("Warning: Endoskeleton integrity at [100 - (damage_ratio * 100)]%."))
if(0.5 to 0.75)
if(prob(10))
notify_owner(owner, SPAN_MACHINE_WARNING("WARNING: Endoskeleton integrity at [100 - (damage_ratio * 100)]%!"))
notify_owner(owner, SPAN_WARNING("The mangled and exposed wires in your endoskeleton spark!"))
spark(owner, 3, GLOB.alldirs)
owner.bodytemperature += 10
if(0.75 to 1)
if(prob(15))
notify_owner(owner, SPAN_MACHINE_DANGER(FONT_LARGE("ENDOSKELETON INTEGRITY CRITICAL. Your self preservation blares at you to return to safety!")))
notify_owner(owner, SPAN_DANGER("Your frame creaks and groans, threatening to break apart at the metallic seams!"))
spark(owner, 4, GLOB.alldirs)
owner.bodytemperature += 20
/**
* Wrapper to_chat proc with a stat check.
*/
/datum/component/synthetic_endoskeleton/proc/notify_owner(mob/living/carbon/human/user, message)
if(user.is_asystole() || (last_sent_message + message_cooldown > world.time))
return
to_chat(user, message)
last_sent_message = world.time
@@ -118,3 +118,14 @@
mid_sounds = list('sound/machines/clanking.ogg' = 1)
mid_length = 5 SECONDS
volume = 75
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/looping_sound/synth_fab
start_sound = 'sound/machines/synthfab/synthfab_start.ogg'
start_length = 1 SECOND
start_volume = 100
mid_sounds = list('sound/machines/synthfab/synthfab_running.ogg' = 1)
mid_length = 3 SECONDS
end_sound = 'sound/machines/synthfab/synthfab_end.ogg'
volume = 75
+6
View File
@@ -0,0 +1,6 @@
/datum/looping_sound/ipc_sizzling
mid_sounds = list('sound/species/synthetic/sizzling_mid.ogg' = 1)
end_sound = 'sound/species/synthetic/sizzling_end.ogg'
mid_length = 30
volume = 50
extra_range = SHORT_RANGE_SOUND_EXTRARANGE
+1 -1
View File
@@ -42,7 +42,7 @@
U.hidden_uplink.bluecrystals = round(uplink_uses / 2)
U.hidden_uplink.tgui_menu = 1
if(isipc(H))
var/obj/item/organ/internal/ipc_tag/tag = H.internal_organs_by_name[BP_IPCTAG]
var/obj/item/organ/internal/machine/ipc_tag/tag = H.internal_organs_by_name[BP_IPCTAG]
if(istype(tag))
tag.modify_tag_data(TRUE)
+1 -1
View File
@@ -59,7 +59,7 @@ GLOBAL_DATUM_INIT(crew_repository, /datum/repository/crew, new())
crewmemberData["pulse"] = H.get_pulse(GETPULSE_TOOL)
else
if(isipc(H) && H.internal_organs_by_name[BP_IPCTAG]) // Don't make untagged IPCs obvious
var/obj/item/organ/internal/cell/cell = H.internal_organs_by_name[BP_CELL]
var/obj/item/organ/internal/machine/power_core/cell = H.internal_organs_by_name[BP_CELL]
if(cell)
crewmemberData["cellCharge"] = cell.percent()
@@ -0,0 +1,70 @@
// Synthetic internals represent things that are not physically modelled as an organ, but are rather part of the organ.
// That means wiring, plating, and electronics. Damaging organs has adverse effects on these components.
// The goal is to emulate damaging effects similar to arterial bleeding, infections, etc. on organics.
// The specific effects of each synthetic internal can vary organ by organ. They all have different health measurements as well.
// These measurements can change depending on the organ they are attached to.
/datum/synthetic_internal
/// The name of the internal component. Shows up in analysis tools.
var/name = "default synthetic internal"
/// The description. Shows up in analysis tools.
var/desc = "A default synthetic internal component."
/// The organ this synthetic internal belongs to.
var/obj/item/organ/internal/machine/organ
/datum/synthetic_internal/New(obj/item/organ/internal/machine/attached_organ)
. = ..()
if(istype(attached_organ))
organ = attached_organ
else
LOG_DEBUG("Synthetic internal [type] generated on invalid organ [attached_organ]. Aborting.")
qdel(src)
/datum/synthetic_internal/Destroy(force)
organ = null
return ..()
/**
* The proc that handles taking damage. As some components take damage differently,
* this is overridden by them.
*/
/datum/synthetic_internal/proc/take_damage(amount)
return
/**
* The proc that handles restoring damage. As some components restore damage differently,
* this is overridden by them.
*/
/datum/synthetic_internal/proc/heal_damage(amount)
return
/**
* This proc returns the status of this synthetic internal as a percentage.
* It will return 100 if the component is 100% healthy, a value between 0 and 100 if not, and 0 if broken.
* This logic can be overridden for custom behaviour.
*/
/datum/synthetic_internal/proc/get_status()
return
/**
* This proc returns the status of this internal component, but imprecisely.
* Basically what could be obvious at an eye-glance.
*/
/datum/synthetic_internal/proc/analyse_integrity_imprecise()
return
/**
* This proc returns the status of this internal component, but precisely.
* Basically the results of an in-depth analysis with an appropriate tool.
*/
/datum/synthetic_internal/proc/analyze_integrity_precise()
return
/**
* Replaces the health by setting a new max health and setting the health to the new max_health value.
* Needs to be overridden on every subtype.
*/
/datum/synthetic_internal/proc/replace_health(new_max_health)
SHOULD_CALL_PARENT(FALSE)
CRASH("Not implemented!")
@@ -0,0 +1,19 @@
// The synthetic's electronics. Represents both the hardware and software component.
// The integrity of electronics only goes from 0% to 100%. It does not vary, unlike other components.
/datum/synthetic_internal/electronics
name = "internal electronics"
desc = "The electronics and the code responsible for many internal functions."
/// The maximum integrity percentage of the internals. Represents code coherence and physical damage.
var/max_integrity = 30
/// The integrity of the internals in percentage. Represents code coherence and physical damage.
var/integrity = 30
/datum/synthetic_internal/electronics/get_status()
return (integrity / max_integrity) * 100
/datum/synthetic_internal/electronics/take_damage(amount)
integrity = max(integrity - amount, 0)
/datum/synthetic_internal/electronics/heal_damage(amount)
integrity = min(integrity + amount, max_integrity)
@@ -0,0 +1,39 @@
// The synthetic organ's plating. Represents its natural armour, which will degrade as it's damaged.
/datum/synthetic_internal/plating
name = "internal steel plating"
desc = "The steel plating that keeps internal components safe and sound from the exterior."
/// The maximum health of the organ's plating. Represents... you know, the plating.
var/max_health = 50
/// The health of the organ's plating. Represents... you know, the plating.
var/health = 50
/datum/synthetic_internal/plating/take_damage(amount)
var/old_health = health
health = max(health - amount, 0)
return max(amount - old_health, 0)
/datum/synthetic_internal/plating/heal_damage(amount)
health = min(health + amount, max_health)
/datum/synthetic_internal/plating/get_status()
return (health / max_health) * 100
/datum/synthetic_internal/plating/replace_health(new_max_health)
max_health = new_max_health
health = new_max_health
/obj/item/synth_plating
name = "synthetic steel plating"
desc = "These steel plates are replacements for the steel plating lining a synthetic's internal components. They are welded to the chassis in order to safeguard the component in question."
icon = 'icons/obj/ipc_utilities.dmi'
icon_state = "ipc_repair_plates"
matter = list(MATERIAL_STEEL = 40000)
surgerysound = 'sound/weapons/saw/drillhit1.ogg'
item_flags = ITEM_FLAG_SURGERY
/obj/item/synth_plating/mechanics_hints()
. = ..()
. += list("These plates are used to replace broken internal plating around a synthetic's organ. \
While normally you can repair it with normal steel plates, in the case that it is broken, \
you'll need to substitute the plating entirely with this item.")
+18
View File
@@ -0,0 +1,18 @@
// The synthetic's wiring. Represents the fragile connections between organs and limbs.
/datum/synthetic_internal/wiring
name = "internal wiring"
desc = "The wiring that connects components together and allows them to talk to eachother."
/// The maximum wires on this organ. Wires represent physical connections between components. They are less plentiful than plating, but they are worse to lose.
var/max_wires = 15
/// The wires left on this organ. Wires represent physical connections between components. They are less plentiful than plating, but they are worse to lose.
var/wires = 15
/datum/synthetic_internal/wiring/get_status()
return (wires / max_wires) * 100
/datum/synthetic_internal/wiring/take_damage(amount)
wires = max(wires - round(amount, 1), 0)
/datum/synthetic_internal/wiring/heal_damage(amount)
wires = min(wires + amount, max_wires)
@@ -142,7 +142,7 @@
if(user.isSynthetic() && ishuman(user)) // Let's do something with it, if we're a robot.
var/mob/living/carbon/human/H = user
charge_to_give = charge_to_give - (core.max_energy - core.energy)
var/obj/item/organ/internal/cell/C = H.internal_organs_by_name[BP_CELL]
var/obj/item/organ/internal/machine/power_core/C = H.internal_organs_by_name[BP_CELL]
var/obj/item/cell/potato = C.get_cell()
potato.give(charge_to_give)
to_chat(user, SPAN_NOTICE("Redirected energy to internal microcell."))
+4 -4
View File
@@ -99,7 +99,7 @@
minimal_player_age = 7
outfit = /obj/outfit/job/warden
blacklisted_species = list(SPECIES_IPC_G1, SPECIES_IPC_G2, SPECIES_IPC_XION, SPECIES_IPC_XION_REMOTE, SPECIES_VAURCA_BULWARK, SPECIES_DIONA_COEUS, SPECIES_VAURCA_BREEDER)
blacklisted_species = list(SPECIES_IPC_XION_REMOTE, SPECIES_VAURCA_BULWARK, SPECIES_DIONA_COEUS, SPECIES_VAURCA_BREEDER)
/obj/outfit/job/warden
name = "Warden"
@@ -163,7 +163,7 @@
)
minimal_player_age = 3
outfit = /obj/outfit/job/forensics
blacklisted_species = list(SPECIES_IPC_G1, SPECIES_IPC_G2, SPECIES_IPC_XION, SPECIES_IPC_XION_REMOTE, SPECIES_VAURCA_BULWARK, SPECIES_VAURCA_BREEDER)
blacklisted_species = list(SPECIES_IPC_XION_REMOTE, SPECIES_VAURCA_BULWARK, SPECIES_VAURCA_BREEDER)
/obj/outfit/job/forensics
name = "Investigator"
@@ -222,7 +222,7 @@
minimal_player_age = 7
outfit = /obj/outfit/job/officer
blacklisted_species = list(SPECIES_IPC_G1, SPECIES_IPC_G2, SPECIES_IPC_XION, SPECIES_IPC_XION_REMOTE, SPECIES_VAURCA_BULWARK, SPECIES_DIONA_COEUS, SPECIES_VAURCA_BREEDER)
blacklisted_species = list(SPECIES_IPC_XION_REMOTE, SPECIES_VAURCA_BULWARK, SPECIES_DIONA_COEUS, SPECIES_VAURCA_BREEDER)
/obj/outfit/job/officer
name = "Security Officer"
@@ -289,7 +289,7 @@
SPECIES_SKRELL_AXIORI = 50
)
blacklisted_species = list(SPECIES_IPC_G1, SPECIES_IPC_G2, SPECIES_IPC_XION, SPECIES_IPC_XION_REMOTE, SPECIES_VAURCA_BULWARK, SPECIES_DIONA_COEUS, SPECIES_VAURCA_BREEDER)
blacklisted_species = list(SPECIES_IPC_XION_REMOTE, SPECIES_VAURCA_BULWARK, SPECIES_DIONA_COEUS, SPECIES_VAURCA_BREEDER)
/obj/outfit/job/intern_sec
name = "Security Cadet"
+42 -6
View File
@@ -26,6 +26,9 @@
///The connected surgery computer
var/obj/machinery/computer/operating/computer = null
/// If the patient must be lying on this surgery table for it to set the patient as occupant.
var/occupant_must_be_lying = TRUE
/obj/machinery/optable/mechanics_hints(mob/user, distance, is_adjacent)
. += ..()
. += "Click your target with Grab intent, then click on the table with an empty hand, to place them on it."
@@ -154,6 +157,7 @@
if((user.a_intent != I_HELP) && buckled)
user_unbuckle(user)
return
if(user != occupant_resolved && !use_check_and_message(user)) // Skip checks if you're doing it to yourself or turning it off, this is an anti-griefing mechanic more than anything.
user.visible_message(SPAN_WARNING("\The [user] begins switching [suppressing ? "off" : "on"] \the [src]'s neural suppressor."))
if(!do_after(user, 3 SECONDS, src, DO_UNIQUE))
@@ -201,10 +205,11 @@
//does not mean it's suppressed, just that it's occupying the table
var/mob/living/carbon/human/new_occupant = locate() in loc
if(istype(new_occupant))
if(new_occupant.lying)
//Set the occupant weakref and get his view
occupant = WEAKREF(new_occupant)
acquire_view(new_occupant)
if(occupant_must_be_lying && !new_occupant.lying)
return FALSE
//Set the occupant weakref and get his view
occupant = WEAKREF(new_occupant)
acquire_view(new_occupant)
//We have a patient, and it's not synthetic
else if(!occupant_resolved.isSynthetic())
@@ -257,11 +262,17 @@
add_fingerprint(patient)
add_fibers(patient)
patient.resting = TRUE
patient.forceMove(loc)
move_patient_to_table(patient, giver)
return TRUE
/**
* Actually moves a patient to the table.
*/
/obj/machinery/optable/proc/move_patient_to_table(mob/living/carbon/patient, mob/living/carbon/giver)
patient.resting = TRUE
patient.forceMove(loc)
/obj/machinery/optable/mouse_drop_receive(atom/dropped, mob/user, params)
//If the user is a ghost, stop.
if(isghost(user))
@@ -333,3 +344,28 @@
return TRUE
if(default_part_replacement(user, attacking_item))
return TRUE
/obj/machinery/optable/robotics
name = "machinery chair"
desc = "Some sort of hybrid between an operating table and a chair, typically used by machinists and roboticists to strap synthetics to while they work on them. \
It comes with an access cable for easy access to a synthetic's diagnostics unit."
icon_state = "machinist_or_table"
density = FALSE
occupant_must_be_lying = FALSE
can_buckle = list(/mob/living/carbon/human)
/obj/machinery/optable/robotics/mechanics_hints(mob/user, distance, is_adjacent)
. = ..()
. += list("Use a <b>non-help</b> intent to unbuckle.")
/obj/machinery/optable/robotics/refresh_icon_state()
return
/obj/machinery/optable/robotics/move_patient_to_table(mob/living/carbon/patient, mob/living/carbon/giver)
buckle(patient, giver)
visible_message(SPAN_NOTICE("[giver] buckles [patient] to \the [src]."))
/obj/machinery/optable/robotics/buckle(atom/movable/buckling_atom, mob/user)
. = ..()
if(.)
playsound(src, 'sound/effects/metal_close.ogg', 20)
+31 -1
View File
@@ -29,6 +29,9 @@
/// The zlevel that this computer is spawned on.
var/starting_z_level = null
/// The access cable inserted into this computer, if any.
var/obj/item/access_cable/inserted_cable
/obj/machinery/computer/Initialize()
. = ..()
overlay_layer = layer
@@ -36,6 +39,12 @@
power_change()
update_icon()
/obj/machinery/computer/Destroy()
if(inserted_cable)
inserted_cable.retract()
inserted_cable = null
return ..()
/obj/machinery/computer/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = TRUE)
if(!operable() || !is_station_level(z) || user.stat)
user.unset_machine()
@@ -47,7 +56,6 @@
if(prob(20/severity))
set_broken()
/obj/machinery/computer/ex_act(severity)
switch(severity)
if(1.0)
@@ -194,6 +202,15 @@
update_icon()
return TRUE
else
if(istype(attacking_item, /obj/item/access_cable))
var/obj/item/access_cable/access_cable = attacking_item
if(inserted_cable)
to_chat(user, SPAN_WARNING("There's already a cable in the universal port!"))
return
if(do_after(user, 1 SECONDS))
visible_message(SPAN_NOTICE("[user] slots \the [access_cable] into \the [src]."))
insert_cable(access_cable, user)
return ..()
/obj/machinery/computer/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
@@ -218,6 +235,19 @@
to_chat(user, SPAN_WARNING("\The [src]'s non-stick surface prevents you from attaching a sticker to it!"))
return FALSE
/obj/machinery/computer/insert_cable(obj/item/access_cable/cable, mob/user)
. = ..()
inserted_cable = cable
cable.create_cable(src)
/obj/machinery/computer/cable_interact(obj/item/access_cable/cable, mob/user)
. = ..()
attack_hand(user)
/obj/machinery/computer/remove_cable(obj/item/access_cable/cable)
..()
inserted_cable = null
/obj/machinery/computer/terminal
name = "terminal"
icon = 'icons/obj/machinery/modular_terminal.dmi'
+1 -1
View File
@@ -554,7 +554,7 @@
var/choice = alert(M, "Would you like to save your tag data?", "Tag Persistence", "Yes", "No")
if(choice == "Yes")
var/mob/living/carbon/human/H = M
var/obj/item/organ/internal/ipc_tag/tag = H.internal_organs_by_name[BP_IPCTAG]
var/obj/item/organ/internal/machine/ipc_tag/tag = H.internal_organs_by_name[BP_IPCTAG]
if(tag)
M.client.prefs.machine_ownership_status = tag.ownership_info
M.client.prefs.machine_serial_number = tag.serial_number
+17 -23
View File
@@ -297,7 +297,7 @@ Class Procs:
update_icon()
/obj/machinery/CanUseTopic(var/mob/user)
/obj/machinery/CanUseTopic(mob/user)
if(stat & BROKEN)
return STATUS_CLOSE
@@ -306,18 +306,18 @@ Class Procs:
return ..()
/obj/machinery/CouldUseTopic(var/mob/user)
/obj/machinery/CouldUseTopic(mob/user)
..()
if(clicksound && iscarbon(user))
playsound(src, clicksound, clickvol)
user.set_machine(src)
/obj/machinery/CouldNotUseTopic(var/mob/user)
/obj/machinery/CouldNotUseTopic(mob/user)
user.unset_machine()
////////////////////////////////////////////////////////////////////////////////////////////
/obj/machinery/attack_ai(mob/user as mob)
/obj/machinery/attack_ai(mob/user)
if(!ai_can_interact(user))
return
if(isrobot(user))
@@ -328,33 +328,27 @@ Class Procs:
else
return src.attack_hand(user)
/obj/machinery/attack_hand(mob/user as mob)
/obj/machinery/attack_hand(mob/user)
if(!operable(MAINT))
return 1
return TRUE
if(user.lying || user.stat)
return 1
if ( ! (istype(usr, /mob/living/carbon/human) || \
istype(usr, /mob/living/silicon)))
return TRUE
if (!istype(usr, /mob/living/carbon/human) && !istype(usr, /mob/living/silicon))
to_chat(usr, SPAN_WARNING("You don't have the dexterity to do this!"))
return 1
/*
//distance checks are made by atom/proc/DblClick
if ((get_dist(src, user) > 1 || !istype(src.loc, /turf)) && !istype(user, /mob/living/silicon))
return 1
*/
if (ishuman(user))
var/mob/living/carbon/human/H = user
if(H.getBrainLoss() >= 60)
visible_message(SPAN_WARNING("[H] stares cluelessly at [src] and drools."))
return 1
else if(prob(H.getBrainLoss()))
to_chat(user, SPAN_WARNING("You momentarily forget how to use [src]."))
return 1
return TRUE
src.add_fingerprint(user)
return ..()
/obj/machinery/attack_ranged(mob/user, params)
. = ..()
if(isipc(user))
var/mob/living/carbon/human/robot = user
var/obj/item/organ/internal/machine/wireless_access/wireless_access_point = robot.internal_organs_by_name[BP_WIRELESS_ACCESS]
if(wireless_access_point?.access_terminal(src))
attack_hand(user)
/obj/machinery/attackby(obj/item/attacking_item, mob/user)
if(obj_flags & OBJ_FLAG_SIGNALER)
if(issignaler(attacking_item))
+79 -55
View File
@@ -1,6 +1,6 @@
/obj/machinery/mecha_part_fabricator
name = "mechatronic fabricator"
desc = "A general purpose fabricator that can be used to fabricate robotic equipment."
name = "synthetic fabricator"
desc = "A general purpose fabricator that can be used to fabricate equipment for synthetics or exosuits."
icon = 'icons/obj/machinery/robotics_fabricator.dmi'
icon_state = "fab"
density = TRUE
@@ -15,6 +15,7 @@
/obj/item/stock_parts/micro_laser,
/obj/item/stock_parts/console_screen
)
manufacturer = "hephaestus"
/**
* A `/list` of enqueued `/datum/design` to be printed, processed in the queue
@@ -36,6 +37,9 @@
var/datum/research/files
/// The looping sound for production.
var/datum/looping_sound/synth_fab/fab_loop
var/list/categories = list()
var/category = null
var/sync_message = ""
@@ -57,11 +61,13 @@
. = ..()
files = new /datum/research(src) //Setup the research data holder.
fab_loop = new(src)
limb_manufacturer = GLOB.basic_robolimb.company
update_categories()
/obj/machinery/mecha_part_fabricator/update_icon()
ClearOverlays()
update_fab_audio()
if(panel_open)
AddOverlays("[icon_state]_panel")
if(!(stat & (NOPOWER|BROKEN)))
@@ -72,9 +78,19 @@
AddOverlays(emissive_appearance(icon, "[icon_state]_lights_working"))
AddOverlays("[icon_state]_lights_working")
/// Starts and stops the necessary audio.
/obj/machinery/mecha_part_fabricator/proc/update_fab_audio()
if(!fab_loop)
return
if(build_callback_timer)
fab_loop.start()
else
fab_loop.stop()
/obj/machinery/mecha_part_fabricator/dismantle()
for(var/f in materials)
eject_materials(f, -1)
eject_materials(f, materials[f])
//Stop the queue building if you're dismantling
deltimer(build_callback_timer)
@@ -106,27 +122,36 @@
do_hair_pull(user)
ui_interact(user)
/obj/machinery/mecha_part_fabricator/ui_interact(var/mob/user, var/ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
/obj/machinery/mecha_part_fabricator/ui_interact(mob/user, datum/tgui/ui)
. = ..()
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "SyntheticFabricator", "Synthetic Fabricator", 900, 700)
ui.open()
/obj/machinery/mecha_part_fabricator/ui_data(mob/user)
var/list/data = list()
data["manufacturer"] = manufacturer
var/datum/design/current = queue.len ? queue[1] : null
if(current)
data["current"] = current.name
data["queue"] = get_queue_names()
data["buildable"] = get_build_options()
data["category"] = category
data["categories"] = categories
data["available_categories"] = list()
for(var/possible_category in categories)
data["available_categories"] += list(list("name" = possible_category))
if(GLOB.fabricator_robolimbs)
var/list/T = list()
for(var/A in GLOB.fabricator_robolimbs)
var/datum/robolimb/R = GLOB.fabricator_robolimbs[A]
T += list(list("id" = A, "company" = R.company))
T += list(list("name" = R.company, "type" = A))
data["manufacturers"] = T
data["manufacturer"] = limb_manufacturer
data["selected_manufacturer"] = limb_manufacturer
data["materials"] = get_materials()
data["maxres"] = res_max_amount
data["maximum_resource_amount"] = res_max_amount
data["sync"] = sync_message
//If we have something in the queue that we're trying to build, show the time left if the build is undergoing, otherwise null
@@ -136,42 +161,46 @@
data["timeleft"] = round(timeleft(build_callback_timer))
else
data["timeleft"] = null
return data
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui)
ui = new(user, src, ui_key, "mechfab.tmpl", "Exosuit Fabricator UI", 800, 600)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
/obj/machinery/mecha_part_fabricator/Topic(href, href_list)
if(..())
/obj/machinery/mecha_part_fabricator/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
if(href_list["build"])
var/path = text2path(href_list["build"])
add_to_queue(path)
switch(action)
if("build")
var/path = text2path(params["build"])
add_to_queue(path)
. = TRUE
if(href_list["remove"])
remove_from_queue(text2num(href_list["remove"]))
if("remove")
remove_from_queue(text2num(params["remove"]))
. = TRUE
if(href_list["category"])
if(href_list["category"] in categories)
category = href_list["category"]
if("category")
if(params["category"] in categories)
category = params["category"]
. = TRUE
if(href_list["manufacturer"])
if(href_list["manufacturer"] in GLOB.fabricator_robolimbs)
limb_manufacturer = href_list["manufacturer"]
if("manufacturer")
if(params["manufacturer"] in GLOB.fabricator_robolimbs)
limb_manufacturer = params["manufacturer"]
. = TRUE
if(href_list["eject"])
eject_materials(href_list["eject"], text2num(href_list["amount"]))
if("eject")
eject_materials(params["eject"], text2num(params["amount"]))
. = TRUE
if(href_list["sync"])
sync()
else
sync_message = ""
if("sync")
sync()
. = TRUE
else
sync_message = ""
return 1
if(.)
playsound(src, 'sound/machines/synthfab/synthfab_button.ogg', 50)
/obj/machinery/mecha_part_fabricator/attackby(obj/item/attacking_item, mob/user)
if(build_callback_timer)
@@ -195,7 +224,7 @@
return TRUE
var/sname = "[M.name]"
if(materials[M.material.name] + M.perunit <= res_max_amount)
if(materials[M.material.name] + SHEET_MATERIAL_AMOUNT <= res_max_amount)
if(M.amount >= 1)
var/count = 0
var/mutable_appearance/MA = mutable_appearance(icon, "material_insertion")
@@ -206,7 +235,7 @@
//now play the progress bar animation
flick_overlay_view(mutable_appearance(icon, "fab_progress"), 1 SECONDS)
while(materials[M.material.name] + M.perunit <= res_max_amount && M.amount >= 1)
while(materials[M.material.name] + SHEET_MATERIAL_AMOUNT <= res_max_amount && M.amount >= 1)
materials[M.material.name] += M.perunit
M.use(1)
count++
@@ -344,6 +373,7 @@
else
visible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] \The [src] flashes: Insufficient materials to complete build: [D.name]."))
remove_from_queue(1)
update_icon()
@@ -383,17 +413,17 @@
/obj/machinery/mecha_part_fabricator/proc/get_queue_names()
. = list()
for(var/i = 2 to queue.len)
for(var/i = 1 to queue.len)
var/datum/design/D = queue[i]
. += D.name
. += list(list("name" = D.name, "time" = get_design_time(D), "index" = i))
/obj/machinery/mecha_part_fabricator/proc/get_build_options()
. = list()
for(var/path in files.known_designs)
var/datum/design/D = files.known_designs[path]
if(!D.build_path || !(D.build_type & MECHFAB) || !D.category)
if(!D.build_path || !(D.build_type & MECHFAB) || !D.category || (D.category != category))
continue
. += list(list("name" = D.name, "id" = D.type, "category" = D.category, "resourses" = get_design_resourses(D), "time" = get_design_time(D)))
. += list(list("name" = D.name, "desc" = D.desc, "type" = D.type, "category" = D.category, "resources" = get_design_resourses(D), "time" = get_design_time(D)))
/obj/machinery/mecha_part_fabricator/proc/get_design_resourses(var/datum/design/D)
var/list/F = list()
@@ -417,25 +447,19 @@
/obj/machinery/mecha_part_fabricator/proc/get_materials()
. = list()
for(var/T in materials)
. += list(list("mat" = capitalize(T), "amt" = materials[T]))
. += list(list("name" = capitalize(T), "amount" = materials[T]))
/obj/machinery/mecha_part_fabricator/proc/eject_materials(var/material, var/amount) // 0 amount = 0 means ejecting a full stack; -1 means eject everything
var/recursive = amount == -1 ? 1 : 0
/obj/machinery/mecha_part_fabricator/proc/eject_materials(var/material, var/amount)
if(!amount)
return
material = lowertext(material)
var/material/mattype = SSmaterials.get_material_by_name(material)
var/stack_type = mattype.stack_type
var/obj/item/stack/material/S = new stack_type(loc)
if(amount <= 0)
amount = S.max_amount
var/ejected = min(round(materials[material] / S.perunit), amount)
S.amount = min(ejected, amount)
if(S.amount <= 0)
qdel(S)
return
materials[material] -= ejected * S.perunit
if(recursive && materials[material] >= S.perunit)
eject_materials(material, -1)
var/real_amount = round(amount / SHEET_MATERIAL_AMOUNT)
var/obj/item/stack/material/S = new stack_type(loc, real_amount)
S.update_icon()
materials[material] -= amount
/obj/machinery/mecha_part_fabricator/proc/sync()
sync_message = "Error: no console found."
+23 -3
View File
@@ -117,6 +117,9 @@
return
var/obj/item/cell/target
// We use the reactor variable to see if this is an IPC that's charging.
var/obj/item/organ/internal/machine/reactor/reactor
if(isrobot(occupant))
var/mob/living/silicon/robot/R = occupant
@@ -132,9 +135,15 @@
if(ishuman(occupant))
var/mob/living/carbon/human/H = occupant
var/obj/item/organ/internal/cell/IC = H.internal_organs_by_name[BP_CELL]
if(IC)
var/obj/item/organ/internal/machine/power_core/IC = H.internal_organs_by_name[BP_CELL]
if(istype(IC))
target = IC.cell
// Different reactor types have different external recharge speeds.
reactor = H.internal_organs_by_name[BP_REACTOR]
if(!istype(reactor))
return
if((!target || target.percent() > 95) && istype(H.back, /obj/item/rig))
var/obj/item/rig/R = H.back
if(R.cell && !R.cell.fully_charged())
@@ -143,7 +152,11 @@
if(target && !target.fully_charged())
var/diff = min(target.maxcharge - target.charge, charging_power * CELLRATE * seconds_per_tick) // Capped by charging_power / tick
var/charge_used = cell.use(diff)
target.give(charge_used*charging_efficiency)
if(!reactor) // not an IPC
target.give(charge_used * charging_efficiency)
else
reactor.generate_power(charge_used * charging_efficiency * reactor.external_charge_multiplier)
if(isDrone(occupant))
var/mob/living/silicon/robot/drone/D = occupant
@@ -172,6 +185,13 @@
if(cell)
cell.emp_act(severity)
/obj/machinery/recharge_station/attack_hand(mob/user)
. = ..()
if(occupant && (user != occupant))
if(do_after(user, 1 SECOND, src))
user.visible_message(SPAN_NOTICE("[user] lowers the manual release lever on \the [src]."), SPAN_NOTICE("You lower the manual release lever on \the [src]."))
go_out()
/obj/machinery/recharge_station/attackby(obj/item/attacking_item, mob/user)
if(!occupant)
if(default_deconstruction_screwdriver(user, attacking_item))
@@ -180,7 +180,7 @@
if(!emagged)
playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
emagged = 1
to_chat(user, SPAN_NOTICE("You you disable the security protocols"))
to_chat(user, SPAN_NOTICE("You disable the security protocols."))
src.updateUsrDialog()
return 1
@@ -157,6 +157,6 @@
if(!emagged)
playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
emagged = 1
to_chat(user, SPAN_NOTICE("You you disable the security protocols"))
to_chat(user, SPAN_NOTICE("You disable the security protocols."))
src.updateUsrDialog()
return 1
@@ -234,6 +234,6 @@
if(!emagged)
playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
emagged = 1
to_chat(user, SPAN_NOTICE("You you disable the security protocols"))
to_chat(user, SPAN_NOTICE("You disable the security protocols."))
src.updateUsrDialog()
return 1
+1 -1
View File
@@ -420,7 +420,7 @@
make_announcement("pings, \"Resuscitation successful.\"", "notice")
playsound(get_turf(src), 'sound/machines/defib_success.ogg', 50, 0)
H.resuscitate()
var/obj/item/organ/internal/cell/potato = H.internal_organs_by_name[BP_CELL]
var/obj/item/organ/internal/machine/power_core/potato = H.internal_organs_by_name[BP_CELL]
if(istype(potato) && potato.cell)
var/obj/item/cell/C = potato.cell
C.give(chargecost)
@@ -23,7 +23,7 @@
var/on = 0 //is it turned on?
var/cover_open = 0 //is the cover open?
var/obj/item/cell/cell
var/max_cooling = 12 //in degrees per second - probably don't need to mess with heat capacity here
var/max_cooling = 24 //in degrees per second - probably don't need to mess with heat capacity here
var/charge_consumption = 8.3 //charge per second at max_cooling
var/thermostat = T20C
@@ -95,7 +95,7 @@
var/charge_usage = (temp_adj/max_cooling)*charge_consumption
H.bodytemperature -= temp_adj*efficiency
H.bodytemperature = max(T0C, H.bodytemperature - temp_adj*efficiency)
cell.use(charge_usage)
+22 -3
View File
@@ -83,6 +83,25 @@
var/obj/item/organ/external/S = H.get_organ(target_zone)
if (S && (S.status & ORGAN_ASSISTED))
var/datum/component/synthetic_endoskeleton/endoskeleton = H.GetComponent(/datum/component/synthetic_endoskeleton)
if(istype(endoskeleton) && endoskeleton.damage)
if(target_mob == user)
if(endoskeleton.damage >= endoskeleton.max_damage * 0.5)
to_chat(user, SPAN_WARNING("Your control over your limbs is too damaged to apply the nanopaste precisely!"))
return
application_time *= application_multiplier // It takes longer to apply nanopaste to yourself than to someone else.
if (application_in_progress == FALSE)
application_in_progress = TRUE
user.visible_message(SPAN_NOTICE("\The [user] begins to apply nanite past to the broken support systems of [user != target_mob ? " \the [target_mob]'s" : "\the [user]'s"] endoskeleton..."), \
SPAN_NOTICE("You begin to apply nanite paste to the broken support systems of [user != target_mob ? " \the [target_mob]'s" : "your"] [endoskeleton]..."))
if(do_mob(user, target_mob, application_time))
SEND_SIGNAL(target_mob, COMSIG_SYNTH_ENDOSKELETON_REPAIR, rand(15, 30))
user.visible_message(SPAN_NOTICE("\The [user] mends the broken links in [user != target_mob ? " \the [target_mob]'s" : "\the [user]'s"] endoskeleton with \the [src]."),\
SPAN_NOTICE("You successfully mend the broken links in[user == target_mob ? "your" : "[target_mob]'s"] endoskeleton with \the [src]."))
use(1)
application_in_progress = FALSE
if(S.get_damage())
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
@@ -144,7 +163,7 @@
return FALSE
if (isipc(M))
var/obj/item/organ/internal/surge/s = M.internal_organs_by_name["surge"]
var/obj/item/organ/internal/machine/surge/s = M.internal_organs_by_name[BP_SURGE_PROTECTOR]
if(isnull(s))
user.visible_message(
SPAN_NOTICE("[user] is trying to apply [src] to [(M == user) ? ("itself") : (M)]!"),
@@ -154,9 +173,9 @@
if (!do_mob(user, M, 2))
return FALSE
s = new /obj/item/organ/internal/surge()
s = new /obj/item/organ/internal/machine/surge()
M.internal_organs += s
M.internal_organs_by_name["surge"] = s
M.internal_organs_by_name[BP_SURGE_PROTECTOR] = s
user.visible_message(
SPAN_NOTICE("[user] applies some nanite paste to [(M == user) ? ("itself") : (M)]!"),
SPAN_NOTICE("You apply [src] to [(M == user) ? ("youself") : (M)].")
@@ -68,11 +68,11 @@
/obj/item/implanter/ipc_tag
name = "IPC tag implanter"
desc = "A special implanter used for implanting synthetics with a special tag."
var/obj/item/organ/internal/ipc_tag/ipc_tag
var/obj/item/organ/internal/machine/ipc_tag/ipc_tag
/obj/item/implanter/ipc_tag/Initialize()
. = ..()
ipc_tag = new /obj/item/organ/internal/ipc_tag(src)
ipc_tag = new /obj/item/organ/internal/machine/ipc_tag(src)
update_icon()
/obj/item/implanter/ipc_tag/update_icon()
@@ -121,7 +121,7 @@
ipc_tag = null
update_icon()
return
if(istype(attacking_item, /obj/item/organ/internal/ipc_tag))
if(istype(attacking_item, /obj/item/organ/internal/machine/ipc_tag))
if(ipc_tag)
to_chat(user, SPAN_WARNING("\The [src] already has an IPC tag loaded."))
return
@@ -40,7 +40,7 @@
to_chat(user, SPAN_WARNING("You analyze \the [target_mob], but find that they're not an IPC at all!"))
return
var/mob/living/carbon/human/IPC = target_mob
var/obj/item/organ/internal/ipc_tag/tag = IPC.internal_organs_by_name[BP_IPCTAG]
var/obj/item/organ/internal/machine/ipc_tag/tag = IPC.internal_organs_by_name[BP_IPCTAG]
if(isnull(tag) || !tag)
to_chat(user, SPAN_WARNING("Error: Serial Identification Missing."))
return
@@ -11,16 +11,17 @@
throw_speed = 3
throw_range = 5
w_class = WEIGHT_CLASS_NORMAL
matter = list(DEFAULT_WALL_MATERIAL = 700, MATERIAL_GLASS = 50)
recyclable = TRUE
/// Note %age converted to actual charge in New()
var/charge = 0
var/maxcharge = 2500
/// Maximum cell charge.
var/maxcharge = 1000
/// True if rigged to explode
var/rigged = FALSE
/// If not 100% reliable, it will build up faults.
var/minor_fault = 0
matter = list(DEFAULT_WALL_MATERIAL = 700, MATERIAL_GLASS = 50)
recyclable = TRUE
/// Percentage of maxcharge to recharge per minute, though it will trickle charge every process tick (every ~2 seconds), leave null for no self-recharge
var/self_charge_percentage
@@ -126,10 +127,10 @@
matter = list(DEFAULT_WALL_MATERIAL = 700, MATERIAL_GLASS = 80)
/obj/item/cell/infinite/check_charge()
return 1
return 1
/obj/item/cell/infinite/use()
return 1
return 1
/obj/item/cell/potato
name = "potato battery"
@@ -139,8 +140,6 @@
icon_state = "potato_cell" //"potato_battery"
charge = 100
maxcharge = 300
minor_fault = 1
/obj/item/cell/slime
name = "charged slime core"
+24
View File
@@ -355,6 +355,30 @@
/obj/proc/do_signaler()
return
/**
* Called when an access cable - from an IPC or from a roboticist tool - is inserted into an object.
*/
/obj/proc/insert_cable(obj/item/access_cable/cable, mob/user)
user.drop_from_inventory(cable)
cable.forceMove(src)
cable.target = src
/**
* Called when an access cable is removed from something, forcibly or not.
* Override as needed to clear variables up.
*/
/obj/proc/remove_cable(obj/item/access_cable/cable)
SHOULD_CALL_PARENT(TRUE)
cable.target = null
/**
* Called when an access cable - from an IPC or from a roboticist tool - is used on an object to interact with it.
* For example, brings up diagnostics for an access port if it's hooked into one.
*/
/obj/proc/cable_interact(obj/item/access_cable/cable, mob/user)
return
/*#############################################
PERSISTENT
#############################################*/
@@ -73,6 +73,7 @@
new /obj/item/storage/belt/utility(src)
new /obj/item/storage/belt/utility/alt(src)
new /obj/item/storage/toolbox/mechanical(src)
new /obj/item/modular_computer/laptop/preset/supply/robotics(src)
// Miner
/obj/structure/closet/secure_closet/miner
@@ -49,6 +49,7 @@
new /obj/item/device/taperecorder(src)
new /obj/item/sampler(src)
new /obj/item/storage/box/shells/tranquilizer(src)
new /obj/item/modular_computer/laptop/preset/supply/robotics(src)
/// Used for when we want to populate the contents of this locker ourself. Good for away sites
/obj/structure/closet/secure_closet/RD/empty
+1 -1
View File
@@ -17,7 +17,7 @@
<br>
BRUTE:<a href='byond://?_src_=vars;mobToDamage=[REF(src)];adjustDamage=brute'>[getBruteLoss()]</a>
FIRE:<a href='byond://?_src_=vars;mobToDamage=[REF(src)];adjustDamage=fire'>[getFireLoss()]</a>
TOXIN:<a href='byond://?_src_=vars;mobToDamage=[REF(src)];adjustDamage=toxin'>[getToxLoss()]</a>
ORGAN:<a href='byond://?_src_=vars;mobToDamage=[REF(src)];adjustDamage=toxin'>[getToxLoss()]</a>
OXY:<a href='byond://?_src_=vars;mobToDamage=[REF(src)];adjustDamage=oxygen'>[getOxyLoss()]</a>
CLONE:<a href='byond://?_src_=vars;mobToDamage=[REF(src)];adjustDamage=clone'>[getCloneLoss()]</a>
BRAIN:<a href='byond://?_src_=vars;mobToDamage=[REF(src)];adjustDamage=brain'>[getBrainLoss()]</a>
@@ -243,6 +243,11 @@ GLOBAL_LIST_INIT(valid_bloodtypes, list(
out += "Limbs: <a href='byond://?src=[REF(src)];limbs=1'>Adjust</a><br>"
if(length(mob_species.alterable_internal_organs))
out += "Internal Organs: <a href='byond://?src=[REF(src)];organs=1'>Adjust</a><br>"
for(var/organ in pref.organ_data)
var/output_pref = pref.organ_data[organ]
if(!pref.organ_data[organ])
output_pref = "Default"
out += "- <b>[capitalize_first_letters(organ)]</b>: [capitalize(output_pref)] <br>"
out += "Prosthesis/Amputations: <a href='byond://?src=[REF(src)];reset_organs=1'>Reset</a><br>"
//display limbs below
@@ -890,6 +895,22 @@ GLOBAL_LIST_INIT(valid_bloodtypes, list(
if("Removed")
pref.organ_data[organ_name] = ORGAN_PREF_REMOVED
// IPC cooling unit options.
if("Air Cooling")
pref.organ_data[organ_name] = null
if("Liquid Cooling")
pref.organ_data[organ_name] = ORGAN_PREF_LIQUIDCOOLED
if("Passive Cooling")
pref.organ_data[organ_name] = ORGAN_PREF_PASSIVECOOLED
// IPC reactor options.
if("Electric")
pref.organ_data[organ_name] = null
if("Biological")
pref.organ_data[organ_name] = ORGAN_PREF_BIOPOWER
if("Solar")
pref.organ_data[organ_name] = ORGAN_PREF_SOLARPOWER
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["reset_organs"])
@@ -774,7 +774,7 @@
interface_desc = "A heat sink with liquid cooled radiator."
icon_state = "suitcooler"
var/charge_consumption = 1
var/max_cooling = 12
var/max_cooling = 24
var/thermostat = T20C
category = MODULE_GENERAL
@@ -785,15 +785,34 @@
var/mob/living/carbon/human/H = holder.wearer
var/temp_adj = min(H.bodytemperature - thermostat, max_cooling)
var/env_temp = get_environment_temperature()
var/temp_adj = min(H.bodytemperature - max(thermostat, env_temp), max_cooling)
if (temp_adj < 0.5)
return passive_power_cost
H.bodytemperature -= temp_adj
H.bodytemperature = max(T0C, H.bodytemperature - temp_adj)
active_power_cost = round((temp_adj/max_cooling)*charge_consumption)
return active_power_cost
/obj/item/rig_module/cooling_unit/proc/get_environment_temperature()
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
if(istype(H.loc, /obj/machinery/atmospherics/unary/cryo_cell))
var/obj/machinery/atmospherics/unary/cryo_cell/C = H.loc
return C.air_contents.temperature
var/turf/T = get_turf(src)
if(istype(T, /turf/space) || !isturf(T))
return FALSE //space has no temperature, this just makes sure the cooling unit works in space
var/datum/gas_mixture/environment = T.return_air()
if(!environment)
return FALSE
return environment.temperature
/obj/item/rig_module/boring
name = "burrowing lasers"
desc = "A set of precise boring lasers designed to carve a hole beneath the user."
+1
View File
@@ -1985,6 +1985,7 @@ All custom items with worn sprites must follow the contained sprite system: http
// adding the hand to the child here means only the arm has to be added to the DB
// since the hand will be attached automatically
LAZYADD(children, new /obj/item/organ/external/hand/fluff/gracia_autakh(src))
internal_organs += new /obj/item/organ/internal/machine/actuators/left(src)
/obj/item/organ/external/hand/fluff/gracia_autakh // gracia's aut'akh left hand - Gracia Hiza - cometblaze
robotize_type = PROSTHETIC_AUTAKH
+9
View File
@@ -34,6 +34,15 @@
for(var/obj/machinery/power/apc/valid_apc in SSmachinery.apc_units)
if((valid_apc.z in affecting_z) && !valid_apc.is_critical)
valid_apcs += valid_apc
for(var/mob/living/carbon/human/victim_ipc in GLOB.human_mob_list)
if(!isipc(victim_ipc))
continue
if(!(victim_ipc.z in affecting_z))
continue
var/obj/item/organ/internal/machine/posibrain/brain = victim_ipc.internal_organs_by_name[BP_BRAIN]
if(istype(brain))
to_chat(victim_ipc, SPAN_MACHINE_WARNING("A jolt of ambient electricity rumbles through your circuitry! Your processors go to work putting the bits back together..."))
brain.brain_scrambling += rand(severity * 35, severity * 45)
endWhen = (severity * 35) + startWhen
/datum/event/electrical_storm/end(faked)
@@ -27,7 +27,7 @@
return
switch(href_list["info"])
if("actuator")
to_chat(usr, SPAN_NOTICE("An actuator can be created at a mechatronic fabricator."))
to_chat(usr, SPAN_NOTICE("An actuator can be created at a synthetic fabricator."))
/obj/item/mech_component/manipulators/return_diagnostics(mob/user)
..()
@@ -79,11 +79,11 @@
return
switch(href_list["info"])
if("cell")
to_chat(usr, SPAN_NOTICE("A power core can be created at a mechatronic fabricator."))
to_chat(usr, SPAN_NOTICE("A power core can be created at a synthetic fabricator."))
if("diagnostics")
to_chat(usr, SPAN_NOTICE("A diagnostics unit can be created at a mechatronic fabricator."))
to_chat(usr, SPAN_NOTICE("A diagnostics unit can be created at a synthetic fabricator."))
if("armor")
to_chat(usr, SPAN_NOTICE("Armor plating can be created at a mechatronic fabricator."))
to_chat(usr, SPAN_NOTICE("Armor plating can be created at a synthetic fabricator."))
/obj/item/mech_component/chassis/return_diagnostics(mob/user)
..()
@@ -66,13 +66,13 @@
return
switch(href_list["info"])
if("manipulators")
to_chat(usr, SPAN_NOTICE("Manipulators, the arms of the exosuit, can be created at a mechatronic fabricator."))
to_chat(usr, SPAN_NOTICE("Manipulators, the arms of the exosuit, can be created at a synthetic fabricator."))
if("propulsion")
to_chat(usr, SPAN_NOTICE("Propulsion, the legs of the exosuit, can be created at a mechatronic fabricator."))
to_chat(usr, SPAN_NOTICE("Propulsion, the legs of the exosuit, can be created at a synthetic fabricator."))
if("sensors")
to_chat(usr, SPAN_NOTICE("Sensors, the head of the exosuit, can be created at a mechatronic fabricator."))
to_chat(usr, SPAN_NOTICE("Sensors, the head of the exosuit, can be created at a synthetic fabricator."))
if("chassis")
to_chat(usr, SPAN_NOTICE("A chassis, the body of the exosuit, can be created at a mechatronic fabricator."))
to_chat(usr, SPAN_NOTICE("A chassis, the body of the exosuit, can be created at a synthetic fabricator."))
if("wire")
if(!is_wired)
to_chat(usr, SPAN_NOTICE("The frame requires wiring between its components. This can be added with cable coil."))
@@ -37,11 +37,11 @@
return
switch(href_list["info"])
if("radio")
to_chat(usr, SPAN_NOTICE("A radio can be created at a mechatronic fabricator."))
to_chat(usr, SPAN_NOTICE("A radio can be created at a synthetic fabricator."))
if("camera")
to_chat(usr, SPAN_NOTICE("A camera can be created at a mechatronic fabricator."))
to_chat(usr, SPAN_NOTICE("A camera can be created at a synthetic fabricator."))
if("module")
to_chat(usr, SPAN_NOTICE("An exosuit control module can be created at a mechatronic fabricator, while the software chips it uses can be printed at the circuit imprinter."))
to_chat(usr, SPAN_NOTICE("An exosuit control module can be created at a synthetic fabricator, while the software chips it uses can be printed at the circuit imprinter."))
/obj/item/mech_component/sensors/return_diagnostics(mob/user)
..()
@@ -26,7 +26,7 @@
return
switch(href_list["info"])
if("actuator")
to_chat(usr, SPAN_NOTICE("An actuator can be created at a mechatronic fabricator."))
to_chat(usr, SPAN_NOTICE("An actuator can be created at a synthetic fabricator."))
/obj/item/mech_component/propulsion/return_diagnostics(mob/user)
..()
@@ -64,6 +64,18 @@
style_open = "<span class='langchat' style=font-size:16pt;text-align:center valign='top'>"
style_close = "</span>"
/atom/movable/screen/text/screen_text/low_integrity_message
maptext_height = 64
maptext_width = 480
maptext_x = 0
maptext_y = 0
screen_loc = "LEFT,TOP-3"
letters_per_update = 1
fade_out_delay = 6 SECONDS
style_open = "<span class='langchat' style=font-size:20pt;text-align:center valign='top'>"
style_close = "</span>"
///proc for actually playing this screen_text on a mob.
/atom/movable/screen/text/screen_text/proc/play_to_client()
player?.add_to_screen(src)
+1 -1
View File
@@ -30,7 +30,7 @@
cell.maxcharge = 10000
cell.charge = 10000
mmi = /obj/item/organ/internal/mmi_holder/circuit
mmi = /obj/item/organ/internal/machine/posibrain/circuit
// Allows mining drones to pull ore boxes, might be useful for supporting miners
pull_list |= /obj/structure/ore_box
+4
View File
@@ -421,6 +421,10 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list(
item = G.throw_held() //throw the person instead of the grab
if(ismob(item) && G.state >= GRAB_NECK)
var/mob/M = item
if(M.mob_weight > get_mob_strength())
to_chat(src, SPAN_WARNING("[M] is far too heavy for you to throw around!"))
return
throw_range = round(throw_range * (src.mob_size/M.mob_size))
itemsize = round(M.mob_size/4)
var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors
@@ -16,7 +16,7 @@
set_species(new_species)
reset_hair()
if(isipc(src))
var/obj/item/organ/internal/ipc_tag/tag = internal_organs_by_name[BP_IPCTAG]
var/obj/item/organ/internal/machine/ipc_tag/tag = internal_organs_by_name[BP_IPCTAG]
if(istype(tag))
tag.modify_tag_data(TRUE)
return 1
+31 -4
View File
@@ -188,6 +188,15 @@
var/obj/item/organ/internal/stomach/stomach = internal_organs_by_name[BP_STOMACH]
if(stomach)
return stomach.ingested
if(should_have_organ(BP_REACTOR))
var/obj/item/organ/internal/machine/reactor/reactor = internal_organs_by_name[BP_REACTOR]
if(!reactor)
return
if(reactor.is_broken())
return FALSE
if(reactor.power_supply_type & POWER_SUPPLY_BIOLOGICAL)
return reactor.bio_reagents
return touching
/mob/living/carbon/human/proc/metabolize_ingested_reagents()
@@ -239,9 +248,16 @@
. += "Tank Pressure: [internal.air_contents.return_pressure()]"
. += "Distribution Pressure: [internal.distribute_pressure]"
var/obj/item/organ/internal/cell/IC = internal_organs_by_name[BP_CELL]
var/obj/item/organ/internal/machine/power_core/IC = internal_organs_by_name[BP_CELL]
if(IC && IC.cell)
. += "Battery charge: [IC.get_charge()]/[IC.cell.maxcharge]"
. += "Battery Charge: [IC.get_charge()]/[IC.cell.maxcharge]"
var/obj/item/organ/internal/machine/internal_diagnostics/diagnostics = internal_organs_by_name[BP_DIAGNOSTICS_SUITE]
if(diagnostics)
if((diagnostics.get_integrity() < IPC_INTEGRITY_THRESHOLD_HIGH) || !diagnostics.is_broken())
. += "Temperature: [round(bodytemperature - T0C, 1)]°C"
else
. += "Temperature: E#RR"
if(mind)
var/datum/vampire/vampire = mind.antag_datums[MODE_VAMPIRE]
@@ -970,6 +986,13 @@
return
/mob/living/carbon/human/proc/check_has_mouth()
// Look, it's not really a mouth, but you gotta do what you gotta do.
// Imagine the Bender shit from Futurama where he opens his stomach hatch and drops shit in there.
if(should_have_organ(BP_REACTOR))
var/obj/item/organ/internal/machine/reactor/reactor = internal_organs_by_name[BP_REACTOR]
if(reactor && (reactor.power_supply_type & POWER_SUPPLY_BIOLOGICAL))
return TRUE
// Todo, check stomach organ when implemented.
var/obj/item/organ/external/E = get_organ(BP_HEAD)
if(E && !E.is_stump())
@@ -1888,6 +1911,10 @@
return 0
/mob/living/carbon/human/proc/can_drink(var/obj/item/I)
if(should_have_organ(BP_REACTOR))
var/obj/item/organ/internal/machine/reactor/reactor = internal_organs_by_name[BP_REACTOR]
if(reactor && (reactor.power_supply_type & POWER_SUPPLY_BIOLOGICAL))
return TRUE
if(!check_has_mouth())
to_chat(src, SPAN_NOTICE("Where do you intend to put \the [I]? You don't have a mouth!"))
return FALSE
@@ -2070,8 +2097,8 @@
// Check if we should die.
/mob/living/carbon/human/proc/handle_death_check()
if(should_have_organ(BP_BRAIN) && !is_mechanical()) //robots don't die via brain damage
var/obj/item/organ/internal/brain/brain = internal_organs_by_name[BP_BRAIN]
if(should_have_organ(BP_BRAIN))
var/obj/item/organ/internal/brain = internal_organs_by_name[BP_BRAIN]
if(!brain || (brain.status & ORGAN_DEAD))
return TRUE
return species.handle_death_check(src)
@@ -306,7 +306,7 @@
return FALSE
var/disarm_cost
var/obj/item/organ/internal/cell/cell = M.internal_organs_by_name[BP_CELL]
var/obj/item/organ/internal/machine/power_core/cell = M.internal_organs_by_name[BP_CELL]
var/obj/item/cell/potato
if(cell)
potato = cell.cell
@@ -404,7 +404,7 @@ This function restores the subjects blood to max.
This function restores all organs.
*/
/mob/living/carbon/human/restore_all_organs()
for(var/obj/item/organ/external/current_organ in organs)
for(var/obj/item/organ/current_organ in organs)
current_organ.rejuvenate()
/mob/living/carbon/human/proc/HealDamage(zone, brute, burn)
@@ -13,7 +13,7 @@
return 1
if(feedback)
if(status[1] == HUMAN_EATING_NO_MOUTH)
to_chat(src, "Where do you intend to put \the [food]? You don't have a mouth!")
to_chat(src, SPAN_NOTICE("Where do you intend to put \the [food]? You don't have a mouth!"))
else if(status[1] == HUMAN_EATING_BLOCKED_MOUTH)
to_chat(src, SPAN_WARNING("\The [status[2]] is in the way!"))
return 0
@@ -133,16 +133,24 @@
else
var/obj/item/organ/I = internal_organs_by_name[name]
if(I)
switch (status)
if (ORGAN_PREF_ASSISTED)
I.mechassist()
if (ORGAN_PREF_MECHANICAL)
if (rlimb_data[name])
I.robotize(rlimb_data[name])
else
I.robotize()
if (ORGAN_PREF_REMOVED)
qdel(I)
if(istype(I, /obj/item/organ/internal/machine))
var/obj/item/organ/internal/machine/machine_organ = I
if(length(machine_organ.possible_modifications))
machine_organ.get_preset_from_pref(status)
// If you add any more presets here, make sure to update the presets on the organ as well.
// Remember also that the default pref, but "default pref" is actually "the absence of any pref".
// We check for unique prefs and if we don't find a unique organ pref, then we default to the base type.
else
switch (status)
if (ORGAN_PREF_ASSISTED)
I.mechassist()
if (ORGAN_PREF_MECHANICAL)
if (rlimb_data[name])
I.robotize(rlimb_data[name])
else
I.robotize()
if (ORGAN_PREF_REMOVED)
qdel(I)
if (apply_markings)
for(var/N in organs_by_name)
@@ -363,7 +371,7 @@
return FALSE
/mob/living/carbon/human/get_cell()
var/obj/item/organ/internal/cell/C = internal_organs_by_name[BP_CELL]
var/obj/item/organ/internal/machine/power_core/C = internal_organs_by_name[BP_CELL]
if(C)
return C.cell
@@ -55,6 +55,11 @@
var/obj/item/P = pulling
tally += P.slowdown
var/obj/item/grab/grab = get_type_in_hands(/obj/item/grab)
if(istype(grab) && ishuman(grab.affecting))
if(grab.affecting.mob_weight > get_mob_strength())
tally += grab.affecting.mob_weight - get_mob_strength()
var/turf/T = get_turf(src)
if(T) // changelings don't get movement costs
var/datum/changeling/changeling
@@ -188,4 +193,4 @@
var/mob/living/carbon/human/H = pulling
if(H.species.slowdown > species.slowdown)
. += H.species.slowdown - species.slowdown
// . += H.ClothesSlowdown()
@@ -37,6 +37,9 @@
internal_organs -= I
continue
if(I.status & ORGAN_DEAD)
continue
I.process(seconds_per_tick)
handle_stance()
@@ -192,10 +195,13 @@
/mob/living/carbon/human/is_asystole()
if(isSynthetic())
var/obj/item/organ/internal/cell/C = internal_organs_by_name[BP_CELL]
var/obj/item/organ/internal/machine/power_core/C = internal_organs_by_name[BP_CELL]
if(istype(C) && C.is_usable() && C.percent())
return FALSE
var/obj/item/organ/internal/machine/posibrain/posi = internal_organs_by_name[BP_BRAIN]
if(istype(posi) && !posi.self_preservation_activated)
return FALSE
return TRUE
else if(should_have_organ(BP_HEART))
var/obj/item/organ/internal/heart/heart = internal_organs_by_name[BP_HEART]
if(!istype(heart) || !heart.is_working())
@@ -1066,9 +1066,6 @@
drop_from_inventory(O)
O.replaced(src)
update_body()
updatehealth()
UpdateDamageIcon()
update_body()
updatehealth()
@@ -1077,45 +1074,6 @@
visible_message(SPAN_NOTICE("\The [src] attaches \the [O] to [get_pronoun("his")] body!"),
SPAN_NOTICE("You attach \the [O] to your body!"))
/mob/living/carbon/human/proc/self_diagnostics()
set name = "Self-Diagnostics"
set desc = "Run an internal self-diagnostic to check for damage."
set category = "Abilities"
if(stat == DEAD) return
to_chat(src, SPAN_NOTICE("Performing self-diagnostic, please wait..."))
if (do_after(src, 10))
var/output = SPAN_NOTICE("Self-Diagnostic Results:\n")
output += "Internal Temperature: [convert_k2c(bodytemperature)] Degrees Celsius\n"
var/obj/item/organ/internal/cell/C = internal_organs_by_name[BP_CELL]
if(!C || !C.cell)
output += SPAN_DANGER("ERROR: NO BATTERY DETECTED")
else
output += "Current Charge Level: [C.percent()]\n"
var/toxDam = getToxLoss()
if(toxDam)
output += "Blood Toxicity: <span class='warning'>[toxDam > 25 ? "Severe" : "Moderate"]</span>. Seek medical facilities for cleanup.\n"
else
output += "Blood Toxicity: <span style='color:green;'>OK</span>\n"
for(var/obj/item/organ/external/EO in organs)
if(EO.brute_dam || EO.burn_dam)
output += "[EO.name] - <span class='warning'>[EO.burn_dam + EO.brute_dam > ROBOLIMB_SELF_REPAIR_CAP ? "Heavy Damage" : "Light Damage"]</span>\n"
else
output += "[EO.name] - <span style='color:green;'>OK</span>\n"
for(var/obj/item/organ/IO in internal_organs)
if(IO.damage)
output += "[IO.name] - <span class='warning'>[IO.damage > 10 ? "Heavy Damage" : "Light Damage"]</span>\n"
else
output += "[IO.name] - <span style='color:green;'>OK</span>\n"
to_chat(src, output)
/mob/living/carbon/human/proc/check_tag()
set name = "Check Tag"
set desc = "Run diagnostics on your tag to display its information."
@@ -1124,7 +1082,7 @@
if(use_check_and_message(usr))
return
var/obj/item/organ/internal/ipc_tag/tag = internal_organs_by_name[BP_IPCTAG]
var/obj/item/organ/internal/machine/ipc_tag/tag = internal_organs_by_name[BP_IPCTAG]
if(isnull(tag) || !tag)
to_chat(src, SPAN_WARNING("Error: No Tag Found."))
return
+4 -3
View File
@@ -439,7 +439,8 @@
/mob/living/carbon/human/proc/stabilize_body_temperature()
if (species.passive_temp_gain) // We produce heat naturally.
bodytemperature += species.passive_temp_gain
species.handle_temperature_regulation(src)
if (species.body_temperature == null)
return //this species doesn't have metabolic thermoregulation
@@ -1015,7 +1016,7 @@
nut_icon = 2
else if (nut_factor >= CREW_NUTRITION_HUNGRY)
nut_icon = 3
else if (nut_factor >= CREW_NUTRITION_VERYHUNGRY )
else if (nut_factor >= CREW_NUTRITION_VERYHUNGRY)
nut_icon = 4
var/new_val = "[isSynthetic() ? "charge" : "nutrition"][nut_icon]"
if (nutrition_icon.icon_state != new_val)
@@ -1039,7 +1040,7 @@
hydration_icon.icon_state = new_val
if(isSynthetic())
var/obj/item/organ/internal/cell/IC = internal_organs_by_name[BP_CELL]
var/obj/item/organ/internal/machine/power_core/IC = internal_organs_by_name[BP_CELL]
if(istype(IC) && IC.is_usable())
var/chargeNum = clamp(Ceiling(IC.percent()/25), 0, 4) //0-100 maps to 0-4, but give it a paranoid clamp just in case.
cells.icon_state = "charge[chargeNum]"
@@ -65,6 +65,12 @@
return ..()
/mob/living/carbon/human/say(message, datum/language/speaking, verb, alt_name, ghost_hearing, whisper, skip_edit)
// Messaging the user is handled by the species proc.
if(!species.can_speak(src, speaking, message))
return FALSE
. = ..()
/mob/living/carbon/human/GetVoice()
var/voice_sub
if(istype(back,/obj/item/rig))
@@ -94,6 +94,10 @@
/// Used for mob icon generation for non-32x32 species.
var/icon/icon_template
var/mob_size = MOB_MEDIUM
/// The weight of the mob. Affects if the mob can be easily lifted or not. Separate from size, as some mobs may be big but not particularly heavy.
var/mob_weight = MOB_WEIGHT_MEDIUM
/// The strength of the mob. A bonus to the checks on lifting/throwing other mobs.
var/mob_strength = MOB_STRENGTH_NORMAL
var/show_ssd = "in a deep slumber"
var/short_sighted
var/bald = 0
@@ -389,6 +393,7 @@
BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right)
)
var/natural_armor_type = /datum/component/armor/natural
var/list/natural_armor
// Bump vars
@@ -545,7 +550,7 @@
if(H.bad_external_organs) H.bad_external_organs.Cut()
if(H.bad_internal_organs) H.bad_internal_organs.Cut()
var/datum/component/armor/armor_component = H.GetComponent(/datum/component/armor)
var/datum/component/armor/armor_component = H.GetComponent(natural_armor_type)
if(armor_component)
qdel(armor_component)
@@ -584,7 +589,7 @@
I.status |= ORGAN_ADV_ROBOT
if(natural_armor)
H.AddComponent(/datum/component/armor, natural_armor)
H.AddComponent(natural_armor_type, natural_armor)
/datum/species/proc/tap(var/mob/living/carbon/human/H,var/mob/living/target)
if(H.on_fire)
@@ -620,13 +625,16 @@
return
/datum/species/proc/handle_post_spawn(var/mob/living/carbon/human/H,var/kpg = 0) //Handles anything not already covered by basic species assignment. Keepgene value should only be used by genetics.
/// Handles anything not already covered by basic species assignment. Keepgene (kpg) value should only be used by genetics.
/datum/species/proc/handle_post_spawn(mob/living/carbon/human/H, kpg = 0)
add_inherent_verbs(H)
H.mob_bump_flag = bump_flag
H.mob_swap_flags = swap_flags
H.mob_push_flags = push_flags
H.pass_flags = pass_flags
H.mob_size = mob_size
H.mob_weight = mob_weight
H.mob_strength = mob_strength
H.eat_types = allowed_eat_types
if(!kpg)
if(islesserform(H))
@@ -1057,3 +1065,18 @@
*/
/datum/species/proc/sleep_examine_msg(var/mob/M)
return SPAN_NOTICE("[M.get_pronoun("He")] appears to be fast asleep.\n")
/**
* This proc is used to override speech checks for human mobs.
* If it returns FALSE, the mob will not be able to speak.
* Make sure to give the user the relevant error message in the override.
*/
/datum/species/proc/can_speak(mob/living/carbon/human/speaker, datum/language/speaking, message)
return TRUE
/**
* This proc handles the species temperature regulation. By default, it just adds `passive_temp_gain` to the human's bodytemperature.
* Can be overridden for more complex calculations.
*/
/datum/species/proc/handle_temperature_regulation(mob/living/carbon/human/human)
human.bodytemperature += passive_temp_gain
@@ -1,24 +1,42 @@
/datum/hud_data
var/icon // If set, overrides ui_style.
var/has_a_intent = TRUE // Set to draw intent box.
var/has_m_intent = TRUE // Set to draw move intent box.
var/has_warnings = TRUE // Set to draw environment warnings.
var/has_pressure = TRUE // Draw the pressure indicator.
var/has_nutrition = TRUE // Draw the nutrition indicator.
var/has_hydration = TRUE // Draw the hydration indicator.
var/has_bodytemp = TRUE // Draw the bodytemp indicator.
var/has_hands = TRUE // Set to draw hands.
var/has_drop = TRUE // Set to draw drop button.
var/has_throw = TRUE // Set to draw throw button.
var/has_resist = TRUE // Set to draw resist button.
var/has_internals = TRUE // Set to draw the internals toggle button.
var/has_up_hint = TRUE // Set to draw the "look-up" hint icon.
var/has_cell = FALSE //Set if the species has a cell.
/// If set, overrides ui_style.
var/icon
/// Set to draw intent box.
var/has_a_intent = TRUE
/// Set to draw move intent box.
var/has_m_intent = TRUE
/// Set to draw environment warnings.
var/has_warnings = TRUE
/// Draw the pressure indicator.
var/has_pressure = TRUE
/// Draw the nutrition indicator.
var/has_nutrition = TRUE
/// Draw the hydration indicator.
var/has_hydration = TRUE
/// Draw the bodytemp indicator.
var/has_bodytemp = TRUE
/// Set to draw hands.
var/has_hands = TRUE
/// Set to draw drop button.
var/has_drop = TRUE
/// Set to draw throw button.
var/has_throw = TRUE
/// Set to draw resist button.
var/has_resist = TRUE
/// Set to draw the internals toggle button.
var/has_internals = TRUE
/// Set to draw the "look-up" hint icon.
var/has_up_hint = TRUE
/// Set if the species has a cell.
var/has_cell = FALSE
/// If the mob in question uses robot pain textures.
var/has_robot_pain = FALSE
var/list/equip_slots = list() // Checked by mob_can_equip().
// Contains information on the position and tag for all inventory slots
// to be drawn for the mob. This is fairly delicate, try to avoid messing with it
// unless you know exactly what it does.
/** Contains information on the position and tag for all inventory slots
* to be drawn for the mob. This is fairly delicate, try to avoid messing with it
* unless you know exactly what it does.
*/
var/list/gear = list(
"i_clothing" = list("loc" = ui_iclothing, "name" = "uniform", "slot" = slot_w_uniform, "state" = "center", "toggle" = 1),
"o_clothing" = list("loc" = ui_oclothing, "name" = "suit", "slot" = slot_wear_suit, "state" = "suit", "toggle" = 1),
@@ -98,6 +116,7 @@
has_hydration = FALSE
has_nutrition = FALSE
has_cell = TRUE
has_robot_pain = TRUE
/datum/hud_data/construct
has_hydration = FALSE
@@ -12,6 +12,7 @@
economic_modifier = 6
default_genders = list(NEUTER)
selectable_pronouns = list(NEUTER, PLURAL)
mob_weight = MOB_WEIGHT_HEAVY
blurb = "IPCs are, quite simply, \"Integrated Positronic Chassis.\" In this scenario, 'positronic' implies that the chassis possesses a positronic processing core (or positronic brain), meaning that an IPC must be positronic to be considered an IPC. The Baseline model is more of a category - the long of the short is that they represent all unbound synthetic units. Baseline models cover anything that is not an Industrial chassis or a Shell chassis. They can be custom made or assembly made. The most common feature of the Baseline model is a simple design, skeletal or semi-humanoid, and ordinary atmospheric diffusion cooling systems."
@@ -43,7 +44,7 @@
hud_type = /datum/hud_data/ipc
brute_mod = 1.0
brute_mod = 1
burn_mod = 1.2
grab_mod = 1.1 // Smooth, no real edges to grab onto
@@ -71,10 +72,9 @@
heat_level_3 = 2400
body_temperature = null
passive_temp_gain = 10 // This should cause IPCs to stabilize at ~80 C in a 20 C environment.
passive_temp_gain = 10
inherent_verbs = list(
/mob/living/carbon/human/proc/self_diagnostics,
/mob/living/carbon/human/proc/change_monitor,
/mob/living/carbon/human/proc/check_tag
)
@@ -88,11 +88,21 @@
flesh_color = "#575757"
reagent_tag = IS_MACHINE
// If you add a new organ to IPCs, remember that a lot of the normal subspecies have overrides because of custom subtype organs.
// You'll need to add the new organ there too.
has_organ = list(
BP_BRAIN = /obj/item/organ/internal/mmi_holder/posibrain,
BP_CELL = /obj/item/organ/internal/cell,
BP_BRAIN = /obj/item/organ/internal/machine/posibrain,
BP_VOICE_SYNTHESIZER = /obj/item/organ/internal/machine/voice_synthesizer,
BP_DIAGNOSTICS_SUITE = /obj/item/organ/internal/machine/internal_diagnostics,
BP_HYDRAULICS = /obj/item/organ/internal/machine/hydraulics,
BP_ACTUATORS_LEFT = /obj/item/organ/internal/machine/actuators/left,
BP_ACTUATORS_RIGHT = /obj/item/organ/internal/machine/actuators/right,
BP_COOLING_UNIT = /obj/item/organ/internal/machine/cooling_unit,
BP_REACTOR = /obj/item/organ/internal/machine/reactor,
BP_ACCESS_PORT = /obj/item/organ/internal/machine/access_port,
BP_CELL = /obj/item/organ/internal/machine/power_core,
BP_EYES = /obj/item/organ/internal/eyes/optical_sensor,
BP_IPCTAG = /obj/item/organ/internal/ipc_tag
BP_IPCTAG = /obj/item/organ/internal/machine/ipc_tag
)
vision_organ = BP_EYES
@@ -114,7 +124,10 @@
heat_discomfort_level = 500 //This will be 100 below the first heat level
heat_discomfort_strings = list(
"Your CPU temperature probes warn you that you are approaching critical heat levels!"
"Your positronic's temperature probes warn you that you are approaching critical heat levels!",
"Your cooling systems are struggling to keep your positronic's temperature down!",
"Your positronic is approaching dangerous temperature levels! Immediate cooling required!",
"Critical temperature levels approaching!"
)
stamina = -1 // Machines use power and generate heat, stamina is not a thing
sprint_speed_factor = 1 // About as capable of speed as a human
@@ -135,13 +148,8 @@
/singleton/origin_item/culture/scrapper
)
alterable_internal_organs = list()
alterable_internal_organs = list(BP_COOLING_UNIT, BP_REACTOR)
possible_speech_bubble_types = list("robot", "default")
// Special snowflake machine vars.
var/sprint_temperature_factor = 1.15
var/move_charge_factor = 1
use_alt_hair_layer = TRUE
psi_deaf = TRUE
@@ -149,23 +157,99 @@
snores = FALSE
indefinite_sleep = TRUE
natural_armor_type = /datum/component/armor/synthetic
natural_armor = list(
ballistic = ARMOR_BALLISTIC_MINOR,
melee = ARMOR_MELEE_KNIVES
)
// Special snowflake machine vars.
var/sprint_temperature_factor = 1.05
/// Species-based multiplier to movement power costs in the power core.
var/move_charge_factor = 1.2
/// The theme of the IPC's personal UIs, if not broken.
var/machine_ui_theme = "hackerman"
/datum/species/machine/handle_temperature_regulation(mob/living/carbon/human/human)
// No cooling unit = you're cooking. Broken cooling unit effects are handled by the organ itself.
// Here we just want to check if it's been removed.
// 500K is about 226 degrees. Spicy!
var/base_heat_gain = passive_temp_gain
var/obj/item/organ/internal/machine/cooling_unit/cooling = human.internal_organs_by_name[BP_COOLING_UNIT]
if(!cooling || (cooling?.status & ORGAN_DEAD))
base_heat_gain *= 4 //uh oh
human.bodytemperature += base_heat_gain
/datum/species/machine/handle_stance_damage(mob/living/carbon/human/H, damage_only)
var/stance_damage = 0
var/obj/item/organ/internal/machine/hydraulics/hydraulics = H.internal_organs_by_name[BP_HYDRAULICS]
if(!hydraulics)
return 6 //no hydraulics, no party
if(hydraulics.status & ORGAN_DEAD)
return 5
switch(hydraulics.get_integrity())
if(0 to IPC_INTEGRITY_THRESHOLD_VERY_HIGH)
to_chat(H, SPAN_MACHINE_WARNING("Your hydraulics are on the verge of breaking completely!"))
spark(H, rand(3, 4), GLOB.alldirs)
stance_damage += 4
if(IPC_INTEGRITY_THRESHOLD_VERY_HIGH to IPC_INTEGRITY_THRESHOLD_HIGH)
to_chat(H, SPAN_MACHINE_WARNING("Your hydraulics spark and whine!"))
spark(H, rand(1, 3), GLOB.alldirs)
stance_damage += 2
var/datum/component/synthetic_endoskeleton/endoskeleton = H.GetComponent(/datum/component/synthetic_endoskeleton)
if(!endoskeleton)
return 6 //how?
var/damage_ratio = endoskeleton.damage / endoskeleton.max_damage
switch(damage_ratio)
if(0.3 to 0.5)
stance_damage += 1
if(0.5 to 0.75)
stance_damage += 2
if(0.75 to INFINITY)
stance_damage += 3
return stance_damage
/datum/species/machine/handle_post_spawn(var/mob/living/carbon/human/H)
. = ..()
H.AddComponent(/datum/component/synthetic_endoskeleton)
check_tag(H, H.client)
var/obj/item/organ/internal/cell/C = H.internal_organs_by_name[BP_CELL]
var/obj/item/organ/internal/machine/power_core/C = H.internal_organs_by_name[BP_CELL]
if(C)
C.move_charge_factor = move_charge_factor
/datum/species/machine/handle_sprint_cost(var/mob/living/carbon/human/H, var/cost, var/pre_move)
if(!pre_move && H.stat == CONSCIOUS)
H.bodytemperature += cost * sprint_temperature_factor
var/obj/item/organ/internal/cell/C = H.internal_organs_by_name[BP_CELL]
if(C)
var/obj/item/organ/internal/machine/hydraulics/hydraulics = H.internal_organs_by_name[BP_HYDRAULICS]
if(istype(hydraulics))
var/hydraulics_integrity = hydraulics.get_integrity()
if(hydraulics_integrity < 75)
var/damage_mod = 2
if(hydraulics.is_bruised())
damage_mod = 3
if(hydraulics.is_broken())
damage_mod = 5
// x = x * (2 + 100 - y ) / 100
// x = x * (2 + 100 - 75) / 100 -> x = x * (2 + (25 / 100)) --> x = x * 2.25
// increases depending on integrity
cost *= damage_mod + ((100 - hydraulics_integrity) / 100)
var/obj/item/organ/internal/machine/power_core/C = H.internal_organs_by_name[BP_CELL]
if(istype(C))
C.use(cost * sprint_cost_factor)
SEND_SIGNAL(H, COMSIG_IPC_HAS_SPRINTED)
return TRUE
/datum/species/machine/handle_emp_act(mob/living/carbon/human/hit_mob, severity)
var/obj/item/organ/internal/surge/S = hit_mob.internal_organs_by_name["surge"]
var/obj/item/organ/internal/machine/surge/S = hit_mob.internal_organs_by_name[BP_SURGE_PROTECTOR]
if(!isnull(S))
if(S.surge_left >= 1)
playsound(hit_mob.loc, 'sound/magic/LightningShock.ogg', 25, 1)
@@ -189,16 +273,22 @@
/datum/species/machine/sanitize_name(var/new_name)
return sanitizeName(new_name, allow_numbers = 1)
/datum/species/machine/bypass_food_fullness(mob/living/carbon/human/H)
var/obj/item/organ/internal/machine/reactor/reactor = H.internal_organs_by_name[BP_REACTOR]
if(reactor && (reactor.power_supply_type & POWER_SUPPLY_BIOLOGICAL))
return TRUE
return FALSE
/datum/species/machine/proc/check_tag(var/mob/living/carbon/human/new_machine, var/client/player)
if(!new_machine || !player)
var/obj/item/organ/internal/ipc_tag/tag = new_machine.internal_organs_by_name[BP_IPCTAG]
var/obj/item/organ/internal/machine/ipc_tag/tag = new_machine.internal_organs_by_name[BP_IPCTAG]
if(tag)
tag.serial_number = uppertext(dd_limittext(md5(new_machine.real_name), 12))
tag.ownership_info = IPC_OWNERSHIP_COMPANY
tag.citizenship_info = CITIZENSHIP_BIESEL
return
var/obj/item/organ/internal/ipc_tag/tag = new_machine.internal_organs_by_name[BP_IPCTAG]
var/obj/item/organ/internal/machine/ipc_tag/tag = new_machine.internal_organs_by_name[BP_IPCTAG]
if(player.prefs.machine_tag_status)
tag.serial_number = player.prefs.machine_serial_number
@@ -381,19 +471,14 @@
. = ..()
check_tag(H, H.client)
/datum/species/machine/handle_death_check(var/mob/living/carbon/human/H)
if(H.get_total_health() <= GLOB.config.health_threshold_dead)
return TRUE
return FALSE
/datum/species/machine/has_stamina_for_pushup(var/mob/living/carbon/human/human)
var/obj/item/organ/internal/cell/C = human.internal_organs_by_name[BP_CELL]
var/obj/item/organ/internal/machine/power_core/C = human.internal_organs_by_name[BP_CELL]
if(!C.cell)
return FALSE
return C.cell.charge > (C.cell.maxcharge / 10)
/datum/species/machine/drain_stamina(var/mob/living/carbon/human/human, var/stamina_cost)
var/obj/item/organ/internal/cell/C = human.internal_organs_by_name[BP_CELL]
var/obj/item/organ/internal/machine/power_core/C = human.internal_organs_by_name[BP_CELL]
if(C)
C.use(stamina_cost * 8)
@@ -403,3 +488,19 @@
/datum/species/machine/sleep_examine_msg(var/mob/M)
return SPAN_NOTICE("[M.get_pronoun("He")] appears to be in standby.\n")
/datum/species/machine/can_speak(mob/living/carbon/human/speaker, datum/language/speaking, message)
var/obj/item/organ/internal/machine/voice_synthesizer/voice_synth = speaker.internal_organs_by_name[BP_VOICE_SYNTHESIZER]
if(istype(voice_synth))
return TRUE
else
to_chat(speaker, SPAN_WARNING("You cannot synthesize a voice without your [BP_VOICE_SYNTHESIZER]!"))
return FALSE
/datum/species/machine/handle_speech_problems(mob/living/carbon/human/H, message, say_verb, message_mode, message_range)
var/obj/item/organ/internal/machine/voice_synthesizer/voice_synth = H.internal_organs_by_name[BP_VOICE_SYNTHESIZER]
if(istype(voice_synth))
if(voice_synth.is_bruised())
// at most, 30 * 2 + 10 = 70, which is the maximum value we can use for Gibberish
message = Gibberish(message, voice_synth.damage * 2 + (voice_synth.is_broken() ? 10 : 0))
return list(HSP_MSG = message, HSP_VERB = pick(list("crackles", "buzzes")))
@@ -9,10 +9,11 @@
height_max = 230
default_genders = list(MALE, FEMALE)
selectable_pronouns = list(MALE, FEMALE, PLURAL, NEUTER)
mob_weight = MOB_WEIGHT_MEDIUM // Would be really easy to find untagged shells otherwise.
alterable_internal_organs = list(BP_EYES)
alterable_internal_organs = list(BP_EYES, BP_COOLING_UNIT, BP_REACTOR)
burn_mod = 1.2
burn_mod = 1.3
grab_mod = 1
blurb = "IPCs with humanlike properties. Their focus is on service, civilian, and medical, but there are no \
@@ -67,11 +68,10 @@
character_color_presets = list("Dark" = "#000000", "Warm" = "#250302", "Cold" = "#1e1e29", "Rubber" = "#000f36")
sprint_temperature_factor = 1.3
move_charge_factor = 0.85
sprint_temperature_factor = 1.15
move_charge_factor = 1.1
inherent_verbs = list(
/mob/living/carbon/human/proc/self_diagnostics,
/mob/living/carbon/human/proc/check_tag,
/mob/living/carbon/human/proc/tie_hair)
@@ -101,10 +101,19 @@
break_cuffs = TRUE
has_organ = list(
BP_BRAIN = /obj/item/organ/internal/mmi_holder/posibrain,
BP_CELL = /obj/item/organ/internal/cell,
BP_BRAIN = /obj/item/organ/internal/machine/posibrain,
BP_VOICE_SYNTHESIZER = /obj/item/organ/internal/machine/voice_synthesizer,
BP_DIAGNOSTICS_SUITE = /obj/item/organ/internal/machine/internal_diagnostics,
BP_HYDRAULICS = /obj/item/organ/internal/machine/hydraulics,
BP_ACTUATORS_LEFT = /obj/item/organ/internal/machine/actuators/left,
BP_ACTUATORS_RIGHT = /obj/item/organ/internal/machine/actuators/right,
BP_COOLING_UNIT = /obj/item/organ/internal/machine/cooling_unit,
BP_REACTOR = /obj/item/organ/internal/machine/reactor,
BP_ACCESS_PORT = /obj/item/organ/internal/machine/access_port,
BP_CELL = /obj/item/organ/internal/machine/power_core,
BP_EYES = /obj/item/organ/internal/eyes/optical_sensor,
"surge" = /obj/item/organ/internal/surge/advanced
BP_SURGE_PROTECTOR = /obj/item/organ/internal/machine/surge/advanced,
BP_TARGETING_CORE = /obj/item/organ/internal/machine/targeting_core
)
unarmed_types = list(
@@ -113,10 +122,6 @@
/datum/unarmed_attack/terminator,
/datum/unarmed_attack/bite/strong)
inherent_verbs = list(
/mob/living/carbon/human/proc/self_diagnostics
)
/datum/species/machine/shell/rogue/check_tag(var/mob/living/carbon/human/new_machine, var/client/player)
return
@@ -138,7 +143,7 @@
grab_mod = 0.8 // Big, easy to grab onto
resist_mod = 10 // Good luck wrestling against this powerhouse.
slowdown = 4
slowdown = 3
blurb = "The first commercialized attempt Hephaestus Industries made at an industrial-type IPC. Designed for extra durability and increased weight loads, the first generation Industrial was considered a success, though it possessed some issues. A limited power cell and actuators designed for heavy lifting and not locomotion resulted in a slow and frequently charging machine. A special addition to the chassis makes up for these drawbacks - the ability to simply slot a suit cooling unit onto the model's back and make use of its built-in heat transferal conduits, allowing the Industrial to perform EVA without any extra peripherals such as a voidsuit."
@@ -178,12 +183,28 @@
sprint_speed_factor = 1.4
sprint_temperature_factor = 0.9
move_charge_factor = 1.1
move_charge_factor = 1.5
inherent_verbs = list(
/mob/living/carbon/human/proc/self_diagnostics,
/mob/living/carbon/human/proc/check_tag
)
has_organ = list(
BP_BRAIN = /obj/item/organ/internal/machine/posibrain,
BP_VOICE_SYNTHESIZER = /obj/item/organ/internal/machine/voice_synthesizer,
BP_DIAGNOSTICS_SUITE = /obj/item/organ/internal/machine/internal_diagnostics,
BP_HYDRAULICS = /obj/item/organ/internal/machine/hydraulics,
BP_ACTUATORS_LEFT = /obj/item/organ/internal/machine/actuators/left,
BP_ACTUATORS_RIGHT = /obj/item/organ/internal/machine/actuators/right,
BP_COOLING_UNIT = /obj/item/organ/internal/machine/cooling_unit,
BP_REACTOR = /obj/item/organ/internal/machine/reactor,
BP_ACCESS_PORT = /obj/item/organ/internal/machine/access_port,
BP_CELL = /obj/item/organ/internal/machine/power_core,
BP_EYES = /obj/item/organ/internal/eyes/optical_sensor,
BP_IPCTAG = /obj/item/organ/internal/machine/ipc_tag,
BP_INTERNAL_STORAGE = /obj/item/organ/internal/machine/internal_storage,
)
machine_ui_theme = "hephaestus"
/datum/species/machine/industrial/get_light_color()
return LIGHT_COLOR_TUNGSTEN
@@ -238,7 +259,6 @@
heat_level_3 = 5000
body_temperature = null
passive_temp_gain = 0
flags = IS_IPC | ACCEPTS_COOLER
spawn_flags = IS_RESTRICTED
@@ -250,16 +270,24 @@
inherent_verbs = list(
/mob/living/carbon/human/proc/self_destruct,
/mob/living/carbon/human/proc/detonate_flechettes,
/mob/living/carbon/human/proc/state_laws,
/mob/living/carbon/human/proc/self_diagnostics
/mob/living/carbon/human/proc/state_laws
)
has_organ = list(
BP_BRAIN = /obj/item/organ/internal/mmi_holder/posibrain/terminator,
BP_CELL = /obj/item/organ/internal/cell/terminator,
BP_EYES = /obj/item/organ/internal/eyes/optical_sensor/terminator,
"data core" = /obj/item/organ/internal/data,
"surge" = /obj/item/organ/internal/surge/advanced
BP_BRAIN = /obj/item/organ/internal/machine/posibrain,
BP_VOICE_SYNTHESIZER = /obj/item/organ/internal/machine/voice_synthesizer,
BP_DIAGNOSTICS_SUITE = /obj/item/organ/internal/machine/internal_diagnostics,
BP_HYDRAULICS = /obj/item/organ/internal/machine/hydraulics,
BP_ACTUATORS_LEFT = /obj/item/organ/internal/machine/actuators/left,
BP_ACTUATORS_RIGHT = /obj/item/organ/internal/machine/actuators/right,
BP_COOLING_UNIT = /obj/item/organ/internal/machine/cooling_unit,
BP_REACTOR = /obj/item/organ/internal/machine/reactor,
BP_ACCESS_PORT = /obj/item/organ/internal/machine/access_port,
BP_CELL = /obj/item/organ/internal/machine/power_core,
BP_EYES = /obj/item/organ/internal/eyes/optical_sensor,
BP_DATACORE = /obj/item/organ/internal/machine/data,
BP_SURGE_PROTECTOR = /obj/item/organ/internal/machine/surge/advanced,
BP_TARGETING_CORE = /obj/item/organ/internal/machine/targeting_core
)
has_limbs = list(
@@ -289,6 +317,8 @@
sprint_temperature_factor = 0.6
move_charge_factor = 0.3
machine_ui_theme = "syndicate"
/datum/species/machine/hunter_killer/get_light_color()
return
@@ -314,8 +344,8 @@
unarmed_types = list(/datum/unarmed_attack/industrial/heavy, /datum/unarmed_attack/palm/industrial)
slowdown = 6
brute_mod = 0.7
slowdown = 2
brute_mod = 0.9
grab_mod = 0.7 // Bulkier and bigger than the G1
resist_mod = 12 // Overall stronger than G1
@@ -326,8 +356,9 @@
heat_discomfort_level = 900
inherent_verbs = list(
/mob/living/carbon/human/proc/self_diagnostics,
/mob/living/carbon/human/proc/check_tag
/mob/living/carbon/human/proc/check_tag,
/mob/living/carbon/human/proc/discard_limb,
/mob/living/carbon/human/proc/attach_hephaestus_limb
)
examine_color = "#688359"
@@ -348,8 +379,100 @@
BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right/ipc/industrial/hephaestus)
)
has_organ = list(
BP_BRAIN = /obj/item/organ/internal/machine/posibrain,
BP_VOICE_SYNTHESIZER = /obj/item/organ/internal/machine/voice_synthesizer,
BP_DIAGNOSTICS_SUITE = /obj/item/organ/internal/machine/internal_diagnostics,
BP_HYDRAULICS = /obj/item/organ/internal/machine/hydraulics,
BP_ACTUATORS_LEFT = /obj/item/organ/internal/machine/actuators/left,
BP_ACTUATORS_RIGHT = /obj/item/organ/internal/machine/actuators/right,
BP_COOLING_UNIT = /obj/item/organ/internal/machine/cooling_unit,
BP_REACTOR = /obj/item/organ/internal/machine/reactor,
BP_ACCESS_PORT = /obj/item/organ/internal/machine/access_port,
BP_CELL = /obj/item/organ/internal/machine/power_core,
BP_EYES = /obj/item/organ/internal/eyes/optical_sensor,
BP_IPCTAG = /obj/item/organ/internal/machine/ipc_tag
)
flags = IS_IPC | ACCEPTS_COOLER | NO_EQUIP_SPEEDMODS
move_charge_factor = 1.7
machine_ui_theme = "hephaestus"
natural_armor = list(
ballistic = ARMOR_BALLISTIC_PISTOL,
melee = ARMOR_MELEE_KEVLAR
)
/mob/living/carbon/human/proc/discard_limb()
set name = "Discard Limb"
set category = "Object"
if(incapacitated(INCAPACITATION_DEFAULT))
return
if(get_active_hand())
to_chat(src, SPAN_WARNING("You need your hand to be free!"))
return
var/limb_list = list()
for(var/obj/item/organ/external/limb in organs)
if(limb.limb_name in list(BP_CHEST, BP_GROIN, BP_HEAD))
continue
if(limb.robotize_type == PROSTHETIC_HI)
limb_list[capitalize_first_letters(limb.name)] = limb
var/chosen_limb = tgui_input_list(src, "Choose a limb to discard.", "Discard Limb", limb_list)
if(!chosen_limb)
return
var/obj/item/organ/external/oopsie = limb_list[chosen_limb]
visible_message(SPAN_DANGER("[src] ejects [get_pronoun("his")] [oopsie] with a hiss!"))
oopsie.droplimb(TRUE, DROPLIMB_EDGE)
/mob/living/carbon/human/proc/attach_hephaestus_limb()
set name = "Attach Limb"
set category = "Object"
if(incapacitated(INCAPACITATION_DEFAULT))
return
var/obj/item/organ/external/limb = get_active_hand()
if(!istype(limb))
to_chat(src, SPAN_WARNING("You need to be holding a compatible Hephaestus Industries limb!"))
return
if(!limb.robotic)
to_chat(src, SPAN_WARNING("That is not a compatible Hephaestus Industries limb!"))
return
if(limb.robotize_type != PROSTHETIC_HI)
to_chat(src,SPAN_WARNING("That prosthetic does not have the right ports for your joint!"))
return
if(organs_by_name[limb.limb_name])
to_chat(src,SPAN_WARNING("You already have a limb of this type!"))
return
if(!organs_by_name[limb.parent_organ])
to_chat(src,SPAN_WARNING("You are missing the appropriate joint to attach that limb!"))
return
visible_message(SPAN_NOTICE("[src] begins attaching \the [limb] to the appropriate joint..."))
if(!do_after(src, 10 SECONDS))
return
drop_from_inventory(limb)
limb.replaced(src)
visible_message(SPAN_NOTICE("[src] attaches \the [limb] to their body!"))
update_body()
updatehealth()
UpdateDamageIcon()
/datum/species/machine/industrial/hephaestus/get_light_color(mob/living/carbon/human/H)
if (istype(H))
@@ -388,7 +511,6 @@
eyes = "xion_eyes"
flags = IS_IPC
passive_temp_gain = 0
examine_color = "#bc4b00"
@@ -408,6 +530,23 @@
BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right/ipc/industrial/xion)
)
has_organ = list(
BP_BRAIN = /obj/item/organ/internal/machine/posibrain,
BP_VOICE_SYNTHESIZER = /obj/item/organ/internal/machine/voice_synthesizer,
BP_DIAGNOSTICS_SUITE = /obj/item/organ/internal/machine/internal_diagnostics,
BP_HYDRAULICS = /obj/item/organ/internal/machine/hydraulics,
BP_ACTUATORS_LEFT = /obj/item/organ/internal/machine/actuators/left,
BP_ACTUATORS_RIGHT = /obj/item/organ/internal/machine/actuators/right,
BP_COOLING_UNIT = /obj/item/organ/internal/machine/cooling_unit/xion,
BP_REACTOR = /obj/item/organ/internal/machine/reactor,
BP_ACCESS_PORT = /obj/item/organ/internal/machine/access_port,
BP_CELL = /obj/item/organ/internal/machine/power_core,
BP_EYES = /obj/item/organ/internal/eyes/optical_sensor,
BP_IPCTAG = /obj/item/organ/internal/machine/ipc_tag
)
machine_ui_theme = "hephaestus"
/datum/species/machine/industrial/xion/remote
name = SPECIES_IPC_XION_REMOTE
short_name = "rem_xmf"
@@ -415,10 +554,18 @@
spawn_flags = IS_RESTRICTED
has_organ = list(
BP_BRAIN = /obj/item/organ/internal/mmi_holder/circuit,
BP_CELL = /obj/item/organ/internal/cell,
BP_BRAIN = /obj/item/organ/internal/machine/posibrain,
BP_VOICE_SYNTHESIZER = /obj/item/organ/internal/machine/voice_synthesizer,
BP_DIAGNOSTICS_SUITE = /obj/item/organ/internal/machine/internal_diagnostics,
BP_HYDRAULICS = /obj/item/organ/internal/machine/hydraulics,
BP_ACTUATORS_LEFT = /obj/item/organ/internal/machine/actuators/left,
BP_ACTUATORS_RIGHT = /obj/item/organ/internal/machine/actuators/right,
BP_COOLING_UNIT = /obj/item/organ/internal/machine/cooling_unit,
BP_REACTOR = /obj/item/organ/internal/machine/reactor,
BP_ACCESS_PORT = /obj/item/organ/internal/machine/access_port,
BP_CELL = /obj/item/organ/internal/machine/power_core,
BP_EYES = /obj/item/organ/internal/eyes/optical_sensor,
BP_IPCTAG = /obj/item/organ/internal/ipc_tag
BP_IPCTAG = /obj/item/organ/internal/machine/ipc_tag
)
/datum/species/machine/industrial/xion/get_light_color(mob/living/carbon/human/H)
@@ -447,11 +594,27 @@
resist_mod = 4 // Not super strong, but still rather strong
appearance_flags = HAS_EYE_COLOR | HAS_UNDERWEAR | HAS_SOCKS
flags = IS_IPC | NO_SLIP
examine_color = "#ff00ff"
blurb = "Being a corporation focused primarily on medical sciences and treatments, Zeng-Hu Pharmaceuticals had little interest in the market of synthetics in the beginning (especially considering a good portion of Zeng-Hu employees are Skrellian). However, after seeing the advances in almost all fields of the galactic market after the advent of synthetics, Zeng-Hu set aside some funds for their own robotics department, focused mainly on medical service and even science related operations. Having taken some inspiration from biological life, the chassis has an interesting leg design: digitigrade legs provide the chassis with enhanced speed. A downside to this development was the reduction of metals on the chassis. Most plates covering the sensitive interior electronics are polymer casts to reduce the weight of the unit, resulting in a not-so-durable android."
has_organ = list(
BP_BRAIN = /obj/item/organ/internal/machine/posibrain,
BP_VOICE_SYNTHESIZER = /obj/item/organ/internal/machine/voice_synthesizer,
BP_DIAGNOSTICS_SUITE = /obj/item/organ/internal/machine/internal_diagnostics,
BP_HYDRAULICS = /obj/item/organ/internal/machine/hydraulics,
BP_ACTUATORS_LEFT = /obj/item/organ/internal/machine/actuators/left,
BP_ACTUATORS_RIGHT = /obj/item/organ/internal/machine/actuators/right,
BP_COOLING_UNIT = /obj/item/organ/internal/machine/cooling_unit/zenghu,
BP_REACTOR = /obj/item/organ/internal/machine/reactor,
BP_ACCESS_PORT = /obj/item/organ/internal/machine/access_port,
BP_CELL = /obj/item/organ/internal/machine/power_core,
BP_EYES = /obj/item/organ/internal/eyes/optical_sensor,
BP_IPCTAG = /obj/item/organ/internal/machine/ipc_tag
)
has_limbs = list(
BP_CHEST = list("path" = /obj/item/organ/external/chest/ipc/industrial/zenghu),
BP_GROIN = list("path" = /obj/item/organ/external/groin/ipc/industrial/zenghu),
@@ -467,13 +630,14 @@
)
inherent_verbs = list(
/mob/living/carbon/human/proc/self_diagnostics,
/mob/living/carbon/human/proc/check_tag
)
maneuvers = list(
/singleton/maneuver/leap/zenghu
)
machine_ui_theme = "zenghu"
/datum/species/machine/zenghu/get_light_color(mob/living/carbon/human/H)
if (istype(H))
@@ -503,6 +667,22 @@
blurb = "Bishop Cybernetics frames are among the sleeker, flashier frames widely produced for IPCs. This brand-new, high end design has a focus on pioneering energy efficiency without sacrifice, fitting to Bishop's company vision. Cutting-edge technology in power management means this frame can operate longer while running more demanding processing algorithms than most. This extreme push to minimize power draw means this frame can be equipped with all sorts of extra equipment: a hologram for a face, flashing status displays and embedded lights solely meant for show. The one thing holding this frame back from perfection is the same common criticism leveled against almost all Bishop products: the shiny chrome and glass meant to put all of this tech on display means it's exposed and fragile. It's because of Bishop's unrelenting pursuit of vanity in their designs that these frames often suffer from issues with reliability and struggle to safely perform the same work as cheaper, more rugged frames."
has_organ = list(
BP_BRAIN = /obj/item/organ/internal/machine/posibrain,
BP_VOICE_SYNTHESIZER = /obj/item/organ/internal/machine/voice_synthesizer,
BP_DIAGNOSTICS_SUITE = /obj/item/organ/internal/machine/internal_diagnostics,
BP_HYDRAULICS = /obj/item/organ/internal/machine/hydraulics,
BP_ACTUATORS_LEFT = /obj/item/organ/internal/machine/actuators/left,
BP_ACTUATORS_RIGHT = /obj/item/organ/internal/machine/actuators/right,
BP_COOLING_UNIT = /obj/item/organ/internal/machine/cooling_unit,
BP_REACTOR = /obj/item/organ/internal/machine/reactor,
BP_ACCESS_PORT = /obj/item/organ/internal/machine/access_port,
BP_CELL = /obj/item/organ/internal/machine/power_core,
BP_EYES = /obj/item/organ/internal/eyes/optical_sensor,
BP_IPCTAG = /obj/item/organ/internal/machine/ipc_tag,
BP_WIRELESS_ACCESS = /obj/item/organ/internal/machine/wireless_access
)
has_limbs = list(
BP_CHEST = list("path" = /obj/item/organ/external/chest/ipc/industrial/bishop),
BP_GROIN = list("path" = /obj/item/organ/external/groin/ipc/industrial/bishop),
@@ -518,10 +698,11 @@
)
inherent_verbs = list(
/mob/living/carbon/human/proc/self_diagnostics,
/mob/living/carbon/human/proc/check_tag,
/mob/living/carbon/human/proc/change_monitor
)
)
machine_ui_theme = "zenghu"
/datum/species/machine/bishop/get_light_color(mob/living/carbon/human/H)
if (istype(H))
@@ -559,7 +740,6 @@
)
inherent_verbs = list(
/mob/living/carbon/human/proc/self_diagnostics,
/mob/living/carbon/human/proc/check_tag
)
@@ -571,10 +751,17 @@
spawn_flags = IS_RESTRICTED
has_organ = list(
BP_BRAIN = /obj/item/organ/internal/mmi_holder/circuit,
BP_CELL = /obj/item/organ/internal/cell,
BP_BRAIN = /obj/item/organ/internal/machine/posibrain,
BP_VOICE_SYNTHESIZER = /obj/item/organ/internal/machine/voice_synthesizer,
BP_DIAGNOSTICS_SUITE = /obj/item/organ/internal/machine/internal_diagnostics,
BP_HYDRAULICS = /obj/item/organ/internal/machine/hydraulics,
BP_ACTUATORS_LEFT = /obj/item/organ/internal/machine/actuators/left,
BP_ACTUATORS_RIGHT = /obj/item/organ/internal/machine/actuators/right,
BP_COOLING_UNIT = /obj/item/organ/internal/machine/cooling_unit,
BP_REACTOR = /obj/item/organ/internal/machine/reactor,
BP_CELL = /obj/item/organ/internal/machine/power_core,
BP_EYES = /obj/item/organ/internal/eyes/optical_sensor,
BP_IPCTAG = /obj/item/organ/internal/ipc_tag
BP_IPCTAG = /obj/item/organ/internal/machine/ipc_tag
)
/datum/species/machine/unbranded/get_light_color(mob/living/carbon/human/H)
@@ -61,6 +61,7 @@
rarity_value = 3
break_cuffs = TRUE
mob_size = 10
mob_weight = MOB_WEIGHT_HEAVY
climb_coeff = 1.35
blurb = "A heavily reptillian species, Unathi (or 'Sinta as they call themselves) hail from the Uuosa-Eso \
@@ -264,6 +264,8 @@
total_health = 200
break_cuffs = TRUE
mob_size = 30
mob_weight = MOB_WEIGHT_SUPERHEAVY
mob_strength = MOB_STRENGTH_VERY_STRONG
speech_sounds = list('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg')
speech_chance = 100
@@ -372,6 +374,8 @@
resist_mod = 14 //also very strong
mob_size = 28
mob_weight = MOB_WEIGHT_SUPERHEAVY
mob_strength = MOB_STRENGTH_STRONG
taste_sensitivity = TASTE_DULL
blurb = {"Type E Vaurca, otherwise known as the Bulwarks, are a new bodyform derived from the worker caste in a collaboration by the C'thur and Nralakk scientists. Originally only the C'thur had access to these behemoths, but after a short amount of time, the bodyform started appearing in the ranks of the Zo'ra and K'lax as well, causing an even more strained relationship between the Hives.<br>
Similar to Workers, Bulwarks are generally passive, and prefer to flee a fight rather than resist. Though due to their speed, they may still choose to defend themselves should they be unable to properly escape a battle. The main exception to this is when another Vaurca is in danger. When this occurs, they tend to put themselves in between the attacker and the Vaurca, acting as a shield of sorts. They won't go out of their way to take down the attacker, but will ensure the others get away safely.<br>
@@ -20,24 +20,14 @@
robotic_analyze_mob(target_mob, user)
add_fingerprint(user)
/proc/robotic_analyze_mob (var/mob/living/M, var/mob/living/user, var/just_scan = FALSE)
/proc/robotic_analyze_mob(var/mob/living/M, var/mob/living/user, var/just_scan = FALSE)
if(!just_scan)
if((user.is_clumsy() || (user.mutations & DUMB)) && prob(50))
to_chat(user, SPAN_WARNING("You try to analyze the floor's vitals!"))
user.visible_message(SPAN_WARNING("\The [user] has analyzed the floor's vitals!"))
to_chat(user, SPAN_NOTICE("Analyzing Results for The floor:"))
to_chat(user, SPAN_NOTICE("Overall Status: Healthy"))
to_chat(user, SPAN_NOTICE("Damage Specifics: [0]-[0]-[0]-[0]"))
to_chat(user, SPAN_NOTICE("Key: Suffocation/Toxin/Burns/Brute"))
to_chat(user, SPAN_NOTICE("Body Temperature: ???"))
return
user.visible_message(SPAN_NOTICE("\The [user] has analyzed \the [M]'s components."), SPAN_NOTICE("You have analyzed \the [M]'s components."))
var/scan_type
if(istype(M, /mob/living/silicon/robot))
if(isrobot(M))
scan_type = "robot"
else if(istype(M, /mob/living/carbon/human))
else if(ishuman(M))
scan_type = "prosthetics"
else
to_chat(user, SPAN_WARNING("You can't analyze non-robotic things!"))
@@ -45,68 +35,102 @@
switch(scan_type)
if("robot")
var/BU = M.getFireLoss() > 50 ? "<b>[M.getFireLoss()]</b>" : M.getFireLoss()
var/BR = M.getBruteLoss() > 50 ? "<b>[M.getBruteLoss()]</b>" : M.getBruteLoss()
to_chat(user, SPAN_NOTICE("Analyzing Results for [M]:"))
to_chat(user, SPAN_NOTICE("Overall Status: [M.stat > 1 ? "fully disabled" : "[M.health - M.getHalLoss()]% functional"]"))
to_chat(user, "Key: <font color='#FFA500'>Electronics</font>/<span class='warning'>Brute</span>")
to_chat(user, "Damage Specifics: <font color='#FFA500'>[BU]</font> - <span class='warning'>[BR]</span>")
if(M.tod && M.stat == DEAD)
to_chat(user, SPAN_NOTICE("Time of Disable: [M.tod]"))
var/mob/living/silicon/robot/H = M
var/list/damaged = H.get_damaged_components(1, 1, 1)
to_chat(user, SPAN_NOTICE("Localized Damage:"))
if(length(damaged) > 0)
for(var/datum/robot_component/org in damaged)
user.show_message(SPAN_NOTICE("\t [capitalize(org.name)]: [(org.installed == -1) ? SPAN_WARNING("<b>DESTROYED</b>") :""]\
[(org.electronics_damage > 0) ? "<font color='#FFA500'>[org.electronics_damage]</font>" : 0] - [(org.brute_damage > 0) ? SPAN_WARNING("[org.brute_damage]") :0] - \
[(org.toggled) ? "Toggled ON" : SPAN_WARNING("Toggled OFF")] - \
[(org.powered) ? "Power ON" : SPAN_WARNING("Power OFF")]"),1)
else
to_chat(user, SPAN_NOTICE("Components are OK."))
if(H.emagged && prob(5))
to_chat(user, SPAN_WARNING("ERROR: INTERNAL SYSTEMS COMPROMISED"))
to_chat(user, SPAN_NOTICE("Operating Temperature: [M.bodytemperature-T0C]&deg;C ([M.bodytemperature*1.8-459.67]&deg;F)"))
robot_scan(user, M)
if("prosthetics")
var/mob/living/carbon/human/H = M
to_chat(user, SPAN_NOTICE("Analyzing Results for \the [H]:"))
to_chat(user, "Key: <font color='#FFA500'>Electronics</font>/<span class='warning'>Brute</span>")
var/obj/item/organ/internal/cell/IC = H.internal_organs_by_name[BP_CELL]
if(IC)
to_chat(user, SPAN_NOTICE("Cell charge: [IC.percent()] %"))
else
to_chat(user, SPAN_NOTICE("Cell charge: ERROR - Cell not present"))
to_chat(user, SPAN_NOTICE("External prosthetics:"))
var/organ_found
if(length(H.internal_organs))
for(var/obj/item/organ/external/E in H.organs)
if(!(E.status & (ORGAN_ROBOT || ORGAN_ASSISTED)))
continue
organ_found = TRUE
to_chat(user, "[E.name]: <span class='warning'>[E.brute_dam]</span> <font color='#FFA500'>[E.burn_dam]</font>")
if(!organ_found)
to_chat(user, SPAN_NOTICE("No prosthetics located."))
to_chat(user, "<hr>")
to_chat(user, SPAN_NOTICE("Internal prosthetics:"))
organ_found = FALSE
if(length(H.internal_organs))
var/obj/item/organ/external/head = H.get_organ(BP_HEAD)
var/show_tag = FALSE
if(head?.open == 3) // Hatch open
show_tag = TRUE
for(var/obj/item/organ/O in H.internal_organs)
if(!(O.status & (ORGAN_ROBOT || ORGAN_ASSISTED)))
continue
if(!show_tag && istype(O, /obj/item/organ/internal/ipc_tag))
continue
organ_found = TRUE
to_chat(user, "[O.name]: <span class='warning'>[O.damage]</span>")
to_chat(user, "<i>[O.desc]</i>")
if(!organ_found)
to_chat(user, SPAN_NOTICE("No prosthetics located."))
prosthetics_scan(user, M)
/proc/robot_scan(mob/user, mob/living/silicon/robot/M)
var/BU = M.getFireLoss() > 50 ? "<b>[get_robot_severity(M.getFireLoss())]</b>" : get_robot_severity(M.getFireLoss())
var/BR = M.getBruteLoss() > 50 ? "<b>[get_robot_severity(M.getBruteLoss())]</b>" : get_robot_severity(M.getBruteLoss())
to_chat(user, SPAN_NOTICE("Analyzing Results for [M]:"))
to_chat(user, SPAN_NOTICE("Overall Status: [M.stat > 1 ? "fully disabled" : "[M.health - M.getHalLoss()]% functional"]"))
to_chat(user, "Key: <font color='#FFA500'>Electronics</font>/<span class='warning'>Physical Damage</span>")
to_chat(user, "Damage Specifics: <font color='#FFA500'>[BU]</font> - <span class='warning'>[BR]</span>")
if(M.tod && M.stat == DEAD)
to_chat(user, SPAN_NOTICE("Time of Disable: [M.tod]"))
var/mob/living/silicon/robot/H = M
var/list/damaged = H.get_damaged_components(1, 1, 1)
to_chat(user, SPAN_NOTICE("Localized Damage:"))
if(length(damaged) > 0)
for(var/datum/robot_component/org in damaged)
user.show_message(SPAN_NOTICE("\t [capitalize(org.name)]: [(org.installed == -1) ? SPAN_WARNING("<b>DESTROYED</b>") :""]\
[(org.electronics_damage > 0) ? "<font color='#FFA500'>[org.electronics_damage]</font>" : 0] - [(org.brute_damage > 0) ? SPAN_WARNING("[org.brute_damage]") :0] - \
[(org.toggled) ? "Toggled ON" : SPAN_WARNING("Toggled OFF")] - \
[(org.powered) ? "Power ON" : SPAN_WARNING("Power OFF")]"),1)
else
to_chat(user, SPAN_NOTICE("Components are OK."))
if(H.emagged && prob(5))
to_chat(user, SPAN_WARNING("ERROR: INTERNAL SYSTEMS COMPROMISED"))
to_chat(user, SPAN_NOTICE("Operating Temperature: [M.bodytemperature-T0C]&deg;C ([M.bodytemperature*1.8-459.67]&deg;F)"))
/proc/prosthetics_scan(mob/user, mob/living/carbon/human/H)
to_chat(user, SPAN_NOTICE("Analyzing Results for \the [H]:"))
if(H.stat == DEAD)
to_chat(user, SPAN_DANGER("No neural coherence detected."))
to_chat(user, "Key: <font color='#FFA500'>Electronics</font>/<span class='warning'>Wiring</span>")
var/obj/item/organ/internal/machine/power_core/IC = H.internal_organs_by_name[BP_CELL]
if(IC)
to_chat(user, SPAN_NOTICE("Cell charge: [IC.percent()] %"))
else
to_chat(user, SPAN_NOTICE("Cell charge: ERROR - Cell not present"))
to_chat(user, SPAN_NOTICE("External prosthetics:"))
var/organ_found
if(length(H.internal_organs))
for(var/obj/item/organ/external/E in H.organs)
if(!(E.status & (ORGAN_ROBOT || ORGAN_ASSISTED)))
continue
organ_found = TRUE
to_chat(user, "[E.name]: <span class='warning'>[get_robot_severity(E.brute_dam)]</span> <font color='#FFA500'>[get_robot_severity(E.burn_dam)]</font>")
if(!organ_found)
to_chat(user, SPAN_NOTICE("No prosthetics located."))
to_chat(user, "<hr>")
to_chat(user, SPAN_NOTICE("Internal prosthetics:"))
organ_found = FALSE
if(length(H.internal_organs))
var/obj/item/organ/external/head = H.get_organ(BP_HEAD)
var/show_tag = FALSE
if(head?.open == 3) // Hatch open
show_tag = TRUE
for(var/obj/item/organ/O in H.internal_organs)
if(!(O.status & (ORGAN_ROBOT || ORGAN_ASSISTED)))
continue
if(!show_tag && istype(O, /obj/item/organ/internal/machine/ipc_tag))
continue
organ_found = TRUE
var/found_damage = FALSE
to_chat(user, SPAN_NOTICE(SPAN_BOLD("[O.name]")))
if(O.damage)
to_chat(user, SPAN_WARNING("Core damage detected."))
found_damage = TRUE
if(istype(O, /obj/item/organ/internal/machine))
var/obj/item/organ/internal/machine/machine_organ = O
if(machine_organ.get_integrity() < 100)
to_chat(user, SPAN_WARNING("<b>[machine_organ.name]</b>: Integrity damage detected."))
found_damage = TRUE
if(!found_damage)
to_chat(user, SPAN_NOTICE("No damage detected."))
if(!organ_found)
to_chat(user, SPAN_NOTICE("No prosthetics located."))
/proc/get_robot_severity(amount, var/uppercase = FALSE)
. = "undamaged"
if(!amount)
. = "undamaged"
else if(amount > 100)
. = "destroyed"
else if(amount > 75)
. = "falling apart"
else if(amount > 50)
. = "heavily compromised"
else if(amount > 25)
. = "problematic"
else if(amount > 10)
. = "fine"
else
. = "minor"
/obj/item/device/robotanalyzer/augment
name = "retractable cyborg analyzer"
@@ -255,7 +255,7 @@
var/mob/living/carbon/human/new_shell = new(get_turf(src), chest.linked_frame)
// replace the IPC's microbattery cell with the one that was in the robot chest
var/obj/item/organ/internal/cell/C = new_shell.internal_organs_by_name[BP_CELL]
var/obj/item/organ/internal/machine/power_core/C = new_shell.internal_organs_by_name[BP_CELL]
C.replace_cell(chest.cell)
//so people won't mess around with the chassis until it is deleted
forceMove(new_shell)
@@ -268,7 +268,7 @@
newname = L.get_random_name()
new_shell.real_name = newname
new_shell.name = new_shell.real_name
var/obj/item/organ/internal/mmi_holder/posibrain/P = new_shell.internal_organs_by_name[BP_BRAIN]
var/obj/item/organ/internal/machine/posibrain/P = new_shell.internal_organs_by_name[BP_BRAIN]
P.setup_brain()
new_shell.change_appearance(APPEARANCE_PLASTICSURGERY, new_shell)
qdel(src)
@@ -207,7 +207,7 @@
if(ishuman(hit_mob))
var/mob/living/carbon/human/H = hit_mob
if(prob(20))
var/obj/item/organ/internal/cell/cell_holder = locate() in H.internal_organs
var/obj/item/organ/internal/machine/power_core/cell_holder = locate() in H.internal_organs
if(cell_holder)
var/obj/item/cell/C = cell_holder.cell
if(C)
@@ -91,7 +91,7 @@
species = SPECIES_IPC
/obj/effect/landmark/corpse/ipc_zombie/do_extra_customization(mob/living/carbon/human/M)
var/obj/item/organ/internal/ipc_tag/tag = M.internal_organs_by_name[BP_IPCTAG]
var/obj/item/organ/internal/machine/ipc_tag/tag = M.internal_organs_by_name[BP_IPCTAG]
if(istype(tag))
tag.serial_number = uppertext(dd_limittext(md5(M.real_name), 12))
tag.ownership_info = IPC_OWNERSHIP_SELF
+9 -1
View File
@@ -55,6 +55,8 @@
var/atom/movable/screen/energy/energy_display = null
var/atom/movable/screen/instability/instability_display = null
var/atom/movable/screen/up_hint = null
/// The IPC version of the fullscreen pain texture.
var/atom/movable/screen/fullscreen/robot_pain
//spells hud icons - this interacts with add_spell and remove_spell
var/list/atom/movable/screen/movable/spell_master/spell_masters = null
@@ -252,11 +254,17 @@
var/list/item_verbs = list()
var/list/shouldnt_see = list() //typecache of objects that this mob shouldn't see in the stat panel. this silliness is needed because of AI alt+click and cult blood runes
var/list/active_genes=list()
var/list/active_genes = list()
var/mob_size = MOB_MEDIUM
/// The icon size width of the mob. Used for langchat resizing.
var/icon_size = 32
/// The weight of the mob. Affects if the mob can be easily lifted or not. Separate from size, as some mobs may be big but not particularly heavy.
var/mob_weight = MOB_WEIGHT_LIGHT
/// The strength of the mob. Affects what kind of mobs can be thrown or carried. By default, does not give any buff.
var/mob_strength = MOB_STRENGTH_NORMAL
var/list/progressbars
var/frozen = FALSE //related to wizard statues, if set to true, life won't process
+1 -1
View File
@@ -443,7 +443,7 @@
to_chat(H, SPAN_WARNING("You can only fireman carry humanoids!"))
return
var/mob/living/carbon/human/affected_human = affecting
if(affected_human.species.mob_size > 25)
if(affected_human.mob_weight > H.get_mob_strength())
to_chat(H, SPAN_WARNING("\The [affected_human] is way too big to fireman carry!"))
return
if(state < GRAB_AGGRESSIVE)
+4
View File
@@ -1357,3 +1357,7 @@ GLOBAL_LIST_INIT(organ_rel_size, list(
message_notifications.Cut(1, 2)
message_notifications[key_check] = world.time + next_message_time
/// Gets a mob's strength.
/mob/proc/get_mob_strength()
return mob_weight + mob_strength
@@ -78,6 +78,18 @@
return
found = TRUE
flashlight = H
else if(istype(H, /obj/item/computer_hardware/universal_port))
if(universal_port)
to_chat(user, SPAN_WARNING("\The [src]'s port slot is already occupied by \the [universal_port]."))
return
found = TRUE
universal_port = H
else if(istype(H, /obj/item/computer_hardware/access_cable_dongle))
if(access_cable_dongle)
to_chat(user, SPAN_WARNING("\The [src]'s port slot is already occupied by \the [access_cable_dongle]."))
return
found = TRUE
access_cable_dongle = H
if(found)
to_chat(user, SPAN_NOTICE("You install \the [H] into \the [src]."))
H.parent_computer = src
@@ -128,6 +140,12 @@
personal_ai.pai.parent_computer = null
update_icon()
personal_ai = null
else if(universal_port == H)
universal_port = null
found = TRUE
else if(access_cable_dongle == H)
access_cable_dongle = null
found = TRUE
if(found)
if(user)
@@ -165,6 +183,10 @@
return tesla_link
if(flashlight && initial(flashlight.name) == name)
return flashlight
if(universal_port && initial(universal_port.name) == name)
return universal_port
if(access_cable_dongle && initial(access_cable_dongle.name) == name)
return access_cable_dongle
return null
// Returns list of all components
@@ -192,4 +214,8 @@
all_components.Add(tesla_link)
if(flashlight)
all_components.Add(flashlight)
if(universal_port)
all_components.Add(universal_port)
if(access_cable_dongle)
all_components.Add(access_cable_dongle)
return all_components
@@ -189,6 +189,14 @@
if(response == "Yes")
turn_on(user)
/obj/item/modular_computer/attack_ranged(mob/user, params)
. = ..()
if(ishuman(user) && isipc(user))
var/mob/living/carbon/human/robot = user
var/obj/item/organ/internal/machine/wireless_access/wireless_access_point = robot.internal_organs_by_name[BP_WIRELESS_ACCESS]
if(wireless_access_point?.access_terminal(src))
attack_hand(user)
/obj/item/modular_computer/attack_hand(var/mob/user)
if(anchored)
return attack_self(user)
@@ -321,6 +329,20 @@
return TRUE
uninstall_component(user, H)
return TRUE
if(istype(attacking_item, /obj/item/access_cable))
var/obj/item/access_cable/access_cable = attacking_item
if(!universal_port)
to_chat(user, SPAN_WARNING("There isn't a port to slot \the [access_cable] into!"))
return
if(universal_port.access_cable)
to_chat(user, SPAN_WARNING("There's already a cable in the universal port!"))
return
if(do_after(user, 1 SECONDS))
visible_message(SPAN_NOTICE("[user] slots \the [access_cable] into \the [src]."))
universal_port.insert_cable(access_cable, user)
return ..()
/obj/item/modular_computer/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
@@ -47,6 +47,8 @@
data["PC_stationdate"] = "[time2text(world.realtime, "DDD, Month DD")], [GLOB.game_year]"
data["PC_showexitprogram"] = !!active_program
data["PC_haslight"] = !!flashlight
data["PC_hascable"] = !!access_cable_dongle
data["PC_cableout"] = access_cable_dongle?.access_cable?.loc == access_cable_dongle ? FALSE : TRUE
data["PC_lighton"] = flashlight?.enabled ? TRUE : FALSE
data["PC_programheaders"] = list()
if(idle_threads.len)
@@ -146,6 +148,13 @@
if(flashlight)
flashlight.toggle()
. = TRUE
if(action == "PC_takecable")
if(access_cable_dongle && access_cable_dongle.access_cable)
if(access_cable_dongle.access_cable.loc == access_cable_dongle)
access_cable_dongle.take_cable(usr)
else
access_cable_dongle.access_cable.retract()
. = TRUE
if(action == "PC_shutdown")
shutdown_computer()
return TRUE
@@ -190,6 +199,11 @@
update_icon()
/obj/item/modular_computer/ui_status(mob/user, datum/ui_state/state)
if(universal_port?.access_cable)
if(istype(universal_port.access_cable.source, /obj/item/organ/internal/machine/access_port))
var/obj/item/organ/internal/machine/access_port/port = universal_port.access_cable.source
if(user == port.owner)
return UI_INTERACTIVE
. = ..()
if(. < UI_INTERACTIVE)
if(user.machine)
@@ -125,6 +125,10 @@
var/obj/item/computer_hardware/tesla_link/tesla_link
/// Personal AI, can control the device via a verb when installed
var/obj/item/device/paicard/personal_ai
/// The universal port. Allows connection from IPCs through access cables.
var/obj/item/computer_hardware/universal_port/universal_port
/// The access cable dongle. Allows the computer to have its very own access cable.
var/obj/item/computer_hardware/access_cable_dongle/access_cable_dongle
var/obj/item/computer_hardware/flashlight/flashlight
var/listener/listener
@@ -15,6 +15,7 @@
hard_drive = new /obj/item/computer_hardware/hard_drive/super(src)
network_card = new /obj/item/computer_hardware/network_card/wired(src)
nano_printer = new /obj/item/computer_hardware/nano_printer(src)
universal_port = new /obj/item/computer_hardware/universal_port(src)
/obj/item/modular_computer/console/preset/install_default_programs()
..()
@@ -140,6 +141,7 @@
/obj/item/modular_computer/console/preset/supply/machinist/install_default_hardware()
..()
ai_slot = new /obj/item/computer_hardware/ai_slot(src)
access_cable_dongle = new /obj/item/computer_hardware/access_cable_dongle(src)
// ERT
/obj/item/modular_computer/console/preset/ert/install_default_hardware()
@@ -18,6 +18,7 @@
nano_printer.max_paper = 10
nano_printer.stored_paper = 5
tesla_link = new /obj/item/computer_hardware/tesla_link/charging_cable(src)
universal_port = new /obj/item/computer_hardware/universal_port(src)
// the laptop in the modular computer loadout
/obj/item/modular_computer/laptop/preset/loadout/install_default_hardware()
@@ -122,6 +123,15 @@
desc = "A portable computer belonging to the operation's manager."
_app_preset_type = /datum/modular_computer_app_presets/supply/om
/obj/item/modular_computer/laptop/preset/supply/robotics
name = "robotics laptop"
desc = "A portable computer with support for specialized robotics software."
_app_preset_type = /datum/modular_computer_app_presets/supply/machinist
/obj/item/modular_computer/laptop/preset/supply/robotics/install_default_hardware()
..()
access_cable_dongle = new /obj/item/computer_hardware/access_cable_dongle(src)
// Representative
/obj/item/modular_computer/laptop/preset/representative
name = "representative's laptop"
@@ -8,6 +8,7 @@
tesla_link = new/obj/item/computer_hardware/tesla_link(src)
hard_drive = new/obj/item/computer_hardware/hard_drive(src)
network_card = new/obj/item/computer_hardware/network_card(src)
universal_port = new /obj/item/computer_hardware/universal_port(src)
/obj/item/modular_computer/telescreen/preset/generic
_app_preset_type = /datum/modular_computer_app_presets/wall_generic
@@ -83,7 +83,6 @@
. = ..()
program_list += list(/datum/computer_file/program/comm, /datum/computer_file/program/records/employment)
/*##########################
BRIDGE CREW PRESETS
##########################*/
@@ -273,7 +272,7 @@
/datum/modular_computer_app_presets/supply/machinist/New()
. = ..()
program_list += list(/datum/computer_file/program/records/medical, /datum/computer_file/program/scanner/science)
program_list += list(/datum/computer_file/program/records/medical, /datum/computer_file/program/scanner/science, /datum/computer_file/program/robotics)
//Machinist is the bastard child of supply/operation, it doesn't have access to shit essentially
program_list -= list(/datum/computer_file/program/civilian/cargocontrol, /datum/computer_file/program/civilian/cargodelivery, /datum/computer_file/program/away_manifest)
@@ -0,0 +1,101 @@
/datum/computer_file/program/robotics
filename = "robotics"
filedesc = "Robotics Interface"
program_icon_state = "ai-fixer-empty"
program_key_icon_state = "teal_key"
extended_desc = "A program made to interface with positronics."
size = 14
requires_access_to_run = PROGRAM_ACCESS_LIST_ONE
required_access_run = list(ACCESS_RESEARCH, ACCESS_ROBOTICS)
required_access_download = list(ACCESS_RESEARCH, ACCESS_ROBOTICS)
available_on_ntnet = FALSE
tgui_id = "RoboticsComputer"
/// The diagnostics module associated with this program.
var/datum/tgui_module/ipc_diagnostic/diagnostic
/datum/computer_file/program/robotics/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(!ishuman(ui.user))
return
var/mob/living/carbon/human/user = ui.user
if(action == "run_diagnostics")
if(computer.access_cable_dongle && computer.access_cable_dongle.access_cable)
var/mob/living/carbon/human/synthetic = computer.access_cable_dongle.access_cable.target
if(istype(user) && istype(synthetic))
ui.user.visible_message(SPAN_NOTICE("[user] begins running a diagnostic scan..."))
if(do_after(user, 3 SECONDS))
diagnostic = new(user, synthetic)
return TRUE
if(action == "open_diagnostic")
if(computer.access_cable_dongle && computer.access_cable_dongle.access_cable)
if(istype(diagnostic))
if(diagnostic.patient != computer.access_cable_dongle.access_cable.target)
to_chat(user, SPAN_WARNING("This diagnostic is no longer valid and has been deleted."))
qdel(diagnostic)
return TRUE
var/mob/living/carbon/human/synthetic = computer.access_cable_dongle.access_cable.target
if(istype(user) && istype(synthetic))
diagnostic.ui_interact(user)
return TRUE
/datum/computer_file/program/robotics/ui_data(mob/user)
var/list/data = list()
var/mob/living/carbon/human/ipc
if(computer.access_cable_dongle && computer.access_cable_dongle.access_cable)
var/obj/item/organ/internal/machine/access_port/port = computer.access_cable_dongle.access_cable.target
if(istype(port))
ipc = port.owner
if(isipc(ipc))
var/datum/species/machine/machine_species = ipc.species // need to manually set to ipc species because of machine_ui_theme
data["patient_name"] = ipc.real_name
data["temp"] = round(convert_k2c(ipc.bodytemperature))
data["machine_ui_theme"] = machine_species.machine_ui_theme
data["organs"] = list()
for(var/obj/item/organ/internal/organ in ipc.internal_organs)
var/list/organ_data = list()
if(istype(organ, /obj/item/organ/internal/machine))
var/obj/item/organ/internal/machine/machine_organ = organ
if(!machine_organ.diagnostics_suite_visible)
continue
organ_data["wiring_status"] = machine_organ.wiring.get_status()
organ_data["plating_status"] = machine_organ.plating.get_status()
organ_data["electronics_status"] = machine_organ.electronics.get_status()
organ_data["diagnostics_info"] = machine_organ.get_diagnostics_info()
organ_data["name"] = organ.name
organ_data["desc"] = organ.desc
organ_data["damage"] = organ.damage
organ_data["max_damage"] = organ.max_damage
data["organs"] += list(organ_data)
data["robolimb_self_repair_cap"] = ROBOLIMB_SELF_REPAIR_CAP
data["limbs"] = list()
for(var/obj/item/organ/external/limb in ipc.organs)
if(limb.brute_dam || limb.burn_dam)
data["limbs"] += list(list("name" = limb.name, "brute_damage" = limb.brute_dam, "burn_damage" = limb.burn_dam, "max_damage" = limb.max_damage))
var/obj/item/organ/internal/machine/power_core/C = ipc.internal_organs_by_name[BP_CELL]
if(C)
data["charge_percent"] = C.percent()
var/datum/component/synthetic_endoskeleton/endoskeleton = ipc.GetComponent(/datum/component/synthetic_endoskeleton)
if(istype(endoskeleton))
data["endoskeleton_damage"] = endoskeleton.damage
data["endoskeleton_max_damage"] = endoskeleton.max_damage
var/datum/component/armor/synthetic/synth_armor = ipc.GetComponent(/datum/component/armor/synthetic)
if(istype(synth_armor))
data["armor_data"] = list()
var/list/armor_damage = synth_armor.get_visible_damage()
for(var/key in armor_damage)
data["armor_data"] += list(list("key" = key, "status" = armor_damage[key]))
return data
@@ -0,0 +1,44 @@
/obj/item/computer_hardware/access_cable_dongle
name = "access cable dongle"
desc = "A dongle from which a positronic-compatible cable can be extended."
critical = FALSE
icon_state = "aislot"
hardware_size = 3
/// The access cable currently inserted into this slot.
var/obj/item/access_cable/access_cable
/obj/item/computer_hardware/access_cable_dongle/Initialize()
. = ..()
access_cable = new(src, src, parent_computer)
/obj/item/computer_hardware/access_cable_dongle/Destroy()
if(access_cable)
access_cable.retract()
QDEL_NULL(access_cable)
return ..()
/obj/item/computer_hardware/access_cable_dongle/proc/take_cable(mob/user)
if(!ishuman(user))
return
var/mob/living/carbon/human/H = user
if(use_check_and_message(H))
return
if(!access_cable)
to_chat(H, SPAN_WARNING("This table does not have an access cable anymore!"))
return
if(H.get_active_hand())
to_chat(H, SPAN_WARNING("You need a free hand to retrieve \the [access_cable]!"))
return
if(access_cable.loc != src)
to_chat(H, SPAN_WARNING("The access cable is already elsewhere!"))
return
H.visible_message(SPAN_NOTICE("[H] retrieves \the [access_cable] from \the [src]."), SPAN_NOTICE("You retrieve \the [access_cable] from \the [src]."))
access_cable.create_cable(H)
H.put_in_active_hand(access_cable)
@@ -1,4 +1,4 @@
/obj/item/computer_hardware/
/obj/item/computer_hardware
name = "Hardware"
desc = "Unknown Hardware."
icon = 'icons/obj/modular_components.dmi'
@@ -0,0 +1,29 @@
/obj/item/computer_hardware/universal_port
name = "universal port"
desc = "A small, universal slot wherein an access cable can be slotted in."
critical = FALSE
icon_state = "aislot"
hardware_size = 3
/// The access cable currently inserted into this slot.
var/obj/item/access_cable/access_cable
/obj/item/computer_hardware/universal_port/Destroy()
if(access_cable)
access_cable.retract()
access_cable = null
return ..()
/obj/item/computer_hardware/universal_port/remove_cable(obj/item/access_cable/cable)
..()
access_cable = null
/obj/item/computer_hardware/universal_port/insert_cable(obj/item/access_cable/cable, mob/user)
. = ..()
access_cable = cable
cable.create_cable(parent_computer)
/obj/item/computer_hardware/universal_port/cable_interact(obj/item/access_cable/cable, mob/user)
. = ..()
if(parent_computer)
parent_computer.attack_self(user)
@@ -0,0 +1,9 @@
/datum/movespeed_modifier/synth_emp
id = "synthemp"
variable = TRUE
multiplicative_slowdown = 1
/datum/movespeed_modifier/endoskeleton
id = "synthendoskeleton"
variable = TRUE
multiplicative_slowdown = 1
@@ -220,6 +220,18 @@
var/modification = tgui_input_list(owner, "Select a Modification", "Organ Modification", O.possible_modifications)
owner.change_organ(organ, modification)
if("organ_preset")
if(can_change(APPEARANCE_PROSTHETICS))
var/organ = tgui_input_list(owner, "Select an Organ", "Organ Presets", valid_organs)
var/obj/item/organ/internal/machine/O = owner.internal_organs_by_name[organ]
var/list/possible_presets = list()
for(var/P in O.organ_presets)
var/singleton/synthetic_organ_preset/preset = GET_SINGLETON(P)
possible_presets[preset.name] = preset
var/singleton/synthetic_organ_preset/chosen_preset = tgui_input_list(owner, "Select a Preset", "Organ Presets", possible_presets)
if(chosen_preset)
O.apply_preset_data(chosen_preset)
/datum/tgui_module/appearance_changer/ui_interact(var/mob/user, var/datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
@@ -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)

Some files were not shown because too many files have changed in this diff Show More