diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index df74b17bc0..1e2b2e63f3 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -283,6 +283,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 cdcaf22fdf..fc12a5b1ee 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -115,7 +115,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]" @@ -276,7 +276,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 653d56e154..e58e427892 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -352,10 +352,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/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 82a34a2526..867301f989 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -213,7 +213,7 @@ var/world_topic_spam_protect_time = world.timeofday var/isactive = (robot.client && robot.client.inactivity <= 10 MINUTES) ? "Active" : "Inactive" 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 af5c5eedae..5ee09212f4 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 a26a4bfd1e..1376c15072 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) ..() @@ -287,10 +289,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)) @@ -300,6 +299,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) @@ -1215,3 +1219,6 @@ if(current_selection_index) // Select what the player had before if possible. select_module(current_selection_index) + +/mob/living/silicon/robot/get_cell() + return cell 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 dfea127f01..66a2702ddf 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 @@ -805,7 +808,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", @@ -828,7 +831,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) @@ -912,7 +915,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 33ba37ad40..2498aba776 100644 --- a/code/modules/projectiles/guns/energy/phase.dm +++ b/code/modules/projectiles/guns/energy/phase.dm @@ -13,6 +13,15 @@ one_handed_penalty = 15 recoil_mode = 0 //CHOMP Addition: Removes recoil for micros. +/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 8f932b0082..5fc685bf1d 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 0000000000..abd5a139d7 Binary files /dev/null and b/icons/effects/thinktank_labels.dmi differ diff --git a/icons/mob/robots_thinktank.dmi b/icons/mob/robots_thinktank.dmi new file mode 100644 index 0000000000..471647bf60 Binary files /dev/null and b/icons/mob/robots_thinktank.dmi differ diff --git a/icons/mob/screen1_robot.dmi b/icons/mob/screen1_robot.dmi index ccad7e0f4a..e4ae2f77c8 100644 Binary files a/icons/mob/screen1_robot.dmi and b/icons/mob/screen1_robot.dmi differ diff --git a/maps/southern_cross/southern_cross.dm b/maps/southern_cross/southern_cross.dm index 376d944c95..4b797eccb7 100644 --- a/maps/southern_cross/southern_cross.dm +++ b/maps/southern_cross/southern_cross.dm @@ -6,6 +6,7 @@ #include "southern_cross_defines.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" #include "southern_cross_shuttles_ch.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 e46c73b175..9d846ec36b 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -2928,6 +2928,12 @@ #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\simple_animal\aliens\synx.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"