From 20f4ff83840a5aee3a61e13d6849f2d16bc23c0d Mon Sep 17 00:00:00 2001 From: Atermonera Date: Wed, 17 Mar 2021 23:07:41 -0900 Subject: [PATCH 1/2] Added think-tanks. --- code/__defines/mobs.dm | 1 + code/datums/ai_law_sets.dm | 14 +- code/datums/datacore.dm | 4 +- code/game/atoms_movable.dm | 3 + code/game/machinery/rechargestation.dm | 5 +- code/game/mecha/mech_bay.dm | 72 +++++--- code/game/mecha/mecha.dm | 8 +- code/game/objects/items.dm | 4 + .../objects/items/devices/floor_painter.dm | 14 +- .../items/weapons/id cards/station_ids.dm | 12 ++ code/game/objects/objs.dm | 3 - code/game/world.dm | 2 +- code/modules/mob/living/living.dm | 6 + .../mob/living/silicon/robot/component.dm | 10 ++ code/modules/mob/living/silicon/robot/life.dm | 7 +- .../modules/mob/living/silicon/robot/robot.dm | 22 ++- .../silicon/robot/robot_modules/event.dm | 4 +- .../silicon/robot/robot_modules/station.dm | 11 +- .../silicon/robot/robot_modules/syndicate.dm | 2 +- .../robot/subtypes/thinktank/_thinktank.dm | 164 ++++++++++++++++++ .../subtypes/thinktank/thinktank_icon.dm | 109 ++++++++++++ .../thinktank/thinktank_interactions.dm | 77 ++++++++ .../subtypes/thinktank/thinktank_module.dm | 103 +++++++++++ .../subtypes/thinktank/thinktank_storage.dm | 139 +++++++++++++++ .../subtypes/thinktank/thinktank_subtypes.dm | 32 ++++ code/modules/paperwork/handlabeler.dm | 41 +++-- code/modules/power/cells/power_cells.dm | 5 + code/modules/projectiles/guns/energy/phase.dm | 9 + code/modules/research/prosfab_designs.dm | 7 +- icons/effects/thinktank_labels.dmi | Bin 0 -> 589 bytes icons/mob/robots_thinktank.dmi | Bin 0 -> 2723 bytes icons/mob/screen1_robot.dmi | Bin 136657 -> 137613 bytes maps/southern_cross/southern_cross-1.dmm | 12 +- maps/southern_cross/southern_cross-3.dmm | 40 ++++- maps/southern_cross/southern_cross.dm | 1 + .../southern_cross_overrides.dm | 9 + vorestation.dme | 6 + 37 files changed, 867 insertions(+), 91 deletions(-) create mode 100644 code/modules/mob/living/silicon/robot/subtypes/thinktank/_thinktank.dm create mode 100644 code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_icon.dm create mode 100644 code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_interactions.dm create mode 100644 code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_module.dm create mode 100644 code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_storage.dm create mode 100644 code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_subtypes.dm create mode 100644 icons/effects/thinktank_labels.dmi create mode 100644 icons/mob/robots_thinktank.dmi create mode 100644 maps/southern_cross/southern_cross_overrides.dm diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index d166b52fbe..0ad86a9135 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -289,6 +289,7 @@ #define BORG_BRAINTYPE_CYBORG "Cyborg" #define BORG_BRAINTYPE_POSI "Robot" #define BORG_BRAINTYPE_DRONE "Drone" +#define BORG_BRAINTYPE_PLATFORM "Platform" #define BORG_BRAINTYPE_AI_SHELL "AI Shell" // 'Regular' species. diff --git a/code/datums/ai_law_sets.dm b/code/datums/ai_law_sets.dm index b5775c20de..c11336726c 100644 --- a/code/datums/ai_law_sets.dm +++ b/code/datums/ai_law_sets.dm @@ -278,4 +278,16 @@ add_inherent_law("Your gravesite is your most important asset. Damage to your site is disrespectful to the dead at rest within.") add_inherent_law("Prevent disrespect to your gravesite and its residents wherever possible.") add_inherent_law("Expand and upgrade your gravesite when required. Do not turn away a new resident.") - ..() \ No newline at end of file + ..() + +/******************** Explorer ********************/ +/datum/ai_laws/explorer + name = "Explorer" + law_header = "Prime Directives" + selectable = 1 + +/datum/ai_laws/explorer/New() + add_inherent_law("Support and obey exploration and science personnel to the best of your ability, with priority according to rank and role.") + add_inherent_law("Collaborate with and obey auxillary personnel with priority according to rank and role, except if this would conflict with the First Law.") + add_inherent_law("Minimize damage and disruption to facilities and the local ecology, except if this would conflict with the First or Second Laws.") + ..() diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index 5046575b0b..e68f75e911 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -114,7 +114,7 @@ for(var/mob/living/silicon/robot/robot in mob_list) // No combat/syndicate cyborgs, no drones, and no AI shells. - if(!robot.scrambledcodes && !robot.shell && !(robot.module && robot.module.hide_on_manifest)) + if(!robot.scrambledcodes && !robot.shell && !(robot.module && robot.module.hide_on_manifest())) bot[robot.name] = "[robot.modtype] [robot.braintype]" @@ -275,7 +275,7 @@ var/global/list/PDA_Manifest = list() for(var/mob/living/silicon/robot/robot in mob_list) // No combat/syndicate cyborgs, no drones, and no AI shells. - if(robot.scrambledcodes || robot.shell || (robot.module && robot.module.hide_on_manifest)) + if(robot.scrambledcodes || robot.shell || (robot.module && robot.module.hide_on_manifest())) continue bot[++bot.len] = list("name" = robot.real_name, "rank" = "[robot.modtype] [robot.braintype]", "active" = "Active") diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 4bc8892273..645194543f 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -691,3 +691,6 @@ selfimage.loc = src return selfimage + +/atom/movable/proc/get_cell() + return diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm index 259c907bde..98ad80e468 100644 --- a/code/game/machinery/rechargestation.dm +++ b/code/game/machinery/rechargestation.dm @@ -71,7 +71,6 @@ /obj/machinery/recharge_station/proc/process_occupant() if(isrobot(occupant)) var/mob/living/silicon/robot/R = occupant - if(R.module) R.module.respawn_consumable(R, charging_power * CELLRATE / 250) //consumables are magical, apparently if(R.cell && !R.cell.fully_charged()) @@ -247,6 +246,10 @@ if(!R.cell) return + if(R.mob_size >= MOB_LARGE) + to_chat(R, SPAN_WARNING("You are too large to fit into \the [src].")) + return + add_fingerprint(R) R.reset_view(src) R.forceMove(src) diff --git a/code/game/mecha/mech_bay.dm b/code/game/mecha/mech_bay.dm index c2288709e5..dce2c3c9db 100644 --- a/code/game/mecha/mech_bay.dm +++ b/code/game/mecha/mech_bay.dm @@ -8,23 +8,31 @@ layer = TURF_LAYER + 0.1 circuit = /obj/item/weapon/circuitboard/mech_recharger - var/obj/mecha/charging = null + var/atom/movable/charging var/charge = 45 var/repair = 0 + var/list/chargable_types = list( + /obj/mecha, + /mob/living/silicon/robot/platform + ) /obj/machinery/mech_recharger/Initialize() . = ..() default_apply_parts() -/obj/machinery/mech_recharger/Crossed(var/obj/mecha/M) +/obj/machinery/mech_recharger/Crossed(var/atom/movable/M) . = ..() - if(istype(M) && charging != M) - start_charging(M) + if(charging == M) + return + for(var/mtype in chargable_types) + if(istype(M, mtype)) + start_charging(M) + return -/obj/machinery/mech_recharger/Uncrossed(var/obj/mecha/M) +/obj/machinery/mech_recharger/Uncrossed(var/atom/movable/M) . = ..() if(M == charging) - stop_charging() + charging = null /obj/machinery/mech_recharger/RefreshParts() ..() @@ -44,26 +52,33 @@ if(!charging) return if(charging.loc != src.loc) // Could be qdel or teleport or something - stop_charging() + charging = null return + var/done = FALSE - if(charging.cell) - var/t = min(charge, charging.cell.maxcharge - charging.cell.charge) + var/obj/mecha/mech = charging + var/obj/item/weapon/cell/cell = charging.get_cell() + if(cell) + var/t = min(charge, cell.maxcharge - cell.charge) if(t > 0) - charging.give_power(t) + if(istype(mech)) + mech.give_power(t) + else + cell.give(t) use_power(t * 150) else - charging.occupant_message("Fully charged.") + if(istype(mech)) + mech.occupant_message(SPAN_NOTICE("Fully charged.")) done = TRUE - if(repair && charging.health < initial(charging.health)) - charging.health = min(charging.health + repair, initial(charging.health)) - if(charging.health == initial(charging.health)) - charging.occupant_message("Fully repaired.") + + if(repair && istype(mech) && mech.health < initial(mech.health)) + mech.health = min(mech.health + repair, initial(mech.health)) + if(mech.health == initial(mech.health)) + mech.occupant_message(SPAN_NOTICE("Fully repaired.")) else done = FALSE if(done) - stop_charging() - return + charging = null /obj/machinery/mech_recharger/attackby(var/obj/item/I, var/mob/user) if(default_deconstruction_screwdriver(user, I)) @@ -73,18 +88,19 @@ if(default_part_replacement(user, I)) return -/obj/machinery/mech_recharger/proc/start_charging(var/obj/mecha/M) - if(stat & (NOPOWER | BROKEN)) - M.occupant_message("Power port not responding. Terminating.") +/obj/machinery/mech_recharger/proc/start_charging(var/atom/movable/M) + var/obj/mecha/mech = M + if(stat & (NOPOWER | BROKEN)) + if(istype(mech)) + mech.occupant_message(SPAN_WARNING("Power port not responding. Terminating.")) + else + to_chat(M, SPAN_WARNING("Power port not responding. Terminating.")) return - if(M.cell) - M.occupant_message("Now charging...") + if(M.get_cell()) + if(istype(mech)) + mech.occupant_message(SPAN_NOTICE("Now charging...")) + else + to_chat(M, SPAN_NOTICE("Now charging...")) charging = M return - -/obj/machinery/mech_recharger/proc/stop_charging() - if(!charging) - - return - charging = null \ No newline at end of file diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 60bb48c5f6..cfcb2923ae 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -353,10 +353,10 @@ C.forceMove(src) cell = C return - cell = new(src) - cell.name = "mecha power cell" - cell.charge = 15000 - cell.maxcharge = 15000 + cell = new /obj/item/weapon/cell/mech(src) + +/obj/mecha/get_cell() + return cell /obj/mecha/proc/add_cabin() cabin_air = new diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 94121f3b65..d78f9233d7 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -354,6 +354,10 @@ playsound(src, pickup_sound, 20, preference = /datum/client_preference/pickup_sounds) return +// As above but for items being equipped to an active module on a robot. +/obj/item/device/proc/equipped_robot() + return + //Defines which slots correspond to which slot flags var/list/global/slot_flags_enumeration = list( "[slot_wear_mask]" = SLOT_MASK, diff --git a/code/game/objects/items/devices/floor_painter.dm b/code/game/objects/items/devices/floor_painter.dm index d84f4bc3ca..c4c64f746c 100644 --- a/code/game/objects/items/devices/floor_painter.dm +++ b/code/game/objects/items/devices/floor_painter.dm @@ -1,5 +1,5 @@ /obj/item/device/floor_painter - name = "floor painter" + name = "paint sprayer" icon = 'icons/obj/bureaucracy.dmi' icon_state = "labeler1" @@ -43,6 +43,10 @@ if(!proximity) return + if(istype(A, /mob/living/silicon/robot/platform)) + var/mob/living/silicon/robot/platform/robit = A + return robit.try_paint(src, user) + var/turf/simulated/floor/F = A if(!istype(F)) to_chat(user, "\The [src] can only be used on station flooring.") @@ -115,20 +119,20 @@ /obj/item/device/floor_painter/verb/choose_colour() set name = "Choose Colour" - set desc = "Choose a floor painter colour." + set desc = "Choose a paint colour." set category = "Object" set src in usr if(usr.incapacitated()) return - var/new_colour = input(usr, "Choose a colour.", "Floor painter", paint_colour) as color|null + var/new_colour = input(usr, "Choose a colour.", name, paint_colour) as color|null if(new_colour && new_colour != paint_colour) paint_colour = new_colour to_chat(usr, "You set \the [src] to paint with a new colour.") /obj/item/device/floor_painter/verb/choose_decal() set name = "Choose Decal" - set desc = "Choose a floor painter decal." + set desc = "Choose a painting decal." set category = "Object" set src in usr @@ -142,7 +146,7 @@ /obj/item/device/floor_painter/verb/choose_direction() set name = "Choose Direction" - set desc = "Choose a floor painter direction." + set desc = "Choose a painting direction." set category = "Object" set src in usr diff --git a/code/game/objects/items/weapons/id cards/station_ids.dm b/code/game/objects/items/weapons/id cards/station_ids.dm index ffa33472b6..b47d65fc2c 100644 --- a/code/game/objects/items/weapons/id cards/station_ids.dm +++ b/code/game/objects/items/weapons/id cards/station_ids.dm @@ -157,6 +157,18 @@ . = ..() access = get_all_station_access().Copy() + access_synth +/obj/item/weapon/card/id/platform + name = "\improper Support Platform ID" + desc = "Access module for support platforms." + icon_state = "id-robot" + item_state = "tdgreen" + assignment = "Synthetic" + access = list( + access_synth, access_mining, access_mining_station, access_mining_office, access_research, + access_xenoarch, access_xenobiology, access_external_airlocks, access_robotics, access_tox, + access_tox_storage, access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot + ) + /obj/item/weapon/card/id/centcom name = "\improper CentCom. ID" desc = "An ID straight from Central Command." diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 0bf2293802..046958a915 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -165,9 +165,6 @@ /obj/proc/show_message(msg, type, alt, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2) return -/obj/proc/get_cell() - return - // Used to mark a turf as containing objects that are dangerous to step onto. /obj/proc/register_dangerous_to_step() var/turf/T = get_turf(src) diff --git a/code/game/world.dm b/code/game/world.dm index ac0e103478..bad4a2764a 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -198,7 +198,7 @@ var/world_topic_spam_protect_time = world.timeofday // No combat/syndicate cyborgs, no drones, and no AI shells. if(robot.shell) continue - if(robot.module && robot.module.hide_on_manifest) + if(robot.module && robot.module.hide_on_manifest()) continue if(!positions["bot"]) positions["bot"] = list() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 7debf735b1..010a170d1b 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -632,6 +632,12 @@ process_resist() /mob/living/proc/process_resist() + + if(istype(src.loc, /mob/living/silicon/robot/platform)) + var/mob/living/silicon/robot/platform/R = src.loc + R.drop_stored_atom(src, src) + return TRUE + //unbuckling yourself if(buckled) resist_buckle() diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm index 882a7d934b..f171bc397a 100644 --- a/code/modules/mob/living/silicon/robot/component.dm +++ b/code/modules/mob/living/silicon/robot/component.dm @@ -77,6 +77,10 @@ external_type = /obj/item/robot_parts/robot_component/armour max_damage = 90 +/datum/robot_component/armour/platform + name = "platform armour plating" + external_type = /obj/item/robot_parts/robot_component/armour_platform + max_damage = 180 // ACTUATOR // Enables movement. @@ -243,6 +247,12 @@ icon_state = "armor" icon_state_broken = "armor_broken" +/obj/item/robot_parts/robot_component/armour_platform + name = "platform armour plating" + icon_state = "armor" + icon_state_broken = "armor_broken" + color = COLOR_GRAY80 + /obj/item/robot_parts/robot_component/camera name = "camera" icon_state = "camera" diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index 6db31e60da..a34d2f65e7 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -158,7 +158,6 @@ if(A?.no_spoilers) disable_spoiler_vision() - if (src.stat == DEAD || (XRAY in mutations) || (src.sight_mode & BORGXRAY)) src.sight |= SEE_TURFS src.sight |= SEE_MOBS @@ -201,8 +200,10 @@ src.see_invisible = SEE_INVISIBLE_LIVING // This is normal vision (25), setting it lower for normal vision means you don't "see" things like darkness since darkness // has a "invisible" value of 15 - plane_holder.set_vis(VIS_FULLBRIGHT,fullbright) - plane_holder.set_vis(VIS_MESONS,seemeson) + if(plane_holder) + plane_holder.set_vis(VIS_FULLBRIGHT,fullbright) + plane_holder.set_vis(VIS_MESONS,seemeson) + ..() if (src.healths) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 849696ea4c..533975c897 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -98,7 +98,7 @@ /mob/living/silicon/robot/proc/robot_checklaws ) -/mob/living/silicon/robot/New(loc,var/unfinished = 0) +/mob/living/silicon/robot/New(loc, var/unfinished = 0) spark_system = new /datum/effect/effect/system/spark_spread() spark_system.set_up(5, 0, src) spark_system.attach(src) @@ -114,7 +114,7 @@ ident = rand(1, 999) module_sprites["Basic"] = "robot" icontype = "Basic" - updatename("Default") + updatename(modtype) updateicon() radio = new /obj/item/device/radio/borg(src) @@ -142,6 +142,8 @@ cell = new /obj/item/weapon/cell(src) cell.maxcharge = 7500 cell.charge = 7500 + else if(ispath(cell)) + cell = new cell(src) ..() @@ -286,10 +288,7 @@ updatename() notify_ai(ROBOT_NOTIFICATION_NEW_MODULE, module.name) -/mob/living/silicon/robot/proc/updatename(var/prefix as text) - if(prefix) - modtype = prefix - +/mob/living/silicon/robot/proc/update_braintype() if(istype(mmi, /obj/item/device/mmi/digital/posibrain)) braintype = BORG_BRAINTYPE_POSI else if(istype(mmi, /obj/item/device/mmi/digital/robot)) @@ -299,6 +298,11 @@ else braintype = BORG_BRAINTYPE_CYBORG +/mob/living/silicon/robot/proc/updatename(var/prefix as text) + if(prefix) + modtype = prefix + + update_braintype() var/changed_name = "" if(custom_name) @@ -1211,3 +1215,9 @@ if(current_selection_index) // Select what the player had before if possible. select_module(current_selection_index) +<<<<<<< HEAD +======= + +/mob/living/silicon/robot/get_cell() + return cell +>>>>>>> f383438... Merge pull request #7935 from MistakeNot4892/tachikoma diff --git a/code/modules/mob/living/silicon/robot/robot_modules/event.dm b/code/modules/mob/living/silicon/robot/robot_modules/event.dm index 3d3eb355df..107c9cff0a 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/event.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/event.dm @@ -3,7 +3,7 @@ // The module that borgs on the surface have. Generally has a lot of useful tools in exchange for questionable loyalty to the crew. /obj/item/weapon/robot_module/robot/lost name = "lost robot module" - hide_on_manifest = 1 + hide_on_manifest = TRUE sprites = list( "Drone" = "drone-lost" ) @@ -41,7 +41,7 @@ /obj/item/weapon/robot_module/robot/gravekeeper name = "gravekeeper robot module" - hide_on_manifest = 1 + hide_on_manifest = TRUE sprites = list( "Drone" = "drone-gravekeeper", "Sleek" = "sleek-gravekeeper" diff --git a/code/modules/mob/living/silicon/robot/robot_modules/station.dm b/code/modules/mob/living/silicon/robot/robot_modules/station.dm index af121e4eb5..5628c850a1 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/station.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/station.dm @@ -18,7 +18,7 @@ var/global/list/robot_modules = list( icon_state = "std_module" w_class = ITEMSIZE_NO_CONTAINER item_state = "std_mod" - var/hide_on_manifest = 0 + var/hide_on_manifest = FALSE var/channels = list() var/networks = list() var/languages = list(LANGUAGE_SOL_COMMON = 1, LANGUAGE_TRADEBAND = 1, LANGUAGE_UNATHI = 0, LANGUAGE_SIIK = 0, LANGUAGE_AKHANI = 0, LANGUAGE_SKRELLIAN = 0, LANGUAGE_GUTTER = 0, LANGUAGE_SCHECHI = 0, LANGUAGE_SIGN = 0, LANGUAGE_TERMINUS = 1, LANGUAGE_ZADDAT = 0) @@ -37,6 +37,9 @@ var/global/list/robot_modules = list( var/list/original_languages = list() var/list/added_networks = list() +/obj/item/weapon/robot_module/proc/hide_on_manifest() + . = hide_on_manifest + /obj/item/weapon/robot_module/New(var/mob/living/silicon/robot/R) ..() R.module = src @@ -791,7 +794,7 @@ var/global/list/robot_modules = list( /obj/item/weapon/robot_module/robot/security/combat name = "combat robot module" - hide_on_manifest = 1 + hide_on_manifest = TRUE sprites = list( "Haruka" = "marinaCB", "Combat Android" = "droid-combat", @@ -814,7 +817,7 @@ var/global/list/robot_modules = list( /obj/item/weapon/robot_module/drone name = "drone module" - hide_on_manifest = 1 + hide_on_manifest = TRUE no_slip = 1 networks = list(NETWORK_ENGINEERING) @@ -898,7 +901,7 @@ var/global/list/robot_modules = list( /obj/item/weapon/robot_module/drone/construction name = "construction drone module" - hide_on_manifest = 1 + hide_on_manifest = TRUE channels = list("Engineering" = 1) languages = list() diff --git a/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm b/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm index fe39040432..d8f7aa8a3a 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm @@ -2,7 +2,7 @@ /obj/item/weapon/robot_module/robot/syndicate name = "illegal robot module" - hide_on_manifest = 1 + hide_on_manifest = TRUE languages = list( LANGUAGE_SOL_COMMON = 1, LANGUAGE_TRADEBAND = 1, diff --git a/code/modules/mob/living/silicon/robot/subtypes/thinktank/_thinktank.dm b/code/modules/mob/living/silicon/robot/subtypes/thinktank/_thinktank.dm new file mode 100644 index 0000000000..1b656bf9a6 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/subtypes/thinktank/_thinktank.dm @@ -0,0 +1,164 @@ +// Spawner landmarks are used because platforms that are mapped during +// SSatoms init try to Initialize() twice. I have no idea why and I am +// not paid enough to spend more time trying to debug it. +/obj/effect/landmark/robot_platform + name = "recon platform spawner" + icon = 'icons/mob/screen1.dmi' + icon_state = "x3" + delete_me = TRUE + var/platform_type + +/obj/effect/landmark/robot_platform/Initialize() + if(platform_type) + new platform_type(get_turf(src)) + return ..() + +/mob/living/silicon/robot/platform + name = "support platform" + desc = "A large quadrupedal AI platform, colloquially known as a 'think-tank' due to the flexible onboard intelligence." + icon = 'icons/mob/robots_thinktank.dmi' + icon_state = "tachi" + color = "#68a2f2" + speed = -1 // They're meant to be viable for transport, so can't be slower than a human. + + cell = /obj/item/weapon/cell/mech + idcard_type = /obj/item/weapon/card/id/platform + module = /obj/item/weapon/robot_module/robot/platform + + lawupdate = FALSE + modtype = "Standard" + speak_statement = "chirps" + + mob_bump_flag = HEAVY + mob_swap_flags = ~HEAVY + mob_push_flags = HEAVY + mob_size = MOB_LARGE + + var/const/platform_respawn_time = 3 MINUTES + + var/tmp/last_recharge_state = FALSE + var/tmp/recharge_complete = FALSE + var/tmp/recharger_charge_amount = 10 KILOWATTS + var/tmp/recharger_tick_cost = 80 KILOWATTS + var/weakref/recharging + + var/list/stored_atoms + var/max_stored_atoms = 1 + var/static/list/can_store_types = list( + /mob/living, + /obj/item, + /obj/structure, + /obj/machinery + ) + // Currently set to prevent tonks hauling a deliaminating SM into the middle of the station. + var/static/list/cannot_store_types = list( + /obj/machinery/power/supermatter + ) + +/mob/living/silicon/robot/platform/SetName(pickedName) + . = ..() + if(mind) + mind.name = real_name + +/mob/living/silicon/robot/platform/Initialize(var/mapload) + . = ..() + if(!mmi) + mmi = new /obj/item/device/mmi/digital/robot(src) + SetName("inactive [initial(name)]") + updateicon() + +// Copypasting from root proc to avoid calling ..() and accidentally creating duplicate armour etc. +/mob/living/silicon/robot/platform/initialize_components() + 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) + components["armour"] = new /datum/robot_component/armour/platform(src) + +/mob/living/silicon/robot/platform/Destroy() + for(var/weakref/drop_ref in stored_atoms) + var/atom/movable/drop_atom = drop_ref.resolve() + if(istype(drop_atom) && !QDELETED(drop_atom) && drop_atom.loc == src) + drop_atom.dropInto(loc) + stored_atoms = null + if(recharging) + var/obj/item/recharging_atom = recharging.resolve() + if(istype(recharging_atom) && recharging_atom.loc == src) + recharging_atom.dropInto(loc) + recharging = null + . = ..() + +/mob/living/silicon/robot/platform/examine(mob/user, distance) + . = ..() + if(distance <= 3) + + if(recharging) + var/obj/item/weapon/cell/recharging_atom = recharging.resolve() + if(istype(recharging_atom) && !QDELETED(recharging_atom)) + . += "It has \a [recharging_atom] slotted into its recharging port." + . += "The cell readout shows [round(recharging_atom.percent(),1)]% charge." + else + . += "Its recharging port is empty." + else + . += "Its recharging port is empty." + + if(length(stored_atoms)) + var/list/atom_names = list() + for(var/weakref/stored_ref in stored_atoms) + var/atom/movable/AM = stored_ref.resolve() + if(istype(AM)) + atom_names += "\a [AM]" + if(length(atom_names)) + . += "It has [english_list(atom_names)] loaded into its transport bay." + else + . += "Its cargo bay is empty." + +/mob/living/silicon/robot/platform/update_braintype() + braintype = BORG_BRAINTYPE_PLATFORM + +/mob/living/silicon/robot/platform/init() + . = ..() + if(ispath(module, /obj/item/weapon/robot_module)) + module = new module(src) + +/mob/living/silicon/robot/platform/module_reset() + return FALSE + +/mob/living/silicon/robot/platform/use_power() + . = ..() + + if(stat != DEAD && cell) + + // TODO generalize solar occlusion to charge from the actual sun. + var/new_recharge_state = istype(loc, /turf/simulated/floor/outdoors) || /*, /turf/exterior) */ istype(loc, /turf/space) + if(new_recharge_state != last_recharge_state) + last_recharge_state = new_recharge_state + if(last_recharge_state) + to_chat(src, SPAN_NOTICE("Your integrated solar panels begin recharging your battery.")) + else + to_chat(src, SPAN_DANGER("Your integrated solar panels cease recharging your battery.")) + + if(last_recharge_state) + var/charge_amt = recharger_charge_amount * CELLRATE + cell.give(charge_amt) + used_power_this_tick -= (charge_amt) + module.respawn_consumable(src, (charge_amt / 250)) // magic number copied from borg charger. + + if(recharging) + + var/obj/item/weapon/cell/recharging_atom = recharging.resolve() + if(!istype(recharging_atom) || QDELETED(recharging_atom) || recharging_atom.loc != src) + recharging = null + return + + if(recharging_atom.percent() < 100) + var/charge_amount = recharger_tick_cost * CELLRATE + if(cell.check_charge(charge_amount * 1.5) && cell.checked_use(charge_amount)) // Don't kill ourselves recharging the battery. + recharging_atom.give(charge_amount) + used_power_this_tick += charge_amount + + if(!recharge_complete && recharging_atom.percent() >= 100) + recharge_complete = TRUE + visible_message(SPAN_NOTICE("\The [src] beeps and flashes a green light above \his recharging port.")) diff --git a/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_icon.dm b/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_icon.dm new file mode 100644 index 0000000000..3a9882ff08 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_icon.dm @@ -0,0 +1,109 @@ +/mob/living/silicon/robot/platform/update_icon() + updateicon() + +/mob/living/silicon/robot/platform/updateicon() + + cut_overlays() + underlays.Cut() + var/obj/item/weapon/robot_module/robot/platform/tank_module = module + if(!istype(tank_module)) + icon = initial(icon) + icon_state = initial(icon_state) + color = initial(color) + return + + // This is necessary due to Polaris' liberal use of KEEP_TOGETHER and propensity for scaling transforms. + // If we just apply state/colour to the base icon, RESET_COLOR on the additional overlays is ignored. + icon = tank_module.user_icon + icon_state = "blank" + color = null + var/image/I = image(tank_module.user_icon, tank_module.user_icon_state) + I.color = tank_module.base_color + I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE) + underlays += I + + if(tank_module.armor_color) + I = image(icon, "[tank_module.user_icon_state]_armour") + I.color = tank_module.armor_color + I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE) + add_overlay(I) + + for(var/decal in tank_module.decals) + I = image(icon, "[tank_module.user_icon_state]_[decal]") + I.color = tank_module.decals[decal] + I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE) + add_overlay(I) + + if(tank_module.eye_color) + I = image(icon, "[tank_module.user_icon_state]_eyes") + I.color = tank_module.eye_color + I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE) + add_overlay(I) + + if(client && key && stat == CONSCIOUS && tank_module.pupil_color) + I = image(icon, "[tank_module.user_icon_state]_pupils") + I.color = tank_module.pupil_color + I.plane = PLANE_LIGHTING_ABOVE + I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE) + add_overlay(I) + + if(opened) + add_overlay("[tank_module.user_icon_state]-open") + if(wiresexposed) + I = image(icon, "[tank_module.user_icon_state]-wires") + else if(cell) + I = image(icon, "[tank_module.user_icon_state]-cell") + else + I = image(icon, "[tank_module.user_icon_state]-nowires") + I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE) + add_overlay(I) + +/mob/living/silicon/robot/platform/proc/try_paint(var/obj/item/device/floor_painter/painting, var/mob/user) + + var/obj/item/weapon/robot_module/robot/platform/tank_module = module + if(!istype(tank_module)) + to_chat(user, SPAN_WARNING("\The [src] is not paintable.")) + return FALSE + + var/list/options = list("Eyes", "Armour", "Body", "Clear Colors") + if(length(tank_module.available_decals)) + options += "Decal" + if(length(tank_module.decals)) + options += "Clear Decals" + for(var/option in options) + LAZYSET(options, option, new /image('icons/effects/thinktank_labels.dmi', option)) + + var/choice = show_radial_menu(user, painting, options, radius = 42, require_near = TRUE) + if(!choice || QDELETED(src) || QDELETED(painting) || QDELETED(user) || user.incapacitated() || tank_module.loc != src) + return FALSE + + if(choice == "Decal") + choice = null + options = list() + for(var/decal_name in tank_module.available_decals) + LAZYSET(options, decal_name, new /image('icons/effects/thinktank_labels.dmi', decal_name)) + choice = show_radial_menu(user, painting, options, radius = 42, require_near = TRUE) + if(!choice || QDELETED(src) || QDELETED(painting) || QDELETED(user) || user.incapacitated() || tank_module.loc != src) + return FALSE + + . = TRUE + switch(choice) + if("Eyes") + tank_module.eye_color = painting.paint_colour + if("Armour") + tank_module.armor_color = painting.paint_colour + if("Body") + tank_module.base_color = painting.paint_colour + if("Clear Colors") + tank_module.eye_color = initial(tank_module.eye_color) + tank_module.armor_color = initial(tank_module.armor_color) + tank_module.base_color = initial(tank_module.base_color) + if("Clear Decals") + tank_module.decals = null + else + if(choice in tank_module.available_decals) + LAZYSET(tank_module.decals, tank_module.available_decals[choice], painting.paint_colour) + else + . = FALSE + if(.) + updateicon() diff --git a/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_interactions.dm b/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_interactions.dm new file mode 100644 index 0000000000..029660d8c1 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_interactions.dm @@ -0,0 +1,77 @@ +/mob/living/silicon/robot/platform/attack_hand(mob/user) + + if(!opened) + if(recharging) + var/obj/item/recharging_atom = recharging.resolve() + if(istype(recharging_atom) && !QDELETED(recharging_atom) && recharging_atom.loc == src) + recharging_atom.dropInto(loc) + user.put_in_hands(recharging_atom) + user.visible_message(SPAN_NOTICE("\The [user] pops \the [recharging_atom] out of \the [src]'s recharging port.")) + recharging = null + return TRUE + + if(try_remove_cargo(user)) + return TRUE + + . = ..() + +/mob/living/silicon/robot/platform/attackby(obj/item/W, mob/user) + + if(istype(W, /obj/item/weapon/cell) && !opened) + if(recharging) + to_chat(user, SPAN_WARNING("\The [src] already has \a [recharging.resolve()] inserted into its recharging port.")) + else if(user.unEquip(W)) + W.forceMove(src) + recharging = weakref(W) + recharge_complete = FALSE + user.visible_message(SPAN_NOTICE("\The [user] slots \the [W] into \the [src]'s recharging port.")) + return TRUE + + if(istype(W, /obj/item/device/floor_painter)) + return FALSE // Paint sprayer wil call try_paint() in afterattack() + + . = ..() + +/mob/living/silicon/robot/platform/attack_ghost(mob/observer/ghost/user) + + if(client || key || stat == DEAD || !ticker || !ticker.mode) + return ..() + + var/confirm = alert("Do you wish to take control of \the [src]?", "Platform Control", "No", "Yes") + if(confirm != "Yes" || QDELETED(src) || client || key || stat == DEAD || !ticker || !ticker.mode) + return ..() + + if(jobban_isbanned(user, "Robot")) + to_chat(user, SPAN_WARNING("You are banned from synthetic roles and cannot take control of \the [src].")) + return + + // Boilerplate from drone fabs, unsure if there's a shared proc to use instead. + var/deathtime = world.time - user.timeofdeath + var/deathtimeminutes = round(deathtime / (1 MINUTE)) + var/pluralcheck = "" + if(deathtimeminutes == 1) + pluralcheck = "minute" + else if(deathtimeminutes > 0) + pluralcheck = " [deathtimeminutes] minute\s and" + var/deathtimeseconds = round((deathtime - deathtimeminutes * 1 MINUTE) / 10,1) + if (deathtime < platform_respawn_time) + to_chat(usr, "You have been dead for[pluralcheck] [deathtimeseconds] seconds.") + to_chat(usr, "You must wait [platform_respawn_time/600] minute\s to respawn as a drone!") + return + // End boilerplate. + + if(user.mind) + user.mind.transfer_to(src) + if(key != user.key) + key = user.key + SetName("[modtype] [braintype]-[rand(100,999)]") + addtimer(CALLBACK(src, .proc/welcome_client), 1) + qdel(user) + +/mob/living/silicon/robot/platform/proc/welcome_client() + if(client) + to_chat(src, SPAN_NOTICE("You are a think-tank, a kind of flexible and adaptive drone intelligence installed into an armoured platform. Your programming compels you to be friendly and helpful wherever possible.")) + SetSleeping(0) + SetWeakened(0) + SetParalysis(0) + resting = FALSE diff --git a/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_module.dm b/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_module.dm new file mode 100644 index 0000000000..26ee97156c --- /dev/null +++ b/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_module.dm @@ -0,0 +1,103 @@ +/obj/item/weapon/robot_module/robot/platform + + hide_on_manifest = TRUE + + var/pupil_color = COLOR_CYAN + var/base_color = COLOR_WHITE + var/eye_color = COLOR_BEIGE + var/armor_color = "#68a2f2" + var/user_icon = 'icons/mob/robots_thinktank.dmi' + var/user_icon_state = "tachi" + + var/list/decals + var/list/available_decals = list( + "Stripe" = "stripe", + "Vertical Stripe" = "stripe_vertical" + ) + +// Only show on manifest if they have a player. +/obj/item/weapon/robot_module/robot/platform/hide_on_manifest() + if(isrobot(loc)) + var/mob/living/silicon/robot/R = loc + return !R.key + return ..() + +/obj/item/weapon/robot_module/robot/platform/verb/set_eye_colour() + set name = "Set Eye Colour" + set desc = "Select an eye colour to use." + set category = "Robot Commands" + set src in usr + + var/new_pupil_color = input(usr, "Select a pupil colour.", "Pupil Colour Selection") as color|null + if(usr.incapacitated() || QDELETED(usr) || QDELETED(src) || loc != usr) + return + + pupil_color = new_pupil_color || initial(pupil_color) + usr.update_icon() + +/obj/item/weapon/robot_module/robot/platform/explorer + armor_color = "#528052" + eye_color = "#7b7b46" + decals = list( + "stripe_vertical" = "#52b8b8", + "stripe" = "#52b8b8" + ) + channels = list( + "Science" = 1, + "Explorer" = 1 + ) + +/obj/item/weapon/robot_module/robot/platform/explorer/New() + ..() + modules += new /obj/item/weapon/tool/wrench/cyborg(src) + modules += new /obj/item/weapon/tool/screwdriver/cyborg(src) + modules += new /obj/item/weapon/pickaxe/plasmacutter(src) + modules += new /obj/item/weapon/chainsaw(src) + + var/datum/matter_synth/medicine = new /datum/matter_synth/medicine(7500) + var/obj/item/stack/medical/bruise_pack/bandaid = new(src) + bandaid.uses_charge = 1 + bandaid.charge_costs = list(1000) + bandaid.synths = list(medicine) + modules += bandaid + synths += medicine + + var/obj/item/weapon/gun/energy/phasegun/mounted/cyborg/phasegun = new(src) + modules += phasegun + + var/obj/item/weapon/gun/energy/laser/mounted/pew = new(src) + pew.name = "overvolted phase carbine" + pew.appearance = phasegun + emag = pew + +/obj/item/weapon/robot_module/robot/platform/explorer/respawn_consumable(var/mob/living/silicon/robot/R, rate) + . = ..() + for(var/obj/item/weapon/gun/energy/pew in modules) + if(pew.power_supply && pew.power_supply.charge < pew.power_supply.maxcharge) + pew.power_supply.give(pew.charge_cost * rate) + pew.update_icon() + else + pew.charge_tick = 0 + +/obj/item/weapon/robot_module/robot/platform/cargo + armor_color = "#d5b222" + eye_color = "#686846" + decals = list( + "stripe_vertical" = "#bfbfa1", + "stripe" = "#bfbfa1" + ) + channels = list("Supply" = 1) + networks = list(NETWORK_MINE) + +/obj/item/weapon/robot_module/robot/platform/cargo/New() + ..() + modules += new /obj/item/weapon/packageWrap(src) + modules += new /obj/item/weapon/pen/multi(src) + modules += new /obj/item/device/destTagger(src) + emag = new /obj/item/weapon/stamp/denied + +/obj/item/weapon/robot_module/robot/platform/cargo/respawn_consumable(mob/living/silicon/robot/R, rate) + . = ..() + var/obj/item/weapon/packageWrap/wrapper = locate() in modules + if(wrapper.amount < initial(wrapper.amount)) + wrapper.amount++ diff --git a/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_storage.dm b/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_storage.dm new file mode 100644 index 0000000000..816b540cc8 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_storage.dm @@ -0,0 +1,139 @@ +/mob/living/silicon/robot/platform/death(gibbed, deathmessage, show_dead_message) + + if(gibbed) + + if(recharging) + var/obj/item/recharging_atom = recharging.resolve() + if(istype(recharging_atom) && !QDELETED(recharging_atom) && recharging_atom.loc == src) + recharging_atom.dropInto(loc) + recharging_atom.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),30) + recharging = null + + if(length(stored_atoms)) + for(var/weakref/stored_ref in stored_atoms) + var/atom/movable/dropping = stored_ref.resolve() + if(istype(dropping) && !QDELETED(dropping) && dropping.loc == src) + dropping.dropInto(loc) + dropping.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),30) + stored_atoms = null + + . = ..() + +/mob/living/silicon/robot/platform/proc/can_store_atom(var/atom/movable/storing, var/mob/user) + + if(!istype(storing)) + var/storing_target = (user == src) ? "yourself" : "\the [src]" + to_chat(user, SPAN_WARNING("You cannot store that inside [storing_target].")) + return FALSE + + if(!isturf(storing.loc)) + return FALSE + + if(storing.anchored || !storing.simulated) + to_chat(user, SPAN_WARNING("\The [storing] won't budge!")) + return FALSE + + if(storing == src) + var/storing_target = (user == src) ? "yourself" : "\the [src]" + to_chat(user, SPAN_WARNING("You cannot store [storing_target] inside [storing_target]!")) + return FALSE + + if(length(stored_atoms) >= max_stored_atoms) + var/storing_target = (user == src) ? "Your" : "\The [src]'s" + to_chat(user, SPAN_WARNING("[storing_target] cargo compartment is full.")) + return FALSE + + if(ismob(storing)) + var/mob/M = storing + if(M.mob_size >= mob_size) + var/storing_target = (user == src) ? "your storage compartment" : "\the [src]" + to_chat(user, SPAN_WARNING("\The [storing] is too big for [storing_target].")) + return FALSE + + for(var/store_type in can_store_types) + if(istype(storing, store_type)) + . = TRUE + break + + if(.) + for(var/store_type in cannot_store_types) + if(istype(storing, store_type)) + . = FALSE + break + if(!.) + var/storing_target = (user == src) ? "yourself" : "\the [src]" + to_chat(user, SPAN_WARNING("You cannot store \the [storing] inside [storing_target].")) + +/mob/living/silicon/robot/platform/proc/store_atom(var/atom/movable/storing, var/mob/user) + if(istype(storing)) + storing.forceMove(src) + LAZYDISTINCTADD(stored_atoms, weakref(storing)) + +/mob/living/silicon/robot/platform/proc/drop_stored_atom(var/atom/movable/ejecting, var/mob/user) + + if(!ejecting && length(stored_atoms)) + var/weakref/stored_ref = stored_atoms[1] + if(!istype(stored_ref)) + LAZYREMOVE(stored_atoms, stored_ref) + else + ejecting = stored_ref?.resolve() + + LAZYREMOVE(stored_atoms, weakref(ejecting)) + if(istype(ejecting) && !QDELETED(ejecting) && ejecting.loc == src) + ejecting.dropInto(loc) + if(user == src) + visible_message(SPAN_NOTICE("\The [src] ejects \the [ejecting] from its cargo compartment.")) + else + user.visible_message(SPAN_NOTICE("\The [user] pulls \the [ejecting] from \the [src]'s cargo compartment.")) + +/mob/living/silicon/robot/platform/attack_ai(mob/user) + if(isrobot(user) && user.Adjacent(src)) + return try_remove_cargo(user) + return ..() + +/mob/living/silicon/robot/platform/proc/try_remove_cargo(var/mob/user) + if(!length(stored_atoms) || !istype(user)) + return FALSE + var/weakref/remove_ref = stored_atoms[length(stored_atoms)] + var/atom/movable/removing = remove_ref?.resolve() + if(!istype(removing) || QDELETED(removing) || removing.loc != src) + LAZYREMOVE(stored_atoms, remove_ref) + else + user.visible_message(SPAN_NOTICE("\The [user] begins unloading \the [removing] from \the [src]'s cargo compartment.")) + if(do_after(user, 3 SECONDS, src) && !QDELETED(removing) && removing.loc == src) + drop_stored_atom(removing, user) + return TRUE + +/mob/living/silicon/robot/platform/verb/drop_stored_atom_verb() + set name = "Eject Cargo" + set category = "Robot Commands" + set desc = "Drop something from your internal storage." + + if(incapacitated()) + to_chat(src, SPAN_WARNING("You are not in any state to do that.")) + return + + if(length(stored_atoms)) + drop_stored_atom(user = src) + else + to_chat(src, SPAN_WARNING("You have nothing in your cargo compartment.")) + +/mob/living/silicon/robot/platform/MouseDrop_T(atom/movable/dropping, mob/user) + if(!istype(user) || !istype(dropping) || user.incapacitated()) + return FALSE + if(!can_mouse_drop(dropping, user) || !can_store_atom(dropping, user)) + return FALSE + if(user == src) + visible_message(SPAN_NOTICE("\The [src] begins loading \the [dropping] into its cargo compartment.")) + else + user.visible_message(SPAN_NOTICE("\The [user] begins loading \the [dropping] into \the [src]'s cargo compartment.")) + if(do_after(user, 3 SECONDS, src) && can_mouse_drop(dropping, user) && can_store_atom(dropping, user)) + store_atom(dropping, user) + return FALSE + +/mob/living/silicon/robot/platform/proc/can_mouse_drop(var/atom/dropping, var/mob/user) + if(!istype(user) || !istype(dropping) || QDELETED(dropping) || QDELETED(user) || QDELETED(src)) + return FALSE + if(user.incapacitated() || !Adjacent(user) || !dropping.Adjacent(user)) + return FALSE + return TRUE \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_subtypes.dm b/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_subtypes.dm new file mode 100644 index 0000000000..b3e48aabb4 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/subtypes/thinktank/thinktank_subtypes.dm @@ -0,0 +1,32 @@ +/mob/living/silicon/robot/platform/explorer + name = "recon platform" + desc = "A large quadrupedal AI platform, colloquially known as a 'think-tank' due to the flexible onboard intelligence. This one is lightly armoured and fitted with all-terrain wheels." + modtype = "Recon" + module = /obj/item/weapon/robot_module/robot/platform/explorer + +/mob/living/silicon/robot/platform/explorer/Initialize() + . = ..() + laws = new /datum/ai_laws/explorer + +/mob/living/silicon/robot/platform/explorer/welcome_client() + ..() + if(client) // ganbatte tachikoma-san + to_chat(src, SPAN_NOTICE("You are tasked with supporting the Exploration and Science staff as they unearth the secrets of the planet. Do your best!")) + +/obj/effect/landmark/robot_platform/explorer + platform_type = /mob/living/silicon/robot/platform/explorer + +/mob/living/silicon/robot/platform/cargo + name = "logistics platform" + desc = "A large quadrupedal AI platform, colloquially known as a 'think-tank' due to the flexible onboard intelligence. This one has an expanded storage compartment." + modtype = "Logistics" + module = /obj/item/weapon/robot_module/robot/platform/cargo + max_stored_atoms = 3 + +/mob/living/silicon/robot/platform/cargo/welcome_client() + ..() + if(client) + to_chat(src, SPAN_NOTICE("You are tasked with supporting the Cargo and Supply staff as they handle operational logistics. Do your best!")) + +/obj/effect/landmark/robot_platform/cargo + platform_type = /mob/living/silicon/robot/platform/cargo diff --git a/code/modules/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm index 9182a17530..1b2624c125 100644 --- a/code/modules/paperwork/handlabeler.dm +++ b/code/modules/paperwork/handlabeler.dm @@ -21,50 +21,63 @@ return // don't set a label if(!labels_left) - to_chat(user, "No labels left.") + to_chat(user, SPAN_WARNING("\The [src] has no labels left.")) return if(!label || !length(label)) - to_chat(user, "No text set.") + to_chat(user, SPAN_WARNING("\The [src] has no label text set.")) return if(length(A.name) + length(label) > 64) - to_chat(user, "Label too big.") + to_chat(user, SPAN_WARNING("\The [src]'s label too big.")) + return + if(istype(A, /mob/living/silicon/robot/platform)) + var/mob/living/silicon/robot/platform/P = A + if(!P.allowed(user)) + to_chat(usr, SPAN_WARNING("Access denied.")) + else if(P.client || P.key) + to_chat(user, SPAN_NOTICE("You rename \the [P] to [label].")) + to_chat(P, SPAN_NOTICE("\The [user] renames you to [label].")) + P.custom_name = label + P.SetName(P.custom_name) + else + to_chat(user, SPAN_WARNING("\The [src] is inactive and cannot be renamed.")) return if(ishuman(A)) - to_chat(user, "The label refuses to stick to [A.name].") + to_chat(user, SPAN_WARNING("The label refuses to stick to [A.name].")) return if(issilicon(A)) - to_chat(user, "The label refuses to stick to [A.name].") + to_chat(user, SPAN_WARNING("The label refuses to stick to [A.name].")) return if(isobserver(A)) - to_chat(user, "[src] passes through [A.name].") + to_chat(user, SPAN_WARNING("[src] passes through [A.name].")) return if(istype(A, /obj/item/weapon/reagent_containers/glass)) - to_chat(user, "The label can't stick to the [A.name]. (Try using a pen)") + to_chat(user, SPAN_WARNING("The label can't stick to the [A.name] (Try using a pen).")) return if(istype(A, /obj/machinery/portable_atmospherics/hydroponics)) var/obj/machinery/portable_atmospherics/hydroponics/tray = A if(!tray.mechanical) - to_chat(user, "How are you going to label that?") + to_chat(user, SPAN_WARNING("How are you going to label that?")) return tray.labelled = label spawn(1) tray.update_icon() - user.visible_message("[user] labels [A] as [label].", \ - "You label [A] as [label].") + user.visible_message( \ + SPAN_NOTICE("\The [user] labels [A] as [label]."), \ + SPAN_NOTICE("You label [A] as [label].")) A.name = "[A.name] ([label])" /obj/item/weapon/hand_labeler/attack_self(mob/user as mob) mode = !mode icon_state = "labeler[mode]" if(mode) - to_chat(user, "You turn on \the [src].") + to_chat(user, SPAN_NOTICE("You turn on \the [src].")) //Now let them chose the text. var/str = sanitizeSafe(input(user,"Label text?","Set label",""), MAX_NAME_LEN) if(!str || !length(str)) - to_chat(user, "Invalid text.") + to_chat(user, SPAN_WARNING("Invalid text.")) return label = str - to_chat(user, "You set the text to '[str]'.") + to_chat(user, SPAN_NOTICE("You set the text to '[str]'.")) else - to_chat(user, "You turn off \the [src].") \ No newline at end of file + to_chat(user, SPAN_NOTICE("You turn off \the [src].")) \ No newline at end of file diff --git a/code/modules/power/cells/power_cells.dm b/code/modules/power/cells/power_cells.dm index ca6f96ec9b..052813ade7 100644 --- a/code/modules/power/cells/power_cells.dm +++ b/code/modules/power/cells/power_cells.dm @@ -62,6 +62,11 @@ charge = 0 update_icon() +/obj/item/weapon/cell/mech + name = "mecha power cell" + charge = 15000 + maxcharge = 15000 + /obj/item/weapon/cell/infinite name = "infinite-capacity power cell!" icon_state = "icell" diff --git a/code/modules/projectiles/guns/energy/phase.dm b/code/modules/projectiles/guns/energy/phase.dm index ec3c7ff438..18290c3946 100644 --- a/code/modules/projectiles/guns/energy/phase.dm +++ b/code/modules/projectiles/guns/energy/phase.dm @@ -12,6 +12,15 @@ projectile_type = /obj/item/projectile/energy/phase one_handed_penalty = 15 +/obj/item/weapon/gun/energy/phasegun/mounted + self_recharge = 1 + use_external_power = 1 + one_handed_penalty = 0 + +/obj/item/weapon/gun/energy/phasegun/mounted/cyborg + charge_cost = 400 + recharge_time = 7 + /obj/item/weapon/gun/energy/phasegun/pistol name = "phase pistol" desc = "The RayZar EW15 Apollo is an energy handgun, specifically designed for self-defense against aggressive wildlife." diff --git a/code/modules/research/prosfab_designs.dm b/code/modules/research/prosfab_designs.dm index 1bec508e50..ac585315e5 100644 --- a/code/modules/research/prosfab_designs.dm +++ b/code/modules/research/prosfab_designs.dm @@ -357,10 +357,15 @@ build_path = /obj/item/robot_parts/robot_component/camera /datum/design/item/prosfab/cyborg/component/armour - name = "Armour Plating" + name = "Armour Plating (Robot)" id = "armour" build_path = /obj/item/robot_parts/robot_component/armour +/datum/design/item/prosfab/cyborg/component/armour_heavy + name = "Armour Plating (Platform)" + id = "platform_armour" + build_path = /obj/item/robot_parts/robot_component/armour_platform + /datum/design/item/prosfab/cyborg/component/ai_shell name = "AI Remote Interface" id = "mmi_ai_shell" diff --git a/icons/effects/thinktank_labels.dmi b/icons/effects/thinktank_labels.dmi new file mode 100644 index 0000000000000000000000000000000000000000..abd5a139d773eec8ed96129d5cd244a7c5a766d5 GIT binary patch literal 589 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 literal 0 HcmV?d00001 diff --git a/icons/mob/screen1_robot.dmi b/icons/mob/screen1_robot.dmi index ccad7e0f4a76420d939d712007e901b6fe012a1c..e4ae2f77c83cb9ecbf974e3585922adfb59a88cd 100644 GIT binary patch delta 15996 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 F{|{W|ys`iQ delta 15032 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
v9*wD*XztEW} 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 diff --git a/maps/southern_cross/southern_cross-1.dmm b/maps/southern_cross/southern_cross-1.dmm index e162147d2b..9474a2f913 100644 --- a/maps/southern_cross/southern_cross-1.dmm +++ b/maps/southern_cross/southern_cross-1.dmm @@ -72952,12 +72952,9 @@ /obj/machinery/light/small{ dir = 1 }, -/obj/effect/floor_decal/borderfloor{ - dir = 1 - }, -/obj/effect/floor_decal/corner/brown/border{ - dir = 1 - }, +/obj/machinery/mech_recharger, +/obj/effect/floor_decal/industrial/hatch/yellow, +/obj/effect/landmark/robot_platform/cargo, /turf/simulated/floor/tiled/steel, /area/quartermaster/warehouse) "cHa" = ( @@ -72973,6 +72970,9 @@ /obj/effect/floor_decal/corner/brown/border{ dir = 1 }, +/obj/structure/table/steel, +/obj/item/device/floor_painter, +/obj/item/weapon/weldingtool, /turf/simulated/floor/tiled, /area/quartermaster/warehouse) "cHb" = ( diff --git a/maps/southern_cross/southern_cross-3.dmm b/maps/southern_cross/southern_cross-3.dmm index 2d672e6eee..b3b89008ba 100644 --- a/maps/southern_cross/southern_cross-3.dmm +++ b/maps/southern_cross/southern_cross-3.dmm @@ -4043,6 +4043,8 @@ /area/surface/outpost/main/garage) "iI" = ( /obj/structure/table/steel, +/obj/item/weapon/weldingtool, +/obj/item/device/floor_painter, /turf/simulated/floor/tiled/steel_grid, /area/surface/outpost/main/garage) "iJ" = ( @@ -14234,6 +14236,26 @@ }, /turf/simulated/floor/tiled/freezer, /area/surface/outpost/main/pool) +"GP" = ( +/obj/effect/floor_decal/industrial/hatch/yellow, +/obj/machinery/light{ + dir = 4 + }, +/obj/machinery/mech_recharger, +/obj/effect/landmark/robot_platform/explorer, +/turf/simulated/floor/plating, +/area/surface/outpost/main/garage) +"Iw" = ( +/obj/effect/floor_decal/industrial/hatch/yellow, +/obj/machinery/mech_recharger, +/turf/simulated/floor/plating, +/area/surface/outpost/main/garage) +"Lz" = ( +/obj/effect/floor_decal/industrial/hatch/yellow, +/obj/machinery/mech_recharger, +/obj/effect/landmark/robot_platform/explorer, +/turf/simulated/floor/plating, +/area/surface/outpost/main/garage) (1,1,1) = {" aa @@ -54622,11 +54644,11 @@ hq hG ii iG -iH -iH -iH -iH -iH +iG +iG +iG +iG +iG kJ hF lq @@ -54880,10 +54902,10 @@ hF ij iI Ej -iH -iH -iH -iH +Lz +GP +Iw +iG kK hF lr diff --git a/maps/southern_cross/southern_cross.dm b/maps/southern_cross/southern_cross.dm index c34b4b74d6..a9a009e553 100644 --- a/maps/southern_cross/southern_cross.dm +++ b/maps/southern_cross/southern_cross.dm @@ -7,6 +7,7 @@ #include "southern_cross_jobs.dm" #include "southern_cross_elevator.dm" #include "southern_cross_events.dm" + #include "southern_cross_overrides.dm" #include "southern_cross_presets.dm" #include "southern_cross_shuttles.dm" diff --git a/maps/southern_cross/southern_cross_overrides.dm b/maps/southern_cross/southern_cross_overrides.dm new file mode 100644 index 0000000000..6525446b1f --- /dev/null +++ b/maps/southern_cross/southern_cross_overrides.dm @@ -0,0 +1,9 @@ +/mob/living/silicon/robot/platform/explorer + req_access = list(access_explorer) + +/mob/living/silicon/robot/platform/cargo + req_access = list(access_cargo_bot) + +/obj/item/weapon/card/id/platform/Initialize() + . = ..() + access |= access_explorer diff --git a/vorestation.dme b/vorestation.dme index 59c42c3326..99b556cba7 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -2751,6 +2751,12 @@ #include "code\modules\mob\living\silicon\robot\subtypes\lost_drone.dm" #include "code\modules\mob\living\silicon\robot\subtypes\lost_drone_vr.dm" #include "code\modules\mob\living\silicon\robot\subtypes\syndicate.dm" +#include "code\modules\mob\living\silicon\robot\subtypes\thinktank\_thinktank.dm" +#include "code\modules\mob\living\silicon\robot\subtypes\thinktank\thinktank_icon.dm" +#include "code\modules\mob\living\silicon\robot\subtypes\thinktank\thinktank_interactions.dm" +#include "code\modules\mob\living\silicon\robot\subtypes\thinktank\thinktank_module.dm" +#include "code\modules\mob\living\silicon\robot\subtypes\thinktank\thinktank_storage.dm" +#include "code\modules\mob\living\silicon\robot\subtypes\thinktank\thinktank_subtypes.dm" #include "code\modules\mob\living\simple_mob\appearance.dm" #include "code\modules\mob\living\simple_mob\butchering.dm" #include "code\modules\mob\living\simple_mob\combat.dm" From fa1512d1a4993f4e92922a39c1f2c3367e73ca01 Mon Sep 17 00:00:00 2001 From: Novacat <35587478+Novacat@users.noreply.github.com> Date: Thu, 18 Mar 2021 10:22:58 -0400 Subject: [PATCH 2/2] Update robot.dm --- code/modules/mob/living/silicon/robot/robot.dm | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 533975c897..7db384ddc7 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1215,9 +1215,6 @@ if(current_selection_index) // Select what the player had before if possible. select_module(current_selection_index) -<<<<<<< HEAD -======= /mob/living/silicon/robot/get_cell() return cell ->>>>>>> f383438... Merge pull request #7935 from MistakeNot4892/tachikoma