From b58326c8fae68040b9fe1a7da0d755afccfaa73e Mon Sep 17 00:00:00 2001 From: cib Date: Sat, 22 Jun 2013 03:22:20 +0200 Subject: [PATCH] Implemented robot components(organs) Features: - You now have to open up a cyborg after constructing it and insert all the components - Cyborgs have per-component damage rather than total damage. - Too much damage can fry a component, making it non-functional - Components consume power - You can disable a component to save power - Actuator component allows you to move - Camera component allows you to see - Comms component allows you to use :b - Radio component allows you to use radio - The same placeholder icon for all components --- code/game/mecha/mech_fabricator.dm | 7 +- code/game/objects/items/robot/robot_parts.dm | 4 +- .../mob/living/silicon/robot/component.dm | 149 ++++++++++++++++++ code/modules/mob/living/silicon/robot/life.dm | 33 ++-- .../modules/mob/living/silicon/robot/robot.dm | 129 +++++++++++++-- .../mob/living/silicon/robot/robot_damage.dm | 86 ++++++++++ code/modules/mob/living/silicon/say.dm | 8 + 7 files changed, 394 insertions(+), 22 deletions(-) create mode 100644 code/modules/mob/living/silicon/robot/component.dm create mode 100644 code/modules/mob/living/silicon/robot/robot_damage.dm diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm index 56c9f74f49f..973b8de1d61 100644 --- a/code/game/mecha/mech_fabricator.dm +++ b/code/game/mecha/mech_fabricator.dm @@ -44,7 +44,12 @@ /obj/item/robot_parts/l_arm, /obj/item/robot_parts/r_arm, /obj/item/robot_parts/l_leg, - /obj/item/robot_parts/r_leg + /obj/item/robot_parts/r_leg, + /obj/item/robot_parts/robot_component/binary_communication_device, + /obj/item/robot_parts/robot_component/radio, + /obj/item/robot_parts/robot_component/actuator, + /obj/item/robot_parts/robot_component/diagnosis_unit, + /obj/item/robot_parts/robot_component/camera ), "Ripley"=list( /obj/item/mecha_parts/chassis/ripley, diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm index d15e17033f7..aa938a13e55 100644 --- a/code/game/objects/items/robot/robot_parts.dm +++ b/code/game/objects/items/robot/robot_parts.dm @@ -162,7 +162,7 @@ else user << "\blue You need to attach a flash to it first!" - if(istype(W, /obj/item/device/mmi) || istype(W, /obj/item/device/mmi/posibrain)) + if(istype(W, /obj/item/device/mmi)) var/obj/item/device/mmi/M = W if(check_completion()) if(!istype(loc,/turf)) @@ -194,7 +194,7 @@ user << "\red This [W] does not seem to fit." return - var/mob/living/silicon/robot/O = new /mob/living/silicon/robot(get_turf(loc)) + var/mob/living/silicon/robot/O = new /mob/living/silicon/robot(get_turf(loc), unfinished = 1) if(!O) return user.drop_item() diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm new file mode 100644 index 00000000000..b45ea8b0ae3 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/component.dm @@ -0,0 +1,149 @@ +// TODO: remove the robot.mmi and robot.cell variables and completely rely on the robot component system + +/datum/robot_component/var/name +/datum/robot_component/var/installed = 0 +/datum/robot_component/var/powered = 0 +/datum/robot_component/var/toggled = 1 +/datum/robot_component/var/brute_damage = 0 +/datum/robot_component/var/electronics_damage = 0 +/datum/robot_component/var/energy_consumption = 0 +/datum/robot_component/var/max_damage = 30 +/datum/robot_component/var/mob/living/silicon/robot/owner + +// The actual device object that has to be installed for this. +/datum/robot_component/var/external_type = null + +// The wrapped device(e.g. radio), only set if external_type isn't null +/datum/robot_component/var/obj/item/wrapped = null + + +/datum/robot_component/proc/is_functioning() + return brute_damage + electronics_damage < 30 + +/datum/robot_component/New(mob/living/silicon/robot/R) + src.owner = R + +/datum/robot_component/proc/install() +/datum/robot_component/proc/uninstall() + +/datum/robot_component/proc/destroy() + if(wrapped) + del wrapped + + + wrapped = new/obj/item/broken_device + + // The thing itself isn't there anymore, but some fried remains are. + installed = -1 + uninstall() + +/datum/robot_component/proc/take_damage(brute, electronics, sharp) + if(installed != 1) return + + brute_damage += brute + electronics_damage += electronics + + if(brute_damage + electronics_damage > max_damage) destroy() + +/datum/robot_component/proc/heal_damage(brute, electronics) + if(!is_functioning()) + // If it's not functioning, it's beyond repairing without taking it out. + return 0 + + brute_damage = max(0, brute_damage - brute) + electronics_damage = max(0, electronics_damage - electronics) + +/datum/robot_component/proc/is_powered() + return installed == 1 && (!energy_consumption || powered) + + +/datum/robot_component/proc/consume_power() + if(toggled == 0) + powered = 0 + return + if(owner.cell.charge >= energy_consumption) + owner.cell.use(energy_consumption) + powered = 1 + else + powered = 0 + + +/datum/robot_component/actuator + name = "actuator" + energy_consumption = 2 + external_type = /obj/item/robot_parts/robot_component/actuator + max_damage = 60 + +/datum/robot_component/cell + name = "power cell" + max_damage = 60 + +/datum/robot_component/cell/destroy() + ..() + owner.cell = null + +/datum/robot_component/radio + name = "radio" + external_type = /obj/item/robot_parts/robot_component/radio + energy_consumption = 3 + max_damage = 10 + +/datum/robot_component/binary_communication + name = "binary communication device" + external_type = /obj/item/robot_parts/robot_component/binary_communication_device + energy_consumption = 0 + max_damage = 30 + +/datum/robot_component/camera + name = "camera" + external_type = /obj/item/robot_parts/robot_component/camera + energy_consumption = 2 + max_damage = 20 + +/datum/robot_component/diagnosis_unit + name = "self-diagnosis unit" + energy_consumption = 1 + external_type = /obj/item/robot_parts/robot_component/diagnosis_unit + max_damage = 30 + +/mob/living/silicon/robot/proc/initialize_components() + // This only initializes the components, it doesn't set them to installed. + + components["actuator"] = new/datum/robot_component/actuator(src) + components["radio"] = new/datum/robot_component/radio(src) + components["power cell"] = new/datum/robot_component/cell(src) + components["diagnosis unit"] = new/datum/robot_component/diagnosis_unit(src) + components["camera"] = new/datum/robot_component/camera(src) + components["comms"] = new/datum/robot_component/binary_communication(src) + +/mob/living/silicon/robot/proc/is_component_functioning(module_name) + var/datum/robot_component/C = components[module_name] + return C && C.installed == 1 && C.toggled && C.is_powered() + +/obj/item/broken_device + name = "broken component" + icon = 'robot_component.dmi' + icon_state = "broken" + +/obj/item/robot_parts/robot_component + icon = 'robot_component.dmi' + icon_state = "working" + construction_time = 200 + construction_cost = list("metal"=5000) + + +// TODO: actual icons ;) +/obj/item/robot_parts/robot_component/binary_communication_device + name = "binary communication device" + +/obj/item/robot_parts/robot_component/actuator + name = "actuator" + +/obj/item/robot_parts/robot_component/camera + name = "camera" + +/obj/item/robot_parts/robot_component/diagnosis_unit + name = "diagnosis unit" + +/obj/item/robot_parts/robot_component/radio + name = "radio" \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index a4d6cdc7481..11d6bea91b4 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -37,23 +37,25 @@ /mob/living/silicon/robot/proc/use_power() - if (src.cell) + if (is_component_functioning("power cell")) if(src.cell.charge <= 0) uneq_all() src.stat = 1 - else if (src.cell.charge <= 100) - uneq_all() - src.sight_mode = 0 - src.cell.use(1) else if(src.module_state_1) - src.cell.use(5) + src.cell.use(3) if(src.module_state_2) - src.cell.use(5) + src.cell.use(3) if(src.module_state_3) - src.cell.use(5) - src.cell.use(1) - src.blinded = 0 + src.cell.use(3) + + for(var/V in components) + var/datum/robot_component/C = components[V] + C.consume_power() + + if(!is_component_functioning("actuator")) + Paralyse(3) + src.stat = 0 else uneq_all() @@ -128,6 +130,17 @@ src.druggy-- src.druggy = max(0, src.druggy) + if(!is_component_functioning("radio")) + radio.on = 0 + else + radio.on = 1 + + if(is_component_functioning("camera")) + src.blinded = 0 + else + src.blinded = 1 + + return 1 /mob/living/silicon/robot/proc/handle_regular_hud_updates() diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index af99686b006..2386e899781 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -27,6 +27,9 @@ var/obj/item/weapon/cell/cell = null var/obj/machinery/camera/camera = null + // Components are basically robot organs. + var/list/components = list() + var/obj/item/device/mmi/mmi = null var/obj/item/device/pda/ai/rbPDA = null @@ -58,7 +61,7 @@ var/braintype = "Cyborg" var/pose -/mob/living/silicon/robot/New(loc,var/syndie = 0) +/mob/living/silicon/robot/New(loc,var/syndie = 0,var/unfinished = 0) spark_system = new /datum/effect/effect/system/spark_spread() spark_system.set_up(5, 0, src) spark_system.attach(src) @@ -67,12 +70,10 @@ updatename("Default") updateicon() - if(!cell) - cell = new /obj/item/weapon/cell(src) - cell.maxcharge = 7500 - cell.charge = 7500 - if(syndie) + if(!cell) + cell = new /obj/item/weapon/cell(src) + laws = new /datum/ai_laws/antimov() lawupdate = 0 scrambledcodes = 1 @@ -99,6 +100,25 @@ camera.network = list("SS13") if(isWireCut(5)) // 5 = BORG CAMERA camera.status = 0 + + initialize_components() + if(!unfinished) + // Create all the robot parts. + for(var/V in components) if(V != "power cell") + var/datum/robot_component/C = components[V] + C.installed = 1 + C.wrapped = new C.external_type + + if(!cell) + cell = new /obj/item/weapon/cell(src) + cell.maxcharge = 7500 + cell.charge = 7500 + + if(cell) + var/datum/robot_component/cell_component = components["power cell"] + cell_component.wrapped = cell + cell_component.installed = 1 + ..() playsound(loc, 'sound/voice/liveagain.ogg', 75, 1) @@ -381,6 +401,53 @@ viewalerts = 1 src << browse(dat, "window=robotalerts&can_close=0") +/mob/living/silicon/robot/proc/self_diagnosis() + if(!is_component_functioning("diagnosis unit")) + return null + + var/dat = "[src.name] Self-Diagnosis Report\n" + for (var/V in components) + var/datum/robot_component/C = components[V] + dat += "[C.name]
Power consumption[C.energy_consumption]
Brute Damage:[C.brute_damage]
Electronics Damage:[C.electronics_damage]
Powered:[(!C.energy_consumption || C.is_powered()) ? "Yes" : "No"]
Toggled:[ C.toggled ? "Yes" : "No"]

" + + return dat + + +/mob/living/silicon/robot/verb/self_diagnosis_verb() + set category = "Robot Commands" + set name = "Self Diagnosis" + + if(!is_component_functioning("diagnosis unit")) + src << "\red Your self-diagnosis component isn't functioning." + + var/dat = self_diagnosis() + src << browse(dat, "window=robotdiagnosis") + + +/mob/living/silicon/robot/verb/toggle_component() + set category = "Robot Commands" + set name = "Toggle Component" + set desc = "Toggle a component, conserving power." + + var/list/installed_components = list() + for(var/V in components) + if(V == "power cell") continue + var/datum/robot_component/C = components[V] + if(C.installed) + installed_components += V + + var/toggle = input(src, "Which component do you want to toggle?", "Toggle Component") as null|anything in installed_components + if(!toggle) + return + + var/datum/robot_component/C = components[toggle] + if(C.toggled) + C.toggled = 0 + src << "\red You disable [C.name]." + else + C.toggled = 1 + src << "\red You enable [C.name]." + /mob/living/silicon/robot/blob_act() if (stat != 2) adjustBruteLoss(60) @@ -564,6 +631,20 @@ if (istype(W, /obj/item/weapon/handcuffs)) // fuck i don't even know why isrobot() in handcuff code isn't working so this will have to do return + if(opened) // Are they trying to insert something? + for(var/V in components) + var/datum/robot_component/C = components[V] + if(!C.installed && istype(W, C.external_type)) + C.installed = 1 + C.wrapped = W + C.install() + user.drop_item() + W.loc = null + + usr << "\blue You install the [W.name]." + + return + if (istype(W, /obj/item/weapon/weldingtool)) if (!getBruteLoss()) user << "Nothing to fix here!" @@ -609,6 +690,27 @@ C.updateicon() new/obj/item/robot_parts/chest(loc) src.Del() + else + // Okay we're not removing the cell or an MMI, but maybe something else? + var/list/removable_components = list() + for(var/V in components) + if(V == "power cell") continue + var/datum/robot_component/C = components[V] + if(C.installed == 1) + removable_components += V + + var/remove = input(user, "Which component do you want to pry out?", "Remove Component") as null|anything in removable_components + if(!remove) + return + var/datum/robot_component/C = components[remove] + var/obj/item/I = C.wrapped + user << "You remove \the [I]." + I.loc = src.loc + + if(C.installed == 1) + C.uninstall() + C.installed = 0 + else if(locked) user << "The cover is locked and cannot be opened." @@ -618,17 +720,20 @@ updateicon() else if (istype(W, /obj/item/weapon/cell) && opened) // trying to put a cell inside + var/datum/robot_component/C = components["power cell"] if(wiresexposed) user << "Close the panel first." - else if(cell) + else if(cell || C.installed) user << "There is a power cell already installed." else user.drop_item() W.loc = src cell = W user << "You insert the power cell." -// chargecount = 0 - updateicon() + + C.installed = 1 + C.wrapped = W + C.install() else if (istype(W, /obj/item/weapon/wirecutters) || istype(W, /obj/item/device/multitool)) if (wiresexposed) @@ -901,6 +1006,7 @@ add_fingerprint(user) if(opened && !wiresexposed && (!istype(user, /mob/living/silicon))) + var/datum/robot_component/cell_component = components["power cell"] if(cell) cell.updateicon() cell.add_fingerprint(user) @@ -908,6 +1014,11 @@ user << "You remove \the [cell]." cell = null updateicon() + else if(cell_component.installed == -1) + cell_component.installed = 0 + var/obj/item/broken_device = cell_component.wrapped + user << "You remove \the [broken_device]." + user.put_in_active_hand(broken_device) if(ishuman(user)) if(istype(user:gloves, /obj/item/clothing/gloves/space_ninja)&&user:gloves:candrain&&!user:gloves:draining) diff --git a/code/modules/mob/living/silicon/robot/robot_damage.dm b/code/modules/mob/living/silicon/robot/robot_damage.dm new file mode 100644 index 00000000000..cc949fa7689 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/robot_damage.dm @@ -0,0 +1,86 @@ +/mob/living/silicon/robot/getBruteLoss() + var/amount = 0 + for(var/V in components) + var/datum/robot_component/C = components[V] + amount += C.brute_damage + return amount + +/mob/living/silicon/robot/getFireLoss() + var/amount = 0 + for(var/V in components) + var/datum/robot_component/C = components[V] + amount += C.electronics_damage + return amount + + +/mob/living/silicon/robot/adjustBruteLoss(var/amount) + if(amount > 0) + take_overall_damage(amount, 0) + else + heal_overall_damage(-amount, 0) + +/mob/living/silicon/robot/adjustFireLoss(var/amount) + if(amount > 0) + take_overall_damage(0, amount) + else + heal_overall_damage(0, -amount) + +/mob/living/silicon/robot/proc/get_damaged_components(var/brute, var/burn) + var/list/datum/robot_component/parts = list() + for(var/V in components) + var/datum/robot_component/C = components[V] + if((brute && C.brute_damage) || (burn && C.electronics_damage)) + parts += C + return parts + +/mob/living/silicon/robot/proc/get_damageable_components() + var/list/rval = new + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed == 1) rval += C + return rval + +/mob/living/silicon/robot/heal_organ_damage(var/brute, var/burn) + var/list/datum/robot_component/parts = get_damaged_components(brute,burn) + if(!parts.len) return + var/datum/robot_component/picked = pick(parts) + picked.heal_damage(brute,burn) + +/mob/living/silicon/robot/take_organ_damage(var/brute, var/burn, var/sharp = 0) + var/datum/robot_component/C = pick(get_damageable_components()) + C.take_damage(brute,burn,sharp) + +/mob/living/silicon/robot/heal_overall_damage(var/brute, var/burn) + var/list/datum/robot_component/parts = get_damaged_components(brute,burn) + + log_debug("Healing [brute] brute and [burn] burn with [parts.len] parts") + while(parts.len && (brute>0 || burn>0) ) + var/datum/robot_component/picked = pick(parts) + + var/brute_was = picked.brute_damage + var/burn_was = picked.electronics_damage + + log_debug("Healing [picked.name]") + picked.heal_damage(brute,burn) + log_debug("[brute] brute and [burn] burn left to heal") + + brute -= (brute_was-picked.brute_damage) + burn -= (burn_was-picked.electronics_damage) + + parts -= picked + +/mob/living/silicon/robot/take_overall_damage(var/brute, var/burn, var/sharp = 0, var/used_weapon = null) + if(status_flags & GODMODE) return //godmode + var/list/datum/robot_component/parts = get_damageable_components() + while(parts.len && (brute>0 || burn>0) ) + var/datum/robot_component/picked = pick(parts) + + var/brute_was = picked.brute_damage + var/burn_was = picked.electronics_damage + + picked.take_damage(brute,burn) + + brute -= (picked.brute_damage - brute_was) + burn -= (picked.electronics_damage - burn_was) + + parts -= picked \ No newline at end of file diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm index 81a85f7a5d8..6ef24637b9c 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -24,6 +24,14 @@ return ..(message) message = copytext(message, 3) message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) + + // TODO: move the component system up to silicon so we don't have to use this ugly hack.. + if(istype(src, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = src + if(!R.is_component_functioning("comms")) + src << "\red Your binary communications component isn't functional." + return + robot_talk(message) else if (department_radio_keys[prefix] == "department") if(isAI(src)&&client)//For patching directly into AI holopads.