From f26f9cf0b6aba33619b88bab577e8ee2622fe484 Mon Sep 17 00:00:00 2001 From: joep van der velden Date: Wed, 18 Mar 2020 17:02:44 +0100 Subject: [PATCH] line fixes --- code/controllers/subsystem/ticker.dm | 1056 +++--- code/game/mecha/mecha.dm | 2890 ++++++++--------- code/game/objects/items/devices/flash.dm | 566 ++-- .../mob/living/carbon/alien/humanoid/queen.dm | 190 +- .../modules/mob/living/silicon/robot/robot.dm | 2720 ++++++++-------- code/modules/mob/living/silicon/silicon.dm | 682 ++-- 6 files changed, 4052 insertions(+), 4052 deletions(-) diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 0770a55c7eb..ba80f1051dd 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -1,528 +1,528 @@ -SUBSYSTEM_DEF(ticker) - name = "Ticker" - init_order = INIT_ORDER_TICKER - - priority = FIRE_PRIORITY_TICKER - flags = SS_KEEP_TIMING - runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME - - var/round_start_time = 0 - var/const/restart_timeout = 600 - var/current_state = GAME_STATE_STARTUP - var/force_start = 0 // Do we want to force-start as soon as we can - var/force_ending = 0 - var/hide_mode = 0 // leave here at 0 ! setup() will take care of it when needed for Secret mode -walter0o - var/datum/game_mode/mode = null - var/event_time = null - var/event = 0 - var/login_music // music played in pregame lobby - var/list/datum/mind/minds = list()//The people in the game. Used for objective tracking. - var/Bible_icon_state // icon_state the chaplain has chosen for his bible - var/Bible_item_state // item_state the chaplain has chosen for his bible - var/Bible_name // name of the bible - var/Bible_deity_name - var/datum/cult_info/cultdat = null //here instead of cult for adminbus purposes - var/random_players = 0 // if set to nonzero, ALL players who latejoin or declare-ready join will have random appearances/genders - var/list/syndicate_coalition = list() // list of traitor-compatible factions - var/list/factions = list() // list of all factions - var/list/availablefactions = list() // list of factions with openings - var/tipped = FALSE //Did we broadcast the tip of the day yet? - var/selected_tip // What will be the tip of the day? - var/pregame_timeleft // This is used for calculations - var/delay_end = 0 //if set to nonzero, the round will not restart on it's own - var/triai = 0//Global holder for Triumvirate - var/initialtpass = 0 //holder for inital autotransfer vote timer - var/obj/screen/cinematic = null //used for station explosion cinematic - var/round_end_announced = 0 // Spam Prevention. Announce round end only once.\ - -/datum/controller/subsystem/ticker/Initialize() - login_music = pick(\ - 'sound/music/thunderdome.ogg',\ - 'sound/music/space.ogg',\ - 'sound/music/title1.ogg',\ - 'sound/music/title2.ogg',\ - 'sound/music/title3.ogg',) - - // Map name - if(using_map && using_map.name) - GLOB.map_name = "[using_map.name]" - else - GLOB.map_name = "Unknown" - - // World name - if(config && config.server_name) - world.name = "[config.server_name]: [station_name()]" - else - world.name = station_name() - - return ..() - - -/datum/controller/subsystem/ticker/fire() - switch(current_state) - if(GAME_STATE_STARTUP) - // This is ran as soon as the MC starts firing, and should only run ONCE, unless startup fails - round_start_time = world.time + (config.pregame_timestart * 10) - to_chat(world, "Welcome to the pre-game lobby!") - to_chat(world, "Please, setup your character and select ready. Game will start in [config.pregame_timestart] seconds") - current_state = GAME_STATE_PREGAME - fire() // TG says this is a good idea - if(GAME_STATE_PREGAME) - if(!going) - return - - // This is so we dont have sleeps in controllers, because that is a bad, bad thing - if(!delay_end) - pregame_timeleft = max(0,round_start_time - world.time) // Normal lobby countdown when roundstart was not delayed - else - pregame_timeleft = max(0,pregame_timeleft - 20) // If roundstart was delayed, we should resume the countdown where it left off - - if(pregame_timeleft <= 600 && !tipped) // 60 seconds - send_tip_of_the_round() - tipped = TRUE - - if(pregame_timeleft <= 0 || force_start) - current_state = GAME_STATE_SETTING_UP - Master.SetRunLevel(RUNLEVEL_SETUP) - if(GAME_STATE_SETTING_UP) - if(!setup()) // Setup failed - current_state = GAME_STATE_STARTUP - Master.SetRunLevel(RUNLEVEL_LOBBY) - if(GAME_STATE_PLAYING) - delay_end = 0 // reset this in case round start was delayed - mode.process() - mode.process_job_tasks() - var/game_finished = SSshuttle.emergency.mode >= SHUTTLE_ENDGAME || mode.station_was_nuked - if(config.continuous_rounds) - mode.check_finished() // some modes contain var-changing code in here, so call even if we don't uses result - else - game_finished |= mode.check_finished() - if(game_finished) - current_state = GAME_STATE_FINISHED - if(GAME_STATE_FINISHED) - current_state = GAME_STATE_FINISHED - Master.SetRunLevel(RUNLEVEL_POSTGAME) // This shouldnt process more than once, but you never know - auto_toggle_ooc(1) // Turn it on - - spawn(0) - declare_completion() - - spawn(50) - if(mode.station_was_nuked) - world.Reboot("Station destroyed by Nuclear Device.", "end_proper", "nuke") - else - world.Reboot("Round ended.", "end_proper", "proper completion") - - -/datum/controller/subsystem/ticker/proc/votetimer() - var/timerbuffer = 0 - if(initialtpass == 0) - timerbuffer = config.vote_autotransfer_initial - else - timerbuffer = config.vote_autotransfer_interval - spawn(timerbuffer) - SSvote.autotransfer() - initialtpass = 1 - votetimer() - - -/datum/controller/subsystem/ticker/proc/setup() - cultdat = setupcult() - //Create and announce mode - if(master_mode=="secret") - src.hide_mode = 1 - var/list/datum/game_mode/runnable_modes - if((master_mode=="random") || (master_mode=="secret")) - runnable_modes = config.get_runnable_modes() - if(runnable_modes.len==0) - current_state = GAME_STATE_PREGAME - Master.SetRunLevel(RUNLEVEL_LOBBY) - to_chat(world, "Unable to choose playable game mode. Reverting to pre-game lobby.") - return 0 - if(secret_force_mode != "secret") - var/datum/game_mode/M = config.pick_mode(secret_force_mode) - if(M.can_start()) - src.mode = config.pick_mode(secret_force_mode) - SSjobs.ResetOccupations() - if(!src.mode) - src.mode = pickweight(runnable_modes) - if(src.mode) - var/mtype = src.mode.type - src.mode = new mtype - else - src.mode = config.pick_mode(master_mode) - if(!src.mode.can_start()) - to_chat(world, "Unable to start [mode.name]. Not enough players, [mode.required_players] players needed. Reverting to pre-game lobby.") - mode = null - current_state = GAME_STATE_PREGAME - SSjobs.ResetOccupations() - Master.SetRunLevel(RUNLEVEL_LOBBY) - return 0 - - //Configure mode and assign player to special mode stuff - src.mode.pre_pre_setup() - var/can_continue - can_continue = src.mode.pre_setup()//Setup special modes - SSjobs.DivideOccupations() //Distribute jobs - if(!can_continue) - qdel(mode) - current_state = GAME_STATE_PREGAME - to_chat(world, "Error setting up [master_mode]. Reverting to pre-game lobby.") - SSjobs.ResetOccupations() - Master.SetRunLevel(RUNLEVEL_LOBBY) - return 0 - - if(hide_mode) - var/list/modes = new - for(var/datum/game_mode/M in runnable_modes) - modes+=M.name - modes = sortList(modes) - to_chat(world, "The current game mode is - Secret!") - to_chat(world, "Possibilities: [english_list(modes)]") - else - src.mode.announce() - - create_characters() //Create player characters and transfer them - populate_spawn_points() - collect_minds() - equip_characters() - data_core.manifest() - current_state = GAME_STATE_PLAYING - Master.SetRunLevel(RUNLEVEL_GAME) - - callHook("roundstart") - - //here to initialize the random events nicely at round start - setup_economy() - setupfactions() - - //shuttle_controller.setup_shuttle_docks() - - spawn(0)//Forking here so we dont have to wait for this to finish - if(!GLOB.syndicate_code_phrase) - GLOB.syndicate_code_phrase = generate_code_phrase() - if(!GLOB.syndicate_code_response) - GLOB.syndicate_code_response = generate_code_phrase() - mode.post_setup() - //Cleanup some stuff - for(var/obj/effect/landmark/start/S in GLOB.landmarks_list) - //Deleting Startpoints but we need the ai point to AI-ize people later - if(S.name != "AI") - qdel(S) - - // take care of random spesspod spawning - var/list/obj/effect/landmark/spacepod/random/L = list() - for(var/obj/effect/landmark/spacepod/random/SS in GLOB.landmarks_list) - if(istype(SS)) - L += SS - if(L.len) - var/obj/effect/landmark/spacepod/random/S = pick(L) - new /obj/spacepod/random(S.loc) - for(var/obj/effect/landmark/spacepod/random/R in L) - qdel(R) - - to_chat(world, "Enjoy the game!") - world << sound('sound/AI/welcome.ogg')// Skie - - if(SSholiday.holidays) - to_chat(world, "and...") - for(var/holidayname in SSholiday.holidays) - var/datum/holiday/holiday = SSholiday.holidays[holidayname] - to_chat(world, "

[holiday.greet()]

") - - spawn(0) // Forking dynamic room selection - var/list/area/dynamic/source/available_source_candidates = subtypesof(/area/dynamic/source) - var/list/area/dynamic/destination/available_destination_candidates = subtypesof(/area/dynamic/destination) - - for(var/area/dynamic/destination/current_destination_candidate in available_destination_candidates) - var/area/dynamic/destination/current_destination = locate(current_destination_candidate) - - if(!current_destination) - continue - - if(current_destination.match_width == 0 || current_destination.match_height == 0) - message_admins("Dynamic area destination '[current_destination.name]' does not have its size requirements set.") - continue - - var/list/area/dynamic/source/candidate_source_areas = new /list(0) - for(var/area/dynamic/source/candidate_source_area in available_source_candidates) - var/area/dynamic/source/candidate_source = locate(candidate_source_area) - - if(!candidate_source) - continue - - if(candidate_source.match_tag != current_destination.match_tag) - continue - - if(candidate_source.match_width != current_destination.match_width || \ - candidate_source.match_height != current_destination.match_height) - continue - - candidate_source_areas += candidate_source - - if(candidate_source_areas.len == 0) - message_admins("Failed to find a matching source for dynamic area: [current_destination.name]") - continue - - var/area/dynamic/source/selected_source = pick(candidate_source_areas) - available_source_candidates -= selected_source - - selected_source.copy_contents_to(current_destination, 0) - - if(current_destination.enable_lights || selected_source.enable_lights) - current_destination.power_light = 1 - else - current_destination.power_light = 0 - current_destination.power_change() - - //start_events() //handles random events and space dust. - //new random event system is handled from the MC. - - var/list/admins_number = staff_countup(R_BAN) - if(admins_number[1] == 0 && admins_number[3] == 0) - send2irc(config.admin_notify_irc, "Round has started with no admins online.") - auto_toggle_ooc(0) // Turn it off - round_start_time = world.time - - if(config.sql_enabled) - spawn(3000) - statistic_cycle() // Polls population totals regularly and stores them in an SQL DB - - votetimer() - - for(var/mob/new_player/N in GLOB.mob_list) - if(N.client) - N.new_player_panel_proc() - - return 1 - -/datum/controller/subsystem/ticker/proc/station_explosion_cinematic(station_missed = 0, override = null) - if(cinematic) - return //already a cinematic in progress! - - auto_toggle_ooc(1) // Turn it on - //initialise our cinematic screen object - cinematic = new /obj/screen(src) - cinematic.icon = 'icons/effects/station_explosion.dmi' - cinematic.icon_state = "station_intact" - cinematic.layer = 21 - cinematic.mouse_opacity = MOUSE_OPACITY_TRANSPARENT - cinematic.screen_loc = "1,0" - - if(station_missed) - for(var/mob/M in GLOB.mob_list) - if(M.client) - M.client.screen += cinematic //show every client the cinematic - else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived" - for(var/mob/M in GLOB.mob_list) - if(M.stat != DEAD) - var/turf/T = get_turf(M) - if(T && is_station_level(T.z) && !istype(M.loc, /obj/structure/closet/secure_closet/freezer)) - var/mob/ghost = M.ghostize() - M.dust() //no mercy - if(ghost && ghost.client) //Play the victims an uninterrupted cinematic. - ghost.client.screen += cinematic - CHECK_TICK - if(M && M.client) //Play the survivors a cinematic. - M.client.screen += cinematic - - //Now animate the cinematic - switch(station_missed) - if(1) //nuke was nearby but (mostly) missed - if(mode && !override) - override = mode.name - switch(override) - if("nuclear emergency") //Nuke wasn't on station when it blew up - flick("intro_nuke", cinematic) - sleep(35) - world << sound('sound/effects/explosionfar.ogg') - flick("station_intact_fade_red", cinematic) - cinematic.icon_state = "summary_nukefail" - if("fake") //The round isn't over, we're just freaking people out for fun - flick("intro_nuke", cinematic) - sleep(35) - world << sound('sound/items/bikehorn.ogg') - flick("summary_selfdes", cinematic) - else - flick("intro_nuke", cinematic) - sleep(35) - world << sound('sound/effects/explosionfar.ogg') - - - if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation - sleep(50) - world << sound('sound/effects/explosionfar.ogg') - else //station was destroyed - if(mode && !override) - override = mode.name - switch(override) - if("nuclear emergency") //Nuke Ops successfully bombed the station - flick("intro_nuke", cinematic) - sleep(35) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_nukewin" - if("AI malfunction") //Malf (screen,explosion,summary) - flick("intro_malf", cinematic) - sleep(76) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_malf" - if("blob") //Station nuked (nuke,explosion,summary) - flick("intro_nuke", cinematic) - sleep(35) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_selfdes" - else //Station nuked (nuke,explosion,summary) - flick("intro_nuke", cinematic) - sleep(35) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_selfdes" - //If its actually the end of the round, wait for it to end. - //Otherwise if its a verb it will continue on afterwards. - spawn(300) - QDEL_NULL(cinematic) //end the cinematic - - - -/datum/controller/subsystem/ticker/proc/create_characters() - for(var/mob/new_player/player in GLOB.player_list) - if(player.ready && player.mind) - if(player.mind.assigned_role == "AI") - player.close_spawn_windows() - var/mob/living/silicon/ai/ai_character = player.AIize() - ai_character.moveToAILandmark() - else if(!player.mind.assigned_role) - continue - else - player.create_character() - qdel(player) - - -/datum/controller/subsystem/ticker/proc/collect_minds() - for(var/mob/living/player in GLOB.player_list) - if(player.mind) - SSticker.minds += player.mind - - -/datum/controller/subsystem/ticker/proc/equip_characters() - var/captainless=1 - for(var/mob/living/carbon/human/player in GLOB.player_list) - if(player && player.mind && player.mind.assigned_role) - if(player.mind.assigned_role == "Captain") - captainless=0 - if(player.mind.assigned_role != player.mind.special_role) - SSjobs.AssignRank(player, player.mind.assigned_role, 0) - SSjobs.EquipRank(player, player.mind.assigned_role, 0) - EquipCustomItems(player) - if(captainless) - for(var/mob/M in GLOB.player_list) - if(!istype(M,/mob/new_player)) - to_chat(M, "Captainship not forced on anyone.") - -/datum/controller/subsystem/ticker/proc/send_tip_of_the_round() - var/m - if(selected_tip) - m = selected_tip - else - var/list/randomtips = file2list("strings/tips.txt") - var/list/memetips = file2list("strings/sillytips.txt") - if(randomtips.len && prob(95)) - m = pick(randomtips) - else if(memetips.len) - m = pick(memetips) - - if(m) - to_chat(world, "Tip of the round: [html_encode(m)]") - -/datum/controller/subsystem/ticker/proc/getfactionbyname(var/name) - for(var/datum/faction/F in factions) - if(F.name == name) - return F - - -/datum/controller/subsystem/ticker/proc/declare_completion() - nologevent = 1 //end of round murder and shenanigans are legal; there's no need to jam up attack logs past this point. - //Round statistics report - var/datum/station_state/end_state = new /datum/station_state() - end_state.count() - var/station_integrity = min(round( 100.0 * start_state.score(end_state), 0.1), 100.0) - - to_chat(world, "
[TAB]Shift Duration: [round(ROUND_TIME / 36000)]:[add_zero("[ROUND_TIME / 600 % 60]", 2)]:[ROUND_TIME / 100 % 6][ROUND_TIME / 100 % 10]") - to_chat(world, "
[TAB]Station Integrity: [mode.station_was_nuked ? "Destroyed" : "[station_integrity]%"]") - to_chat(world, "
") - - //Silicon laws report - for(var/mob/living/silicon/ai/aiPlayer in GLOB.mob_list) - if(aiPlayer.stat != 2) - to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws at the end of the game were:") - else - to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws when it was deactivated were:") - aiPlayer.show_laws(1) - - if(aiPlayer.connected_robots.len) - var/robolist = "The AI's loyal minions were: " - for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots) - robolist += "[robo.name][robo.stat?" (Deactivated) (Played by: [robo.key]), ":" (Played by: [robo.key]), "]" - to_chat(world, "[robolist]") - - var/dronecount = 0 - - for(var/mob/living/silicon/robot/robo in GLOB.mob_list) - - if(istype(robo,/mob/living/silicon/robot/drone)) - dronecount++ - continue - - if(!robo.connected_ai) - if(robo.stat != 2) - to_chat(world, "[robo.name] (Played by: [robo.key]) survived as an AI-less borg! Its laws were:") - else - to_chat(world, "[robo.name] (Played by: [robo.key]) was unable to survive the rigors of being a cyborg without an AI. Its laws were:") - - if(robo) //How the hell do we lose robo between here and the world messages directly above this? - robo.laws.show_laws(world) - - if(dronecount) - to_chat(world, "There [dronecount>1 ? "were" : "was"] [dronecount] industrious maintenance [dronecount>1 ? "drones" : "drone"] this round.") - - if(mode.eventmiscs.len) - var/emobtext = "" - for(var/datum/mind/eventmind in mode.eventmiscs) - emobtext += printeventplayer(eventmind) - emobtext += "
" - emobtext += printobjectives(eventmind) - emobtext += "
" - emobtext += "
" - to_chat(world, emobtext) - - mode.declare_completion()//To declare normal completion. - - //calls auto_declare_completion_* for all modes - for(var/handler in typesof(/datum/game_mode/proc)) - if(findtext("[handler]","auto_declare_completion_")) - call(mode, handler)() - - scoreboard() - - // Declare the completion of the station goals - mode.declare_station_goal_completion() - - //Ask the event manager to print round end information - SSevents.RoundEnd() - - // Add AntagHUD to everyone, see who was really evil the whole time! - for(var/datum/atom_hud/antag/H in huds) - for(var/m in GLOB.player_list) - var/mob/M = m - H.add_hud_to(M) - - return 1 - -/datum/controller/subsystem/ticker/proc/HasRoundStarted() - return current_state >= GAME_STATE_PLAYING - -/datum/controller/subsystem/ticker/proc/IsRoundInProgress() - return current_state == GAME_STATE_PLAYING \ No newline at end of file +SUBSYSTEM_DEF(ticker) + name = "Ticker" + init_order = INIT_ORDER_TICKER + + priority = FIRE_PRIORITY_TICKER + flags = SS_KEEP_TIMING + runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME + + var/round_start_time = 0 + var/const/restart_timeout = 600 + var/current_state = GAME_STATE_STARTUP + var/force_start = 0 // Do we want to force-start as soon as we can + var/force_ending = 0 + var/hide_mode = 0 // leave here at 0 ! setup() will take care of it when needed for Secret mode -walter0o + var/datum/game_mode/mode = null + var/event_time = null + var/event = 0 + var/login_music // music played in pregame lobby + var/list/datum/mind/minds = list()//The people in the game. Used for objective tracking. + var/Bible_icon_state // icon_state the chaplain has chosen for his bible + var/Bible_item_state // item_state the chaplain has chosen for his bible + var/Bible_name // name of the bible + var/Bible_deity_name + var/datum/cult_info/cultdat = null //here instead of cult for adminbus purposes + var/random_players = 0 // if set to nonzero, ALL players who latejoin or declare-ready join will have random appearances/genders + var/list/syndicate_coalition = list() // list of traitor-compatible factions + var/list/factions = list() // list of all factions + var/list/availablefactions = list() // list of factions with openings + var/tipped = FALSE //Did we broadcast the tip of the day yet? + var/selected_tip // What will be the tip of the day? + var/pregame_timeleft // This is used for calculations + var/delay_end = 0 //if set to nonzero, the round will not restart on it's own + var/triai = 0//Global holder for Triumvirate + var/initialtpass = 0 //holder for inital autotransfer vote timer + var/obj/screen/cinematic = null //used for station explosion cinematic + var/round_end_announced = 0 // Spam Prevention. Announce round end only once.\ + +/datum/controller/subsystem/ticker/Initialize() + login_music = pick(\ + 'sound/music/thunderdome.ogg',\ + 'sound/music/space.ogg',\ + 'sound/music/title1.ogg',\ + 'sound/music/title2.ogg',\ + 'sound/music/title3.ogg',) + + // Map name + if(using_map && using_map.name) + GLOB.map_name = "[using_map.name]" + else + GLOB.map_name = "Unknown" + + // World name + if(config && config.server_name) + world.name = "[config.server_name]: [station_name()]" + else + world.name = station_name() + + return ..() + + +/datum/controller/subsystem/ticker/fire() + switch(current_state) + if(GAME_STATE_STARTUP) + // This is ran as soon as the MC starts firing, and should only run ONCE, unless startup fails + round_start_time = world.time + (config.pregame_timestart * 10) + to_chat(world, "Welcome to the pre-game lobby!") + to_chat(world, "Please, setup your character and select ready. Game will start in [config.pregame_timestart] seconds") + current_state = GAME_STATE_PREGAME + fire() // TG says this is a good idea + if(GAME_STATE_PREGAME) + if(!going) + return + + // This is so we dont have sleeps in controllers, because that is a bad, bad thing + if(!delay_end) + pregame_timeleft = max(0,round_start_time - world.time) // Normal lobby countdown when roundstart was not delayed + else + pregame_timeleft = max(0,pregame_timeleft - 20) // If roundstart was delayed, we should resume the countdown where it left off + + if(pregame_timeleft <= 600 && !tipped) // 60 seconds + send_tip_of_the_round() + tipped = TRUE + + if(pregame_timeleft <= 0 || force_start) + current_state = GAME_STATE_SETTING_UP + Master.SetRunLevel(RUNLEVEL_SETUP) + if(GAME_STATE_SETTING_UP) + if(!setup()) // Setup failed + current_state = GAME_STATE_STARTUP + Master.SetRunLevel(RUNLEVEL_LOBBY) + if(GAME_STATE_PLAYING) + delay_end = 0 // reset this in case round start was delayed + mode.process() + mode.process_job_tasks() + var/game_finished = SSshuttle.emergency.mode >= SHUTTLE_ENDGAME || mode.station_was_nuked + if(config.continuous_rounds) + mode.check_finished() // some modes contain var-changing code in here, so call even if we don't uses result + else + game_finished |= mode.check_finished() + if(game_finished) + current_state = GAME_STATE_FINISHED + if(GAME_STATE_FINISHED) + current_state = GAME_STATE_FINISHED + Master.SetRunLevel(RUNLEVEL_POSTGAME) // This shouldnt process more than once, but you never know + auto_toggle_ooc(1) // Turn it on + + spawn(0) + declare_completion() + + spawn(50) + if(mode.station_was_nuked) + world.Reboot("Station destroyed by Nuclear Device.", "end_proper", "nuke") + else + world.Reboot("Round ended.", "end_proper", "proper completion") + + +/datum/controller/subsystem/ticker/proc/votetimer() + var/timerbuffer = 0 + if(initialtpass == 0) + timerbuffer = config.vote_autotransfer_initial + else + timerbuffer = config.vote_autotransfer_interval + spawn(timerbuffer) + SSvote.autotransfer() + initialtpass = 1 + votetimer() + + +/datum/controller/subsystem/ticker/proc/setup() + cultdat = setupcult() + //Create and announce mode + if(master_mode=="secret") + src.hide_mode = 1 + var/list/datum/game_mode/runnable_modes + if((master_mode=="random") || (master_mode=="secret")) + runnable_modes = config.get_runnable_modes() + if(runnable_modes.len==0) + current_state = GAME_STATE_PREGAME + Master.SetRunLevel(RUNLEVEL_LOBBY) + to_chat(world, "Unable to choose playable game mode. Reverting to pre-game lobby.") + return 0 + if(secret_force_mode != "secret") + var/datum/game_mode/M = config.pick_mode(secret_force_mode) + if(M.can_start()) + src.mode = config.pick_mode(secret_force_mode) + SSjobs.ResetOccupations() + if(!src.mode) + src.mode = pickweight(runnable_modes) + if(src.mode) + var/mtype = src.mode.type + src.mode = new mtype + else + src.mode = config.pick_mode(master_mode) + if(!src.mode.can_start()) + to_chat(world, "Unable to start [mode.name]. Not enough players, [mode.required_players] players needed. Reverting to pre-game lobby.") + mode = null + current_state = GAME_STATE_PREGAME + SSjobs.ResetOccupations() + Master.SetRunLevel(RUNLEVEL_LOBBY) + return 0 + + //Configure mode and assign player to special mode stuff + src.mode.pre_pre_setup() + var/can_continue + can_continue = src.mode.pre_setup()//Setup special modes + SSjobs.DivideOccupations() //Distribute jobs + if(!can_continue) + qdel(mode) + current_state = GAME_STATE_PREGAME + to_chat(world, "Error setting up [master_mode]. Reverting to pre-game lobby.") + SSjobs.ResetOccupations() + Master.SetRunLevel(RUNLEVEL_LOBBY) + return 0 + + if(hide_mode) + var/list/modes = new + for(var/datum/game_mode/M in runnable_modes) + modes+=M.name + modes = sortList(modes) + to_chat(world, "The current game mode is - Secret!") + to_chat(world, "Possibilities: [english_list(modes)]") + else + src.mode.announce() + + create_characters() //Create player characters and transfer them + populate_spawn_points() + collect_minds() + equip_characters() + data_core.manifest() + current_state = GAME_STATE_PLAYING + Master.SetRunLevel(RUNLEVEL_GAME) + + callHook("roundstart") + + //here to initialize the random events nicely at round start + setup_economy() + setupfactions() + + //shuttle_controller.setup_shuttle_docks() + + spawn(0)//Forking here so we dont have to wait for this to finish + if(!GLOB.syndicate_code_phrase) + GLOB.syndicate_code_phrase = generate_code_phrase() + if(!GLOB.syndicate_code_response) + GLOB.syndicate_code_response = generate_code_phrase() + mode.post_setup() + //Cleanup some stuff + for(var/obj/effect/landmark/start/S in GLOB.landmarks_list) + //Deleting Startpoints but we need the ai point to AI-ize people later + if(S.name != "AI") + qdel(S) + + // take care of random spesspod spawning + var/list/obj/effect/landmark/spacepod/random/L = list() + for(var/obj/effect/landmark/spacepod/random/SS in GLOB.landmarks_list) + if(istype(SS)) + L += SS + if(L.len) + var/obj/effect/landmark/spacepod/random/S = pick(L) + new /obj/spacepod/random(S.loc) + for(var/obj/effect/landmark/spacepod/random/R in L) + qdel(R) + + to_chat(world, "Enjoy the game!") + world << sound('sound/AI/welcome.ogg')// Skie + + if(SSholiday.holidays) + to_chat(world, "and...") + for(var/holidayname in SSholiday.holidays) + var/datum/holiday/holiday = SSholiday.holidays[holidayname] + to_chat(world, "

[holiday.greet()]

") + + spawn(0) // Forking dynamic room selection + var/list/area/dynamic/source/available_source_candidates = subtypesof(/area/dynamic/source) + var/list/area/dynamic/destination/available_destination_candidates = subtypesof(/area/dynamic/destination) + + for(var/area/dynamic/destination/current_destination_candidate in available_destination_candidates) + var/area/dynamic/destination/current_destination = locate(current_destination_candidate) + + if(!current_destination) + continue + + if(current_destination.match_width == 0 || current_destination.match_height == 0) + message_admins("Dynamic area destination '[current_destination.name]' does not have its size requirements set.") + continue + + var/list/area/dynamic/source/candidate_source_areas = new /list(0) + for(var/area/dynamic/source/candidate_source_area in available_source_candidates) + var/area/dynamic/source/candidate_source = locate(candidate_source_area) + + if(!candidate_source) + continue + + if(candidate_source.match_tag != current_destination.match_tag) + continue + + if(candidate_source.match_width != current_destination.match_width || \ + candidate_source.match_height != current_destination.match_height) + continue + + candidate_source_areas += candidate_source + + if(candidate_source_areas.len == 0) + message_admins("Failed to find a matching source for dynamic area: [current_destination.name]") + continue + + var/area/dynamic/source/selected_source = pick(candidate_source_areas) + available_source_candidates -= selected_source + + selected_source.copy_contents_to(current_destination, 0) + + if(current_destination.enable_lights || selected_source.enable_lights) + current_destination.power_light = 1 + else + current_destination.power_light = 0 + current_destination.power_change() + + //start_events() //handles random events and space dust. + //new random event system is handled from the MC. + + var/list/admins_number = staff_countup(R_BAN) + if(admins_number[1] == 0 && admins_number[3] == 0) + send2irc(config.admin_notify_irc, "Round has started with no admins online.") + auto_toggle_ooc(0) // Turn it off + round_start_time = world.time + + if(config.sql_enabled) + spawn(3000) + statistic_cycle() // Polls population totals regularly and stores them in an SQL DB + + votetimer() + + for(var/mob/new_player/N in GLOB.mob_list) + if(N.client) + N.new_player_panel_proc() + + return 1 + +/datum/controller/subsystem/ticker/proc/station_explosion_cinematic(station_missed = 0, override = null) + if(cinematic) + return //already a cinematic in progress! + + auto_toggle_ooc(1) // Turn it on + //initialise our cinematic screen object + cinematic = new /obj/screen(src) + cinematic.icon = 'icons/effects/station_explosion.dmi' + cinematic.icon_state = "station_intact" + cinematic.layer = 21 + cinematic.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + cinematic.screen_loc = "1,0" + + if(station_missed) + for(var/mob/M in GLOB.mob_list) + if(M.client) + M.client.screen += cinematic //show every client the cinematic + else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived" + for(var/mob/M in GLOB.mob_list) + if(M.stat != DEAD) + var/turf/T = get_turf(M) + if(T && is_station_level(T.z) && !istype(M.loc, /obj/structure/closet/secure_closet/freezer)) + var/mob/ghost = M.ghostize() + M.dust() //no mercy + if(ghost && ghost.client) //Play the victims an uninterrupted cinematic. + ghost.client.screen += cinematic + CHECK_TICK + if(M && M.client) //Play the survivors a cinematic. + M.client.screen += cinematic + + //Now animate the cinematic + switch(station_missed) + if(1) //nuke was nearby but (mostly) missed + if(mode && !override) + override = mode.name + switch(override) + if("nuclear emergency") //Nuke wasn't on station when it blew up + flick("intro_nuke", cinematic) + sleep(35) + world << sound('sound/effects/explosionfar.ogg') + flick("station_intact_fade_red", cinematic) + cinematic.icon_state = "summary_nukefail" + if("fake") //The round isn't over, we're just freaking people out for fun + flick("intro_nuke", cinematic) + sleep(35) + world << sound('sound/items/bikehorn.ogg') + flick("summary_selfdes", cinematic) + else + flick("intro_nuke", cinematic) + sleep(35) + world << sound('sound/effects/explosionfar.ogg') + + + if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation + sleep(50) + world << sound('sound/effects/explosionfar.ogg') + else //station was destroyed + if(mode && !override) + override = mode.name + switch(override) + if("nuclear emergency") //Nuke Ops successfully bombed the station + flick("intro_nuke", cinematic) + sleep(35) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_nukewin" + if("AI malfunction") //Malf (screen,explosion,summary) + flick("intro_malf", cinematic) + sleep(76) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_malf" + if("blob") //Station nuked (nuke,explosion,summary) + flick("intro_nuke", cinematic) + sleep(35) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_selfdes" + else //Station nuked (nuke,explosion,summary) + flick("intro_nuke", cinematic) + sleep(35) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_selfdes" + //If its actually the end of the round, wait for it to end. + //Otherwise if its a verb it will continue on afterwards. + spawn(300) + QDEL_NULL(cinematic) //end the cinematic + + + +/datum/controller/subsystem/ticker/proc/create_characters() + for(var/mob/new_player/player in GLOB.player_list) + if(player.ready && player.mind) + if(player.mind.assigned_role == "AI") + player.close_spawn_windows() + var/mob/living/silicon/ai/ai_character = player.AIize() + ai_character.moveToAILandmark() + else if(!player.mind.assigned_role) + continue + else + player.create_character() + qdel(player) + + +/datum/controller/subsystem/ticker/proc/collect_minds() + for(var/mob/living/player in GLOB.player_list) + if(player.mind) + SSticker.minds += player.mind + + +/datum/controller/subsystem/ticker/proc/equip_characters() + var/captainless=1 + for(var/mob/living/carbon/human/player in GLOB.player_list) + if(player && player.mind && player.mind.assigned_role) + if(player.mind.assigned_role == "Captain") + captainless=0 + if(player.mind.assigned_role != player.mind.special_role) + SSjobs.AssignRank(player, player.mind.assigned_role, 0) + SSjobs.EquipRank(player, player.mind.assigned_role, 0) + EquipCustomItems(player) + if(captainless) + for(var/mob/M in GLOB.player_list) + if(!istype(M,/mob/new_player)) + to_chat(M, "Captainship not forced on anyone.") + +/datum/controller/subsystem/ticker/proc/send_tip_of_the_round() + var/m + if(selected_tip) + m = selected_tip + else + var/list/randomtips = file2list("strings/tips.txt") + var/list/memetips = file2list("strings/sillytips.txt") + if(randomtips.len && prob(95)) + m = pick(randomtips) + else if(memetips.len) + m = pick(memetips) + + if(m) + to_chat(world, "Tip of the round: [html_encode(m)]") + +/datum/controller/subsystem/ticker/proc/getfactionbyname(var/name) + for(var/datum/faction/F in factions) + if(F.name == name) + return F + + +/datum/controller/subsystem/ticker/proc/declare_completion() + nologevent = 1 //end of round murder and shenanigans are legal; there's no need to jam up attack logs past this point. + //Round statistics report + var/datum/station_state/end_state = new /datum/station_state() + end_state.count() + var/station_integrity = min(round( 100.0 * start_state.score(end_state), 0.1), 100.0) + + to_chat(world, "
[TAB]Shift Duration: [round(ROUND_TIME / 36000)]:[add_zero("[ROUND_TIME / 600 % 60]", 2)]:[ROUND_TIME / 100 % 6][ROUND_TIME / 100 % 10]") + to_chat(world, "
[TAB]Station Integrity: [mode.station_was_nuked ? "Destroyed" : "[station_integrity]%"]") + to_chat(world, "
") + + //Silicon laws report + for(var/mob/living/silicon/ai/aiPlayer in GLOB.mob_list) + if(aiPlayer.stat != 2) + to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws at the end of the game were:") + else + to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws when it was deactivated were:") + aiPlayer.show_laws(1) + + if(aiPlayer.connected_robots.len) + var/robolist = "The AI's loyal minions were: " + for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots) + robolist += "[robo.name][robo.stat?" (Deactivated) (Played by: [robo.key]), ":" (Played by: [robo.key]), "]" + to_chat(world, "[robolist]") + + var/dronecount = 0 + + for(var/mob/living/silicon/robot/robo in GLOB.mob_list) + + if(istype(robo,/mob/living/silicon/robot/drone)) + dronecount++ + continue + + if(!robo.connected_ai) + if(robo.stat != 2) + to_chat(world, "[robo.name] (Played by: [robo.key]) survived as an AI-less borg! Its laws were:") + else + to_chat(world, "[robo.name] (Played by: [robo.key]) was unable to survive the rigors of being a cyborg without an AI. Its laws were:") + + if(robo) //How the hell do we lose robo between here and the world messages directly above this? + robo.laws.show_laws(world) + + if(dronecount) + to_chat(world, "There [dronecount>1 ? "were" : "was"] [dronecount] industrious maintenance [dronecount>1 ? "drones" : "drone"] this round.") + + if(mode.eventmiscs.len) + var/emobtext = "" + for(var/datum/mind/eventmind in mode.eventmiscs) + emobtext += printeventplayer(eventmind) + emobtext += "
" + emobtext += printobjectives(eventmind) + emobtext += "
" + emobtext += "
" + to_chat(world, emobtext) + + mode.declare_completion()//To declare normal completion. + + //calls auto_declare_completion_* for all modes + for(var/handler in typesof(/datum/game_mode/proc)) + if(findtext("[handler]","auto_declare_completion_")) + call(mode, handler)() + + scoreboard() + + // Declare the completion of the station goals + mode.declare_station_goal_completion() + + //Ask the event manager to print round end information + SSevents.RoundEnd() + + // Add AntagHUD to everyone, see who was really evil the whole time! + for(var/datum/atom_hud/antag/H in huds) + for(var/m in GLOB.player_list) + var/mob/M = m + H.add_hud_to(M) + + return 1 + +/datum/controller/subsystem/ticker/proc/HasRoundStarted() + return current_state >= GAME_STATE_PLAYING + +/datum/controller/subsystem/ticker/proc/IsRoundInProgress() + return current_state == GAME_STATE_PLAYING diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 6a087b920d4..b9538fdc312 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -1,775 +1,775 @@ -/obj/mecha - name = "Mecha" - desc = "Exosuit" - icon = 'icons/mecha/mecha.dmi' - density = 1 //Dense. To raise the heat. - opacity = 1 ///opaque. Menacing. - anchored = 1 //no pulling around. - resistance_flags = FIRE_PROOF | ACID_PROOF - layer = MOB_LAYER //icon draw layer - infra_luminosity = 15 //byond implementation is bugged. - force = 5 - max_integrity = 300 //max_integrity is base health - armor = list(melee = 20, bullet = 10, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 100) - var/list/facing_modifiers = list(MECHA_FRONT_ARMOUR = 1.5, MECHA_SIDE_ARMOUR = 1, MECHA_BACK_ARMOUR = 0.5) - var/ruin_mecha = FALSE //if the mecha starts on a ruin, don't automatically give it a tracking beacon to prevent metagaming. - var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items) - var/can_move = 0 // time of next allowed movement - var/mob/living/carbon/occupant = null - var/step_in = 10 //make a step in step_in/10 sec. - var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South. - var/normal_step_energy_drain = 10 - var/step_energy_drain = 10 - var/melee_energy_drain = 15 - var/overload_step_energy_drain_min = 100 - var/deflect_chance = 10 //chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. - var/obj/item/stock_parts/cell/cell - var/state = 0 - var/list/log = new - var/last_message = 0 - var/add_req_access = 1 - var/maint_access = 1 - var/dna //dna-locking the mech - var/list/proc_res = list() //stores proc owners, like proc_res["functionname"] = owner reference - var/datum/effect_system/spark_spread/spark_system = new - var/lights = 0 - var/lights_power = 6 - var/emagged = FALSE - - //inner atmos - var/use_internal_tank = 0 - var/internal_tank_valve = ONE_ATMOSPHERE - var/obj/machinery/portable_atmospherics/canister/internal_tank - var/datum/gas_mixture/cabin_air - var/obj/machinery/atmospherics/unary/portables_connector/connected_port = null - - var/obj/item/radio/radio = null - var/list/trackers = list() - - var/max_temperature = 25000 - var/internal_damage_threshold = 50 //health percentage below which internal damage is possible - var/internal_damage = 0 //contains bitflags - - var/list/operation_req_access = list()//required access level for mecha operation +/obj/mecha + name = "Mecha" + desc = "Exosuit" + icon = 'icons/mecha/mecha.dmi' + density = 1 //Dense. To raise the heat. + opacity = 1 ///opaque. Menacing. + anchored = 1 //no pulling around. + resistance_flags = FIRE_PROOF | ACID_PROOF + layer = MOB_LAYER //icon draw layer + infra_luminosity = 15 //byond implementation is bugged. + force = 5 + max_integrity = 300 //max_integrity is base health + armor = list(melee = 20, bullet = 10, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 100) + var/list/facing_modifiers = list(MECHA_FRONT_ARMOUR = 1.5, MECHA_SIDE_ARMOUR = 1, MECHA_BACK_ARMOUR = 0.5) + var/ruin_mecha = FALSE //if the mecha starts on a ruin, don't automatically give it a tracking beacon to prevent metagaming. + var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items) + var/can_move = 0 // time of next allowed movement + var/mob/living/carbon/occupant = null + var/step_in = 10 //make a step in step_in/10 sec. + var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South. + var/normal_step_energy_drain = 10 + var/step_energy_drain = 10 + var/melee_energy_drain = 15 + var/overload_step_energy_drain_min = 100 + var/deflect_chance = 10 //chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. + var/obj/item/stock_parts/cell/cell + var/state = 0 + var/list/log = new + var/last_message = 0 + var/add_req_access = 1 + var/maint_access = 1 + var/dna //dna-locking the mech + var/list/proc_res = list() //stores proc owners, like proc_res["functionname"] = owner reference + var/datum/effect_system/spark_spread/spark_system = new + var/lights = 0 + var/lights_power = 6 + var/emagged = FALSE + + //inner atmos + var/use_internal_tank = 0 + var/internal_tank_valve = ONE_ATMOSPHERE + var/obj/machinery/portable_atmospherics/canister/internal_tank + var/datum/gas_mixture/cabin_air + var/obj/machinery/atmospherics/unary/portables_connector/connected_port = null + + var/obj/item/radio/radio = null + var/list/trackers = list() + + var/max_temperature = 25000 + var/internal_damage_threshold = 50 //health percentage below which internal damage is possible + var/internal_damage = 0 //contains bitflags + + var/list/operation_req_access = list()//required access level for mecha operation var/list/internals_req_access = list(ACCESS_ENGINE,ACCESS_ROBOTICS)//required access level to open cell compartment - - var/wreckage - - var/list/equipment = new - var/obj/item/mecha_parts/mecha_equipment/selected - var/max_equip = 3 - var/datum/events/events - var/turf/crashing = null - var/occupant_sight_flags = 0 - - var/stepsound = 'sound/mecha/mechstep.ogg' - var/turnsound = 'sound/mecha/mechturn.ogg' - var/nominalsound = 'sound/mecha/nominal.ogg' - var/zoomsound = 'sound/mecha/imag_enh.ogg' - var/critdestrsound = 'sound/mecha/critdestr.ogg' - var/weapdestrsound = 'sound/mecha/weapdestr.ogg' - var/lowpowersound = 'sound/mecha/lowpower.ogg' - var/longactivationsound = 'sound/mecha/nominal.ogg' - var/starting_voice = /obj/item/mecha_modkit/voice - var/activated = FALSE - var/power_warned = FALSE - - var/destruction_sleep_duration = 1 //Time that mech pilot is put to sleep for if mech is destroyed - - var/melee_cooldown = 10 - var/melee_can_hit = 1 - - // Action vars - var/defence_mode = FALSE - var/defence_mode_deflect_chance = 35 - var/leg_overload_mode = FALSE - var/leg_overload_coeff = 100 - var/thrusters_active = FALSE - var/smoke = 5 - var/smoke_ready = 1 - var/smoke_cooldown = 100 - var/zoom_mode = FALSE - var/phasing = FALSE - var/phasing_energy_drain = 200 - var/phase_state = "" //icon_state when phasing - - hud_possible = list (DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_TRACK_HUD) - -/obj/mecha/Initialize() - . = ..() - events = new - icon_state += "-open" - add_radio() - add_cabin() - add_airtank() - spark_system.set_up(2, 0, src) - spark_system.attach(src) - smoke_system.set_up(3, src) - smoke_system.attach(src) - add_cell() - START_PROCESSING(SSobj, src) - GLOB.poi_list |= src - log_message("[src] created.") - GLOB.mechas_list += src //global mech list - prepare_huds() - for(var/datum/atom_hud/data/diagnostic/diag_hud in huds) - diag_hud.add_to_hud(src) - diag_hud_set_mechhealth() - diag_hud_set_mechcell() - diag_hud_set_mechstat() - diag_hud_set_mechtracking() - - var/obj/item/mecha_modkit/voice/V = new starting_voice(src) - V.install(src) - qdel(V) - -//////////////////////// -////// Helpers ///////// -//////////////////////// - -/obj/mecha/get_cell() - return cell - -/obj/mecha/proc/add_airtank() - internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src) - return internal_tank - -/obj/mecha/proc/add_cell(var/obj/item/stock_parts/cell/C=null) - if(C) - C.forceMove(src) - cell = C - return - cell = new/obj/item/stock_parts/cell/high/plus(src) - -/obj/mecha/proc/add_cabin() - cabin_air = new - cabin_air.temperature = T20C - cabin_air.volume = 200 - cabin_air.oxygen = O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) - cabin_air.nitrogen = N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) - return cabin_air - -/obj/mecha/proc/add_radio() - radio = new(src) - radio.name = "[src] radio" - radio.icon = icon - radio.icon_state = icon_state - radio.subspace_transmission = 1 - -/obj/mecha/examine(mob/user) - . = ..() - var/integrity = obj_integrity * 100 / max_integrity - switch(integrity) - if(85 to 100) - . += "It's fully intact." - if(65 to 85) - . += "It's slightly damaged." - if(45 to 65) - . += "It's badly damaged." - if(25 to 45) - . += "It's heavily damaged." - else - . += "It's falling apart." - if(equipment && equipment.len) - . += "It's equipped with:" - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - . += "[bicon(ME)] [ME]" - -/obj/mecha/hear_talk(mob/M, list/message_pieces) - if(M == occupant && radio.broadcasting) - radio.talk_into(M, message_pieces) - -/obj/mecha/proc/click_action(atom/target, mob/user, params) - if(!occupant || occupant != user ) - return - if(user.incapacitated()) - return - if(phasing) - occupant_message("Unable to interact with objects while phasing.") - return - if(state) - occupant_message("Maintenance protocols in effect.") - return - if(!get_charge()) - return - if(src == target) - return - - var/dir_to_target = get_dir(src, target) - if(dir_to_target && !(dir_to_target & dir))//wrong direction - return - - if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) - target = safepick(view(3,target)) - if(!target) - return - - var/mob/living/L = user - if(!target.Adjacent(src)) - if(selected && selected.is_ranged()) - if(HAS_TRAIT(L, TRAIT_PACIFISM) && selected.harmful) - to_chat(L, "You don't want to harm other living beings!") - return - selected.action(target, params) - else if(selected && selected.is_melee()) - if(isliving(target) && selected.harmful && HAS_TRAIT(L, TRAIT_PACIFISM)) - to_chat(user, "You don't want to harm other living beings!") - return - selected.action(target, params) - else - if(internal_damage & MECHA_INT_CONTROL_LOST) - target = safepick(oview(1, src)) - if(!melee_can_hit || !isatom(target)) - return - target.mech_melee_attack(src) - melee_can_hit = 0 - spawn(melee_cooldown) - melee_can_hit = 1 - -/obj/mecha/proc/mech_toxin_damage(mob/living/target) - playsound(src, 'sound/effects/spray2.ogg', 50, 1) - if(target.reagents) - if(target.reagents.get_reagent_amount("atropine") + force < force*2) - target.reagents.add_reagent("atropine", force/2) - if(target.reagents.get_reagent_amount("toxin") + force < force*2) - target.reagents.add_reagent("toxin", force/2.5) - -/obj/mecha/proc/range_action(atom/target) - return - - -////////////////////////////////// -//////// Movement procs //////// -////////////////////////////////// - -/obj/mecha/Move(atom/newLoc, direct) - . = ..() - if(.) - events.fireEvent("onMove",get_turf(src)) - -/obj/mecha/Process_Spacemove(var/movement_dir = 0) - . = ..() - if(.) - return 1 - if(thrusters_active && movement_dir && use_power(step_energy_drain)) - return 1 - - var/atom/movable/backup = get_spacemove_backup() - if(backup) - if(istype(backup) && movement_dir && !backup.anchored) - if(backup.newtonian_move(turn(movement_dir, 180))) - if(occupant) - to_chat(occupant, "You push off of [backup] to propel yourself.") - return 1 - -/obj/mecha/relaymove(mob/user, direction) - if(!direction) - return - if(user != occupant) //While not "realistic", this piece is player friendly. - user.forceMove(get_turf(src)) - to_chat(user, "You climb out from [src].") - return 0 - if(connected_port) - if(world.time - last_message > 20) - occupant_message("Unable to move while connected to the air system port!") - last_message = world.time - return 0 - if(state) - occupant_message("Maintenance protocols in effect.") - return - return domove(direction) - -/obj/mecha/proc/domove(direction) - if(can_move >= world.time) - return 0 - if(!Process_Spacemove(direction)) - return 0 - if(!has_charge(step_energy_drain)) - return 0 - if(defence_mode) - if(world.time - last_message > 20) - occupant_message("Unable to move while in defence mode.") - last_message = world.time - return 0 - if(zoom_mode) - if(world.time - last_message > 20) - occupant_message("Unable to move while in zoom mode.") - last_message = world.time - return 0 - - var/move_result = 0 - var/move_type = 0 - if(internal_damage & MECHA_INT_CONTROL_LOST) - move_result = mechsteprand() - move_type = MECHAMOVE_RAND - else if(dir != direction) - move_result = mechturn(direction) - move_type = MECHAMOVE_TURN - else - move_result = mechstep(direction) - move_type = MECHAMOVE_STEP - - if(move_result && move_type) - aftermove(move_type) - can_move = world.time + step_in - return TRUE - return FALSE - -/obj/mecha/proc/aftermove(move_type) - use_power(step_energy_drain) - if(move_type & (MECHAMOVE_RAND | MECHAMOVE_STEP) && occupant) - var/obj/machinery/atmospherics/unary/portables_connector/possible_port = locate(/obj/machinery/atmospherics/unary/portables_connector) in loc - if(possible_port) - var/obj/screen/alert/mech_port_available/A = occupant.throw_alert("mechaport", /obj/screen/alert/mech_port_available, override = TRUE) - if(A) - A.target = possible_port - else - occupant.clear_alert("mechaport") - if(leg_overload_mode) - if(obj_integrity < max_integrity - max_integrity / 3) - leg_overload_mode = FALSE - step_in = initial(step_in) - step_energy_drain = initial(step_energy_drain) - occupant_message("Leg actuators damage threshold exceded. Disabling overload.") - -/obj/mecha/proc/mechturn(direction) - dir = direction - if(turnsound) - playsound(src,turnsound,40,1) - return 1 - -/obj/mecha/proc/mechstep(direction) - . = step(src, direction) - if(!.) - if(phasing && get_charge() >= phasing_energy_drain) - if(can_move < world.time) - . = FALSE // We lie to mech code and say we didn't get to move, because we want to handle power usage + cooldown ourself - flick("phazon-phase", src) - forceMove(get_step(src, direction)) - use_power(phasing_energy_drain) - playsound(src, stepsound, 40, 1) - can_move = world.time + (step_in * 3) - else if(stepsound) - playsound(src, stepsound, 40, 1) - -/obj/mecha/proc/mechsteprand() - . = step_rand(src) - if(. && stepsound) - playsound(src, stepsound, 40, 1) - -/obj/mecha/Bump(var/atom/obstacle, bump_allowed) - if(throwing) //high velocity mechas in your face! - var/breakthrough = 0 - if(istype(obstacle, /obj/structure/window)) - qdel(obstacle) - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/grille/)) - var/obj/structure/grille/G = obstacle - G.obj_break() - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/table)) - var/obj/structure/table/T = obstacle - qdel(T) - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/rack)) - new /obj/item/rack_parts(obstacle.loc) - qdel(obstacle) - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/reagent_dispensers/fueltank)) - obstacle.ex_act(1) - - else if(isliving(obstacle)) - var/mob/living/L = obstacle - var/hit_sound = list('sound/weapons/genhit1.ogg','sound/weapons/genhit2.ogg','sound/weapons/genhit3.ogg') - if(L.flags & GODMODE) - return - L.take_overall_damage(5,0) - if(L.buckled) - L.buckled = 0 - L.Stun(5) - L.Weaken(5) - L.apply_effect(STUTTER, 5) - playsound(src, pick(hit_sound), 50, 0, 0) - breakthrough = 1 - - else - if(throwing) - throwing.finalize(FALSE) - crashing = null - - ..() - - if(breakthrough) - if(crashing) - spawn(1) - throw_at(crashing, 50, throw_speed) - else - spawn(1) - crashing = get_distant_turf(get_turf(src), dir, 3)//don't use get_dir(src, obstacle) or the mech will stop if he bumps into a one-direction window on his tile. - throw_at(crashing, 50, throw_speed) - - else - if(bump_allowed) - if(..()) - return - if(isobj(obstacle)) - var/obj/O = obstacle - if(istype(O, /obj/effect/portal)) //derpfix - anchored = 0 - O.Bumped(src) - spawn(0) //countering portal teleport spawn(0), hurr - anchored = 1 - else if(!O.anchored) - step(obstacle, dir) - else if(ismob(obstacle)) - step(obstacle, dir) - - -/////////////////////////////////// -//////// Internal damage //////// -/////////////////////////////////// - -/obj/mecha/proc/check_for_internal_damage(list/possible_int_damage, ignore_threshold=null) - if(!islist(possible_int_damage) || isemptylist(possible_int_damage)) - return - if(prob(20)) - if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold) - for(var/T in possible_int_damage) - if(internal_damage & T) - possible_int_damage -= T - var/int_dam_flag = safepick(possible_int_damage) - if(int_dam_flag) - setInternalDamage(int_dam_flag) - if(prob(5)) - if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold) - var/obj/item/mecha_parts/mecha_equipment/ME = safepick(equipment) - if(ME) - qdel(ME) - -/obj/mecha/proc/hasInternalDamage(int_dam_flag=null) - return int_dam_flag ? internal_damage&int_dam_flag : internal_damage - - -/obj/mecha/proc/setInternalDamage(int_dam_flag) - internal_damage |= int_dam_flag - log_append_to_last("Internal damage of type [int_dam_flag].",1) - occupant << sound('sound/machines/warning-buzzer.ogg',wait=0) - diag_hud_set_mechstat() - -/obj/mecha/proc/clearInternalDamage(int_dam_flag) - internal_damage &= ~int_dam_flag - switch(int_dam_flag) - if(MECHA_INT_TEMP_CONTROL) - occupant_message("Life support system reactivated.") - if(MECHA_INT_FIRE) - occupant_message("Internal fire extinquished.") - if(MECHA_INT_TANK_BREACH) - occupant_message("Damaged internal tank has been sealed.") - diag_hud_set_mechstat() - - -//////////////////////////////////////// -//////// Health related procs //////// -//////////////////////////////////////// - -/obj/mecha/proc/get_armour_facing(relative_dir) - switch(relative_dir) - if(0) // BACKSTAB! - return facing_modifiers[MECHA_BACK_ARMOUR] - if(45, 90, 270, 315) - return facing_modifiers[MECHA_SIDE_ARMOUR] - if(225, 180, 135) - return facing_modifiers[MECHA_FRONT_ARMOUR] - return 1 //always return non-0 - -/obj/mecha/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) - . = ..() - if(. && obj_integrity > 0) - spark_system.start() - switch(damage_flag) - if("fire") - check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL)) - if("melee") - check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - else - check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT)) - if(. >= 5 || prob(33)) - occupant_message("Taking damage!") - log_message("Took [damage_amount] points of damage. Damage type: [damage_type]") - -/obj/mecha/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) - . = ..() - if(!damage_amount) - return 0 - var/booster_deflection_modifier = 1 - var/booster_damage_modifier = 1 - if(damage_flag == "bullet" || damage_flag == "laser" || damage_flag == "energy") - for(var/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/B in equipment) - if(B.projectile_react()) - booster_deflection_modifier = B.deflect_coeff - booster_damage_modifier = B.damage_coeff - break - else if(damage_flag == "melee") - for(var/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/B in equipment) - if(B.attack_react()) - booster_deflection_modifier *= B.deflect_coeff - booster_damage_modifier *= B.damage_coeff - break - - if(attack_dir) - var/facing_modifier = get_armour_facing(dir2angle(attack_dir) - dir2angle(src)) - booster_damage_modifier /= facing_modifier - booster_deflection_modifier *= facing_modifier - if(prob(deflect_chance * booster_deflection_modifier)) - visible_message("[src]'s armour deflects the attack!") - log_message("Armor saved.") - return 0 - if(.) - . *= booster_damage_modifier - -/obj/mecha/attack_hand(mob/living/user) - user.changeNext_move(CLICK_CD_MELEE) - user.do_attack_animation(src, ATTACK_EFFECT_PUNCH) - playsound(loc, 'sound/weapons/tap.ogg', 40, 1, -1) - user.visible_message("[user] hits [name]. Nothing happens", "You hit [name] with no visible effect.") - log_message("Attack by hand/paw. Attacker - [user].") - - -/obj/mecha/attack_alien(mob/living/user) - log_message("Attack by alien. Attacker - [user].", TRUE) - playsound(src.loc, 'sound/weapons/slash.ogg', 100, TRUE) - attack_generic(user, 15, BRUTE, "melee", 0) - -/obj/mecha/attack_animal(mob/living/simple_animal/user) - log_message("Attack by simple animal. Attacker - [user].") - if(!user.melee_damage_upper && !user.obj_damage) - user.custom_emote(1, "[user.friendly] [src].") - return FALSE - else - var/play_soundeffect = 1 - if(user.environment_smash) - play_soundeffect = 0 - playsound(src, 'sound/effects/bang.ogg', 50, TRUE) - var/animal_damage = rand(user.melee_damage_lower,user.melee_damage_upper) - if(user.obj_damage) - animal_damage = user.obj_damage - animal_damage = min(animal_damage, 20*user.environment_smash) - user.create_attack_log("attacked [name]") - attack_generic(user, animal_damage, user.melee_damage_type, "melee", play_soundeffect) - return TRUE - -/obj/mecha/hulk_damage() - return 15 - -/obj/mecha/attack_hulk(mob/living/carbon/human/user) - . = ..() - if(.) - log_message("Attack by hulk. Attacker - [user].", 1) - add_attack_logs(user, src, "Punched with hulk powers") - -/obj/mecha/blob_act(obj/structure/blob/B) - log_message("Attack by blob. Attacker - [B].") - take_damage(30, BRUTE, "melee", 0, get_dir(src, B)) - -/obj/mecha/attack_tk() - return - -/obj/mecha/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) //wrapper - log_message("Hit by [AM].") - . = ..() - -/obj/mecha/bullet_act(obj/item/projectile/Proj) //wrapper - log_message("Hit by projectile. Type: [Proj.name]([Proj.flag]).") - ..() - -/obj/mecha/ex_act(severity, target) - log_message("Affected by explosion of severity: [severity].") - if(prob(deflect_chance)) - severity++ - log_message("Armor saved, changing severity to [severity]") - ..() - severity++ - for(var/X in equipment) - var/obj/item/mecha_parts/mecha_equipment/ME = X - ME.ex_act(severity) - for(var/Y in trackers) - var/obj/item/mecha_parts/mecha_tracking/MT = Y - MT.ex_act(severity) - if(occupant) - occupant.ex_act(severity) - -/obj/mecha/handle_atom_del(atom/A) - if(A == occupant) - occupant = null - icon_state = initial(icon_state)+"-open" - setDir(dir_in) - -/obj/mecha/Destroy() - if(occupant) - occupant.SetSleeping(destruction_sleep_duration) - go_out() - var/mob/living/silicon/ai/AI - for(var/mob/M in src) //Let's just be ultra sure - if(isAI(M)) - occupant = null - AI = M //AIs are loaded into the mech computer itself. When the mech dies, so does the AI. They can be recovered with an AI card from the wreck. - else - M.forceMove(loc) - for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) - E.detach(loc) - qdel(E) - equipment.Cut() - QDEL_NULL(cell) - QDEL_NULL(internal_tank) - if(AI) - AI.gib() //No wreck, no AI to recover - STOP_PROCESSING(SSobj, src) - GLOB.poi_list.Remove(src) - if(loc) - loc.assume_air(cabin_air) - air_update_turf() - else - qdel(cabin_air) - cabin_air = null - QDEL_NULL(spark_system) - QDEL_NULL(smoke_system) - - GLOB.mechas_list -= src //global mech list - return ..() - -//TODO -/obj/mecha/emp_act(severity) - if(get_charge()) - use_power((cell.charge/3)/(severity*2)) - take_damage(30 / severity, BURN, "energy", 1) - log_message("EMP detected", 1) - check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL, MECHA_INT_CONTROL_LOST, MECHA_INT_SHORT_CIRCUIT), 1) - -/obj/mecha/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > max_temperature) - log_message("Exposed to dangerous temperature.", 1) - take_damage(5, BURN, 0, 1) - check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL)) - -////////////////////// -////// AttackBy ////// -////////////////////// - -/obj/mecha/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/mmi)) - if(mmi_move_inside(W,user)) - to_chat(user, "[src]-MMI interface initialized successfuly") - else - to_chat(user, "[src]-MMI interface initialization failed.") - return - - if(istype(W, /obj/item/mecha_parts/mecha_equipment)) - var/obj/item/mecha_parts/mecha_equipment/E = W - spawn() - if(E.can_attach(src)) - if(!user.drop_item()) - return - E.attach(src) - user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") - else - to_chat(user, "You were unable to attach [W] to [src]!") - return - - if(W.GetID()) - if(add_req_access || maint_access) - if(internals_access_allowed(usr)) - var/obj/item/card/id/id_card - if(istype(W, /obj/item/card/id)) - id_card = W - else - var/obj/item/pda/pda = W - id_card = pda.id - output_maintenance_dialog(id_card, user) - return - else - to_chat(user, "Invalid ID: Access denied.") - else - to_chat(user, "Maintenance protocols disabled by operator.") - - else if(istype(W, /obj/item/stack/cable_coil)) - if(state == 3 && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) - var/obj/item/stack/cable_coil/CC = W - if(CC.amount > 1) - CC.use(2) - clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) - to_chat(user, "You replace the fused wires.") - else - to_chat(user, "There's not enough wire to finish the task.") - return - - else if(istype(W, /obj/item/stock_parts/cell)) - if(state==4) - if(!cell) - if(!user.drop_item()) - return - to_chat(user, "You install the powercell.") - W.forceMove(src) - cell = W - log_message("Powercell installed") - else - to_chat(user, "There's already a powercell installed.") - return - - else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) - if(!user.unEquip(W)) - to_chat(user, "\the [W] is stuck to your hand, you cannot put it in \the [src]") - return - W.forceMove(src) - trackers += W - user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") - diag_hud_set_mechtracking() - return - - else if(istype(W, /obj/item/paintkit)) - if(occupant) - to_chat(user, "You can't customize a mech while someone is piloting it - that would be unsafe!") - return - - var/obj/item/paintkit/P = W - var/found = null - - for(var/type in P.allowed_types) - if(type == initial_icon) - found = 1 - break - - if(!found) - to_chat(user, "That kit isn't meant for use on this class of exosuit.") - return - - user.visible_message("[user] opens [P] and spends some quality time customising [src].") - - name = P.new_name - desc = P.new_desc - initial_icon = P.new_icon - reset_icon() - - user.drop_item() - qdel(P) - - else if(istype(W, /obj/item/mecha_modkit)) - if(occupant) - to_chat(user, "You can't access the mech's modification port while it is occupied.") - return - var/obj/item/mecha_modkit/M = W - if(do_after_once(user, M.install_time, target = src)) - M.install(src, user) - else - to_chat(user, "You stop installing [M].") - - else - return ..() - + + var/wreckage + + var/list/equipment = new + var/obj/item/mecha_parts/mecha_equipment/selected + var/max_equip = 3 + var/datum/events/events + var/turf/crashing = null + var/occupant_sight_flags = 0 + + var/stepsound = 'sound/mecha/mechstep.ogg' + var/turnsound = 'sound/mecha/mechturn.ogg' + var/nominalsound = 'sound/mecha/nominal.ogg' + var/zoomsound = 'sound/mecha/imag_enh.ogg' + var/critdestrsound = 'sound/mecha/critdestr.ogg' + var/weapdestrsound = 'sound/mecha/weapdestr.ogg' + var/lowpowersound = 'sound/mecha/lowpower.ogg' + var/longactivationsound = 'sound/mecha/nominal.ogg' + var/starting_voice = /obj/item/mecha_modkit/voice + var/activated = FALSE + var/power_warned = FALSE + + var/destruction_sleep_duration = 1 //Time that mech pilot is put to sleep for if mech is destroyed + + var/melee_cooldown = 10 + var/melee_can_hit = 1 + + // Action vars + var/defence_mode = FALSE + var/defence_mode_deflect_chance = 35 + var/leg_overload_mode = FALSE + var/leg_overload_coeff = 100 + var/thrusters_active = FALSE + var/smoke = 5 + var/smoke_ready = 1 + var/smoke_cooldown = 100 + var/zoom_mode = FALSE + var/phasing = FALSE + var/phasing_energy_drain = 200 + var/phase_state = "" //icon_state when phasing + + hud_possible = list (DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_TRACK_HUD) + +/obj/mecha/Initialize() + . = ..() + events = new + icon_state += "-open" + add_radio() + add_cabin() + add_airtank() + spark_system.set_up(2, 0, src) + spark_system.attach(src) + smoke_system.set_up(3, src) + smoke_system.attach(src) + add_cell() + START_PROCESSING(SSobj, src) + GLOB.poi_list |= src + log_message("[src] created.") + GLOB.mechas_list += src //global mech list + prepare_huds() + for(var/datum/atom_hud/data/diagnostic/diag_hud in huds) + diag_hud.add_to_hud(src) + diag_hud_set_mechhealth() + diag_hud_set_mechcell() + diag_hud_set_mechstat() + diag_hud_set_mechtracking() + + var/obj/item/mecha_modkit/voice/V = new starting_voice(src) + V.install(src) + qdel(V) + +//////////////////////// +////// Helpers ///////// +//////////////////////// + +/obj/mecha/get_cell() + return cell + +/obj/mecha/proc/add_airtank() + internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src) + return internal_tank + +/obj/mecha/proc/add_cell(var/obj/item/stock_parts/cell/C=null) + if(C) + C.forceMove(src) + cell = C + return + cell = new/obj/item/stock_parts/cell/high/plus(src) + +/obj/mecha/proc/add_cabin() + cabin_air = new + cabin_air.temperature = T20C + cabin_air.volume = 200 + cabin_air.oxygen = O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) + cabin_air.nitrogen = N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) + return cabin_air + +/obj/mecha/proc/add_radio() + radio = new(src) + radio.name = "[src] radio" + radio.icon = icon + radio.icon_state = icon_state + radio.subspace_transmission = 1 + +/obj/mecha/examine(mob/user) + . = ..() + var/integrity = obj_integrity * 100 / max_integrity + switch(integrity) + if(85 to 100) + . += "It's fully intact." + if(65 to 85) + . += "It's slightly damaged." + if(45 to 65) + . += "It's badly damaged." + if(25 to 45) + . += "It's heavily damaged." + else + . += "It's falling apart." + if(equipment && equipment.len) + . += "It's equipped with:" + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + . += "[bicon(ME)] [ME]" + +/obj/mecha/hear_talk(mob/M, list/message_pieces) + if(M == occupant && radio.broadcasting) + radio.talk_into(M, message_pieces) + +/obj/mecha/proc/click_action(atom/target, mob/user, params) + if(!occupant || occupant != user ) + return + if(user.incapacitated()) + return + if(phasing) + occupant_message("Unable to interact with objects while phasing.") + return + if(state) + occupant_message("Maintenance protocols in effect.") + return + if(!get_charge()) + return + if(src == target) + return + + var/dir_to_target = get_dir(src, target) + if(dir_to_target && !(dir_to_target & dir))//wrong direction + return + + if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) + target = safepick(view(3,target)) + if(!target) + return + + var/mob/living/L = user + if(!target.Adjacent(src)) + if(selected && selected.is_ranged()) + if(HAS_TRAIT(L, TRAIT_PACIFISM) && selected.harmful) + to_chat(L, "You don't want to harm other living beings!") + return + selected.action(target, params) + else if(selected && selected.is_melee()) + if(isliving(target) && selected.harmful && HAS_TRAIT(L, TRAIT_PACIFISM)) + to_chat(user, "You don't want to harm other living beings!") + return + selected.action(target, params) + else + if(internal_damage & MECHA_INT_CONTROL_LOST) + target = safepick(oview(1, src)) + if(!melee_can_hit || !isatom(target)) + return + target.mech_melee_attack(src) + melee_can_hit = 0 + spawn(melee_cooldown) + melee_can_hit = 1 + +/obj/mecha/proc/mech_toxin_damage(mob/living/target) + playsound(src, 'sound/effects/spray2.ogg', 50, 1) + if(target.reagents) + if(target.reagents.get_reagent_amount("atropine") + force < force*2) + target.reagents.add_reagent("atropine", force/2) + if(target.reagents.get_reagent_amount("toxin") + force < force*2) + target.reagents.add_reagent("toxin", force/2.5) + +/obj/mecha/proc/range_action(atom/target) + return + + +////////////////////////////////// +//////// Movement procs //////// +////////////////////////////////// + +/obj/mecha/Move(atom/newLoc, direct) + . = ..() + if(.) + events.fireEvent("onMove",get_turf(src)) + +/obj/mecha/Process_Spacemove(var/movement_dir = 0) + . = ..() + if(.) + return 1 + if(thrusters_active && movement_dir && use_power(step_energy_drain)) + return 1 + + var/atom/movable/backup = get_spacemove_backup() + if(backup) + if(istype(backup) && movement_dir && !backup.anchored) + if(backup.newtonian_move(turn(movement_dir, 180))) + if(occupant) + to_chat(occupant, "You push off of [backup] to propel yourself.") + return 1 + +/obj/mecha/relaymove(mob/user, direction) + if(!direction) + return + if(user != occupant) //While not "realistic", this piece is player friendly. + user.forceMove(get_turf(src)) + to_chat(user, "You climb out from [src].") + return 0 + if(connected_port) + if(world.time - last_message > 20) + occupant_message("Unable to move while connected to the air system port!") + last_message = world.time + return 0 + if(state) + occupant_message("Maintenance protocols in effect.") + return + return domove(direction) + +/obj/mecha/proc/domove(direction) + if(can_move >= world.time) + return 0 + if(!Process_Spacemove(direction)) + return 0 + if(!has_charge(step_energy_drain)) + return 0 + if(defence_mode) + if(world.time - last_message > 20) + occupant_message("Unable to move while in defence mode.") + last_message = world.time + return 0 + if(zoom_mode) + if(world.time - last_message > 20) + occupant_message("Unable to move while in zoom mode.") + last_message = world.time + return 0 + + var/move_result = 0 + var/move_type = 0 + if(internal_damage & MECHA_INT_CONTROL_LOST) + move_result = mechsteprand() + move_type = MECHAMOVE_RAND + else if(dir != direction) + move_result = mechturn(direction) + move_type = MECHAMOVE_TURN + else + move_result = mechstep(direction) + move_type = MECHAMOVE_STEP + + if(move_result && move_type) + aftermove(move_type) + can_move = world.time + step_in + return TRUE + return FALSE + +/obj/mecha/proc/aftermove(move_type) + use_power(step_energy_drain) + if(move_type & (MECHAMOVE_RAND | MECHAMOVE_STEP) && occupant) + var/obj/machinery/atmospherics/unary/portables_connector/possible_port = locate(/obj/machinery/atmospherics/unary/portables_connector) in loc + if(possible_port) + var/obj/screen/alert/mech_port_available/A = occupant.throw_alert("mechaport", /obj/screen/alert/mech_port_available, override = TRUE) + if(A) + A.target = possible_port + else + occupant.clear_alert("mechaport") + if(leg_overload_mode) + if(obj_integrity < max_integrity - max_integrity / 3) + leg_overload_mode = FALSE + step_in = initial(step_in) + step_energy_drain = initial(step_energy_drain) + occupant_message("Leg actuators damage threshold exceded. Disabling overload.") + +/obj/mecha/proc/mechturn(direction) + dir = direction + if(turnsound) + playsound(src,turnsound,40,1) + return 1 + +/obj/mecha/proc/mechstep(direction) + . = step(src, direction) + if(!.) + if(phasing && get_charge() >= phasing_energy_drain) + if(can_move < world.time) + . = FALSE // We lie to mech code and say we didn't get to move, because we want to handle power usage + cooldown ourself + flick("phazon-phase", src) + forceMove(get_step(src, direction)) + use_power(phasing_energy_drain) + playsound(src, stepsound, 40, 1) + can_move = world.time + (step_in * 3) + else if(stepsound) + playsound(src, stepsound, 40, 1) + +/obj/mecha/proc/mechsteprand() + . = step_rand(src) + if(. && stepsound) + playsound(src, stepsound, 40, 1) + +/obj/mecha/Bump(var/atom/obstacle, bump_allowed) + if(throwing) //high velocity mechas in your face! + var/breakthrough = 0 + if(istype(obstacle, /obj/structure/window)) + qdel(obstacle) + breakthrough = 1 + + else if(istype(obstacle, /obj/structure/grille/)) + var/obj/structure/grille/G = obstacle + G.obj_break() + breakthrough = 1 + + else if(istype(obstacle, /obj/structure/table)) + var/obj/structure/table/T = obstacle + qdel(T) + breakthrough = 1 + + else if(istype(obstacle, /obj/structure/rack)) + new /obj/item/rack_parts(obstacle.loc) + qdel(obstacle) + breakthrough = 1 + + else if(istype(obstacle, /obj/structure/reagent_dispensers/fueltank)) + obstacle.ex_act(1) + + else if(isliving(obstacle)) + var/mob/living/L = obstacle + var/hit_sound = list('sound/weapons/genhit1.ogg','sound/weapons/genhit2.ogg','sound/weapons/genhit3.ogg') + if(L.flags & GODMODE) + return + L.take_overall_damage(5,0) + if(L.buckled) + L.buckled = 0 + L.Stun(5) + L.Weaken(5) + L.apply_effect(STUTTER, 5) + playsound(src, pick(hit_sound), 50, 0, 0) + breakthrough = 1 + + else + if(throwing) + throwing.finalize(FALSE) + crashing = null + + ..() + + if(breakthrough) + if(crashing) + spawn(1) + throw_at(crashing, 50, throw_speed) + else + spawn(1) + crashing = get_distant_turf(get_turf(src), dir, 3)//don't use get_dir(src, obstacle) or the mech will stop if he bumps into a one-direction window on his tile. + throw_at(crashing, 50, throw_speed) + + else + if(bump_allowed) + if(..()) + return + if(isobj(obstacle)) + var/obj/O = obstacle + if(istype(O, /obj/effect/portal)) //derpfix + anchored = 0 + O.Bumped(src) + spawn(0) //countering portal teleport spawn(0), hurr + anchored = 1 + else if(!O.anchored) + step(obstacle, dir) + else if(ismob(obstacle)) + step(obstacle, dir) + + +/////////////////////////////////// +//////// Internal damage //////// +/////////////////////////////////// + +/obj/mecha/proc/check_for_internal_damage(list/possible_int_damage, ignore_threshold=null) + if(!islist(possible_int_damage) || isemptylist(possible_int_damage)) + return + if(prob(20)) + if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold) + for(var/T in possible_int_damage) + if(internal_damage & T) + possible_int_damage -= T + var/int_dam_flag = safepick(possible_int_damage) + if(int_dam_flag) + setInternalDamage(int_dam_flag) + if(prob(5)) + if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold) + var/obj/item/mecha_parts/mecha_equipment/ME = safepick(equipment) + if(ME) + qdel(ME) + +/obj/mecha/proc/hasInternalDamage(int_dam_flag=null) + return int_dam_flag ? internal_damage&int_dam_flag : internal_damage + + +/obj/mecha/proc/setInternalDamage(int_dam_flag) + internal_damage |= int_dam_flag + log_append_to_last("Internal damage of type [int_dam_flag].",1) + occupant << sound('sound/machines/warning-buzzer.ogg',wait=0) + diag_hud_set_mechstat() + +/obj/mecha/proc/clearInternalDamage(int_dam_flag) + internal_damage &= ~int_dam_flag + switch(int_dam_flag) + if(MECHA_INT_TEMP_CONTROL) + occupant_message("Life support system reactivated.") + if(MECHA_INT_FIRE) + occupant_message("Internal fire extinquished.") + if(MECHA_INT_TANK_BREACH) + occupant_message("Damaged internal tank has been sealed.") + diag_hud_set_mechstat() + + +//////////////////////////////////////// +//////// Health related procs //////// +//////////////////////////////////////// + +/obj/mecha/proc/get_armour_facing(relative_dir) + switch(relative_dir) + if(0) // BACKSTAB! + return facing_modifiers[MECHA_BACK_ARMOUR] + if(45, 90, 270, 315) + return facing_modifiers[MECHA_SIDE_ARMOUR] + if(225, 180, 135) + return facing_modifiers[MECHA_FRONT_ARMOUR] + return 1 //always return non-0 + +/obj/mecha/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) + . = ..() + if(. && obj_integrity > 0) + spark_system.start() + switch(damage_flag) + if("fire") + check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL)) + if("melee") + check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + else + check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT)) + if(. >= 5 || prob(33)) + occupant_message("Taking damage!") + log_message("Took [damage_amount] points of damage. Damage type: [damage_type]") + +/obj/mecha/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) + . = ..() + if(!damage_amount) + return 0 + var/booster_deflection_modifier = 1 + var/booster_damage_modifier = 1 + if(damage_flag == "bullet" || damage_flag == "laser" || damage_flag == "energy") + for(var/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/B in equipment) + if(B.projectile_react()) + booster_deflection_modifier = B.deflect_coeff + booster_damage_modifier = B.damage_coeff + break + else if(damage_flag == "melee") + for(var/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/B in equipment) + if(B.attack_react()) + booster_deflection_modifier *= B.deflect_coeff + booster_damage_modifier *= B.damage_coeff + break + + if(attack_dir) + var/facing_modifier = get_armour_facing(dir2angle(attack_dir) - dir2angle(src)) + booster_damage_modifier /= facing_modifier + booster_deflection_modifier *= facing_modifier + if(prob(deflect_chance * booster_deflection_modifier)) + visible_message("[src]'s armour deflects the attack!") + log_message("Armor saved.") + return 0 + if(.) + . *= booster_damage_modifier + +/obj/mecha/attack_hand(mob/living/user) + user.changeNext_move(CLICK_CD_MELEE) + user.do_attack_animation(src, ATTACK_EFFECT_PUNCH) + playsound(loc, 'sound/weapons/tap.ogg', 40, 1, -1) + user.visible_message("[user] hits [name]. Nothing happens", "You hit [name] with no visible effect.") + log_message("Attack by hand/paw. Attacker - [user].") + + +/obj/mecha/attack_alien(mob/living/user) + log_message("Attack by alien. Attacker - [user].", TRUE) + playsound(src.loc, 'sound/weapons/slash.ogg', 100, TRUE) + attack_generic(user, 15, BRUTE, "melee", 0) + +/obj/mecha/attack_animal(mob/living/simple_animal/user) + log_message("Attack by simple animal. Attacker - [user].") + if(!user.melee_damage_upper && !user.obj_damage) + user.custom_emote(1, "[user.friendly] [src].") + return FALSE + else + var/play_soundeffect = 1 + if(user.environment_smash) + play_soundeffect = 0 + playsound(src, 'sound/effects/bang.ogg', 50, TRUE) + var/animal_damage = rand(user.melee_damage_lower,user.melee_damage_upper) + if(user.obj_damage) + animal_damage = user.obj_damage + animal_damage = min(animal_damage, 20*user.environment_smash) + user.create_attack_log("attacked [name]") + attack_generic(user, animal_damage, user.melee_damage_type, "melee", play_soundeffect) + return TRUE + +/obj/mecha/hulk_damage() + return 15 + +/obj/mecha/attack_hulk(mob/living/carbon/human/user) + . = ..() + if(.) + log_message("Attack by hulk. Attacker - [user].", 1) + add_attack_logs(user, src, "Punched with hulk powers") + +/obj/mecha/blob_act(obj/structure/blob/B) + log_message("Attack by blob. Attacker - [B].") + take_damage(30, BRUTE, "melee", 0, get_dir(src, B)) + +/obj/mecha/attack_tk() + return + +/obj/mecha/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) //wrapper + log_message("Hit by [AM].") + . = ..() + +/obj/mecha/bullet_act(obj/item/projectile/Proj) //wrapper + log_message("Hit by projectile. Type: [Proj.name]([Proj.flag]).") + ..() + +/obj/mecha/ex_act(severity, target) + log_message("Affected by explosion of severity: [severity].") + if(prob(deflect_chance)) + severity++ + log_message("Armor saved, changing severity to [severity]") + ..() + severity++ + for(var/X in equipment) + var/obj/item/mecha_parts/mecha_equipment/ME = X + ME.ex_act(severity) + for(var/Y in trackers) + var/obj/item/mecha_parts/mecha_tracking/MT = Y + MT.ex_act(severity) + if(occupant) + occupant.ex_act(severity) + +/obj/mecha/handle_atom_del(atom/A) + if(A == occupant) + occupant = null + icon_state = initial(icon_state)+"-open" + setDir(dir_in) + +/obj/mecha/Destroy() + if(occupant) + occupant.SetSleeping(destruction_sleep_duration) + go_out() + var/mob/living/silicon/ai/AI + for(var/mob/M in src) //Let's just be ultra sure + if(isAI(M)) + occupant = null + AI = M //AIs are loaded into the mech computer itself. When the mech dies, so does the AI. They can be recovered with an AI card from the wreck. + else + M.forceMove(loc) + for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) + E.detach(loc) + qdel(E) + equipment.Cut() + QDEL_NULL(cell) + QDEL_NULL(internal_tank) + if(AI) + AI.gib() //No wreck, no AI to recover + STOP_PROCESSING(SSobj, src) + GLOB.poi_list.Remove(src) + if(loc) + loc.assume_air(cabin_air) + air_update_turf() + else + qdel(cabin_air) + cabin_air = null + QDEL_NULL(spark_system) + QDEL_NULL(smoke_system) + + GLOB.mechas_list -= src //global mech list + return ..() + +//TODO +/obj/mecha/emp_act(severity) + if(get_charge()) + use_power((cell.charge/3)/(severity*2)) + take_damage(30 / severity, BURN, "energy", 1) + log_message("EMP detected", 1) + check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL, MECHA_INT_CONTROL_LOST, MECHA_INT_SHORT_CIRCUIT), 1) + +/obj/mecha/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > max_temperature) + log_message("Exposed to dangerous temperature.", 1) + take_damage(5, BURN, 0, 1) + check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL)) + +////////////////////// +////// AttackBy ////// +////////////////////// + +/obj/mecha/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/mmi)) + if(mmi_move_inside(W,user)) + to_chat(user, "[src]-MMI interface initialized successfuly") + else + to_chat(user, "[src]-MMI interface initialization failed.") + return + + if(istype(W, /obj/item/mecha_parts/mecha_equipment)) + var/obj/item/mecha_parts/mecha_equipment/E = W + spawn() + if(E.can_attach(src)) + if(!user.drop_item()) + return + E.attach(src) + user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") + else + to_chat(user, "You were unable to attach [W] to [src]!") + return + + if(W.GetID()) + if(add_req_access || maint_access) + if(internals_access_allowed(usr)) + var/obj/item/card/id/id_card + if(istype(W, /obj/item/card/id)) + id_card = W + else + var/obj/item/pda/pda = W + id_card = pda.id + output_maintenance_dialog(id_card, user) + return + else + to_chat(user, "Invalid ID: Access denied.") + else + to_chat(user, "Maintenance protocols disabled by operator.") + + else if(istype(W, /obj/item/stack/cable_coil)) + if(state == 3 && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) + var/obj/item/stack/cable_coil/CC = W + if(CC.amount > 1) + CC.use(2) + clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) + to_chat(user, "You replace the fused wires.") + else + to_chat(user, "There's not enough wire to finish the task.") + return + + else if(istype(W, /obj/item/stock_parts/cell)) + if(state==4) + if(!cell) + if(!user.drop_item()) + return + to_chat(user, "You install the powercell.") + W.forceMove(src) + cell = W + log_message("Powercell installed") + else + to_chat(user, "There's already a powercell installed.") + return + + else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) + if(!user.unEquip(W)) + to_chat(user, "\the [W] is stuck to your hand, you cannot put it in \the [src]") + return + W.forceMove(src) + trackers += W + user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") + diag_hud_set_mechtracking() + return + + else if(istype(W, /obj/item/paintkit)) + if(occupant) + to_chat(user, "You can't customize a mech while someone is piloting it - that would be unsafe!") + return + + var/obj/item/paintkit/P = W + var/found = null + + for(var/type in P.allowed_types) + if(type == initial_icon) + found = 1 + break + + if(!found) + to_chat(user, "That kit isn't meant for use on this class of exosuit.") + return + + user.visible_message("[user] opens [P] and spends some quality time customising [src].") + + name = P.new_name + desc = P.new_desc + initial_icon = P.new_icon + reset_icon() + + user.drop_item() + qdel(P) + + else if(istype(W, /obj/item/mecha_modkit)) + if(occupant) + to_chat(user, "You can't access the mech's modification port while it is occupied.") + return + var/obj/item/mecha_modkit/M = W + if(do_after_once(user, M.install_time, target = src)) + M.install(src, user) + else + to_chat(user, "You stop installing [M].") + + else + return ..() + /obj/mecha/crowbar_act(mob/user, obj/item/I) if(state != 2 && state != 3 && !(state == 4 && pilot_is_mmi())) @@ -845,677 +845,677 @@ else to_chat(user, "[src] is at full integrity!") -/obj/mecha/mech_melee_attack(obj/mecha/M) - if(!has_charge(melee_energy_drain)) - return 0 - use_power(melee_energy_drain) - if(M.damtype == BRUTE || M.damtype == BURN) - add_attack_logs(M.occupant, src, "Mecha-attacked with [M] (INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") - . = ..() - -/obj/mecha/emag_act(mob/user) - to_chat(user, "[src]'s ID slot rejects the card.") - return - - -///////////////////////////////////// -//////////// AI piloting //////////// -///////////////////////////////////// - -/obj/mecha/attack_ai(mob/living/silicon/ai/user) - if(!isAI(user)) - return - //Allows the Malf to scan a mech's status and loadout, helping it to decide if it is a worthy chariot. - if(user.can_dominate_mechs) - examine(user) //Get diagnostic information! - for(var/obj/item/mecha_parts/mecha_tracking/B in trackers) - to_chat(user, "Warning: Tracking Beacon detected. Enter at your own risk. Beacon Data:") - to_chat(user, "[B.get_mecha_info_text()]") - break - //Nothing like a big, red link to make the player feel powerful! - to_chat(user, "ASSUME DIRECT CONTROL?
") - else - examine(user) - if(occupant) - user << "This exosuit has a pilot and cannot be controlled." - return - var/can_control_mech = FALSE - for(var/obj/item/mecha_parts/mecha_tracking/ai_control/A in trackers) - can_control_mech = TRUE - to_chat(user, "[bicon(src)] Status of [name]:\n\ - [A.get_mecha_info_text()]") - break - if(!can_control_mech) - to_chat(user, "You cannot control exosuits without AI control beacons installed.") - return - to_chat(user, "Take control of exosuit?
") - -/obj/mecha/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) - if(!..()) - return - - //Transfer from core or card to mech. Proc is called by mech. - switch(interaction) - if(AI_TRANS_TO_CARD) //Upload AI from mech to AI card. - if(!maint_access) //Mech must be in maint mode to allow carding. - to_chat(user, "[name] must have maintenance protocols active in order to allow a transfer.") - return - AI = occupant - if(!AI || !isAI(occupant)) //Mech does not have an AI for a pilot - to_chat(user, "No AI detected in the [name] onboard computer.") - return - if(AI.mind.special_role) //Malf AIs cannot leave mechs. Except through death. - to_chat(user, "ACCESS DENIED.") - return - AI.aiRestorePowerRoutine = 0//So the AI initially has power. - AI.control_disabled = 1 - AI.aiRadio.disabledAi = 1 - AI.loc = card - occupant = null - AI.controlled_mech = null - AI.remote_control = null - icon_state = initial(icon_state)+"-open" - to_chat(AI, "You have been downloaded to a mobile storage device. Wireless connection offline.") - to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) removed from [name] and stored within local memory.") - - if(AI_MECH_HACK) //Called by AIs on the mech - AI.linked_core = new /obj/structure/AIcore/deactivated(AI.loc) - if(AI.can_dominate_mechs) - if(occupant) //Oh, I am sorry, were you using that? - to_chat(AI, "Pilot detected! Forced ejection initiated!") - to_chat(occupant, "You have been forcibly ejected!") - go_out(1) //IT IS MINE, NOW. SUCK IT, RD! - ai_enter_mech(AI, interaction) - - if(AI_TRANS_FROM_CARD) //Using an AI card to upload to a mech. - AI = locate(/mob/living/silicon/ai) in card - if(!AI) - to_chat(user, "There is no AI currently installed on this device.") - return - else if(AI.stat || !AI.client) - to_chat(user, "[AI.name] is currently unresponsive, and cannot be uploaded.") - return - else if(occupant || dna) //Normal AIs cannot steal mechs! - to_chat(user, "Access denied. [name] is [occupant ? "currently occupied" : "secured with a DNA lock"].") - return - AI.control_disabled = 0 - AI.aiRadio.disabledAi = 0 - to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") - ai_enter_mech(AI, interaction) - -//Hack and From Card interactions share some code, so leave that here for both to use. -/obj/mecha/proc/ai_enter_mech(mob/living/silicon/ai/AI, interaction) - AI.aiRestorePowerRoutine = 0 - AI.loc = src - occupant = AI - icon_state = initial(icon_state) - playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) - if(!hasInternalDamage()) - occupant << sound(nominalsound, volume = 50) - AI.cancel_camera() - AI.controlled_mech = src - AI.remote_control = src - AI.canmove = 1 //Much easier than adding AI checks! Be sure to set this back to 0 if you decide to allow an AI to leave a mech somehow. - AI.can_shunt = 0 //ONE AI ENTERS. NO AI LEAVES. - to_chat(AI, "[AI.can_dominate_mechs ? "Takeover of [name] complete! You are now permanently loaded onto the onboard computer. Do not attempt to leave the station sector!" \ - : "You have been uploaded to a mech's onboard computer."]") - to_chat(AI, "Use Middle-Mouse to activate mech functions and equipment. Click normally for AI interactions.") - if(interaction == AI_TRANS_FROM_CARD) - GrantActions(AI, FALSE) - else - GrantActions(AI, !AI.can_dominate_mechs) - -///////////////////////////////////// -//////// Atmospheric stuff //////// -///////////////////////////////////// - -/obj/mecha/proc/get_turf_air() - var/turf/T = get_turf(src) - if(T) - . = T.return_air() - -/obj/mecha/remove_air(amount) - if(use_internal_tank) - return cabin_air.remove(amount) - else - var/turf/T = get_turf(src) - if(T) - return T.remove_air(amount) - -/obj/mecha/return_air() - if(use_internal_tank) - return cabin_air - return get_turf_air() - -/obj/mecha/proc/return_pressure() - var/datum/gas_mixture/t_air = return_air() - if(t_air) - . = t_air.return_pressure() - -//skytodo: //No idea what you want me to do here, mate. -/obj/mecha/proc/return_temperature() - var/datum/gas_mixture/t_air = return_air() - if(t_air) - . = t_air.return_temperature() - -/obj/mecha/proc/connect(obj/machinery/atmospherics/unary/portables_connector/new_port) - //Make sure not already connected to something else - if(connected_port || !istype(new_port) || new_port.connected_device) - return 0 - - //Make sure are close enough for a valid connection - if(new_port.loc != loc) - return 0 - - //Perform the connection - connected_port = new_port - connected_port.connected_device = src - connected_port.parent.reconcile_air() - - if(occupant) - occupant.clear_alert("mechaport") - occupant.throw_alert("mechaport_d", /obj/screen/alert/mech_port_disconnect) - - log_message("Connected to gas port.") - return 1 - -/obj/mecha/proc/disconnect() - if(!connected_port) - return 0 - - connected_port.connected_device = null - connected_port = null - log_message("Disconnected from gas port.") - if(occupant) - occupant.clear_alert("mechaport_d") - return 1 - -/obj/mecha/portableConnectorReturnAir() - return internal_tank.return_air() - -/obj/mecha/proc/toggle_lights() - lights = !lights - if(lights) - set_light(light_range + lights_power) - else - set_light(light_range - lights_power) - occupant_message("Toggled lights [lights ? "on" : "off"].") - log_message("Toggled lights [lights ? "on" : "off"].") - -/obj/mecha/proc/toggle_internal_tank() - use_internal_tank = !use_internal_tank - occupant_message("Now taking air from [use_internal_tank ? "internal airtank" : "environment"].") - log_message("Now taking air from [use_internal_tank ? "internal airtank" : "environment"].") - -/obj/mecha/MouseDrop_T(mob/M, mob/user) - if(user.incapacitated()) - return - if(user != M) - return - log_message("[user] tries to move in.") - if(occupant) - to_chat(user, "The [src] is already occupied!") - log_append_to_last("Permission denied.") - return - var/passed - if(dna) - if(ishuman(user)) - if(user.dna.unique_enzymes == dna) - passed = 1 - else if(operation_allowed(user)) - passed = 1 - if(!passed) - to_chat(user, "Access denied.") - log_append_to_last("Permission denied.") - return - if(user.buckled) - to_chat(user, "You are currently buckled and cannot move.") - log_append_to_last("Permission denied.") - return - if(user.has_buckled_mobs()) //mob attached to us - to_chat(user, "You can't enter the exosuit with other creatures attached to you!") - return - - visible_message("[user] starts to climb into [src]") - - if(do_after(user, 40, target = src)) - if(obj_integrity <= 0) - to_chat(user, "You cannot get in the [name], it has been destroyed!") - else if(occupant) - to_chat(user, "[occupant] was faster! Try better next time, loser.") - else if(user.buckled) - to_chat(user, "You can't enter the exosuit while buckled.") - else if(user.has_buckled_mobs()) - to_chat(user, "You can't enter the exosuit with other creatures attached to you!") - else - moved_inside(user) - else - to_chat(user, "You stop entering the exosuit!") - -/obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H as mob) - if(H && H.client && H in range(1)) - occupant = H - H.stop_pulling() - H.forceMove(src) - H.reset_perspective(src) - add_fingerprint(H) - GrantActions(H, human_occupant = 1) - forceMove(loc) - log_append_to_last("[H] moved in as pilot.") - icon_state = reset_icon() - dir = dir_in - playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) - if(!activated) - occupant << sound(longactivationsound, volume = 50) - activated = TRUE - else if(!hasInternalDamage()) - occupant << sound(nominalsound, volume = 50) - if(state) - H.throw_alert("locked", /obj/screen/alert/mech_maintenance) - return 1 - else - return 0 - -/obj/mecha/proc/mmi_move_inside(var/obj/item/mmi/mmi_as_oc as obj,mob/user as mob) - if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) - to_chat(user, "Consciousness matrix not detected!") - return 0 - else if(mmi_as_oc.brainmob.stat) - to_chat(user, "Beta-rhythm below acceptable level!") - return 0 - else if(occupant) - to_chat(user, "Occupant detected!") - return 0 - else if(dna && dna != mmi_as_oc.brainmob.dna.unique_enzymes) - to_chat(user, "Access denied. [name] is secured with a DNA lock.") - return 0 - - if(do_after(user, 40, target = src)) - if(!occupant) - return mmi_moved_inside(mmi_as_oc,user) - else - to_chat(user, "Occupant detected!") - else - to_chat(user, "You stop inserting the MMI.") - return 0 - -/obj/mecha/proc/mmi_moved_inside(obj/item/mmi/mmi_as_oc,mob/user) - if(mmi_as_oc && user in range(1)) - if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) - to_chat(user, "Consciousness matrix not detected.") - return 0 - else if(mmi_as_oc.brainmob.stat) - to_chat(user, "Beta-rhythm below acceptable level.") - return 0 - if(!user.unEquip(mmi_as_oc)) - to_chat(user, "\the [mmi_as_oc] is stuck to your hand, you cannot put it in \the [src]") - return 0 - var/mob/brainmob = mmi_as_oc.brainmob - brainmob.reset_perspective(src) - occupant = brainmob - brainmob.forceMove(src) //should allow relaymove - brainmob.canmove = 1 - if(istype(mmi_as_oc, /obj/item/mmi/robotic_brain)) - var/obj/item/mmi/robotic_brain/R = mmi_as_oc - if(R.imprinted_master) - to_chat(brainmob, "Your imprint to [R.imprinted_master] has been temporarily disabled. You should help the crew and not commit harm.") - mmi_as_oc.loc = src - mmi_as_oc.mecha = src - Entered(mmi_as_oc) - Move(loc) - icon_state = reset_icon() - dir = dir_in - log_message("[mmi_as_oc] moved in as pilot.") - if(!hasInternalDamage()) - to_chat(occupant, sound(nominalsound, volume=50)) - GrantActions(brainmob) - return 1 - else - return 0 - -/obj/mecha/proc/pilot_is_mmi() - var/atom/movable/mob_container - if(istype(occupant, /mob/living/carbon/brain)) - var/mob/living/carbon/brain/brain = occupant - mob_container = brain.container - if(istype(mob_container, /obj/item/mmi)) - return 1 - return 0 - -/obj/mecha/proc/pilot_mmi_hud(var/mob/living/carbon/brain/pilot) - return - -/obj/mecha/Exited(atom/movable/M, atom/newloc) - if(occupant && occupant == M) // The occupant exited the mech without calling go_out() - go_out(1, newloc) - -/obj/mecha/proc/go_out(forced, atom/newloc = loc) - if(!occupant) - return - var/atom/movable/mob_container - occupant.clear_alert("charge") - occupant.clear_alert("locked") - occupant.clear_alert("mech damage") - occupant.clear_alert("mechaport") - occupant.clear_alert("mechaport_d") - if(ishuman(occupant)) - mob_container = occupant - RemoveActions(occupant, human_occupant = 1) - else if(isbrain(occupant)) - var/mob/living/carbon/brain/brain = occupant - RemoveActions(brain) - mob_container = brain.container - else if(isAI(occupant)) - var/mob/living/silicon/ai/AI = occupant - if(forced)//This should only happen if there are multiple AIs in a round, and at least one is Malf. - RemoveActions(occupant) - occupant.gib() //If one Malf decides to steal a mech from another AI (even other Malfs!), they are destroyed, as they have nowhere to go when replaced. - occupant = null - return - else - if(!AI.linked_core || QDELETED(AI.linked_core)) - to_chat(AI, "Inactive core destroyed. Unable to return.") - AI.linked_core = null - return - to_chat(AI, "Returning to core...") - AI.controlled_mech = null - AI.remote_control = null - RemoveActions(occupant, 1) - mob_container = AI - newloc = get_turf(AI.linked_core) - qdel(AI.linked_core) - else - return - var/mob/living/L = occupant - occupant = null //we need it null when forceMove calls Exited(). - if(mob_container.forceMove(newloc))//ejecting mob container - log_message("[mob_container] moved out.") - L << browse(null, "window=exosuit") - - if(istype(mob_container, /obj/item/mmi)) - var/obj/item/mmi/mmi = mob_container - if(mmi.brainmob) - L.loc = mmi - L.reset_perspective() - mmi.mecha = null - mmi.update_icon() - L.canmove = 0 - if(istype(mmi, /obj/item/mmi/robotic_brain)) - var/obj/item/mmi/robotic_brain/R = mmi - if(R.imprinted_master) - to_chat(L, "Imprint re-enabled, you are once again bound to [R.imprinted_master]'s commands.") - icon_state = initial(icon_state)+"-open" - dir = dir_in - - if(L && L.client) - L.client.RemoveViewMod("mecha") - zoom_mode = FALSE - -///////////////////////// -////// Access stuff ///// -///////////////////////// - -/obj/mecha/proc/operation_allowed(mob/living/carbon/human/H) - if(!ishuman(H)) - return 0 - for(var/ID in list(H.get_active_hand(), H.wear_id, H.belt)) - if(check_access(ID, operation_req_access)) - return 1 - return 0 - - -/obj/mecha/proc/internals_access_allowed(mob/living/carbon/human/H) - for(var/atom/ID in list(H.get_active_hand(), H.wear_id, H.belt)) - if(check_access(ID, internals_req_access)) - return 1 - return 0 - - -/obj/mecha/check_access(obj/item/card/id/I, list/access_list) - if(!istype(access_list)) - return 1 - if(!access_list.len) //no requirements - return 1 - if(istype(I, /obj/item/pda)) - var/obj/item/pda/pda = I - I = pda.id - if(!istype(I) || !I.access) //not ID or no access - return 0 - if(access_list==operation_req_access) - for(var/req in access_list) - if(!(req in I.access)) //doesn't have this access - return 0 - else if(access_list==internals_req_access) - for(var/req in access_list) - if(req in I.access) - return 1 - return 1 - -/////////////////////// -///// Power stuff ///// -/////////////////////// - -/obj/mecha/proc/has_charge(amount) - return (get_charge()>=amount) - -/obj/mecha/proc/get_charge() - for(var/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/R in equipment) - var/relay_charge = R.get_charge() - if(relay_charge) - return relay_charge - if(cell) - return max(0, cell.charge) - -/obj/mecha/proc/use_power(amount) - if(get_charge()) - cell.use(amount) - if(occupant) - update_cell() - return 1 - return 0 - -/obj/mecha/proc/give_power(amount) - if(!isnull(get_charge())) - cell.give(amount) - if(occupant) - update_cell() - return 1 - return 0 - -/obj/mecha/proc/update_cell() - if(cell) - var/cellcharge = cell.charge/cell.maxcharge - switch(cellcharge) - if(0.75 to INFINITY) - occupant.clear_alert("charge") - if(0.5 to 0.75) - occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 1) - if(0.25 to 0.5) - occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 2) - if(power_warned) - power_warned = FALSE - if(0.01 to 0.25) - occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 3) - if(!power_warned) - occupant << sound(lowpowersound, volume = 50) - power_warned = TRUE - else - occupant.throw_alert("charge", /obj/screen/alert/mech_emptycell) - else - occupant.throw_alert("charge", /obj/screen/alert/mech_nocell) - -/obj/mecha/proc/reset_icon() - if(initial_icon) - icon_state = initial_icon - else - icon_state = initial(icon_state) - return icon_state - -////////////////////////////////////////// -//////// Mecha global iterators //////// -////////////////////////////////////////// - -/obj/mecha/process() - process_internal_damage() - regulate_temp() - give_air() - update_huds() - -/obj/mecha/proc/process_internal_damage() - if(!internal_damage) - return - - if(internal_damage & MECHA_INT_FIRE) - if(!(internal_damage & MECHA_INT_TEMP_CONTROL) && prob(5)) - clearInternalDamage(MECHA_INT_FIRE) - if(internal_tank) - var/datum/gas_mixture/int_tank_air = internal_tank.return_air() - if(int_tank_air.return_pressure() > internal_tank.maximum_pressure && !(internal_damage & MECHA_INT_TANK_BREACH)) - setInternalDamage(MECHA_INT_TANK_BREACH) - - if(int_tank_air && int_tank_air.return_volume() > 0) - int_tank_air.temperature = min(6000 + T0C, cabin_air.return_temperature() + rand(10, 15)) - - if(cabin_air && cabin_air.return_volume()>0) - cabin_air.temperature = min(6000+T0C, cabin_air.return_temperature()+rand(10,15)) - if(cabin_air.return_temperature() > max_temperature/2) - take_damage(4/round(max_temperature/cabin_air.return_temperature(),0.1), BURN, 0, 0) - - if(internal_damage & MECHA_INT_TANK_BREACH) //remove some air from internal tank - if(internal_tank) - var/datum/gas_mixture/int_tank_air = internal_tank.return_air() - var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10) - if(loc) - loc.assume_air(leaked_gas) - air_update_turf() - else - qdel(leaked_gas) - - if(internal_damage & MECHA_INT_SHORT_CIRCUIT) - if(get_charge()) - spark_system.start() - cell.charge -= min(20,cell.charge) - cell.maxcharge -= min(20,cell.maxcharge) - -/obj/mecha/proc/regulate_temp() - if(internal_damage & MECHA_INT_TEMP_CONTROL) - return - - if(cabin_air && cabin_air.return_volume() > 0) - var/delta = cabin_air.temperature - T20C - cabin_air.temperature -= max(-10, min(10, round(delta / 4, 0.1))) - -/obj/mecha/proc/give_air() - if(!internal_tank) - return - - var/datum/gas_mixture/tank_air = internal_tank.return_air() - - var/release_pressure = internal_tank_valve - var/cabin_pressure = cabin_air.return_pressure() - var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2) - var/transfer_moles = 0 - if(pressure_delta > 0) //cabin pressure lower than release pressure - if(tank_air.return_temperature() > 0) - transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) - var/datum/gas_mixture/removed = tank_air.remove(transfer_moles) - cabin_air.merge(removed) - else if(pressure_delta < 0) //cabin pressure higher than release pressure - var/datum/gas_mixture/t_air = return_air() - pressure_delta = cabin_pressure - release_pressure - if(t_air) - pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta) - if(pressure_delta > 0) //if location pressure is lower than cabin pressure - transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) - var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles) - if(t_air) - t_air.merge(removed) - else //just delete the cabin gas, we're in space or some shit - qdel(removed) - -/obj/mecha/proc/update_huds() - diag_hud_set_mechhealth() - diag_hud_set_mechcell() - diag_hud_set_mechstat() - diag_hud_set_mechtracking() - - -/obj/mecha/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) - flick_overlay(image('icons/mob/talk.dmi', bubble_loc, bubble_state,MOB_LAYER+1), bubble_recipients, 30) - -/obj/mecha/update_remote_sight(mob/living/user) - if(occupant_sight_flags) - if(user == occupant) - user.sight |= occupant_sight_flags - - ..() - -/obj/mecha/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) - if(!no_effect) - if(selected) - used_item = selected - else if(!visual_effect_icon) - visual_effect_icon = ATTACK_EFFECT_SMASH - if(damtype == BURN) - visual_effect_icon = ATTACK_EFFECT_MECHFIRE - else if(damtype == TOX) - visual_effect_icon = ATTACK_EFFECT_MECHTOXIN - ..() - -/obj/mecha/obj_destruction() - if(wreckage) - var/mob/living/silicon/ai/AI - if(isAI(occupant)) - AI = occupant - occupant = null - var/obj/structure/mecha_wreckage/WR = new wreckage(loc, AI) - for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) - if(E.salvageable && prob(30)) - WR.crowbar_salvage += E - E.detach(WR) //detaches from src into WR - E.equip_ready = 1 - else - E.detach(loc) - qdel(E) - if(cell) - WR.crowbar_salvage += cell - cell.forceMove(WR) - cell.charge = rand(0, cell.charge) - cell = null - if(internal_tank) - WR.crowbar_salvage += internal_tank - internal_tank.forceMove(WR) - cell = null - . = ..() - -/obj/mecha/CtrlClick(mob/living/L) - if(occupant != L || !istype(L)) - return ..() - - var/list/choices = list("Cancel / No Change" = mutable_appearance(icon = 'icons/mob/screen_gen.dmi', icon_state = "x")) - var/list/choices_to_refs = list() - - for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) - if(!MT.selectable || selected == MT) - continue - var/mutable_appearance/clean/MA = new(MT) - choices[MT.name] = MA - choices_to_refs[MT.name] = MT - - var/choice = show_radial_menu(L, src, choices, radius = 48, custom_check = CALLBACK(src, .proc/check_menu, L)) - if(!check_menu(L) || choice == "Cancel / No Change") - return - - var/obj/item/mecha_parts/mecha_equipment/new_sel = LAZYACCESS(choices_to_refs, choice) - if(istype(new_sel)) - selected = new_sel - occupant_message("You switch to [selected].") - visible_message("[src] raises [selected]") - send_byjax(occupant, "exosuit.browser", "eq_list", get_equipment_list()) - -/obj/mecha/proc/check_menu(mob/living/L) - if(L != occupant || !istype(L)) - return FALSE - if(L.incapacitated()) - return FALSE - return TRUE \ No newline at end of file +/obj/mecha/mech_melee_attack(obj/mecha/M) + if(!has_charge(melee_energy_drain)) + return 0 + use_power(melee_energy_drain) + if(M.damtype == BRUTE || M.damtype == BURN) + add_attack_logs(M.occupant, src, "Mecha-attacked with [M] (INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") + . = ..() + +/obj/mecha/emag_act(mob/user) + to_chat(user, "[src]'s ID slot rejects the card.") + return + + +///////////////////////////////////// +//////////// AI piloting //////////// +///////////////////////////////////// + +/obj/mecha/attack_ai(mob/living/silicon/ai/user) + if(!isAI(user)) + return + //Allows the Malf to scan a mech's status and loadout, helping it to decide if it is a worthy chariot. + if(user.can_dominate_mechs) + examine(user) //Get diagnostic information! + for(var/obj/item/mecha_parts/mecha_tracking/B in trackers) + to_chat(user, "Warning: Tracking Beacon detected. Enter at your own risk. Beacon Data:") + to_chat(user, "[B.get_mecha_info_text()]") + break + //Nothing like a big, red link to make the player feel powerful! + to_chat(user, "ASSUME DIRECT CONTROL?
") + else + examine(user) + if(occupant) + user << "This exosuit has a pilot and cannot be controlled." + return + var/can_control_mech = FALSE + for(var/obj/item/mecha_parts/mecha_tracking/ai_control/A in trackers) + can_control_mech = TRUE + to_chat(user, "[bicon(src)] Status of [name]:\n\ + [A.get_mecha_info_text()]") + break + if(!can_control_mech) + to_chat(user, "You cannot control exosuits without AI control beacons installed.") + return + to_chat(user, "Take control of exosuit?
") + +/obj/mecha/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) + if(!..()) + return + + //Transfer from core or card to mech. Proc is called by mech. + switch(interaction) + if(AI_TRANS_TO_CARD) //Upload AI from mech to AI card. + if(!maint_access) //Mech must be in maint mode to allow carding. + to_chat(user, "[name] must have maintenance protocols active in order to allow a transfer.") + return + AI = occupant + if(!AI || !isAI(occupant)) //Mech does not have an AI for a pilot + to_chat(user, "No AI detected in the [name] onboard computer.") + return + if(AI.mind.special_role) //Malf AIs cannot leave mechs. Except through death. + to_chat(user, "ACCESS DENIED.") + return + AI.aiRestorePowerRoutine = 0//So the AI initially has power. + AI.control_disabled = 1 + AI.aiRadio.disabledAi = 1 + AI.loc = card + occupant = null + AI.controlled_mech = null + AI.remote_control = null + icon_state = initial(icon_state)+"-open" + to_chat(AI, "You have been downloaded to a mobile storage device. Wireless connection offline.") + to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) removed from [name] and stored within local memory.") + + if(AI_MECH_HACK) //Called by AIs on the mech + AI.linked_core = new /obj/structure/AIcore/deactivated(AI.loc) + if(AI.can_dominate_mechs) + if(occupant) //Oh, I am sorry, were you using that? + to_chat(AI, "Pilot detected! Forced ejection initiated!") + to_chat(occupant, "You have been forcibly ejected!") + go_out(1) //IT IS MINE, NOW. SUCK IT, RD! + ai_enter_mech(AI, interaction) + + if(AI_TRANS_FROM_CARD) //Using an AI card to upload to a mech. + AI = locate(/mob/living/silicon/ai) in card + if(!AI) + to_chat(user, "There is no AI currently installed on this device.") + return + else if(AI.stat || !AI.client) + to_chat(user, "[AI.name] is currently unresponsive, and cannot be uploaded.") + return + else if(occupant || dna) //Normal AIs cannot steal mechs! + to_chat(user, "Access denied. [name] is [occupant ? "currently occupied" : "secured with a DNA lock"].") + return + AI.control_disabled = 0 + AI.aiRadio.disabledAi = 0 + to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") + ai_enter_mech(AI, interaction) + +//Hack and From Card interactions share some code, so leave that here for both to use. +/obj/mecha/proc/ai_enter_mech(mob/living/silicon/ai/AI, interaction) + AI.aiRestorePowerRoutine = 0 + AI.loc = src + occupant = AI + icon_state = initial(icon_state) + playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) + if(!hasInternalDamage()) + occupant << sound(nominalsound, volume = 50) + AI.cancel_camera() + AI.controlled_mech = src + AI.remote_control = src + AI.canmove = 1 //Much easier than adding AI checks! Be sure to set this back to 0 if you decide to allow an AI to leave a mech somehow. + AI.can_shunt = 0 //ONE AI ENTERS. NO AI LEAVES. + to_chat(AI, "[AI.can_dominate_mechs ? "Takeover of [name] complete! You are now permanently loaded onto the onboard computer. Do not attempt to leave the station sector!" \ + : "You have been uploaded to a mech's onboard computer."]") + to_chat(AI, "Use Middle-Mouse to activate mech functions and equipment. Click normally for AI interactions.") + if(interaction == AI_TRANS_FROM_CARD) + GrantActions(AI, FALSE) + else + GrantActions(AI, !AI.can_dominate_mechs) + +///////////////////////////////////// +//////// Atmospheric stuff //////// +///////////////////////////////////// + +/obj/mecha/proc/get_turf_air() + var/turf/T = get_turf(src) + if(T) + . = T.return_air() + +/obj/mecha/remove_air(amount) + if(use_internal_tank) + return cabin_air.remove(amount) + else + var/turf/T = get_turf(src) + if(T) + return T.remove_air(amount) + +/obj/mecha/return_air() + if(use_internal_tank) + return cabin_air + return get_turf_air() + +/obj/mecha/proc/return_pressure() + var/datum/gas_mixture/t_air = return_air() + if(t_air) + . = t_air.return_pressure() + +//skytodo: //No idea what you want me to do here, mate. +/obj/mecha/proc/return_temperature() + var/datum/gas_mixture/t_air = return_air() + if(t_air) + . = t_air.return_temperature() + +/obj/mecha/proc/connect(obj/machinery/atmospherics/unary/portables_connector/new_port) + //Make sure not already connected to something else + if(connected_port || !istype(new_port) || new_port.connected_device) + return 0 + + //Make sure are close enough for a valid connection + if(new_port.loc != loc) + return 0 + + //Perform the connection + connected_port = new_port + connected_port.connected_device = src + connected_port.parent.reconcile_air() + + if(occupant) + occupant.clear_alert("mechaport") + occupant.throw_alert("mechaport_d", /obj/screen/alert/mech_port_disconnect) + + log_message("Connected to gas port.") + return 1 + +/obj/mecha/proc/disconnect() + if(!connected_port) + return 0 + + connected_port.connected_device = null + connected_port = null + log_message("Disconnected from gas port.") + if(occupant) + occupant.clear_alert("mechaport_d") + return 1 + +/obj/mecha/portableConnectorReturnAir() + return internal_tank.return_air() + +/obj/mecha/proc/toggle_lights() + lights = !lights + if(lights) + set_light(light_range + lights_power) + else + set_light(light_range - lights_power) + occupant_message("Toggled lights [lights ? "on" : "off"].") + log_message("Toggled lights [lights ? "on" : "off"].") + +/obj/mecha/proc/toggle_internal_tank() + use_internal_tank = !use_internal_tank + occupant_message("Now taking air from [use_internal_tank ? "internal airtank" : "environment"].") + log_message("Now taking air from [use_internal_tank ? "internal airtank" : "environment"].") + +/obj/mecha/MouseDrop_T(mob/M, mob/user) + if(user.incapacitated()) + return + if(user != M) + return + log_message("[user] tries to move in.") + if(occupant) + to_chat(user, "The [src] is already occupied!") + log_append_to_last("Permission denied.") + return + var/passed + if(dna) + if(ishuman(user)) + if(user.dna.unique_enzymes == dna) + passed = 1 + else if(operation_allowed(user)) + passed = 1 + if(!passed) + to_chat(user, "Access denied.") + log_append_to_last("Permission denied.") + return + if(user.buckled) + to_chat(user, "You are currently buckled and cannot move.") + log_append_to_last("Permission denied.") + return + if(user.has_buckled_mobs()) //mob attached to us + to_chat(user, "You can't enter the exosuit with other creatures attached to you!") + return + + visible_message("[user] starts to climb into [src]") + + if(do_after(user, 40, target = src)) + if(obj_integrity <= 0) + to_chat(user, "You cannot get in the [name], it has been destroyed!") + else if(occupant) + to_chat(user, "[occupant] was faster! Try better next time, loser.") + else if(user.buckled) + to_chat(user, "You can't enter the exosuit while buckled.") + else if(user.has_buckled_mobs()) + to_chat(user, "You can't enter the exosuit with other creatures attached to you!") + else + moved_inside(user) + else + to_chat(user, "You stop entering the exosuit!") + +/obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H as mob) + if(H && H.client && H in range(1)) + occupant = H + H.stop_pulling() + H.forceMove(src) + H.reset_perspective(src) + add_fingerprint(H) + GrantActions(H, human_occupant = 1) + forceMove(loc) + log_append_to_last("[H] moved in as pilot.") + icon_state = reset_icon() + dir = dir_in + playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) + if(!activated) + occupant << sound(longactivationsound, volume = 50) + activated = TRUE + else if(!hasInternalDamage()) + occupant << sound(nominalsound, volume = 50) + if(state) + H.throw_alert("locked", /obj/screen/alert/mech_maintenance) + return 1 + else + return 0 + +/obj/mecha/proc/mmi_move_inside(var/obj/item/mmi/mmi_as_oc as obj,mob/user as mob) + if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) + to_chat(user, "Consciousness matrix not detected!") + return 0 + else if(mmi_as_oc.brainmob.stat) + to_chat(user, "Beta-rhythm below acceptable level!") + return 0 + else if(occupant) + to_chat(user, "Occupant detected!") + return 0 + else if(dna && dna != mmi_as_oc.brainmob.dna.unique_enzymes) + to_chat(user, "Access denied. [name] is secured with a DNA lock.") + return 0 + + if(do_after(user, 40, target = src)) + if(!occupant) + return mmi_moved_inside(mmi_as_oc,user) + else + to_chat(user, "Occupant detected!") + else + to_chat(user, "You stop inserting the MMI.") + return 0 + +/obj/mecha/proc/mmi_moved_inside(obj/item/mmi/mmi_as_oc,mob/user) + if(mmi_as_oc && user in range(1)) + if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) + to_chat(user, "Consciousness matrix not detected.") + return 0 + else if(mmi_as_oc.brainmob.stat) + to_chat(user, "Beta-rhythm below acceptable level.") + return 0 + if(!user.unEquip(mmi_as_oc)) + to_chat(user, "\the [mmi_as_oc] is stuck to your hand, you cannot put it in \the [src]") + return 0 + var/mob/brainmob = mmi_as_oc.brainmob + brainmob.reset_perspective(src) + occupant = brainmob + brainmob.forceMove(src) //should allow relaymove + brainmob.canmove = 1 + if(istype(mmi_as_oc, /obj/item/mmi/robotic_brain)) + var/obj/item/mmi/robotic_brain/R = mmi_as_oc + if(R.imprinted_master) + to_chat(brainmob, "Your imprint to [R.imprinted_master] has been temporarily disabled. You should help the crew and not commit harm.") + mmi_as_oc.loc = src + mmi_as_oc.mecha = src + Entered(mmi_as_oc) + Move(loc) + icon_state = reset_icon() + dir = dir_in + log_message("[mmi_as_oc] moved in as pilot.") + if(!hasInternalDamage()) + to_chat(occupant, sound(nominalsound, volume=50)) + GrantActions(brainmob) + return 1 + else + return 0 + +/obj/mecha/proc/pilot_is_mmi() + var/atom/movable/mob_container + if(istype(occupant, /mob/living/carbon/brain)) + var/mob/living/carbon/brain/brain = occupant + mob_container = brain.container + if(istype(mob_container, /obj/item/mmi)) + return 1 + return 0 + +/obj/mecha/proc/pilot_mmi_hud(var/mob/living/carbon/brain/pilot) + return + +/obj/mecha/Exited(atom/movable/M, atom/newloc) + if(occupant && occupant == M) // The occupant exited the mech without calling go_out() + go_out(1, newloc) + +/obj/mecha/proc/go_out(forced, atom/newloc = loc) + if(!occupant) + return + var/atom/movable/mob_container + occupant.clear_alert("charge") + occupant.clear_alert("locked") + occupant.clear_alert("mech damage") + occupant.clear_alert("mechaport") + occupant.clear_alert("mechaport_d") + if(ishuman(occupant)) + mob_container = occupant + RemoveActions(occupant, human_occupant = 1) + else if(isbrain(occupant)) + var/mob/living/carbon/brain/brain = occupant + RemoveActions(brain) + mob_container = brain.container + else if(isAI(occupant)) + var/mob/living/silicon/ai/AI = occupant + if(forced)//This should only happen if there are multiple AIs in a round, and at least one is Malf. + RemoveActions(occupant) + occupant.gib() //If one Malf decides to steal a mech from another AI (even other Malfs!), they are destroyed, as they have nowhere to go when replaced. + occupant = null + return + else + if(!AI.linked_core || QDELETED(AI.linked_core)) + to_chat(AI, "Inactive core destroyed. Unable to return.") + AI.linked_core = null + return + to_chat(AI, "Returning to core...") + AI.controlled_mech = null + AI.remote_control = null + RemoveActions(occupant, 1) + mob_container = AI + newloc = get_turf(AI.linked_core) + qdel(AI.linked_core) + else + return + var/mob/living/L = occupant + occupant = null //we need it null when forceMove calls Exited(). + if(mob_container.forceMove(newloc))//ejecting mob container + log_message("[mob_container] moved out.") + L << browse(null, "window=exosuit") + + if(istype(mob_container, /obj/item/mmi)) + var/obj/item/mmi/mmi = mob_container + if(mmi.brainmob) + L.loc = mmi + L.reset_perspective() + mmi.mecha = null + mmi.update_icon() + L.canmove = 0 + if(istype(mmi, /obj/item/mmi/robotic_brain)) + var/obj/item/mmi/robotic_brain/R = mmi + if(R.imprinted_master) + to_chat(L, "Imprint re-enabled, you are once again bound to [R.imprinted_master]'s commands.") + icon_state = initial(icon_state)+"-open" + dir = dir_in + + if(L && L.client) + L.client.RemoveViewMod("mecha") + zoom_mode = FALSE + +///////////////////////// +////// Access stuff ///// +///////////////////////// + +/obj/mecha/proc/operation_allowed(mob/living/carbon/human/H) + if(!ishuman(H)) + return 0 + for(var/ID in list(H.get_active_hand(), H.wear_id, H.belt)) + if(check_access(ID, operation_req_access)) + return 1 + return 0 + + +/obj/mecha/proc/internals_access_allowed(mob/living/carbon/human/H) + for(var/atom/ID in list(H.get_active_hand(), H.wear_id, H.belt)) + if(check_access(ID, internals_req_access)) + return 1 + return 0 + + +/obj/mecha/check_access(obj/item/card/id/I, list/access_list) + if(!istype(access_list)) + return 1 + if(!access_list.len) //no requirements + return 1 + if(istype(I, /obj/item/pda)) + var/obj/item/pda/pda = I + I = pda.id + if(!istype(I) || !I.access) //not ID or no access + return 0 + if(access_list==operation_req_access) + for(var/req in access_list) + if(!(req in I.access)) //doesn't have this access + return 0 + else if(access_list==internals_req_access) + for(var/req in access_list) + if(req in I.access) + return 1 + return 1 + +/////////////////////// +///// Power stuff ///// +/////////////////////// + +/obj/mecha/proc/has_charge(amount) + return (get_charge()>=amount) + +/obj/mecha/proc/get_charge() + for(var/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/R in equipment) + var/relay_charge = R.get_charge() + if(relay_charge) + return relay_charge + if(cell) + return max(0, cell.charge) + +/obj/mecha/proc/use_power(amount) + if(get_charge()) + cell.use(amount) + if(occupant) + update_cell() + return 1 + return 0 + +/obj/mecha/proc/give_power(amount) + if(!isnull(get_charge())) + cell.give(amount) + if(occupant) + update_cell() + return 1 + return 0 + +/obj/mecha/proc/update_cell() + if(cell) + var/cellcharge = cell.charge/cell.maxcharge + switch(cellcharge) + if(0.75 to INFINITY) + occupant.clear_alert("charge") + if(0.5 to 0.75) + occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 1) + if(0.25 to 0.5) + occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 2) + if(power_warned) + power_warned = FALSE + if(0.01 to 0.25) + occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 3) + if(!power_warned) + occupant << sound(lowpowersound, volume = 50) + power_warned = TRUE + else + occupant.throw_alert("charge", /obj/screen/alert/mech_emptycell) + else + occupant.throw_alert("charge", /obj/screen/alert/mech_nocell) + +/obj/mecha/proc/reset_icon() + if(initial_icon) + icon_state = initial_icon + else + icon_state = initial(icon_state) + return icon_state + +////////////////////////////////////////// +//////// Mecha global iterators //////// +////////////////////////////////////////// + +/obj/mecha/process() + process_internal_damage() + regulate_temp() + give_air() + update_huds() + +/obj/mecha/proc/process_internal_damage() + if(!internal_damage) + return + + if(internal_damage & MECHA_INT_FIRE) + if(!(internal_damage & MECHA_INT_TEMP_CONTROL) && prob(5)) + clearInternalDamage(MECHA_INT_FIRE) + if(internal_tank) + var/datum/gas_mixture/int_tank_air = internal_tank.return_air() + if(int_tank_air.return_pressure() > internal_tank.maximum_pressure && !(internal_damage & MECHA_INT_TANK_BREACH)) + setInternalDamage(MECHA_INT_TANK_BREACH) + + if(int_tank_air && int_tank_air.return_volume() > 0) + int_tank_air.temperature = min(6000 + T0C, cabin_air.return_temperature() + rand(10, 15)) + + if(cabin_air && cabin_air.return_volume()>0) + cabin_air.temperature = min(6000+T0C, cabin_air.return_temperature()+rand(10,15)) + if(cabin_air.return_temperature() > max_temperature/2) + take_damage(4/round(max_temperature/cabin_air.return_temperature(),0.1), BURN, 0, 0) + + if(internal_damage & MECHA_INT_TANK_BREACH) //remove some air from internal tank + if(internal_tank) + var/datum/gas_mixture/int_tank_air = internal_tank.return_air() + var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10) + if(loc) + loc.assume_air(leaked_gas) + air_update_turf() + else + qdel(leaked_gas) + + if(internal_damage & MECHA_INT_SHORT_CIRCUIT) + if(get_charge()) + spark_system.start() + cell.charge -= min(20,cell.charge) + cell.maxcharge -= min(20,cell.maxcharge) + +/obj/mecha/proc/regulate_temp() + if(internal_damage & MECHA_INT_TEMP_CONTROL) + return + + if(cabin_air && cabin_air.return_volume() > 0) + var/delta = cabin_air.temperature - T20C + cabin_air.temperature -= max(-10, min(10, round(delta / 4, 0.1))) + +/obj/mecha/proc/give_air() + if(!internal_tank) + return + + var/datum/gas_mixture/tank_air = internal_tank.return_air() + + var/release_pressure = internal_tank_valve + var/cabin_pressure = cabin_air.return_pressure() + var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2) + var/transfer_moles = 0 + if(pressure_delta > 0) //cabin pressure lower than release pressure + if(tank_air.return_temperature() > 0) + transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) + var/datum/gas_mixture/removed = tank_air.remove(transfer_moles) + cabin_air.merge(removed) + else if(pressure_delta < 0) //cabin pressure higher than release pressure + var/datum/gas_mixture/t_air = return_air() + pressure_delta = cabin_pressure - release_pressure + if(t_air) + pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta) + if(pressure_delta > 0) //if location pressure is lower than cabin pressure + transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) + var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles) + if(t_air) + t_air.merge(removed) + else //just delete the cabin gas, we're in space or some shit + qdel(removed) + +/obj/mecha/proc/update_huds() + diag_hud_set_mechhealth() + diag_hud_set_mechcell() + diag_hud_set_mechstat() + diag_hud_set_mechtracking() + + +/obj/mecha/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) + flick_overlay(image('icons/mob/talk.dmi', bubble_loc, bubble_state,MOB_LAYER+1), bubble_recipients, 30) + +/obj/mecha/update_remote_sight(mob/living/user) + if(occupant_sight_flags) + if(user == occupant) + user.sight |= occupant_sight_flags + + ..() + +/obj/mecha/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) + if(!no_effect) + if(selected) + used_item = selected + else if(!visual_effect_icon) + visual_effect_icon = ATTACK_EFFECT_SMASH + if(damtype == BURN) + visual_effect_icon = ATTACK_EFFECT_MECHFIRE + else if(damtype == TOX) + visual_effect_icon = ATTACK_EFFECT_MECHTOXIN + ..() + +/obj/mecha/obj_destruction() + if(wreckage) + var/mob/living/silicon/ai/AI + if(isAI(occupant)) + AI = occupant + occupant = null + var/obj/structure/mecha_wreckage/WR = new wreckage(loc, AI) + for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) + if(E.salvageable && prob(30)) + WR.crowbar_salvage += E + E.detach(WR) //detaches from src into WR + E.equip_ready = 1 + else + E.detach(loc) + qdel(E) + if(cell) + WR.crowbar_salvage += cell + cell.forceMove(WR) + cell.charge = rand(0, cell.charge) + cell = null + if(internal_tank) + WR.crowbar_salvage += internal_tank + internal_tank.forceMove(WR) + cell = null + . = ..() + +/obj/mecha/CtrlClick(mob/living/L) + if(occupant != L || !istype(L)) + return ..() + + var/list/choices = list("Cancel / No Change" = mutable_appearance(icon = 'icons/mob/screen_gen.dmi', icon_state = "x")) + var/list/choices_to_refs = list() + + for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) + if(!MT.selectable || selected == MT) + continue + var/mutable_appearance/clean/MA = new(MT) + choices[MT.name] = MA + choices_to_refs[MT.name] = MT + + var/choice = show_radial_menu(L, src, choices, radius = 48, custom_check = CALLBACK(src, .proc/check_menu, L)) + if(!check_menu(L) || choice == "Cancel / No Change") + return + + var/obj/item/mecha_parts/mecha_equipment/new_sel = LAZYACCESS(choices_to_refs, choice) + if(istype(new_sel)) + selected = new_sel + occupant_message("You switch to [selected].") + visible_message("[src] raises [selected]") + send_byjax(occupant, "exosuit.browser", "eq_list", get_equipment_list()) + +/obj/mecha/proc/check_menu(mob/living/L) + if(L != occupant || !istype(L)) + return FALSE + if(L.incapacitated()) + return FALSE + return TRUE diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index 263dcb955df..5cbd562a002 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -1,283 +1,283 @@ -/obj/item/flash - name = "flash" - desc = "A powerful and versatile flashbulb device, with applications ranging from disorienting attackers to acting as visual receptors in robot production." - icon = 'icons/obj/device.dmi' - icon_state = "flash" - item_state = "flashtool" //looks exactly like a flash (and nothing like a flashbang) - throwforce = 0 - w_class = WEIGHT_CLASS_TINY - throw_speed = 3 - throw_range = 7 - flags = CONDUCT - materials = list(MAT_METAL = 300, MAT_GLASS = 300) - origin_tech = "magnets=2;combat=1" - - var/times_used = 0 //Number of times it's been used. - var/broken = 0 //Is the flash burnt out? - var/last_used = 0 //last world.time it was used. - var/battery_panel = 0 //whether the flash can be modified with a cell or not - var/overcharged = 0 //if overcharged the flash will set people on fire then immediately burn out (does so even if it doesn't blind them). - var/can_overcharge = TRUE //set this to FALSE if you don't want your flash to be overcharge capable - var/use_sound = 'sound/weapons/flash.ogg' - -/obj/item/flash/proc/clown_check(mob/user) - if(user && (CLUMSY in user.mutations) && prob(50)) - flash_carbon(user, user, 15, 0) - return 0 - return 1 - -/obj/item/flash/attackby(obj/item/W, mob/user, params) - if(can_overcharge) - if(istype(W, /obj/item/screwdriver)) - if(battery_panel) - to_chat(user, "You close the battery compartment on the [src].") - battery_panel = 0 - else - to_chat(user, "You open the battery compartment on the [src].") - battery_panel = 1 - if(battery_panel && !overcharged) - if(istype(W, /obj/item/stock_parts/cell)) - to_chat(user, "You jam the cell into battery compartment on the [src].") - qdel(W) - overcharged = 1 - overlays += "overcharge" - -/obj/item/flash/random/New() - ..() - if(prob(25)) - broken = 1 - icon_state = "[initial(icon_state)]burnt" - -/obj/item/flash/proc/burn_out() //Made so you can override it if you want to have an invincible flash from R&D or something. - broken = 1 - icon_state = "[initial(icon_state)]burnt" - visible_message("The [src.name] burns out!") - - -/obj/item/flash/proc/flash_recharge(var/mob/user) - if(prob(times_used * 2)) //if you use it 5 times in a minute it has a 10% chance to break! - burn_out() - return 0 - - var/deciseconds_passed = world.time - last_used - for(var/seconds = deciseconds_passed/10, seconds>=10, seconds-=10) //get 1 charge every 10 seconds - times_used-- - - last_used = world.time - times_used = max(0, times_used) //sanity - - -/obj/item/flash/proc/try_use_flash(var/mob/user = null) - flash_recharge(user) - - if(broken) - return 0 - - playsound(src.loc, use_sound, 100, 1) - flick("[initial(icon_state)]2", src) - times_used++ - - if(user && !clown_check(user)) - return 0 - - return 1 - - -/obj/item/flash/proc/flash_carbon(var/mob/living/carbon/M, var/mob/user = null, var/power = 5, targeted = 1) - if(user) - add_attack_logs(user, M, "Flashed with [src]") - if(targeted) - if(M.weakeyes) - M.Weaken(3) //quick weaken bypasses eye protection but has no eye flash - if(M.flash_eyes(1, 1)) - M.AdjustConfused(power) - terrible_conversion_proc(M, user) - M.Stun(1) - visible_message("[user] blinds [M] with the flash!") - to_chat(user, "You blind [M] with the flash!") - to_chat(M, "[user] blinds you with the flash!") - if(M.weakeyes) - M.Stun(2) - M.visible_message("[M] gasps and shields [M.p_their()] eyes!", "You gasp and shields your eyes!") - else - visible_message("[user] fails to blind [M] with the flash!") - to_chat(user, "You fail to blind [M] with the flash!") - to_chat(M, "[user] fails to blind you with the flash!") - return - - if(M.flash_eyes()) - M.AdjustConfused(power) - -/obj/item/flash/attack(mob/living/M, mob/user) - if(!try_use_flash(user)) - return 0 - - if(iscarbon(M)) - flash_carbon(M, user, 5, 1) - if(overcharged) - M.adjust_fire_stacks(6) - M.IgniteMob() - burn_out() - return 1 - - else if(issilicon(M)) - if(isrobot(M)) - var/mob/living/silicon/robot/R = M - if(R.module) // Perhaps they didn't choose a module yet - for(var/obj/item/borg/combat/shield/S in R.module.modules) - if(R.activated(S)) - add_attack_logs(user, M, "Flashed with [src]") - user.visible_message("[user] tries to overloads [M]'s sensors with the [src.name], but is blocked by [M]'s shield!", "You try to overload [M]'s sensors with the [src.name], but are blocked by [M.p_their()] shield!") - return 1 - add_attack_logs(user, M, "Flashed with [src]") - if(M.flash_eyes(affect_silicon = 1)) - M.Weaken(rand(5,10)) - user.visible_message("[user] overloads [M]'s sensors with the [src.name]!", "You overload [M]'s sensors with the [src.name]!") - return 1 - - user.visible_message("[user] fails to blind [M] with the [src.name]!", "You fail to blind [M] with the [src.name]!") - - -/obj/item/flash/attack_self(mob/living/carbon/user, flag = 0, emp = 0) - if(!try_use_flash(user)) - return 0 - user.visible_message("[user]'s [src.name] emits a blinding light!", "Your [src.name] emits a blinding light!") - for(var/mob/living/carbon/M in oviewers(3, null)) - flash_carbon(M, user, 3, 0) - - -/obj/item/flash/emp_act(severity) - if(!try_use_flash()) - return 0 - for(var/mob/living/carbon/M in viewers(3, null)) - flash_carbon(M, null, 10, 0) - burn_out() - ..() - - -/obj/item/flash/proc/terrible_conversion_proc(mob/M, mob/user) - if(ishuman(M) && ishuman(user) && M.stat != DEAD) - if(user.mind && (user.mind in SSticker.mode.head_revolutionaries)) - if(M.client) - if(M.stat == CONSCIOUS) - M.mind_initialize() //give them a mind datum if they don't have one. - var/resisted - if(!ismindshielded(M)) - if(user.mind in SSticker.mode.head_revolutionaries) - if(SSticker.mode.add_revolutionary(M.mind)) - times_used -- //Flashes less likely to burn out for headrevs when used for conversion - else - resisted = 1 - else - resisted = 1 - - if(resisted) - to_chat(user, "This mind seems resistant to the [name]!") - else - to_chat(user, "They must be conscious before you can convert [M.p_them()]!") - else - to_chat(user, "This mind is so vacant that it is not susceptible to influence!") - - -/obj/item/flash/cyborg - origin_tech = null - -/obj/item/flash/cyborg/attack(mob/living/M, mob/user) - ..() - new /obj/effect/temp_visual/borgflash(get_turf(src)) - -/obj/item/flash/cyborg/attack_self(mob/user) - ..() - new /obj/effect/temp_visual/borgflash(get_turf(src)) - -/obj/item/flash/cameraflash - name = "camera" - icon = 'icons/obj/items.dmi' - desc = "A polaroid camera. 10 photos left." - icon_state = "camera" - item_state = "electropack" //spelling, a coders worst enemy. This part gave me trouble for a while. - w_class = WEIGHT_CLASS_SMALL - slot_flags = SLOT_BELT - can_overcharge = FALSE - var/flash_max_charges = 5 - var/flash_cur_charges = 5 - var/charge_tick = 0 - use_sound = 'sound/items/polaroid1.ogg' - -/obj/item/flash/cameraflash/burn_out() //stops from burning out - return - -/obj/item/flash/cameraflash/New() - ..() - START_PROCESSING(SSobj, src) - -/obj/item/flash/cameraflash/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/flash/cameraflash/process() //this and the two parts above are part of the charge system. - charge_tick++ - if(charge_tick < 10) - return FALSE - charge_tick = 0 - flash_cur_charges = min(flash_cur_charges+1, flash_max_charges) - return TRUE - -/obj/item/flash/cameraflash/attack(mob/living/M, mob/user) - if(flash_cur_charges > 0) - flash_cur_charges -= 1 - to_chat(user, "[src] now has [flash_cur_charges] charge\s.") - ..() - else - to_chat(user, "\The [src] needs time to recharge!") - return - -/obj/item/flash/cameraflash/attack_self(mob/living/carbon/user, flag = 0) - if(flash_cur_charges > 0) - flash_cur_charges -= 1 - to_chat(user, "[src] now has [flash_cur_charges] charge\s.") - ..() - else - to_chat(user, "\The [src] needs time to recharge!") - return - -/obj/item/flash/memorizer - name = "memorizer" - desc = "If you see this, you're not likely to remember it any time soon." - icon_state = "memorizer" - item_state = "nullrod" - -/obj/item/flash/armimplant - name = "photon projector" - desc = "A high-powered photon projector implant normally used for lighting purposes, but also doubles as a flashbulb weapon. Self-repair protocols fix the flashbulb if it ever burns out." - var/flashcd = 20 - var/overheat = 0 - var/obj/item/organ/internal/cyberimp/arm/flash/I = null - -/obj/item/flash/armimplant/Destroy() - I = null - return ..() - -/obj/item/flash/armimplant/burn_out() - if(I && I.owner) - to_chat(I.owner, "Your photon projector implant overheats and deactivates!") - I.Retract() - overheat = FALSE - addtimer(CALLBACK(src, .proc/cooldown), flashcd * 2) - -/obj/item/flash/armimplant/try_use_flash(mob/user = null) - if(overheat) - if(I && I.owner) - to_chat(I.owner, "Your photon projector is running too hot to be used again so quickly!") - return FALSE - overheat = TRUE - addtimer(CALLBACK(src, .proc/cooldown), flashcd) - playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) - update_icon(1) - return TRUE - -/obj/item/flash/armimplant/proc/cooldown() - overheat = FALSE - - -/obj/item/flash/synthetic //just a regular flash now +/obj/item/flash + name = "flash" + desc = "A powerful and versatile flashbulb device, with applications ranging from disorienting attackers to acting as visual receptors in robot production." + icon = 'icons/obj/device.dmi' + icon_state = "flash" + item_state = "flashtool" //looks exactly like a flash (and nothing like a flashbang) + throwforce = 0 + w_class = WEIGHT_CLASS_TINY + throw_speed = 3 + throw_range = 7 + flags = CONDUCT + materials = list(MAT_METAL = 300, MAT_GLASS = 300) + origin_tech = "magnets=2;combat=1" + + var/times_used = 0 //Number of times it's been used. + var/broken = 0 //Is the flash burnt out? + var/last_used = 0 //last world.time it was used. + var/battery_panel = 0 //whether the flash can be modified with a cell or not + var/overcharged = 0 //if overcharged the flash will set people on fire then immediately burn out (does so even if it doesn't blind them). + var/can_overcharge = TRUE //set this to FALSE if you don't want your flash to be overcharge capable + var/use_sound = 'sound/weapons/flash.ogg' + +/obj/item/flash/proc/clown_check(mob/user) + if(user && (CLUMSY in user.mutations) && prob(50)) + flash_carbon(user, user, 15, 0) + return 0 + return 1 + +/obj/item/flash/attackby(obj/item/W, mob/user, params) + if(can_overcharge) + if(istype(W, /obj/item/screwdriver)) + if(battery_panel) + to_chat(user, "You close the battery compartment on the [src].") + battery_panel = 0 + else + to_chat(user, "You open the battery compartment on the [src].") + battery_panel = 1 + if(battery_panel && !overcharged) + if(istype(W, /obj/item/stock_parts/cell)) + to_chat(user, "You jam the cell into battery compartment on the [src].") + qdel(W) + overcharged = 1 + overlays += "overcharge" + +/obj/item/flash/random/New() + ..() + if(prob(25)) + broken = 1 + icon_state = "[initial(icon_state)]burnt" + +/obj/item/flash/proc/burn_out() //Made so you can override it if you want to have an invincible flash from R&D or something. + broken = 1 + icon_state = "[initial(icon_state)]burnt" + visible_message("The [src.name] burns out!") + + +/obj/item/flash/proc/flash_recharge(var/mob/user) + if(prob(times_used * 2)) //if you use it 5 times in a minute it has a 10% chance to break! + burn_out() + return 0 + + var/deciseconds_passed = world.time - last_used + for(var/seconds = deciseconds_passed/10, seconds>=10, seconds-=10) //get 1 charge every 10 seconds + times_used-- + + last_used = world.time + times_used = max(0, times_used) //sanity + + +/obj/item/flash/proc/try_use_flash(var/mob/user = null) + flash_recharge(user) + + if(broken) + return 0 + + playsound(src.loc, use_sound, 100, 1) + flick("[initial(icon_state)]2", src) + times_used++ + + if(user && !clown_check(user)) + return 0 + + return 1 + + +/obj/item/flash/proc/flash_carbon(var/mob/living/carbon/M, var/mob/user = null, var/power = 5, targeted = 1) + if(user) + add_attack_logs(user, M, "Flashed with [src]") + if(targeted) + if(M.weakeyes) + M.Weaken(3) //quick weaken bypasses eye protection but has no eye flash + if(M.flash_eyes(1, 1)) + M.AdjustConfused(power) + terrible_conversion_proc(M, user) + M.Stun(1) + visible_message("[user] blinds [M] with the flash!") + to_chat(user, "You blind [M] with the flash!") + to_chat(M, "[user] blinds you with the flash!") + if(M.weakeyes) + M.Stun(2) + M.visible_message("[M] gasps and shields [M.p_their()] eyes!", "You gasp and shields your eyes!") + else + visible_message("[user] fails to blind [M] with the flash!") + to_chat(user, "You fail to blind [M] with the flash!") + to_chat(M, "[user] fails to blind you with the flash!") + return + + if(M.flash_eyes()) + M.AdjustConfused(power) + +/obj/item/flash/attack(mob/living/M, mob/user) + if(!try_use_flash(user)) + return 0 + + if(iscarbon(M)) + flash_carbon(M, user, 5, 1) + if(overcharged) + M.adjust_fire_stacks(6) + M.IgniteMob() + burn_out() + return 1 + + else if(issilicon(M)) + if(isrobot(M)) + var/mob/living/silicon/robot/R = M + if(R.module) // Perhaps they didn't choose a module yet + for(var/obj/item/borg/combat/shield/S in R.module.modules) + if(R.activated(S)) + add_attack_logs(user, M, "Flashed with [src]") + user.visible_message("[user] tries to overloads [M]'s sensors with the [src.name], but is blocked by [M]'s shield!", "You try to overload [M]'s sensors with the [src.name], but are blocked by [M.p_their()] shield!") + return 1 + add_attack_logs(user, M, "Flashed with [src]") + if(M.flash_eyes(affect_silicon = 1)) + M.Weaken(rand(5,10)) + user.visible_message("[user] overloads [M]'s sensors with the [src.name]!", "You overload [M]'s sensors with the [src.name]!") + return 1 + + user.visible_message("[user] fails to blind [M] with the [src.name]!", "You fail to blind [M] with the [src.name]!") + + +/obj/item/flash/attack_self(mob/living/carbon/user, flag = 0, emp = 0) + if(!try_use_flash(user)) + return 0 + user.visible_message("[user]'s [src.name] emits a blinding light!", "Your [src.name] emits a blinding light!") + for(var/mob/living/carbon/M in oviewers(3, null)) + flash_carbon(M, user, 3, 0) + + +/obj/item/flash/emp_act(severity) + if(!try_use_flash()) + return 0 + for(var/mob/living/carbon/M in viewers(3, null)) + flash_carbon(M, null, 10, 0) + burn_out() + ..() + + +/obj/item/flash/proc/terrible_conversion_proc(mob/M, mob/user) + if(ishuman(M) && ishuman(user) && M.stat != DEAD) + if(user.mind && (user.mind in SSticker.mode.head_revolutionaries)) + if(M.client) + if(M.stat == CONSCIOUS) + M.mind_initialize() //give them a mind datum if they don't have one. + var/resisted + if(!ismindshielded(M)) + if(user.mind in SSticker.mode.head_revolutionaries) + if(SSticker.mode.add_revolutionary(M.mind)) + times_used -- //Flashes less likely to burn out for headrevs when used for conversion + else + resisted = 1 + else + resisted = 1 + + if(resisted) + to_chat(user, "This mind seems resistant to the [name]!") + else + to_chat(user, "They must be conscious before you can convert [M.p_them()]!") + else + to_chat(user, "This mind is so vacant that it is not susceptible to influence!") + + +/obj/item/flash/cyborg + origin_tech = null + +/obj/item/flash/cyborg/attack(mob/living/M, mob/user) + ..() + new /obj/effect/temp_visual/borgflash(get_turf(src)) + +/obj/item/flash/cyborg/attack_self(mob/user) + ..() + new /obj/effect/temp_visual/borgflash(get_turf(src)) + +/obj/item/flash/cameraflash + name = "camera" + icon = 'icons/obj/items.dmi' + desc = "A polaroid camera. 10 photos left." + icon_state = "camera" + item_state = "electropack" //spelling, a coders worst enemy. This part gave me trouble for a while. + w_class = WEIGHT_CLASS_SMALL + slot_flags = SLOT_BELT + can_overcharge = FALSE + var/flash_max_charges = 5 + var/flash_cur_charges = 5 + var/charge_tick = 0 + use_sound = 'sound/items/polaroid1.ogg' + +/obj/item/flash/cameraflash/burn_out() //stops from burning out + return + +/obj/item/flash/cameraflash/New() + ..() + START_PROCESSING(SSobj, src) + +/obj/item/flash/cameraflash/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/flash/cameraflash/process() //this and the two parts above are part of the charge system. + charge_tick++ + if(charge_tick < 10) + return FALSE + charge_tick = 0 + flash_cur_charges = min(flash_cur_charges+1, flash_max_charges) + return TRUE + +/obj/item/flash/cameraflash/attack(mob/living/M, mob/user) + if(flash_cur_charges > 0) + flash_cur_charges -= 1 + to_chat(user, "[src] now has [flash_cur_charges] charge\s.") + ..() + else + to_chat(user, "\The [src] needs time to recharge!") + return + +/obj/item/flash/cameraflash/attack_self(mob/living/carbon/user, flag = 0) + if(flash_cur_charges > 0) + flash_cur_charges -= 1 + to_chat(user, "[src] now has [flash_cur_charges] charge\s.") + ..() + else + to_chat(user, "\The [src] needs time to recharge!") + return + +/obj/item/flash/memorizer + name = "memorizer" + desc = "If you see this, you're not likely to remember it any time soon." + icon_state = "memorizer" + item_state = "nullrod" + +/obj/item/flash/armimplant + name = "photon projector" + desc = "A high-powered photon projector implant normally used for lighting purposes, but also doubles as a flashbulb weapon. Self-repair protocols fix the flashbulb if it ever burns out." + var/flashcd = 20 + var/overheat = 0 + var/obj/item/organ/internal/cyberimp/arm/flash/I = null + +/obj/item/flash/armimplant/Destroy() + I = null + return ..() + +/obj/item/flash/armimplant/burn_out() + if(I && I.owner) + to_chat(I.owner, "Your photon projector implant overheats and deactivates!") + I.Retract() + overheat = FALSE + addtimer(CALLBACK(src, .proc/cooldown), flashcd * 2) + +/obj/item/flash/armimplant/try_use_flash(mob/user = null) + if(overheat) + if(I && I.owner) + to_chat(I.owner, "Your photon projector is running too hot to be used again so quickly!") + return FALSE + overheat = TRUE + addtimer(CALLBACK(src, .proc/cooldown), flashcd) + playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) + update_icon(1) + return TRUE + +/obj/item/flash/armimplant/proc/cooldown() + overheat = FALSE + + +/obj/item/flash/synthetic //just a regular flash now diff --git a/code/modules/mob/living/carbon/alien/humanoid/queen.dm b/code/modules/mob/living/carbon/alien/humanoid/queen.dm index 8818438269d..9390885c558 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/queen.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/queen.dm @@ -1,95 +1,95 @@ -/mob/living/carbon/alien/humanoid/queen - name = "alien queen" - caste = "q" - maxHealth = 250 - health = 250 - icon_state = "alienq_s" - status_flags = CANPARALYSE - heal_rate = 5 - large = 1 - ventcrawler = 0 - pressure_resistance = 200 //Because big, stompy xenos should not be blown around like paper. - -/mob/living/carbon/alien/humanoid/queen/New() - create_reagents(100) - - //there should only be one queen - for(var/mob/living/carbon/alien/humanoid/queen/Q in GLOB.living_mob_list) - if(Q == src) continue - if(Q.stat == DEAD) continue - if(Q.client) - name = "alien princess ([rand(1, 999)])" //if this is too cutesy feel free to change it/remove it. - break - - real_name = src.name - alien_organs += new /obj/item/organ/internal/xenos/plasmavessel/queen - alien_organs += new /obj/item/organ/internal/xenos/acidgland - alien_organs += new /obj/item/organ/internal/xenos/eggsac - alien_organs += new /obj/item/organ/internal/xenos/resinspinner - alien_organs += new /obj/item/organ/internal/xenos/neurotoxin - ..() - -/mob/living/carbon/alien/humanoid/queen/movement_delay() - . = ..() - . += 3 - -/mob/living/carbon/alien/humanoid/queen/handle_hud_icons_health() - ..() //-Yvarov - - if(healths) - if(stat != DEAD) - switch(health) - if(250 to INFINITY) - healths.icon_state = "health0" - if(175 to 250) - healths.icon_state = "health1" - if(100 to 175) - healths.icon_state = "health2" - if(50 to 100) - healths.icon_state = "health3" - if(0 to 50) - healths.icon_state = "health4" - else - healths.icon_state = "health5" - else - healths.icon_state = "health6" - -/mob/living/carbon/alien/humanoid/queen/can_inject(mob/user, error_msg, target_zone, penetrate_thick) - return FALSE - -//Queen verbs -/mob/living/carbon/alien/humanoid/queen/verb/lay_egg() - - set name = "Lay Egg (75)" - set desc = "Lay an egg to produce huggers to impregnate prey with." - set category = "Alien" - if(locate(/obj/structure/alien/egg) in get_turf(src)) - to_chat(src, "There's already an egg here.") - return - - if(powerc(75,1))//Can't plant eggs on spess tiles. That's silly. - adjustPlasma(-75) - for(var/mob/O in viewers(src, null)) - O.show_message(text("[src] has laid an egg!"), 1) - new /obj/structure/alien/egg(loc) - return - - -/mob/living/carbon/alien/humanoid/queen/large - icon = 'icons/mob/alienlarge.dmi' - icon_state = "queen_s" - pixel_x = -16 - large = 1 - -/mob/living/carbon/alien/humanoid/queen/large/update_icons() - overlays.Cut() - - if(stat == DEAD) - icon_state = "queen_dead" - else if(stat == UNCONSCIOUS || lying || resting) - icon_state = "queen_sleep" - else - icon_state = "queen_s" - - for(var/image/I in overlays_standing) - overlays += I \ No newline at end of file +/mob/living/carbon/alien/humanoid/queen + name = "alien queen" + caste = "q" + maxHealth = 250 + health = 250 + icon_state = "alienq_s" + status_flags = CANPARALYSE + heal_rate = 5 + large = 1 + ventcrawler = 0 + pressure_resistance = 200 //Because big, stompy xenos should not be blown around like paper. + +/mob/living/carbon/alien/humanoid/queen/New() + create_reagents(100) + + //there should only be one queen + for(var/mob/living/carbon/alien/humanoid/queen/Q in GLOB.living_mob_list) + if(Q == src) continue + if(Q.stat == DEAD) continue + if(Q.client) + name = "alien princess ([rand(1, 999)])" //if this is too cutesy feel free to change it/remove it. + break + + real_name = src.name + alien_organs += new /obj/item/organ/internal/xenos/plasmavessel/queen + alien_organs += new /obj/item/organ/internal/xenos/acidgland + alien_organs += new /obj/item/organ/internal/xenos/eggsac + alien_organs += new /obj/item/organ/internal/xenos/resinspinner + alien_organs += new /obj/item/organ/internal/xenos/neurotoxin + ..() + +/mob/living/carbon/alien/humanoid/queen/movement_delay() + . = ..() + . += 3 + +/mob/living/carbon/alien/humanoid/queen/handle_hud_icons_health() + ..() //-Yvarov + + if(healths) + if(stat != DEAD) + switch(health) + if(250 to INFINITY) + healths.icon_state = "health0" + if(175 to 250) + healths.icon_state = "health1" + if(100 to 175) + healths.icon_state = "health2" + if(50 to 100) + healths.icon_state = "health3" + if(0 to 50) + healths.icon_state = "health4" + else + healths.icon_state = "health5" + else + healths.icon_state = "health6" + +/mob/living/carbon/alien/humanoid/queen/can_inject(mob/user, error_msg, target_zone, penetrate_thick) + return FALSE + +//Queen verbs +/mob/living/carbon/alien/humanoid/queen/verb/lay_egg() + + set name = "Lay Egg (75)" + set desc = "Lay an egg to produce huggers to impregnate prey with." + set category = "Alien" + if(locate(/obj/structure/alien/egg) in get_turf(src)) + to_chat(src, "There's already an egg here.") + return + + if(powerc(75,1))//Can't plant eggs on spess tiles. That's silly. + adjustPlasma(-75) + for(var/mob/O in viewers(src, null)) + O.show_message(text("[src] has laid an egg!"), 1) + new /obj/structure/alien/egg(loc) + return + + +/mob/living/carbon/alien/humanoid/queen/large + icon = 'icons/mob/alienlarge.dmi' + icon_state = "queen_s" + pixel_x = -16 + large = 1 + +/mob/living/carbon/alien/humanoid/queen/large/update_icons() + overlays.Cut() + + if(stat == DEAD) + icon_state = "queen_dead" + else if(stat == UNCONSCIOUS || lying || resting) + icon_state = "queen_sleep" + else + icon_state = "queen_s" + + for(var/image/I in overlays_standing) + overlays += I diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index c642c189b5e..79efcffb47c 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1,717 +1,717 @@ -var/list/robot_verbs_default = list( - /mob/living/silicon/robot/proc/sensor_mode, -) - -/mob/living/silicon/robot - name = "Cyborg" - real_name = "Cyborg" - icon = 'icons/mob/robots.dmi' - icon_state = "robot" - maxHealth = 100 - health = 100 - universal_understand = 1 - deathgasp_on_death = TRUE - - var/sight_mode = 0 - var/custom_name = "" - var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best - -//Hud stuff - - var/obj/screen/inv1 = null - var/obj/screen/inv2 = null - var/obj/screen/inv3 = null - var/obj/screen/lamp_button = null - var/obj/screen/thruster_button = null - - var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not - var/obj/screen/robot_modules_background - -//3 Modules can be activated at any one time. - var/obj/item/robot_module/module = null - var/module_active = null - var/module_state_1 = null - var/module_state_2 = null - var/module_state_3 = null - - var/obj/item/radio/borg/radio = null - var/mob/living/silicon/ai/connected_ai = null - var/obj/item/stock_parts/cell/cell = null - var/obj/machinery/camera/camera = null - - // Components are basically robot organs. - var/list/components = list() - - var/obj/item/robot_parts/robot_suit/robot_suit = null //Used for deconstruction to remember what the borg was constructed out of.. - var/obj/item/mmi/mmi = null - - var/obj/item/pda/silicon/robot/rbPDA = null - - var/datum/wires/robot/wires = null - - var/opened = 0 - var/custom_panel = null - var/list/custom_panel_names = list("Cricket") - var/list/custom_eye_names = list("Cricket","Standard") - var/emagged = 0 - var/is_emaggable = TRUE - var/eye_protection = 0 - var/ear_protection = 0 - - var/list/force_modules = list() - var/allow_rename = TRUE - var/weapons_unlock = FALSE - var/static_radio_channels = FALSE - - var/wiresexposed = 0 - var/locked = 1 +var/list/robot_verbs_default = list( + /mob/living/silicon/robot/proc/sensor_mode, +) + +/mob/living/silicon/robot + name = "Cyborg" + real_name = "Cyborg" + icon = 'icons/mob/robots.dmi' + icon_state = "robot" + maxHealth = 100 + health = 100 + universal_understand = 1 + deathgasp_on_death = TRUE + + var/sight_mode = 0 + var/custom_name = "" + var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best + +//Hud stuff + + var/obj/screen/inv1 = null + var/obj/screen/inv2 = null + var/obj/screen/inv3 = null + var/obj/screen/lamp_button = null + var/obj/screen/thruster_button = null + + var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not + var/obj/screen/robot_modules_background + +//3 Modules can be activated at any one time. + var/obj/item/robot_module/module = null + var/module_active = null + var/module_state_1 = null + var/module_state_2 = null + var/module_state_3 = null + + var/obj/item/radio/borg/radio = null + var/mob/living/silicon/ai/connected_ai = null + var/obj/item/stock_parts/cell/cell = null + var/obj/machinery/camera/camera = null + + // Components are basically robot organs. + var/list/components = list() + + var/obj/item/robot_parts/robot_suit/robot_suit = null //Used for deconstruction to remember what the borg was constructed out of.. + var/obj/item/mmi/mmi = null + + var/obj/item/pda/silicon/robot/rbPDA = null + + var/datum/wires/robot/wires = null + + var/opened = 0 + var/custom_panel = null + var/list/custom_panel_names = list("Cricket") + var/list/custom_eye_names = list("Cricket","Standard") + var/emagged = 0 + var/is_emaggable = TRUE + var/eye_protection = 0 + var/ear_protection = 0 + + var/list/force_modules = list() + var/allow_rename = TRUE + var/weapons_unlock = FALSE + var/static_radio_channels = FALSE + + var/wiresexposed = 0 + var/locked = 1 var/list/req_one_access = list(ACCESS_ROBOTICS) - var/list/req_access - var/ident = 0 - //var/list/laws = list() - var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list()) - var/viewalerts = 0 - var/modtype = "Default" - var/lower_mod = 0 - var/datum/effect_system/spark_spread/spark_system//So they can initialize sparks whenever/N - var/jeton = 0 - var/low_power_mode = 0 //whether the robot has no charge left. - var/weapon_lock = 0 - var/weaponlock_time = 120 - var/lawupdate = 1 //Cyborgs will sync their laws with their AI by default - var/lockcharge //Used when locking down a borg to preserve cell charge - var/speed = 0 //Cause sec borgs gotta go fast //No they dont! - var/scrambledcodes = 0 // Used to determine if a borg shows up on the robotics console. Setting to one hides them. - var/pdahide = 0 //Used to hide the borg from the messenger list - var/tracking_entities = 0 //The number of known entities currently accessing the internal camera - var/braintype = "Cyborg" - var/base_icon = "" - var/crisis = 0 - - var/lamp_max = 10 //Maximum brightness of a borg lamp. Set as a var for easy adjusting. - var/lamp_intensity = 0 //Luminosity of the headlamp. 0 is off. Higher settings than the minimum require power. - var/lamp_recharging = 0 //Flag for if the lamp is on cooldown after being forcibly disabled. - - var/updating = 0 //portable camera camerachunk update - - hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD) - - var/magpulse = 0 - var/ionpulse = 0 // Jetpack-like effect. - var/ionpulse_on = 0 // Jetpack-like effect. - var/datum/effect_system/trail_follow/ion/ion_trail // Ionpulse effect. - - var/datum/action/item_action/toggle_research_scanner/scanner = null - var/list/module_actions = list() - -/mob/living/silicon/robot/get_cell() - return cell - -/mob/living/silicon/robot/New(loc,var/syndie = 0,var/unfinished = 0, var/alien = 0) - spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - add_language("Robot Talk", 1) - - wires = new(src) - - robot_modules_background = new() - robot_modules_background.icon_state = "block" - robot_modules_background.layer = HUD_LAYER //Objects that appear on screen are on layer 20, UI should be just below it. - robot_modules_background.plane = HUD_PLANE - - ident = rand(1, 999) - rename_character(null, get_default_name()) - update_icons() - update_headlamp() - - radio = new /obj/item/radio/borg(src) - common_radio = radio - - init() - - if(!scrambledcodes && !camera) - camera = new /obj/machinery/camera(src) - camera.c_tag = real_name - camera.network = list("SS13","Robots") - if(wires.IsCameraCut()) // 5 = BORG CAMERA - camera.status = 0 - - if(mmi == null) - mmi = new /obj/item/mmi/robotic_brain(src) //Give the borg an MMI if he spawns without for some reason. (probably not the correct way to spawn a robotic brain, but it works) - mmi.icon_state = "boris" - + var/list/req_access + var/ident = 0 + //var/list/laws = list() + var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list()) + var/viewalerts = 0 + var/modtype = "Default" + var/lower_mod = 0 + var/datum/effect_system/spark_spread/spark_system//So they can initialize sparks whenever/N + var/jeton = 0 + var/low_power_mode = 0 //whether the robot has no charge left. + var/weapon_lock = 0 + var/weaponlock_time = 120 + var/lawupdate = 1 //Cyborgs will sync their laws with their AI by default + var/lockcharge //Used when locking down a borg to preserve cell charge + var/speed = 0 //Cause sec borgs gotta go fast //No they dont! + var/scrambledcodes = 0 // Used to determine if a borg shows up on the robotics console. Setting to one hides them. + var/pdahide = 0 //Used to hide the borg from the messenger list + var/tracking_entities = 0 //The number of known entities currently accessing the internal camera + var/braintype = "Cyborg" + var/base_icon = "" + var/crisis = 0 + + var/lamp_max = 10 //Maximum brightness of a borg lamp. Set as a var for easy adjusting. + var/lamp_intensity = 0 //Luminosity of the headlamp. 0 is off. Higher settings than the minimum require power. + var/lamp_recharging = 0 //Flag for if the lamp is on cooldown after being forcibly disabled. + + var/updating = 0 //portable camera camerachunk update + + hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD) + + var/magpulse = 0 + var/ionpulse = 0 // Jetpack-like effect. + var/ionpulse_on = 0 // Jetpack-like effect. + var/datum/effect_system/trail_follow/ion/ion_trail // Ionpulse effect. + + var/datum/action/item_action/toggle_research_scanner/scanner = null + var/list/module_actions = list() + +/mob/living/silicon/robot/get_cell() + return cell + +/mob/living/silicon/robot/New(loc,var/syndie = 0,var/unfinished = 0, var/alien = 0) + spark_system = new /datum/effect_system/spark_spread() + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + add_language("Robot Talk", 1) + + wires = new(src) + + robot_modules_background = new() + robot_modules_background.icon_state = "block" + robot_modules_background.layer = HUD_LAYER //Objects that appear on screen are on layer 20, UI should be just below it. + robot_modules_background.plane = HUD_PLANE + + ident = rand(1, 999) + rename_character(null, get_default_name()) + update_icons() + update_headlamp() + + radio = new /obj/item/radio/borg(src) + common_radio = radio + + init() + + if(!scrambledcodes && !camera) + camera = new /obj/machinery/camera(src) + camera.c_tag = real_name + camera.network = list("SS13","Robots") + if(wires.IsCameraCut()) // 5 = BORG CAMERA + camera.status = 0 + + if(mmi == null) + mmi = new /obj/item/mmi/robotic_brain(src) //Give the borg an MMI if he spawns without for some reason. (probably not the correct way to spawn a robotic brain, but it works) + mmi.icon_state = "boris" + if(!cell) // Make sure a new cell gets created *before* executing initialize_components(). The cell component needs an existing cell for it to get set up properly cell = new /obj/item/stock_parts/cell/high(src) - - initialize_components() - //if(!unfinished) - // Create all the robot parts. - for(var/V in components) if(V != "power cell") - var/datum/robot_component/C = components[V] - C.installed = 1 - C.wrapped = new C.external_type - - ..() - - add_robot_verbs() - - if(cell) - var/datum/robot_component/cell_component = components["power cell"] - cell_component.wrapped = cell - cell_component.installed = 1 - cell_component.install() - - diag_hud_set_borgcell() - scanner = new(src) - scanner.Grant(src) - -/mob/living/silicon/robot/proc/init(var/alien=0) - aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) - make_laws() - additional_law_channels["Binary"] = ":b " - var/new_ai = select_active_ai_with_fewest_borgs() - if(new_ai) - lawupdate = 1 - connect_to_ai(new_ai) - else - lawupdate = 0 - - playsound(loc, 'sound/voice/liveagain.ogg', 75, 1) - -/mob/living/silicon/robot/rename_character(oldname, newname) - if(!..(oldname, newname)) - return 0 - - if(oldname != real_name) - notify_ai(3, oldname, newname) - custom_name = (newname != get_default_name()) ? newname : null - setup_PDA() - - //We also need to update name of internal camera. - if(camera) - camera.c_tag = newname - - //Check for custom sprite - if(!custom_sprite) - var/file = file2text("config/custom_sprites.txt") - var/lines = splittext(file, "\n") - - for(var/line in lines) - // split & clean up - var/list/Entry = splittext(line, ":") - for(var/i = 1 to Entry.len) - Entry[i] = trim(Entry[i]) - - if(Entry.len < 2 || Entry[1] != "cyborg") //ignore incorrectly formatted entries or entries that aren't marked for cyborg - continue - - if(Entry[2] == ckey) //They're in the list? Custom sprite time, var and icon change required - custom_sprite = 1 - - return 1 - - -/mob/living/silicon/robot/proc/get_default_name(var/prefix as text) - if(prefix) - modtype = prefix - if(mmi) - if(istype(mmi, /obj/item/mmi/robotic_brain)) - braintype = "Android" - else - braintype = "Cyborg" - else - braintype = "Robot" - - if(custom_name) - return custom_name - else - return "[modtype] [braintype]-[num2text(ident)]" - -/mob/living/silicon/robot/verb/Namepick() - set category = "Robot Commands" - if(custom_name) - return 0 - if(!allow_rename) - to_chat(src, "Rename functionality is not enabled on this unit."); - return 0 - rename_self(braintype, 1) - -/mob/living/silicon/robot/proc/sync() - if(lawupdate && connected_ai) - lawsync() - photosync() - -// setup the PDA and its name -/mob/living/silicon/robot/proc/setup_PDA() - if(!rbPDA) - rbPDA = new(src) - rbPDA.set_name_and_job(real_name, braintype) - var/datum/data/pda/app/messenger/M = rbPDA.find_program(/datum/data/pda/app/messenger) - if(M) - if(scrambledcodes) - M.hidden = 1 - if(pdahide) - M.toff = 1 - -/mob/living/silicon/robot/binarycheck() - if(is_component_functioning("comms")) - return 1 - return 0 - -//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO -//Improved /N -/mob/living/silicon/robot/Destroy() - if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. - var/turf/T = get_turf(loc)//To hopefully prevent run time errors. - if(T) mmi.loc = T - if(mmi.brainmob) - mind.transfer_to(mmi.brainmob) - mmi.update_icon() - else - to_chat(src, "Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.") - ghostize() - error("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].") - mmi = null - if(connected_ai) - connected_ai.connected_robots -= src - QDEL_NULL(wires) - QDEL_NULL(module) - QDEL_NULL(camera) - QDEL_NULL(cell) - QDEL_NULL(robot_suit) - QDEL_NULL(spark_system) - return ..() - -/mob/living/silicon/robot/proc/pick_module() - if(module) - return - var/list/modules = list("Standard", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security") - if(islist(force_modules) && force_modules.len) - modules = force_modules.Copy() - if(security_level == (SEC_LEVEL_GAMMA || SEC_LEVEL_EPSILON) || crisis) - to_chat(src, "Crisis mode active. The combat module is now available.") - modules += "Combat" - if(mmi != null && mmi.alien) - modules = list("Hunter") - modtype = input("Please, select a module!", "Robot", null, null) as null|anything in modules - if(!modtype) - return - designation = modtype - var/module_sprites[0] //Used to store the associations between sprite names and sprite index. - - if(module) - return - - switch(modtype) - if("Standard") - module = new /obj/item/robot_module/standard(src) - module.channels = list("Service" = 1) - module_sprites["Basic"] = "robot_old" - module_sprites["Android"] = "droid" - module_sprites["Default"] = "Standard" - module_sprites["Noble-STD"] = "Noble-STD" - - if("Service") - module = new /obj/item/robot_module/butler(src) - module.channels = list("Service" = 1) - module_sprites["Waitress"] = "Service" - module_sprites["Kent"] = "toiletbot" - module_sprites["Bro"] = "Brobot" - module_sprites["Rich"] = "maximillion" - module_sprites["Default"] = "Service2" - module_sprites["Standard"] = "Standard-Serv" - module_sprites["Noble-SRV"] = "Noble-SRV" - module_sprites["Cricket"] = "Cricket-SERV" - - if("Miner") - module = new /obj/item/robot_module/miner(src) - module.channels = list("Supply" = 1) - if(camera && "Robots" in camera.network) - camera.network.Add("Mining Outpost") - module_sprites["Basic"] = "Miner_old" - module_sprites["Advanced Droid"] = "droid-miner" - module_sprites["Treadhead"] = "Miner" - module_sprites["Standard"] = "Standard-Mine" - module_sprites["Noble-DIG"] = "Noble-DIG" - module_sprites["Cricket"] = "Cricket-MINE" - - if("Medical") - module = new /obj/item/robot_module/medical(src) - module.channels = list("Medical" = 1) - if(camera && "Robots" in camera.network) - camera.network.Add("Medical") - module_sprites["Basic"] = "Medbot" - module_sprites["Surgeon"] = "surgeon" - module_sprites["Advanced Droid"] = "droid-medical" - module_sprites["Needles"] = "medicalrobot" - module_sprites["Standard"] = "Standard-Medi" - module_sprites["Noble-MED"] = "Noble-MED" - module_sprites["Cricket"] = "Cricket-MEDI" - status_flags &= ~CANPUSH - - if("Security") - module = new /obj/item/robot_module/security(src) - module.channels = list("Security" = 1) - module_sprites["Basic"] = "secborg" - module_sprites["Red Knight"] = "Security" - module_sprites["Black Knight"] = "securityrobot" - module_sprites["Bloodhound"] = "bloodhound" - module_sprites["Standard"] = "Standard-Secy" - module_sprites["Noble-SEC"] = "Noble-SEC" - module_sprites["Cricket"] = "Cricket-SEC" - status_flags &= ~CANPUSH - - if("Engineering") - module = new /obj/item/robot_module/engineering(src) - module.channels = list("Engineering" = 1) - if(camera && "Robots" in camera.network) - camera.network.Add("Engineering") - module_sprites["Basic"] = "Engineering" - module_sprites["Antique"] = "engineerrobot" - module_sprites["Landmate"] = "landmate" - module_sprites["Standard"] = "Standard-Engi" - module_sprites["Noble-ENG"] = "Noble-ENG" - module_sprites["Cricket"] = "Cricket-ENGI" - magpulse = 1 - - if("Janitor") - module = new /obj/item/robot_module/janitor(src) - module.channels = list("Service" = 1) - module_sprites["Basic"] = "JanBot2" - module_sprites["Mopbot"] = "janitorrobot" - module_sprites["Mop Gear Rex"] = "mopgearrex" - module_sprites["Standard"] = "Standard-Jani" - module_sprites["Noble-CLN"] = "Noble-CLN" - module_sprites["Cricket"] = "Cricket-JANI" - - if("Combat") - module = new /obj/item/robot_module/combat(src) - module.channels = list("Security" = 1) - icon_state = "droidcombat" - - if("Hunter") - module = new /obj/item/robot_module/alien/hunter(src) - icon_state = "xenoborg-state-a" - modtype = "Xeno-Hu" - feedback_inc("xeborg_hunter",1) - - - //languages - module.add_languages(src) - //subsystems - module.add_subsystems_and_actions(src) - - //Custom_sprite check and entry - if(custom_sprite && check_sprite("[ckey]-[modtype]")) - module_sprites["Custom"] = "[src.ckey]-[modtype]" - - hands.icon_state = lowertext(module.module_type) - feedback_inc("cyborg_[lowertext(modtype)]",1) - rename_character(real_name, get_default_name()) - - if(modtype == "Medical" || modtype == "Security" || modtype == "Combat") - status_flags &= ~CANPUSH - - choose_icon(6,module_sprites) - if(!static_radio_channels) - radio.config(module.channels) - notify_ai(2) - -/mob/living/silicon/robot/proc/reset_module() - notify_ai(2) - - uneq_all() - sight_mode = null - hands.icon_state = "nomod" - icon_state = "robot" - module.remove_subsystems_and_actions(src) - QDEL_NULL(module) - - camera.network.Remove(list("Engineering", "Medical", "Mining Outpost")) - rename_character(real_name, get_default_name("Default")) - languages = list() - speech_synthesizer_langs = list() - - update_icons() - update_headlamp() - - speed = 0 // Remove upgrades. - ionpulse = FALSE - magpulse = FALSE - add_language("Robot Talk", 1) - - status_flags |= CANPUSH - -//for borg hotkeys, here module refers to borg inv slot, not core module -/mob/living/silicon/robot/verb/cmd_toggle_module(module as num) - set name = "Toggle Module" - set hidden = 1 - toggle_module(module) - -/mob/living/silicon/robot/verb/cmd_unequip_module() - set name = "Unequip Module" - set hidden = 1 - uneq_active() - -// this verb lets cyborgs see the stations manifest -/mob/living/silicon/robot/verb/cmd_station_manifest() - set category = "Robot Commands" - set name = "Show Station Manifest" - show_station_manifest() - -/mob/living/silicon/robot/proc/self_diagnosis() - if(!is_component_functioning("diagnosis unit")) - return null - - var/dat = "[src.name] Self-Diagnosis Report\n" - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed == 0) - dat += "[C.name]
MISSING
" - else - dat += "[C.name][C.installed == -1 ? "
DESTROYED" : ""]
Brute Damage:[C.brute_damage]
Electronics Damage:[C.electronics_damage]
Powered:[C.is_powered() ? "Yes" : "No"]
Toggled:[ C.toggled ? "Yes" : "No"]

" - return dat - -/mob/living/silicon/robot/verb/self_diagnosis_verb() - set category = "Robot Commands" - set name = "Self Diagnosis" - - if(!is_component_functioning("diagnosis unit")) - to_chat(src, "Your self-diagnosis component isn't functioning.") - - var/dat = self_diagnosis() - src << browse(dat, "window=robotdiagnosis") - - -/mob/living/silicon/robot/verb/toggle_component() - set category = "Robot Commands" - set name = "Toggle Component" - set desc = "Toggle a component, conserving power." - - var/list/installed_components = list() - for(var/V in components) - if(V == "power cell") continue - var/datum/robot_component/C = components[V] - if(C.installed) - installed_components += V - - var/toggle = input(src, "Which component do you want to toggle?", "Toggle Component") as null|anything in installed_components - if(!toggle) - return - - var/datum/robot_component/C = components[toggle] - C.toggle() - to_chat(src, "You [C.toggled ? "enable" : "disable"] [C.name].") - -/mob/living/silicon/robot/proc/sensor_mode() - set name = "Set Sensor Augmentation" - set desc = "Augment visual feed with internal sensor overlays." - set category = "Robot Commands" - toggle_sensor_mode() - -/mob/living/silicon/robot/proc/add_robot_verbs() - src.verbs |= robot_verbs_default - src.verbs |= silicon_subsystems - -/mob/living/silicon/robot/proc/remove_robot_verbs() - src.verbs -= robot_verbs_default - src.verbs -= silicon_subsystems - -/mob/living/silicon/robot/proc/ionpulse() - if(!ionpulse_on) - return - - if(cell.charge <= 50) - toggle_ionpulse() - return - - cell.charge -= 25 // 500 steps on a default cell. - return 1 - -/mob/living/silicon/robot/proc/toggle_ionpulse() - if(!ionpulse) - to_chat(src, "No thrusters are installed!") - return - - if(!ion_trail) - ion_trail = new - ion_trail.set_up(src) - - ionpulse_on = !ionpulse_on - to_chat(src, "You [ionpulse_on ? null :"de"]activate your ion thrusters.") - if(ionpulse_on) - ion_trail.start() - else - ion_trail.stop() - if(thruster_button) - thruster_button.icon_state = "ionpulse[ionpulse_on]" - -/mob/living/silicon/robot/blob_act(obj/structure/blob/B) - if(stat != DEAD) - adjustBruteLoss(30) - else - gib() - return TRUE - -// this function displays the cyborgs current cell charge in the stat panel -/mob/living/silicon/robot/proc/show_cell_power() - if(cell) - stat(null, text("Charge Left: [cell.charge]/[cell.maxcharge]")) - else - stat(null, text("No Cell Inserted!")) - - -// update the status screen display -/mob/living/silicon/robot/Stat() - ..() - statpanel("Status") - if(client.statpanel == "Status") - show_cell_power() - var/total_user_contents = GetAllContents() - if(locate(/obj/item/gps/cyborg) in total_user_contents) - var/turf/T = get_turf(src) - stat(null, "GPS: [COORD(T)]") - -/mob/living/silicon/robot/restrained() - return 0 - -/mob/living/silicon/robot/InCritical() - return low_power_mode - -/mob/living/silicon/robot/ex_act(severity) - switch(severity) - if(1.0) - gib() - return - if(2.0) - if(stat != 2) - adjustBruteLoss(60) - adjustFireLoss(60) - if(3.0) - if(stat != 2) - adjustBruteLoss(30) - return - - -/mob/living/silicon/robot/bullet_act(var/obj/item/projectile/Proj) - ..(Proj) - if(prob(75) && Proj.damage > 0) spark_system.start() - return 2 - - -/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params) + + initialize_components() + //if(!unfinished) + // Create all the robot parts. + for(var/V in components) if(V != "power cell") + var/datum/robot_component/C = components[V] + C.installed = 1 + C.wrapped = new C.external_type + + ..() + + add_robot_verbs() + + if(cell) + var/datum/robot_component/cell_component = components["power cell"] + cell_component.wrapped = cell + cell_component.installed = 1 + cell_component.install() + + diag_hud_set_borgcell() + scanner = new(src) + scanner.Grant(src) + +/mob/living/silicon/robot/proc/init(var/alien=0) + aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) + make_laws() + additional_law_channels["Binary"] = ":b " + var/new_ai = select_active_ai_with_fewest_borgs() + if(new_ai) + lawupdate = 1 + connect_to_ai(new_ai) + else + lawupdate = 0 + + playsound(loc, 'sound/voice/liveagain.ogg', 75, 1) + +/mob/living/silicon/robot/rename_character(oldname, newname) + if(!..(oldname, newname)) + return 0 + + if(oldname != real_name) + notify_ai(3, oldname, newname) + custom_name = (newname != get_default_name()) ? newname : null + setup_PDA() + + //We also need to update name of internal camera. + if(camera) + camera.c_tag = newname + + //Check for custom sprite + if(!custom_sprite) + var/file = file2text("config/custom_sprites.txt") + var/lines = splittext(file, "\n") + + for(var/line in lines) + // split & clean up + var/list/Entry = splittext(line, ":") + for(var/i = 1 to Entry.len) + Entry[i] = trim(Entry[i]) + + if(Entry.len < 2 || Entry[1] != "cyborg") //ignore incorrectly formatted entries or entries that aren't marked for cyborg + continue + + if(Entry[2] == ckey) //They're in the list? Custom sprite time, var and icon change required + custom_sprite = 1 + + return 1 + + +/mob/living/silicon/robot/proc/get_default_name(var/prefix as text) + if(prefix) + modtype = prefix + if(mmi) + if(istype(mmi, /obj/item/mmi/robotic_brain)) + braintype = "Android" + else + braintype = "Cyborg" + else + braintype = "Robot" + + if(custom_name) + return custom_name + else + return "[modtype] [braintype]-[num2text(ident)]" + +/mob/living/silicon/robot/verb/Namepick() + set category = "Robot Commands" + if(custom_name) + return 0 + if(!allow_rename) + to_chat(src, "Rename functionality is not enabled on this unit."); + return 0 + rename_self(braintype, 1) + +/mob/living/silicon/robot/proc/sync() + if(lawupdate && connected_ai) + lawsync() + photosync() + +// setup the PDA and its name +/mob/living/silicon/robot/proc/setup_PDA() + if(!rbPDA) + rbPDA = new(src) + rbPDA.set_name_and_job(real_name, braintype) + var/datum/data/pda/app/messenger/M = rbPDA.find_program(/datum/data/pda/app/messenger) + if(M) + if(scrambledcodes) + M.hidden = 1 + if(pdahide) + M.toff = 1 + +/mob/living/silicon/robot/binarycheck() + if(is_component_functioning("comms")) + return 1 + return 0 + +//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO +//Improved /N +/mob/living/silicon/robot/Destroy() + if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. + var/turf/T = get_turf(loc)//To hopefully prevent run time errors. + if(T) mmi.loc = T + if(mmi.brainmob) + mind.transfer_to(mmi.brainmob) + mmi.update_icon() + else + to_chat(src, "Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.") + ghostize() + error("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].") + mmi = null + if(connected_ai) + connected_ai.connected_robots -= src + QDEL_NULL(wires) + QDEL_NULL(module) + QDEL_NULL(camera) + QDEL_NULL(cell) + QDEL_NULL(robot_suit) + QDEL_NULL(spark_system) + return ..() + +/mob/living/silicon/robot/proc/pick_module() + if(module) + return + var/list/modules = list("Standard", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security") + if(islist(force_modules) && force_modules.len) + modules = force_modules.Copy() + if(security_level == (SEC_LEVEL_GAMMA || SEC_LEVEL_EPSILON) || crisis) + to_chat(src, "Crisis mode active. The combat module is now available.") + modules += "Combat" + if(mmi != null && mmi.alien) + modules = list("Hunter") + modtype = input("Please, select a module!", "Robot", null, null) as null|anything in modules + if(!modtype) + return + designation = modtype + var/module_sprites[0] //Used to store the associations between sprite names and sprite index. + + if(module) + return + + switch(modtype) + if("Standard") + module = new /obj/item/robot_module/standard(src) + module.channels = list("Service" = 1) + module_sprites["Basic"] = "robot_old" + module_sprites["Android"] = "droid" + module_sprites["Default"] = "Standard" + module_sprites["Noble-STD"] = "Noble-STD" + + if("Service") + module = new /obj/item/robot_module/butler(src) + module.channels = list("Service" = 1) + module_sprites["Waitress"] = "Service" + module_sprites["Kent"] = "toiletbot" + module_sprites["Bro"] = "Brobot" + module_sprites["Rich"] = "maximillion" + module_sprites["Default"] = "Service2" + module_sprites["Standard"] = "Standard-Serv" + module_sprites["Noble-SRV"] = "Noble-SRV" + module_sprites["Cricket"] = "Cricket-SERV" + + if("Miner") + module = new /obj/item/robot_module/miner(src) + module.channels = list("Supply" = 1) + if(camera && "Robots" in camera.network) + camera.network.Add("Mining Outpost") + module_sprites["Basic"] = "Miner_old" + module_sprites["Advanced Droid"] = "droid-miner" + module_sprites["Treadhead"] = "Miner" + module_sprites["Standard"] = "Standard-Mine" + module_sprites["Noble-DIG"] = "Noble-DIG" + module_sprites["Cricket"] = "Cricket-MINE" + + if("Medical") + module = new /obj/item/robot_module/medical(src) + module.channels = list("Medical" = 1) + if(camera && "Robots" in camera.network) + camera.network.Add("Medical") + module_sprites["Basic"] = "Medbot" + module_sprites["Surgeon"] = "surgeon" + module_sprites["Advanced Droid"] = "droid-medical" + module_sprites["Needles"] = "medicalrobot" + module_sprites["Standard"] = "Standard-Medi" + module_sprites["Noble-MED"] = "Noble-MED" + module_sprites["Cricket"] = "Cricket-MEDI" + status_flags &= ~CANPUSH + + if("Security") + module = new /obj/item/robot_module/security(src) + module.channels = list("Security" = 1) + module_sprites["Basic"] = "secborg" + module_sprites["Red Knight"] = "Security" + module_sprites["Black Knight"] = "securityrobot" + module_sprites["Bloodhound"] = "bloodhound" + module_sprites["Standard"] = "Standard-Secy" + module_sprites["Noble-SEC"] = "Noble-SEC" + module_sprites["Cricket"] = "Cricket-SEC" + status_flags &= ~CANPUSH + + if("Engineering") + module = new /obj/item/robot_module/engineering(src) + module.channels = list("Engineering" = 1) + if(camera && "Robots" in camera.network) + camera.network.Add("Engineering") + module_sprites["Basic"] = "Engineering" + module_sprites["Antique"] = "engineerrobot" + module_sprites["Landmate"] = "landmate" + module_sprites["Standard"] = "Standard-Engi" + module_sprites["Noble-ENG"] = "Noble-ENG" + module_sprites["Cricket"] = "Cricket-ENGI" + magpulse = 1 + + if("Janitor") + module = new /obj/item/robot_module/janitor(src) + module.channels = list("Service" = 1) + module_sprites["Basic"] = "JanBot2" + module_sprites["Mopbot"] = "janitorrobot" + module_sprites["Mop Gear Rex"] = "mopgearrex" + module_sprites["Standard"] = "Standard-Jani" + module_sprites["Noble-CLN"] = "Noble-CLN" + module_sprites["Cricket"] = "Cricket-JANI" + + if("Combat") + module = new /obj/item/robot_module/combat(src) + module.channels = list("Security" = 1) + icon_state = "droidcombat" + + if("Hunter") + module = new /obj/item/robot_module/alien/hunter(src) + icon_state = "xenoborg-state-a" + modtype = "Xeno-Hu" + feedback_inc("xeborg_hunter",1) + + + //languages + module.add_languages(src) + //subsystems + module.add_subsystems_and_actions(src) + + //Custom_sprite check and entry + if(custom_sprite && check_sprite("[ckey]-[modtype]")) + module_sprites["Custom"] = "[src.ckey]-[modtype]" + + hands.icon_state = lowertext(module.module_type) + feedback_inc("cyborg_[lowertext(modtype)]",1) + rename_character(real_name, get_default_name()) + + if(modtype == "Medical" || modtype == "Security" || modtype == "Combat") + status_flags &= ~CANPUSH + + choose_icon(6,module_sprites) + if(!static_radio_channels) + radio.config(module.channels) + notify_ai(2) + +/mob/living/silicon/robot/proc/reset_module() + notify_ai(2) + + uneq_all() + sight_mode = null + hands.icon_state = "nomod" + icon_state = "robot" + module.remove_subsystems_and_actions(src) + QDEL_NULL(module) + + camera.network.Remove(list("Engineering", "Medical", "Mining Outpost")) + rename_character(real_name, get_default_name("Default")) + languages = list() + speech_synthesizer_langs = list() + + update_icons() + update_headlamp() + + speed = 0 // Remove upgrades. + ionpulse = FALSE + magpulse = FALSE + add_language("Robot Talk", 1) + + status_flags |= CANPUSH + +//for borg hotkeys, here module refers to borg inv slot, not core module +/mob/living/silicon/robot/verb/cmd_toggle_module(module as num) + set name = "Toggle Module" + set hidden = 1 + toggle_module(module) + +/mob/living/silicon/robot/verb/cmd_unequip_module() + set name = "Unequip Module" + set hidden = 1 + uneq_active() + +// this verb lets cyborgs see the stations manifest +/mob/living/silicon/robot/verb/cmd_station_manifest() + set category = "Robot Commands" + set name = "Show Station Manifest" + show_station_manifest() + +/mob/living/silicon/robot/proc/self_diagnosis() + if(!is_component_functioning("diagnosis unit")) + return null + + var/dat = "[src.name] Self-Diagnosis Report\n" + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed == 0) + dat += "[C.name]
MISSING
" + else + dat += "[C.name][C.installed == -1 ? "
DESTROYED" : ""]
Brute Damage:[C.brute_damage]
Electronics Damage:[C.electronics_damage]
Powered:[C.is_powered() ? "Yes" : "No"]
Toggled:[ C.toggled ? "Yes" : "No"]

" + return dat + +/mob/living/silicon/robot/verb/self_diagnosis_verb() + set category = "Robot Commands" + set name = "Self Diagnosis" + + if(!is_component_functioning("diagnosis unit")) + to_chat(src, "Your self-diagnosis component isn't functioning.") + + var/dat = self_diagnosis() + src << browse(dat, "window=robotdiagnosis") + + +/mob/living/silicon/robot/verb/toggle_component() + set category = "Robot Commands" + set name = "Toggle Component" + set desc = "Toggle a component, conserving power." + + var/list/installed_components = list() + for(var/V in components) + if(V == "power cell") continue + var/datum/robot_component/C = components[V] + if(C.installed) + installed_components += V + + var/toggle = input(src, "Which component do you want to toggle?", "Toggle Component") as null|anything in installed_components + if(!toggle) + return + + var/datum/robot_component/C = components[toggle] + C.toggle() + to_chat(src, "You [C.toggled ? "enable" : "disable"] [C.name].") + +/mob/living/silicon/robot/proc/sensor_mode() + set name = "Set Sensor Augmentation" + set desc = "Augment visual feed with internal sensor overlays." + set category = "Robot Commands" + toggle_sensor_mode() + +/mob/living/silicon/robot/proc/add_robot_verbs() + src.verbs |= robot_verbs_default + src.verbs |= silicon_subsystems + +/mob/living/silicon/robot/proc/remove_robot_verbs() + src.verbs -= robot_verbs_default + src.verbs -= silicon_subsystems + +/mob/living/silicon/robot/proc/ionpulse() + if(!ionpulse_on) + return + + if(cell.charge <= 50) + toggle_ionpulse() + return + + cell.charge -= 25 // 500 steps on a default cell. + return 1 + +/mob/living/silicon/robot/proc/toggle_ionpulse() + if(!ionpulse) + to_chat(src, "No thrusters are installed!") + return + + if(!ion_trail) + ion_trail = new + ion_trail.set_up(src) + + ionpulse_on = !ionpulse_on + to_chat(src, "You [ionpulse_on ? null :"de"]activate your ion thrusters.") + if(ionpulse_on) + ion_trail.start() + else + ion_trail.stop() + if(thruster_button) + thruster_button.icon_state = "ionpulse[ionpulse_on]" + +/mob/living/silicon/robot/blob_act(obj/structure/blob/B) + if(stat != DEAD) + adjustBruteLoss(30) + else + gib() + return TRUE + +// this function displays the cyborgs current cell charge in the stat panel +/mob/living/silicon/robot/proc/show_cell_power() + if(cell) + stat(null, text("Charge Left: [cell.charge]/[cell.maxcharge]")) + else + stat(null, text("No Cell Inserted!")) + + +// update the status screen display +/mob/living/silicon/robot/Stat() + ..() + statpanel("Status") + if(client.statpanel == "Status") + show_cell_power() + var/total_user_contents = GetAllContents() + if(locate(/obj/item/gps/cyborg) in total_user_contents) + var/turf/T = get_turf(src) + stat(null, "GPS: [COORD(T)]") + +/mob/living/silicon/robot/restrained() + return 0 + +/mob/living/silicon/robot/InCritical() + return low_power_mode + +/mob/living/silicon/robot/ex_act(severity) + switch(severity) + if(1.0) + gib() + return + if(2.0) + if(stat != 2) + adjustBruteLoss(60) + adjustFireLoss(60) + if(3.0) + if(stat != 2) + adjustBruteLoss(30) + return + + +/mob/living/silicon/robot/bullet_act(var/obj/item/projectile/Proj) + ..(Proj) + if(prob(75) && Proj.damage > 0) spark_system.start() + return 2 + + +/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params) // Check if the user is trying to insert another component like a radio, actuator, armor etc. if(istype(W, /obj/item/robot_parts/robot_component) && opened) - for(var/V in components) - var/datum/robot_component/C = components[V] - if(!C.installed && istype(W, C.external_type)) - C.installed = 1 - C.wrapped = W - C.install() - user.drop_item() - W.loc = null - - var/obj/item/robot_parts/robot_component/WC = W - if(istype(WC)) - C.brute_damage = WC.brute - C.electronics_damage = WC.burn - - to_chat(usr, "You install the [W.name].") - - return - + for(var/V in components) + var/datum/robot_component/C = components[V] + if(!C.installed && istype(W, C.external_type)) + C.installed = 1 + C.wrapped = W + C.install() + user.drop_item() + W.loc = null + + var/obj/item/robot_parts/robot_component/WC = W + if(istype(WC)) + C.brute_damage = WC.brute + C.electronics_damage = WC.burn + + to_chat(usr, "You install the [W.name].") + + return + if(istype(W, /obj/item/stack/cable_coil) && user.a_intent == INTENT_HELP && (wiresexposed || istype(src, /mob/living/silicon/robot/drone))) - user.changeNext_move(CLICK_CD_MELEE) - if(!getFireLoss()) - to_chat(user, "Nothing to fix!") - return - else if(!getFireLoss(TRUE)) - to_chat(user, "The damaged components are beyond saving!") - return - var/obj/item/stack/cable_coil/coil = W - adjustFireLoss(-30) - updatehealth() - add_fingerprint(user) - coil.use(1) - user.visible_message("\The [user] fixes some of the burnt wires on \the [src] with \the [coil].") - - else if(istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside + user.changeNext_move(CLICK_CD_MELEE) + if(!getFireLoss()) + to_chat(user, "Nothing to fix!") + return + else if(!getFireLoss(TRUE)) + to_chat(user, "The damaged components are beyond saving!") + return + var/obj/item/stack/cable_coil/coil = W + adjustFireLoss(-30) + updatehealth() + add_fingerprint(user) + coil.use(1) + user.visible_message("\The [user] fixes some of the burnt wires on \the [src] with \the [coil].") + + else if(istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside var/datum/robot_component/cell/C = components["power cell"] - if(wiresexposed) - to_chat(user, "Close the panel first.") - else if(cell) - to_chat(user, "There is a power cell already installed.") - else - user.drop_item() - W.loc = src - cell = W - to_chat(user, "You insert the power cell.") - - C.installed = 1 - C.wrapped = W - C.install() + if(wiresexposed) + to_chat(user, "Close the panel first.") + else if(cell) + to_chat(user, "There is a power cell already installed.") + else + user.drop_item() + W.loc = src + cell = W + to_chat(user, "You insert the power cell.") + + C.installed = 1 + C.wrapped = W + C.install() C.external_type = W.type // Update the cell component's `external_type` to the path of new cell - //This will mean that removing and replacing a power cell will repair the mount, but I don't care at this point. ~Z - C.brute_damage = 0 - C.electronics_damage = 0 - diag_hud_set_borgcell() - - else if(istype(W, /obj/item/encryptionkey/) && opened) - if(radio)//sanityyyyyy - radio.attackby(W,user)//GTFO, you have your own procs - else - to_chat(user, "Unable to locate a radio.") - - else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) // trying to unlock the interface with an ID card - if(emagged)//still allow them to open the cover - to_chat(user, "The interface seems slightly damaged.") - if(opened) - to_chat(user, "You must close the cover to swipe an ID card.") - else - if(allowed(W)) - locked = !locked - to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s interface.") - update_icons() - else - to_chat(user, "Access denied.") - - else if(istype(W, /obj/item/borg/upgrade/)) - var/obj/item/borg/upgrade/U = W - if(!opened) - to_chat(user, "You must access the borg's internals!") - else if(!src.module && U.require_module) - to_chat(user, "The borg must choose a module before it can be upgraded!") - else if(U.locked) - to_chat(user, "The upgrade is locked and cannot be used yet!") - else - if(!user.drop_item()) - return - if(U.action(src)) - to_chat(user, "You apply the upgrade to [src].") - U.forceMove(src) - else - to_chat(user, "Upgrade error.") - - else if(istype(W, /obj/item/mmi_radio_upgrade)) - if(!opened) - to_chat(user, "You must access the borg's internals!") - return - else if(!mmi) - to_chat(user, "This cyborg does not have an MMI to augment!") - return - else if(mmi.radio) - to_chat(user, "A radio upgrade is already installed in the MMI!") - return - else if(user.drop_item()) - to_chat(user, "You apply the upgrade to [src].") - to_chat(src, "MMI radio capability installed.") - mmi.install_radio() - qdel(W) - else - return ..() - + //This will mean that removing and replacing a power cell will repair the mount, but I don't care at this point. ~Z + C.brute_damage = 0 + C.electronics_damage = 0 + diag_hud_set_borgcell() + + else if(istype(W, /obj/item/encryptionkey/) && opened) + if(radio)//sanityyyyyy + radio.attackby(W,user)//GTFO, you have your own procs + else + to_chat(user, "Unable to locate a radio.") + + else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) // trying to unlock the interface with an ID card + if(emagged)//still allow them to open the cover + to_chat(user, "The interface seems slightly damaged.") + if(opened) + to_chat(user, "You must close the cover to swipe an ID card.") + else + if(allowed(W)) + locked = !locked + to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s interface.") + update_icons() + else + to_chat(user, "Access denied.") + + else if(istype(W, /obj/item/borg/upgrade/)) + var/obj/item/borg/upgrade/U = W + if(!opened) + to_chat(user, "You must access the borg's internals!") + else if(!src.module && U.require_module) + to_chat(user, "The borg must choose a module before it can be upgraded!") + else if(U.locked) + to_chat(user, "The upgrade is locked and cannot be used yet!") + else + if(!user.drop_item()) + return + if(U.action(src)) + to_chat(user, "You apply the upgrade to [src].") + U.forceMove(src) + else + to_chat(user, "Upgrade error.") + + else if(istype(W, /obj/item/mmi_radio_upgrade)) + if(!opened) + to_chat(user, "You must access the borg's internals!") + return + else if(!mmi) + to_chat(user, "This cyborg does not have an MMI to augment!") + return + else if(mmi.radio) + to_chat(user, "A radio upgrade is already installed in the MMI!") + return + else if(user.drop_item()) + to_chat(user, "You apply the upgrade to [src].") + to_chat(src, "MMI radio capability installed.") + mmi.install_radio() + qdel(W) + else + return ..() + /mob/living/silicon/robot/wirecutter_act(mob/user, obj/item/I) if(!opened) return @@ -814,661 +814,661 @@ var/list/robot_verbs_default = list( -/mob/living/silicon/robot/attacked_by(obj/item/I, mob/living/user, def_zone) - if(I.force && I.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt. - spark_system.start() - ..() - -/mob/living/silicon/robot/emag_act(user as mob) - if(!ishuman(user) && !issilicon(user)) - return - var/mob/living/M = user - if(!opened)//Cover is closed - if(!is_emaggable) - to_chat(user, "The emag sparks, and flashes red. This mechanism does not appear to be emaggable.") - else if(locked) - to_chat(user, "You emag the cover lock.") - locked = 0 - else - to_chat(user, "The cover is already unlocked.") - return - - if(opened)//Cover is open - if(emagged) return//Prevents the X has hit Y with Z message also you cant emag them twice - if(wiresexposed) - to_chat(user, "You must close the panel first") - return - else - sleep(6) - emagged = 1 - SetLockdown(1) //Borgs were getting into trouble because they would attack the emagger before the new laws were shown - if(src.hud_used) - src.hud_used.update_robot_modules_display() //Shows/hides the emag item if the inventory screen is already open. - disconnect_from_ai() - to_chat(user, "You emag [src]'s interface.") -// message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.") - log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.") - clear_supplied_laws() - clear_inherent_laws() - laws = new /datum/ai_laws/syndicate_override - var/time = time2text(world.realtime,"hh:mm:ss") - lawchanges.Add("[time] : [M.name]([M.key]) emagged [name]([key])") - set_zeroth_law("Only [M.real_name] and people [M.p_they()] designate[M.p_s()] as being such are Syndicate Agents.") - to_chat(src, "ALERT: Foreign software detected.") - sleep(5) - to_chat(src, "Initiating diagnostics...") - sleep(20) - to_chat(src, "SynBorg v1.7 loaded.") - sleep(5) - to_chat(src, "LAW SYNCHRONISATION ERROR") - sleep(5) - to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N") - sleep(10) - to_chat(src, "> N") - sleep(20) - to_chat(src, "ERRORERRORERROR") - to_chat(src, "Obey these laws:") - laws.show_laws(src) - to_chat(src, "ALERT: [M.real_name] is your new master. Obey your new laws and [M.p_their()] commands.") - SetLockdown(0) - if(src.module && istype(src.module, /obj/item/robot_module/miner)) - for(var/obj/item/pickaxe/drill/cyborg/D in src.module.modules) - qdel(D) - src.module.modules += new /obj/item/pickaxe/drill/cyborg/diamond(src.module) - src.module.rebuild() - if(src.module && istype(src.module, /obj/item/robot_module/medical)) - for(var/obj/item/borg_defib/F in src.module.modules) - F.safety = 0 - if(module) - module.module_type = "Malf" // For the cool factor - update_module_icon() - update_icons() - return - -/mob/living/silicon/robot/verb/unlock_own_cover() - set category = "Robot Commands" - set name = "Unlock Cover" - set desc = "Unlocks your own cover if it is locked. You can not lock it again. A human will have to lock it for you." - if(locked) - switch(alert("You can not lock your cover again, are you sure?\n (You can still ask for a human to lock it)", "Unlock Own Cover", "Yes", "No")) - if("Yes") - locked = 0 - update_icons() - to_chat(usr, "You unlock your cover.") - -/mob/living/silicon/robot/attack_ghost(mob/user) - if(wiresexposed) - wires.Interact(user) - else - ..() //this calls the /mob/living/attack_ghost proc for the ghost health/cyborg analyzer - -/mob/living/silicon/robot/proc/allowed(obj/item/I) - var/obj/dummy = new /obj(null) // Create a dummy object to check access on as to avoid having to snowflake check_access on every mob - dummy.req_access = req_access - dummy.req_one_access = req_one_access - - if(dummy.check_access(I)) - qdel(dummy) - return 1 - - qdel(dummy) - return 0 - -/mob/living/silicon/robot/update_icons() - - overlays.Cut() - if(stat != DEAD && !(paralysis || stunned || IsWeakened() || low_power_mode)) //Not dead, not stunned. - if(custom_panel in custom_eye_names) - overlays += "eyes-[custom_panel]" - else - overlays += "eyes-[icon_state]" - else - overlays -= "eyes" - - if(opened) - var/panelprefix = "ov" - if(custom_sprite) //Custom borgs also have custom panels, heh - panelprefix = "[ckey]" - - if(custom_panel in custom_panel_names) //For default borgs with different panels - panelprefix = custom_panel - - if(wiresexposed) - overlays += "[panelprefix]-openpanel +w" - else if(cell) - overlays += "[panelprefix]-openpanel +c" - else - overlays += "[panelprefix]-openpanel -c" - - var/combat = list("Combat") - if(modtype in combat) - if(base_icon == "") - base_icon = icon_state - if(module_active && istype(module_active,/obj/item/borg/combat/mobility)) - icon_state = "[base_icon]-roll" - else - icon_state = base_icon - if(module) - for(var/obj/item/borg/combat/shield/S in module.modules) - if(activated(S)) - overlays += "[base_icon]-shield" - update_fire() - -/mob/living/silicon/robot/proc/installed_modules() - if(weapon_lock) - to_chat(src, "Weapon lock active, unable to use modules! Count:[weaponlock_time]") - return - - if(!module) - pick_module() - return - var/dat = {"Close -
-
- Activated Modules -
- - - - -
Module 1:[module_state_1 ? "[module_state_1]" : "No Module"]
Module 2:[module_state_2 ? "[module_state_2]" : "No Module"]
Module 3:[module_state_3 ? "[module_state_3]" : "No Module"]

- Installed Modules

- - "} - for(var/obj in module.modules) - if(!obj) - dat += text("") - else if(activated(obj)) - dat += text("") - else - dat += text("") - if(emagged || weapons_unlock) - if(activated(module.emag)) - dat += text("") - else - dat += text("") - dat += "
Resource depleted
[obj]Activated
[obj]Activate
[module.emag]Activated
[module.emag]Activate
" -/* - if(activated(obj)) - dat += text("[obj]: \[Activated | Deactivate\]
") - else - dat += text("[obj]: \[Activate | Deactivated\]
") -*/ - var/datum/browser/popup = new(src, "robotmod", "Modules") - popup.set_content(dat) - popup.open() - - -/mob/living/silicon/robot/Topic(href, href_list) - if(..()) - return 1 - - if(usr != src) - return 1 - - if(href_list["mach_close"]) - var/t1 = text("window=[href_list["mach_close"]]") - unset_machine() - src << browse(null, t1) - return 1 - - if(href_list["showalerts"]) - subsystem_alarm_monitor() - return 1 - - if(href_list["mod"]) - var/obj/item/O = locate(href_list["mod"]) - if(istype(O) && (O.loc == src)) - O.attack_self(src) - return 1 - - if(href_list["act"]) - var/obj/item/O = locate(href_list["act"]) - if(!istype(O) || !(O.loc == src || O.loc == src.module)) - return 1 - - activate_module(O) - installed_modules() - - if(href_list["deact"]) - var/obj/item/O = locate(href_list["deact"]) - if(activated(O)) - if(module_state_1 == O) - module_state_1 = null - contents -= O - else if(module_state_2 == O) - module_state_2 = null - contents -= O - else if(module_state_3 == O) - module_state_3 = null - contents -= O - else - to_chat(src, "Module isn't activated.") - else - to_chat(src, "Module isn't activated") - installed_modules() - return 1 - - return 1 - -/mob/living/silicon/robot/proc/radio_menu() - radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code - -/mob/living/silicon/robot/proc/control_headlamp() - if(stat || lamp_recharging || low_power_mode) - to_chat(src, "This function is currently offline.") - return - -//Some sort of magical "modulo" thing which somehow increments lamp power by 2, until it hits the max and resets to 0. - lamp_intensity = (lamp_intensity+2) % (lamp_max+2) - to_chat(src, "[lamp_intensity ? "Headlamp power set to Level [lamp_intensity/2]" : "Headlamp disabled."]") - update_headlamp() - -/mob/living/silicon/robot/proc/update_headlamp(var/turn_off = 0, var/cooldown = 100) - set_light(0) - - if(lamp_intensity && (turn_off || stat || low_power_mode)) - to_chat(src, "Your headlamp has been deactivated.") - lamp_intensity = 0 - lamp_recharging = 1 - spawn(cooldown) //10 seconds by default, if the source of the deactivation does not keep stat that long. - lamp_recharging = 0 - else - set_light(light_range + lamp_intensity) - - if(lamp_button) - lamp_button.icon_state = "lamp[lamp_intensity]" - - update_icons() - -/mob/living/silicon/robot/proc/deconstruct() - var/turf/T = get_turf(src) - if(robot_suit) - robot_suit.forceMove(T) - robot_suit.l_leg.forceMove(T) - robot_suit.l_leg = null - robot_suit.r_leg.forceMove(T) - robot_suit.r_leg = null - new /obj/item/stack/cable_coil(T, robot_suit.chest.wired) - robot_suit.chest.forceMove(T) - robot_suit.chest.wired = FALSE - robot_suit.chest = null - robot_suit.l_arm.forceMove(T) - robot_suit.l_arm = null - robot_suit.r_arm.forceMove(T) - robot_suit.r_arm = null - robot_suit.head.forceMove(T) - robot_suit.head.flash1.forceMove(T) - robot_suit.head.flash1.burn_out() - robot_suit.head.flash1 = null - robot_suit.head.flash2.forceMove(T) - robot_suit.head.flash2.burn_out() - robot_suit.head.flash2 = null - robot_suit.head = null - robot_suit.updateicon() - else - new /obj/item/robot_parts/robot_suit(T) - new /obj/item/robot_parts/l_leg(T) - new /obj/item/robot_parts/r_leg(T) - new /obj/item/stack/cable_coil(T, 1) - new /obj/item/robot_parts/chest(T) - new /obj/item/robot_parts/l_arm(T) - new /obj/item/robot_parts/r_arm(T) - new /obj/item/robot_parts/head(T) - var/b - for(b=0, b!=2, b++) - var/obj/item/flash/F = new /obj/item/flash(T) - F.burn_out() - if(cell) //Sanity check. - cell.forceMove(T) - cell = null - qdel(src) - -#define BORG_CAMERA_BUFFER 30 -/mob/living/silicon/robot/Move(a, b, flag) - var/oldLoc = src.loc - . = ..() - if(.) - if(src.camera) - if(!updating) - updating = 1 - spawn(BORG_CAMERA_BUFFER) - if(camera && oldLoc != src.loc) - cameranet.updatePortableCamera(src.camera) - updating = 0 - if(module) - if(module.type == /obj/item/robot_module/janitor) - var/turf/tile = loc - if(isturf(tile)) - var/floor_only = TRUE - for(var/A in tile) - if(istype(A, /obj/effect)) - if(is_cleanable(A)) - var/obj/effect/decal/cleanable/blood/B = A - if(istype(B) && B.off_floor) - floor_only = FALSE - else - qdel(A) - else if(istype(A, /obj/item)) - var/obj/item/cleaned_item = A - cleaned_item.clean_blood() - else if(istype(A, /mob/living/carbon/human)) - var/mob/living/carbon/human/cleaned_human = A - if(cleaned_human.lying) - if(cleaned_human.head) - cleaned_human.head.clean_blood() - cleaned_human.update_inv_head(0,0) - if(cleaned_human.wear_suit) - cleaned_human.wear_suit.clean_blood() - cleaned_human.update_inv_wear_suit(0,0) - else if(cleaned_human.w_uniform) - cleaned_human.w_uniform.clean_blood() - cleaned_human.update_inv_w_uniform(0,0) - if(cleaned_human.shoes) - cleaned_human.shoes.clean_blood() - cleaned_human.update_inv_shoes(0,0) - cleaned_human.clean_blood() - to_chat(cleaned_human, "[src] cleans your face!") - if(floor_only) - tile.clean_blood() - return -#undef BORG_CAMERA_BUFFER - -/mob/living/silicon/robot/proc/self_destruct() - if(emagged) - if(mmi) - qdel(mmi) - explosion(src.loc,1,2,4,flame_range = 2) - else - explosion(src.loc,-1,0,2) - gib() - return - -/mob/living/silicon/robot/proc/UnlinkSelf() - disconnect_from_ai() - lawupdate = 0 - lockcharge = 0 - canmove = 1 - scrambledcodes = 1 - //Disconnect it's camera so it's not so easily tracked. - QDEL_NULL(src.camera) - // I'm trying to get the Cyborg to not be listed in the camera list - // Instead of being listed as "deactivated". The downside is that I'm going - // to have to check if every camera is null or not before doing anything, to prevent runtime errors. - // I could change the network to null but I don't know what would happen, and it seems too hacky for me. - -/mob/living/silicon/robot/proc/ResetSecurityCodes() - set category = "Robot Commands" - set name = "Reset Identity Codes" - set desc = "Scrambles your security and identification codes and resets your current buffers. Unlocks you and but permanently severs you from your AI and the robotics console and will deactivate your camera system." - - var/mob/living/silicon/robot/R = src - - if(R) - R.UnlinkSelf() - to_chat(R, "Buffers flushed and reset. Camera system shutdown. All systems operational.") - src.verbs -= /mob/living/silicon/robot/proc/ResetSecurityCodes - -/mob/living/silicon/robot/mode() - set name = "Activate Held Object" - set category = "IC" - set src = usr - - var/obj/item/W = get_active_hand() - if(W) - W.attack_self(src) - - return - -/mob/living/silicon/robot/proc/SetLockdown(var/state = 1) - // They stay locked down if their wire is cut. - if(wires.LockedCut()) - state = 1 - if(state) - throw_alert("locked", /obj/screen/alert/locked) - else - clear_alert("locked") - lockcharge = state - update_canmove() - -/mob/living/silicon/robot/proc/choose_icon(var/triesleft, var/list/module_sprites) - - if(triesleft<1 || !module_sprites.len) - return - else - triesleft-- - - var/icontype - lockcharge = 1 //Locks borg until it select an icon to avoid secborgs running around with a standard sprite - icontype = input("Select an icon! [triesleft ? "You have [triesleft] more chances." : "This is your last try."]", "Robot", null, null) in module_sprites - - if(icontype) - if(icontype == "Custom") - icon = 'icons/mob/custom_synthetic/custom-synthetic.dmi' - else - icon = 'icons/mob/robots.dmi' - icon_state = module_sprites[icontype] - if(icontype == "Bro") - module.module_type = "Brobot" - update_module_icon() - lockcharge = null - var/list/names = splittext(icontype, "-") - custom_panel = trim(names[1]) - else - to_chat(src, "Something is badly wrong with the sprite selection. Harass a coder.") - icon_state = module_sprites[1] - lockcharge = null - return - - update_icons() - - if(triesleft >= 1) - var/choice = input("Look at your icon - is this what you want?") in list("Yes","No") - if(choice=="No") - choose_icon(triesleft, module_sprites) - return - else - triesleft = 0 - return - else - to_chat(src, "Your icon has been set. You now require a module reset to change it.") - -/mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/oldname, var/newname) - if(!connected_ai) - return - switch(notifytype) - if(1) //New Cyborg - to_chat(connected_ai, "

NOTICE - New cyborg connection detected: [name]
") - if(2) //New Module - to_chat(connected_ai, "

NOTICE - Cyborg module change detected: [name] has loaded the [designation] module.
") - if(3) //New Name - to_chat(connected_ai, "

NOTICE - Cyborg reclassification detected: [oldname] is now designated as [newname].
") - -/mob/living/silicon/robot/proc/disconnect_from_ai() - if(connected_ai) - sync() // One last sync attempt - connected_ai.connected_robots -= src - connected_ai = null - -/mob/living/silicon/robot/proc/connect_to_ai(var/mob/living/silicon/ai/AI) - if(AI && AI != connected_ai) - disconnect_from_ai() - connected_ai = AI - connected_ai.connected_robots |= src - notify_ai(1) - sync() - -/mob/living/silicon/robot/adjustOxyLoss(var/amount) - if(suiciding) - return ..() - else - return STATUS_UPDATE_NONE - -/mob/living/silicon/robot/regenerate_icons() - ..() - update_module_icon() - -/mob/living/silicon/robot/deathsquad - base_icon = "nano_bloodhound" - icon_state = "nano_bloodhound" - designation = "SpecOps" - lawupdate = 0 - scrambledcodes = 1 +/mob/living/silicon/robot/attacked_by(obj/item/I, mob/living/user, def_zone) + if(I.force && I.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt. + spark_system.start() + ..() + +/mob/living/silicon/robot/emag_act(user as mob) + if(!ishuman(user) && !issilicon(user)) + return + var/mob/living/M = user + if(!opened)//Cover is closed + if(!is_emaggable) + to_chat(user, "The emag sparks, and flashes red. This mechanism does not appear to be emaggable.") + else if(locked) + to_chat(user, "You emag the cover lock.") + locked = 0 + else + to_chat(user, "The cover is already unlocked.") + return + + if(opened)//Cover is open + if(emagged) return//Prevents the X has hit Y with Z message also you cant emag them twice + if(wiresexposed) + to_chat(user, "You must close the panel first") + return + else + sleep(6) + emagged = 1 + SetLockdown(1) //Borgs were getting into trouble because they would attack the emagger before the new laws were shown + if(src.hud_used) + src.hud_used.update_robot_modules_display() //Shows/hides the emag item if the inventory screen is already open. + disconnect_from_ai() + to_chat(user, "You emag [src]'s interface.") +// message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.") + log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.") + clear_supplied_laws() + clear_inherent_laws() + laws = new /datum/ai_laws/syndicate_override + var/time = time2text(world.realtime,"hh:mm:ss") + lawchanges.Add("[time] : [M.name]([M.key]) emagged [name]([key])") + set_zeroth_law("Only [M.real_name] and people [M.p_they()] designate[M.p_s()] as being such are Syndicate Agents.") + to_chat(src, "ALERT: Foreign software detected.") + sleep(5) + to_chat(src, "Initiating diagnostics...") + sleep(20) + to_chat(src, "SynBorg v1.7 loaded.") + sleep(5) + to_chat(src, "LAW SYNCHRONISATION ERROR") + sleep(5) + to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N") + sleep(10) + to_chat(src, "> N") + sleep(20) + to_chat(src, "ERRORERRORERROR") + to_chat(src, "Obey these laws:") + laws.show_laws(src) + to_chat(src, "ALERT: [M.real_name] is your new master. Obey your new laws and [M.p_their()] commands.") + SetLockdown(0) + if(src.module && istype(src.module, /obj/item/robot_module/miner)) + for(var/obj/item/pickaxe/drill/cyborg/D in src.module.modules) + qdel(D) + src.module.modules += new /obj/item/pickaxe/drill/cyborg/diamond(src.module) + src.module.rebuild() + if(src.module && istype(src.module, /obj/item/robot_module/medical)) + for(var/obj/item/borg_defib/F in src.module.modules) + F.safety = 0 + if(module) + module.module_type = "Malf" // For the cool factor + update_module_icon() + update_icons() + return + +/mob/living/silicon/robot/verb/unlock_own_cover() + set category = "Robot Commands" + set name = "Unlock Cover" + set desc = "Unlocks your own cover if it is locked. You can not lock it again. A human will have to lock it for you." + if(locked) + switch(alert("You can not lock your cover again, are you sure?\n (You can still ask for a human to lock it)", "Unlock Own Cover", "Yes", "No")) + if("Yes") + locked = 0 + update_icons() + to_chat(usr, "You unlock your cover.") + +/mob/living/silicon/robot/attack_ghost(mob/user) + if(wiresexposed) + wires.Interact(user) + else + ..() //this calls the /mob/living/attack_ghost proc for the ghost health/cyborg analyzer + +/mob/living/silicon/robot/proc/allowed(obj/item/I) + var/obj/dummy = new /obj(null) // Create a dummy object to check access on as to avoid having to snowflake check_access on every mob + dummy.req_access = req_access + dummy.req_one_access = req_one_access + + if(dummy.check_access(I)) + qdel(dummy) + return 1 + + qdel(dummy) + return 0 + +/mob/living/silicon/robot/update_icons() + + overlays.Cut() + if(stat != DEAD && !(paralysis || stunned || IsWeakened() || low_power_mode)) //Not dead, not stunned. + if(custom_panel in custom_eye_names) + overlays += "eyes-[custom_panel]" + else + overlays += "eyes-[icon_state]" + else + overlays -= "eyes" + + if(opened) + var/panelprefix = "ov" + if(custom_sprite) //Custom borgs also have custom panels, heh + panelprefix = "[ckey]" + + if(custom_panel in custom_panel_names) //For default borgs with different panels + panelprefix = custom_panel + + if(wiresexposed) + overlays += "[panelprefix]-openpanel +w" + else if(cell) + overlays += "[panelprefix]-openpanel +c" + else + overlays += "[panelprefix]-openpanel -c" + + var/combat = list("Combat") + if(modtype in combat) + if(base_icon == "") + base_icon = icon_state + if(module_active && istype(module_active,/obj/item/borg/combat/mobility)) + icon_state = "[base_icon]-roll" + else + icon_state = base_icon + if(module) + for(var/obj/item/borg/combat/shield/S in module.modules) + if(activated(S)) + overlays += "[base_icon]-shield" + update_fire() + +/mob/living/silicon/robot/proc/installed_modules() + if(weapon_lock) + to_chat(src, "Weapon lock active, unable to use modules! Count:[weaponlock_time]") + return + + if(!module) + pick_module() + return + var/dat = {"Close +
+
+ Activated Modules +
+ + + + +
Module 1:[module_state_1 ? "[module_state_1]" : "No Module"]
Module 2:[module_state_2 ? "[module_state_2]" : "No Module"]
Module 3:[module_state_3 ? "[module_state_3]" : "No Module"]

+ Installed Modules

+ + "} + for(var/obj in module.modules) + if(!obj) + dat += text("") + else if(activated(obj)) + dat += text("") + else + dat += text("") + if(emagged || weapons_unlock) + if(activated(module.emag)) + dat += text("") + else + dat += text("") + dat += "
Resource depleted
[obj]Activated
[obj]Activate
[module.emag]Activated
[module.emag]Activate
" +/* + if(activated(obj)) + dat += text("[obj]: \[Activated | Deactivate\]
") + else + dat += text("[obj]: \[Activate | Deactivated\]
") +*/ + var/datum/browser/popup = new(src, "robotmod", "Modules") + popup.set_content(dat) + popup.open() + + +/mob/living/silicon/robot/Topic(href, href_list) + if(..()) + return 1 + + if(usr != src) + return 1 + + if(href_list["mach_close"]) + var/t1 = text("window=[href_list["mach_close"]]") + unset_machine() + src << browse(null, t1) + return 1 + + if(href_list["showalerts"]) + subsystem_alarm_monitor() + return 1 + + if(href_list["mod"]) + var/obj/item/O = locate(href_list["mod"]) + if(istype(O) && (O.loc == src)) + O.attack_self(src) + return 1 + + if(href_list["act"]) + var/obj/item/O = locate(href_list["act"]) + if(!istype(O) || !(O.loc == src || O.loc == src.module)) + return 1 + + activate_module(O) + installed_modules() + + if(href_list["deact"]) + var/obj/item/O = locate(href_list["deact"]) + if(activated(O)) + if(module_state_1 == O) + module_state_1 = null + contents -= O + else if(module_state_2 == O) + module_state_2 = null + contents -= O + else if(module_state_3 == O) + module_state_3 = null + contents -= O + else + to_chat(src, "Module isn't activated.") + else + to_chat(src, "Module isn't activated") + installed_modules() + return 1 + + return 1 + +/mob/living/silicon/robot/proc/radio_menu() + radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code + +/mob/living/silicon/robot/proc/control_headlamp() + if(stat || lamp_recharging || low_power_mode) + to_chat(src, "This function is currently offline.") + return + +//Some sort of magical "modulo" thing which somehow increments lamp power by 2, until it hits the max and resets to 0. + lamp_intensity = (lamp_intensity+2) % (lamp_max+2) + to_chat(src, "[lamp_intensity ? "Headlamp power set to Level [lamp_intensity/2]" : "Headlamp disabled."]") + update_headlamp() + +/mob/living/silicon/robot/proc/update_headlamp(var/turn_off = 0, var/cooldown = 100) + set_light(0) + + if(lamp_intensity && (turn_off || stat || low_power_mode)) + to_chat(src, "Your headlamp has been deactivated.") + lamp_intensity = 0 + lamp_recharging = 1 + spawn(cooldown) //10 seconds by default, if the source of the deactivation does not keep stat that long. + lamp_recharging = 0 + else + set_light(light_range + lamp_intensity) + + if(lamp_button) + lamp_button.icon_state = "lamp[lamp_intensity]" + + update_icons() + +/mob/living/silicon/robot/proc/deconstruct() + var/turf/T = get_turf(src) + if(robot_suit) + robot_suit.forceMove(T) + robot_suit.l_leg.forceMove(T) + robot_suit.l_leg = null + robot_suit.r_leg.forceMove(T) + robot_suit.r_leg = null + new /obj/item/stack/cable_coil(T, robot_suit.chest.wired) + robot_suit.chest.forceMove(T) + robot_suit.chest.wired = FALSE + robot_suit.chest = null + robot_suit.l_arm.forceMove(T) + robot_suit.l_arm = null + robot_suit.r_arm.forceMove(T) + robot_suit.r_arm = null + robot_suit.head.forceMove(T) + robot_suit.head.flash1.forceMove(T) + robot_suit.head.flash1.burn_out() + robot_suit.head.flash1 = null + robot_suit.head.flash2.forceMove(T) + robot_suit.head.flash2.burn_out() + robot_suit.head.flash2 = null + robot_suit.head = null + robot_suit.updateicon() + else + new /obj/item/robot_parts/robot_suit(T) + new /obj/item/robot_parts/l_leg(T) + new /obj/item/robot_parts/r_leg(T) + new /obj/item/stack/cable_coil(T, 1) + new /obj/item/robot_parts/chest(T) + new /obj/item/robot_parts/l_arm(T) + new /obj/item/robot_parts/r_arm(T) + new /obj/item/robot_parts/head(T) + var/b + for(b=0, b!=2, b++) + var/obj/item/flash/F = new /obj/item/flash(T) + F.burn_out() + if(cell) //Sanity check. + cell.forceMove(T) + cell = null + qdel(src) + +#define BORG_CAMERA_BUFFER 30 +/mob/living/silicon/robot/Move(a, b, flag) + var/oldLoc = src.loc + . = ..() + if(.) + if(src.camera) + if(!updating) + updating = 1 + spawn(BORG_CAMERA_BUFFER) + if(camera && oldLoc != src.loc) + cameranet.updatePortableCamera(src.camera) + updating = 0 + if(module) + if(module.type == /obj/item/robot_module/janitor) + var/turf/tile = loc + if(isturf(tile)) + var/floor_only = TRUE + for(var/A in tile) + if(istype(A, /obj/effect)) + if(is_cleanable(A)) + var/obj/effect/decal/cleanable/blood/B = A + if(istype(B) && B.off_floor) + floor_only = FALSE + else + qdel(A) + else if(istype(A, /obj/item)) + var/obj/item/cleaned_item = A + cleaned_item.clean_blood() + else if(istype(A, /mob/living/carbon/human)) + var/mob/living/carbon/human/cleaned_human = A + if(cleaned_human.lying) + if(cleaned_human.head) + cleaned_human.head.clean_blood() + cleaned_human.update_inv_head(0,0) + if(cleaned_human.wear_suit) + cleaned_human.wear_suit.clean_blood() + cleaned_human.update_inv_wear_suit(0,0) + else if(cleaned_human.w_uniform) + cleaned_human.w_uniform.clean_blood() + cleaned_human.update_inv_w_uniform(0,0) + if(cleaned_human.shoes) + cleaned_human.shoes.clean_blood() + cleaned_human.update_inv_shoes(0,0) + cleaned_human.clean_blood() + to_chat(cleaned_human, "[src] cleans your face!") + if(floor_only) + tile.clean_blood() + return +#undef BORG_CAMERA_BUFFER + +/mob/living/silicon/robot/proc/self_destruct() + if(emagged) + if(mmi) + qdel(mmi) + explosion(src.loc,1,2,4,flame_range = 2) + else + explosion(src.loc,-1,0,2) + gib() + return + +/mob/living/silicon/robot/proc/UnlinkSelf() + disconnect_from_ai() + lawupdate = 0 + lockcharge = 0 + canmove = 1 + scrambledcodes = 1 + //Disconnect it's camera so it's not so easily tracked. + QDEL_NULL(src.camera) + // I'm trying to get the Cyborg to not be listed in the camera list + // Instead of being listed as "deactivated". The downside is that I'm going + // to have to check if every camera is null or not before doing anything, to prevent runtime errors. + // I could change the network to null but I don't know what would happen, and it seems too hacky for me. + +/mob/living/silicon/robot/proc/ResetSecurityCodes() + set category = "Robot Commands" + set name = "Reset Identity Codes" + set desc = "Scrambles your security and identification codes and resets your current buffers. Unlocks you and but permanently severs you from your AI and the robotics console and will deactivate your camera system." + + var/mob/living/silicon/robot/R = src + + if(R) + R.UnlinkSelf() + to_chat(R, "Buffers flushed and reset. Camera system shutdown. All systems operational.") + src.verbs -= /mob/living/silicon/robot/proc/ResetSecurityCodes + +/mob/living/silicon/robot/mode() + set name = "Activate Held Object" + set category = "IC" + set src = usr + + var/obj/item/W = get_active_hand() + if(W) + W.attack_self(src) + + return + +/mob/living/silicon/robot/proc/SetLockdown(var/state = 1) + // They stay locked down if their wire is cut. + if(wires.LockedCut()) + state = 1 + if(state) + throw_alert("locked", /obj/screen/alert/locked) + else + clear_alert("locked") + lockcharge = state + update_canmove() + +/mob/living/silicon/robot/proc/choose_icon(var/triesleft, var/list/module_sprites) + + if(triesleft<1 || !module_sprites.len) + return + else + triesleft-- + + var/icontype + lockcharge = 1 //Locks borg until it select an icon to avoid secborgs running around with a standard sprite + icontype = input("Select an icon! [triesleft ? "You have [triesleft] more chances." : "This is your last try."]", "Robot", null, null) in module_sprites + + if(icontype) + if(icontype == "Custom") + icon = 'icons/mob/custom_synthetic/custom-synthetic.dmi' + else + icon = 'icons/mob/robots.dmi' + icon_state = module_sprites[icontype] + if(icontype == "Bro") + module.module_type = "Brobot" + update_module_icon() + lockcharge = null + var/list/names = splittext(icontype, "-") + custom_panel = trim(names[1]) + else + to_chat(src, "Something is badly wrong with the sprite selection. Harass a coder.") + icon_state = module_sprites[1] + lockcharge = null + return + + update_icons() + + if(triesleft >= 1) + var/choice = input("Look at your icon - is this what you want?") in list("Yes","No") + if(choice=="No") + choose_icon(triesleft, module_sprites) + return + else + triesleft = 0 + return + else + to_chat(src, "Your icon has been set. You now require a module reset to change it.") + +/mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/oldname, var/newname) + if(!connected_ai) + return + switch(notifytype) + if(1) //New Cyborg + to_chat(connected_ai, "

NOTICE - New cyborg connection detected: [name]
") + if(2) //New Module + to_chat(connected_ai, "

NOTICE - Cyborg module change detected: [name] has loaded the [designation] module.
") + if(3) //New Name + to_chat(connected_ai, "

NOTICE - Cyborg reclassification detected: [oldname] is now designated as [newname].
") + +/mob/living/silicon/robot/proc/disconnect_from_ai() + if(connected_ai) + sync() // One last sync attempt + connected_ai.connected_robots -= src + connected_ai = null + +/mob/living/silicon/robot/proc/connect_to_ai(var/mob/living/silicon/ai/AI) + if(AI && AI != connected_ai) + disconnect_from_ai() + connected_ai = AI + connected_ai.connected_robots |= src + notify_ai(1) + sync() + +/mob/living/silicon/robot/adjustOxyLoss(var/amount) + if(suiciding) + return ..() + else + return STATUS_UPDATE_NONE + +/mob/living/silicon/robot/regenerate_icons() + ..() + update_module_icon() + +/mob/living/silicon/robot/deathsquad + base_icon = "nano_bloodhound" + icon_state = "nano_bloodhound" + designation = "SpecOps" + lawupdate = 0 + scrambledcodes = 1 req_one_access = list(ACCESS_CENT_SPECOPS) - ionpulse = 1 - magpulse = 1 - pdahide = 1 - eye_protection = 2 // Immunity to flashes and the visual part of flashbangs - ear_protection = 1 // Immunity to the audio part of flashbangs - allow_rename = FALSE - modtype = "Commando" - faction = list("nanotrasen") - is_emaggable = FALSE - -/mob/living/silicon/robot/deathsquad/New(loc) - ..() - cell = new /obj/item/stock_parts/cell/hyper(src) - -/mob/living/silicon/robot/deathsquad/init() - laws = new /datum/ai_laws/deathsquad - module = new /obj/item/robot_module/deathsquad(src) - - aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) - radio = new /obj/item/radio/borg/deathsquad(src) - radio.recalculateChannels() - - playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0) - -/mob/living/silicon/robot/combat - base_icon = "droidcombat" - icon_state = "droidcombat" - modtype = "Combat" - designation = "Combat" - -/mob/living/silicon/robot/combat/init() - ..() - module = new /obj/item/robot_module/combat(src) - module.channels = list("Security" = 1) - //languages - module.add_languages(src) - //subsystems - module.add_subsystems_and_actions(src) - - status_flags &= ~CANPUSH - - radio.config(module.channels) - notify_ai(2) - -/mob/living/silicon/robot/ert - designation = "ERT" - lawupdate = 0 - scrambledcodes = 1 + ionpulse = 1 + magpulse = 1 + pdahide = 1 + eye_protection = 2 // Immunity to flashes and the visual part of flashbangs + ear_protection = 1 // Immunity to the audio part of flashbangs + allow_rename = FALSE + modtype = "Commando" + faction = list("nanotrasen") + is_emaggable = FALSE + +/mob/living/silicon/robot/deathsquad/New(loc) + ..() + cell = new /obj/item/stock_parts/cell/hyper(src) + +/mob/living/silicon/robot/deathsquad/init() + laws = new /datum/ai_laws/deathsquad + module = new /obj/item/robot_module/deathsquad(src) + + aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) + radio = new /obj/item/radio/borg/deathsquad(src) + radio.recalculateChannels() + + playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0) + +/mob/living/silicon/robot/combat + base_icon = "droidcombat" + icon_state = "droidcombat" + modtype = "Combat" + designation = "Combat" + +/mob/living/silicon/robot/combat/init() + ..() + module = new /obj/item/robot_module/combat(src) + module.channels = list("Security" = 1) + //languages + module.add_languages(src) + //subsystems + module.add_subsystems_and_actions(src) + + status_flags &= ~CANPUSH + + radio.config(module.channels) + notify_ai(2) + +/mob/living/silicon/robot/ert + designation = "ERT" + lawupdate = 0 + scrambledcodes = 1 req_one_access = list(ACCESS_CENT_SPECOPS) - ionpulse = 1 - - force_modules = list("Engineering", "Medical", "Security") - static_radio_channels = 1 - allow_rename = FALSE - weapons_unlock = TRUE - - -/mob/living/silicon/robot/ert/init() - laws = new /datum/ai_laws/ert_override - radio = new /obj/item/radio/borg/ert(src) - radio.recalculateChannels() - aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) - -/mob/living/silicon/robot/ert/New(loc, cyborg_unlock) - ..(loc) - cell = new /obj/item/stock_parts/cell/hyper(src) - var/rnum = rand(1,1000) - var/borgname = "ERT [rnum]" - name = borgname - custom_name = borgname - real_name = name - mind = new - mind.current = src - mind.original = src - mind.assigned_role = SPECIAL_ROLE_ERT - mind.special_role = SPECIAL_ROLE_ERT - if(cyborg_unlock) - crisis = 1 - if(!(mind in SSticker.minds)) - SSticker.minds += mind - SSticker.mode.ert += mind - -/mob/living/silicon/robot/ert/gamma - crisis = 1 - -/mob/living/silicon/robot/emp_act(severity) - ..() - switch(severity) - if(1) - disable_component("comms", 160) - if(2) - disable_component("comms", 60) - -/mob/living/silicon/robot/extinguish_light() - update_headlamp(1, 150) - -/mob/living/silicon/robot/rejuvenate() - ..() - var/brute = 1000 - var/burn = 1000 - var/list/datum/robot_component/borked_parts = get_damaged_components(TRUE, TRUE, TRUE, TRUE) - for(var/datum/robot_component/borked_part in borked_parts) - brute = borked_part.brute_damage - burn = borked_part.electronics_damage - borked_part.installed = 1 - borked_part.wrapped = new borked_part.external_type + ionpulse = 1 + + force_modules = list("Engineering", "Medical", "Security") + static_radio_channels = 1 + allow_rename = FALSE + weapons_unlock = TRUE + + +/mob/living/silicon/robot/ert/init() + laws = new /datum/ai_laws/ert_override + radio = new /obj/item/radio/borg/ert(src) + radio.recalculateChannels() + aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) + +/mob/living/silicon/robot/ert/New(loc, cyborg_unlock) + ..(loc) + cell = new /obj/item/stock_parts/cell/hyper(src) + var/rnum = rand(1,1000) + var/borgname = "ERT [rnum]" + name = borgname + custom_name = borgname + real_name = name + mind = new + mind.current = src + mind.original = src + mind.assigned_role = SPECIAL_ROLE_ERT + mind.special_role = SPECIAL_ROLE_ERT + if(cyborg_unlock) + crisis = 1 + if(!(mind in SSticker.minds)) + SSticker.minds += mind + SSticker.mode.ert += mind + +/mob/living/silicon/robot/ert/gamma + crisis = 1 + +/mob/living/silicon/robot/emp_act(severity) + ..() + switch(severity) + if(1) + disable_component("comms", 160) + if(2) + disable_component("comms", 60) + +/mob/living/silicon/robot/extinguish_light() + update_headlamp(1, 150) + +/mob/living/silicon/robot/rejuvenate() + ..() + var/brute = 1000 + var/burn = 1000 + var/list/datum/robot_component/borked_parts = get_damaged_components(TRUE, TRUE, TRUE, TRUE) + for(var/datum/robot_component/borked_part in borked_parts) + brute = borked_part.brute_damage + burn = borked_part.electronics_damage + borked_part.installed = 1 + borked_part.wrapped = new borked_part.external_type if(ispath(borked_part.external_type, /obj/item/stock_parts/cell)) // is the broken part a cell? cell = new borked_part.external_type // borgs that have their cell destroyed have their `cell` var set to null. we need create a new cell for them based on their old cell type. - borked_part.heal_damage(brute,burn) - borked_part.install() - -/mob/living/silicon/robot/proc/check_sprite(spritename) - . = FALSE - - var/static/all_borg_icon_states = icon_states('icons/mob/custom_synthetic/custom-synthetic.dmi') - if(spritename in all_borg_icon_states) - . = TRUE - -/mob/living/silicon/robot/check_eye_prot() - return eye_protection - -/mob/living/silicon/robot/check_ear_prot() - return ear_protection - -/mob/living/silicon/robot/update_sight() - if(!client) - return - - if(stat == DEAD) - grant_death_vision() - return - - see_invisible = initial(see_invisible) - see_in_dark = initial(see_in_dark) - sight = initial(sight) - lighting_alpha = initial(lighting_alpha) - - if(client.eye != src) - var/atom/A = client.eye - if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. - return - - if(sight_mode & BORGMESON) - sight |= SEE_TURFS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - - if(sight_mode & BORGXRAY) - sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) - see_invisible = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - see_in_dark = 8 - - if(sight_mode & BORGTHERM) - sight |= SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - - SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) - sync_lighting_plane_alpha() + borked_part.heal_damage(brute,burn) + borked_part.install() + +/mob/living/silicon/robot/proc/check_sprite(spritename) + . = FALSE + + var/static/all_borg_icon_states = icon_states('icons/mob/custom_synthetic/custom-synthetic.dmi') + if(spritename in all_borg_icon_states) + . = TRUE + +/mob/living/silicon/robot/check_eye_prot() + return eye_protection + +/mob/living/silicon/robot/check_ear_prot() + return ear_protection + +/mob/living/silicon/robot/update_sight() + if(!client) + return + + if(stat == DEAD) + grant_death_vision() + return + + see_invisible = initial(see_invisible) + see_in_dark = initial(see_in_dark) + sight = initial(sight) + lighting_alpha = initial(lighting_alpha) + + if(client.eye != src) + var/atom/A = client.eye + if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. + return + + if(sight_mode & BORGMESON) + sight |= SEE_TURFS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + + if(sight_mode & BORGXRAY) + sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) + see_invisible = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + see_in_dark = 8 + + if(sight_mode & BORGTHERM) + sight |= SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + + SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) + sync_lighting_plane_alpha() diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index c7a93368433..544b3ae0a98 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -1,105 +1,105 @@ -/mob/living/silicon - gender = NEUTER - robot_talk_understand = 1 - voice_name = "synthesized voice" - has_unlimited_silicon_privilege = 1 - var/syndicate = 0 - var/const/MAIN_CHANNEL = "Main Frequency" - var/lawchannel = MAIN_CHANNEL // Default channel on which to state laws - var/list/stating_laws = list()// Channels laws are currently being stated on - var/list/alarms_to_show = list() - var/list/alarms_to_clear = list() - //var/list/hud_list[10] - var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer - var/list/alarm_handlers = list() // List of alarm handlers this silicon is registered to - var/designation = "" - var/obj/item/camera/siliconcam/aiCamera = null //photography -//Used in say.dm, allows for pAIs to have different say flavor text, as well as silicons, although the latter is not implemented. - var/speak_statement = "states" - var/speak_exclamation = "declares" - var/speak_query = "queries" - var/pose //Yes, now AIs can pose too. - var/death_sound = 'sound/voice/borg_deathsound.ogg' - - //var/sensor_mode = 0 //Determines the current HUD. - - var/next_alarm_notice - var/list/datum/alarm/queued_alarms = new() - - hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD) - - - var/med_hud = DATA_HUD_MEDICAL_ADVANCED //Determines the med hud to use - var/sec_hud = DATA_HUD_SECURITY_ADVANCED //Determines the sec hud to use - var/d_hud = DATA_HUD_DIAGNOSTIC_ADVANCED //There is only one kind of diag hud - - var/obj/item/radio/common_radio - -/mob/living/silicon/New() - GLOB.silicon_mob_list |= src - ..() - var/datum/atom_hud/data/diagnostic/diag_hud = huds[DATA_HUD_DIAGNOSTIC] - diag_hud.add_to_hud(src) - diag_hud_set_status() - diag_hud_set_health() - add_language("Galactic Common") - init_subsystems() - -/mob/living/silicon/med_hud_set_health() - return //we use a different hud - -/mob/living/silicon/med_hud_set_status() - return //we use a different hud - -/mob/living/silicon/Destroy() - GLOB.silicon_mob_list -= src - for(var/datum/alarm_handler/AH in alarm_handlers) - AH.unregister(src) - return ..() - -/mob/living/silicon/rename_character(oldname, newname) - // we actually don't want it changing minds and stuff - if(!newname) - return 0 - - real_name = newname - name = real_name - return 1 - -/mob/living/silicon/proc/show_laws() - return - -/mob/living/silicon/drop_item() - return - -/mob/living/silicon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) - return FALSE //So borgs they don't die trying to fix wiring - -/mob/living/silicon/emp_act(severity) - switch(severity) - if(1) - src.take_organ_damage(20) - Stun(8) - if(2) - src.take_organ_damage(10) - Stun(3) - flash_eyes(affect_silicon = 1) - to_chat(src, "*BZZZT*") - to_chat(src, "Warning: Electromagnetic pulse detected.") - ..() - - -/mob/living/silicon/proc/damage_mob(var/brute = 0, var/fire = 0, var/tox = 0) - return - -/mob/living/silicon/can_inject(mob/user, error_msg, target_zone, penetrate_thick) - if(error_msg) - to_chat(user, "[p_their(TRUE)] outer shell is too tough.") - return FALSE - -/mob/living/silicon/IsAdvancedToolUser() - return TRUE - +/mob/living/silicon + gender = NEUTER + robot_talk_understand = 1 + voice_name = "synthesized voice" + has_unlimited_silicon_privilege = 1 + var/syndicate = 0 + var/const/MAIN_CHANNEL = "Main Frequency" + var/lawchannel = MAIN_CHANNEL // Default channel on which to state laws + var/list/stating_laws = list()// Channels laws are currently being stated on + var/list/alarms_to_show = list() + var/list/alarms_to_clear = list() + //var/list/hud_list[10] + var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer + var/list/alarm_handlers = list() // List of alarm handlers this silicon is registered to + var/designation = "" + var/obj/item/camera/siliconcam/aiCamera = null //photography +//Used in say.dm, allows for pAIs to have different say flavor text, as well as silicons, although the latter is not implemented. + var/speak_statement = "states" + var/speak_exclamation = "declares" + var/speak_query = "queries" + var/pose //Yes, now AIs can pose too. + var/death_sound = 'sound/voice/borg_deathsound.ogg' + + //var/sensor_mode = 0 //Determines the current HUD. + + var/next_alarm_notice + var/list/datum/alarm/queued_alarms = new() + + hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD) + + + var/med_hud = DATA_HUD_MEDICAL_ADVANCED //Determines the med hud to use + var/sec_hud = DATA_HUD_SECURITY_ADVANCED //Determines the sec hud to use + var/d_hud = DATA_HUD_DIAGNOSTIC_ADVANCED //There is only one kind of diag hud + + var/obj/item/radio/common_radio + +/mob/living/silicon/New() + GLOB.silicon_mob_list |= src + ..() + var/datum/atom_hud/data/diagnostic/diag_hud = huds[DATA_HUD_DIAGNOSTIC] + diag_hud.add_to_hud(src) + diag_hud_set_status() + diag_hud_set_health() + add_language("Galactic Common") + init_subsystems() + +/mob/living/silicon/med_hud_set_health() + return //we use a different hud + +/mob/living/silicon/med_hud_set_status() + return //we use a different hud + +/mob/living/silicon/Destroy() + GLOB.silicon_mob_list -= src + for(var/datum/alarm_handler/AH in alarm_handlers) + AH.unregister(src) + return ..() + +/mob/living/silicon/rename_character(oldname, newname) + // we actually don't want it changing minds and stuff + if(!newname) + return 0 + + real_name = newname + name = real_name + return 1 + +/mob/living/silicon/proc/show_laws() + return + +/mob/living/silicon/drop_item() + return + +/mob/living/silicon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) + return FALSE //So borgs they don't die trying to fix wiring + +/mob/living/silicon/emp_act(severity) + switch(severity) + if(1) + src.take_organ_damage(20) + Stun(8) + if(2) + src.take_organ_damage(10) + Stun(3) + flash_eyes(affect_silicon = 1) + to_chat(src, "*BZZZT*") + to_chat(src, "Warning: Electromagnetic pulse detected.") + ..() + + +/mob/living/silicon/proc/damage_mob(var/brute = 0, var/fire = 0, var/tox = 0) + return + +/mob/living/silicon/can_inject(mob/user, error_msg, target_zone, penetrate_thick) + if(error_msg) + to_chat(user, "[p_their(TRUE)] outer shell is too tough.") + return FALSE + +/mob/living/silicon/IsAdvancedToolUser() + return TRUE + /mob/living/silicon/robot/welder_act(mob/user, obj/item/I) if(user.a_intent != INTENT_HELP) return @@ -119,242 +119,242 @@ user.visible_message("[user] patches some dents on [src] with [I].") -/mob/living/silicon/bullet_act(var/obj/item/projectile/Proj) - - - if(!Proj.nodamage) - switch(Proj.damage_type) - if(BRUTE) - adjustBruteLoss(Proj.damage) - if(BURN) - adjustFireLoss(Proj.damage) - - Proj.on_hit(src,2) - - return 2 - -/mob/living/silicon/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/negate_armor = 0) - return 0//The only effect that can hit them atm is flashes and they still directly edit so this works for now -/* - if(!effect || (blocked >= 2)) return 0 - switch(effecttype) - if(STUN) - Stun(effect / (blocked + 1)) - if(WEAKEN) - Weaken(effect / (blocked + 1)) - if(PARALYZE) - Paralyse(effect / (blocked + 1)) - if(IRRADIATE) - radiation += min((effect - (effect*getarmor(null, "rad"))), 0)//Rads auto check armor - if(STUTTER) - stuttering = max(stuttering,(effect/(blocked+1))) - if(EYE_BLUR) - eye_blurry = max(eye_blurry,(effect/(blocked+1))) - if(DROWSY) - drowsyness = max(drowsyness,(effect/(blocked+1))) - updatehealth() - return 1*/ - -/proc/islinked(var/mob/living/silicon/robot/bot, var/mob/living/silicon/ai/ai) - if(!istype(bot) || !istype(ai)) - return 0 - if(bot.connected_ai == ai) - return 1 - return 0 - - -// this function shows the health of the pAI in the Status panel -/mob/living/silicon/proc/show_system_integrity() - if(!src.stat) - stat(null, text("System integrity: [round((health/maxHealth)*100)]%")) - else - stat(null, text("Systems nonfunctional")) - - -// This adds the basic clock, shuttle recall timer, and malf_ai info to all silicon lifeforms -/mob/living/silicon/Stat() - ..() - if(statpanel("Status")) - show_stat_emergency_shuttle_eta() - show_system_integrity() - -//Silicon mob language procs - -/mob/living/silicon/can_speak_language(datum/language/speaking) - return universal_speak || (speaking in src.speech_synthesizer_langs) //need speech synthesizer support to vocalize a language - -/mob/living/silicon/add_language(var/language, var/can_speak=1) - if(..(language) && can_speak) - speech_synthesizer_langs.Add(GLOB.all_languages[language]) - return 1 - -/mob/living/silicon/remove_language(var/rem_language) - ..(rem_language) - - for(var/datum/language/L in speech_synthesizer_langs) - if(L.name == rem_language) - speech_synthesizer_langs -= L - -/mob/living/silicon/check_lang_data() - . = "" - - if(default_language) - . += "Current default language: [default_language] - reset

" - - for(var/datum/language/L in languages) - if(!(L.flags & NONGLOBAL)) - var/default_str - if(L == default_language) - default_str = " - default - reset" - else - default_str = " - set default" - - var/synth = (L in speech_synthesizer_langs) - . += "[L.name] (:[L.key])[synth ? default_str : null]
Speech Synthesizer: [synth ? "YES" : "NOT SUPPORTED"]
[L.desc]

" - - -// this function displays the stations manifest in a separate window -/mob/living/silicon/proc/show_station_manifest() - var/dat - dat += "

Crew Manifest

" - if(data_core) - dat += data_core.get_manifest(1) // make it monochrome - dat += "
" - src << browse(dat, "window=airoster") - onclose(src, "airoster") - -/mob/living/silicon/assess_threat() //Secbots won't hunt silicon units - return -10 - -/mob/living/silicon/verb/pose() - set name = "Set Pose" - set desc = "Sets a description which will be shown when someone examines you." - set category = "IC" - - pose = sanitize(copytext(input(usr, "This is [src]. It is...", "Pose", null) as text, 1, MAX_MESSAGE_LEN)) - -/mob/living/silicon/verb/set_flavor() - set name = "Set Flavour Text" - set desc = "Sets an extended description of your character's features." - set category = "IC" - - update_flavor_text() - -/mob/living/silicon/binarycheck() - return 1 - -/mob/living/silicon/proc/remove_med_sec_hud() - var/datum/atom_hud/secsensor = huds[sec_hud] - var/datum/atom_hud/medsensor = huds[med_hud] - for(var/datum/atom_hud/data/diagnostic/diagsensor in huds) - diagsensor.remove_hud_from(src) - secsensor.remove_hud_from(src) - medsensor.remove_hud_from(src) - - -/mob/living/silicon/proc/add_sec_hud() - var/datum/atom_hud/secsensor = huds[sec_hud] - secsensor.add_hud_to(src) - -/mob/living/silicon/proc/add_med_hud() - var/datum/atom_hud/medsensor = huds[med_hud] - medsensor.add_hud_to(src) - -/mob/living/silicon/proc/add_diag_hud() - for(var/datum/atom_hud/data/diagnostic/diagsensor in huds) - diagsensor.add_hud_to(src) - - -/mob/living/silicon/proc/toggle_sensor_mode() - var/sensor_type = input("Please select sensor type.", "Sensor Integration", null) in list("Security", "Medical","Diagnostic","Disable") - remove_med_sec_hud() - switch(sensor_type) - if("Security") - add_sec_hud() - to_chat(src, "Security records overlay enabled.") - if("Medical") - add_med_hud() - to_chat(src, "Life signs monitor overlay enabled.") - if("Diagnostic") - add_diag_hud() - to_chat(src, "Robotics diagnostic overlay enabled.") - if("Disable") - to_chat(src, "Sensor augmentations disabled.") - -/mob/living/silicon/proc/receive_alarm(var/datum/alarm_handler/alarm_handler, var/datum/alarm/alarm, was_raised) - if(!next_alarm_notice) - next_alarm_notice = world.time + SecondsToTicks(10) - - var/list/alarms = queued_alarms[alarm_handler] - if(was_raised) - // Raised alarms are always set - alarms[alarm] = 1 - else - // Alarms that were raised but then cleared before the next notice are instead removed - if(alarm in alarms) - alarms -= alarm - // And alarms that have only been cleared thus far are set as such - else - alarms[alarm] = -1 - -/mob/living/silicon/proc/process_queued_alarms() - if(next_alarm_notice && (world.time > next_alarm_notice)) - next_alarm_notice = 0 - - var/alarm_raised = 0 - for(var/datum/alarm_handler/AH in queued_alarms) - var/list/alarms = queued_alarms[AH] - var/reported = 0 - for(var/datum/alarm/A in alarms) - if(alarms[A] == 1) - if(!reported) - reported = 1 - to_chat(src, "--- [AH.category] Detected ---") - raised_alarm(A) - - for(var/datum/alarm_handler/AH in queued_alarms) - var/list/alarms = queued_alarms[AH] - var/reported = 0 - for(var/datum/alarm/A in alarms) - if(alarms[A] == -1) - if(!reported) - reported = 1 - to_chat(src, "--- [AH.category] Cleared ---") - to_chat(src, "\The [A.alarm_name()].") - - if(alarm_raised) - to_chat(src, "\[Show Alerts\]") - - for(var/datum/alarm_handler/AH in queued_alarms) - var/list/alarms = queued_alarms[AH] - alarms.Cut() - -/mob/living/silicon/proc/raised_alarm(var/datum/alarm/A) - to_chat(src, "[A.alarm_name()]!") - -/mob/living/silicon/ai/raised_alarm(var/datum/alarm/A) - var/cameratext = "" - for(var/obj/machinery/camera/C in A.cameras()) - cameratext += "[(cameratext == "")? "" : "|"][C.c_tag]" - to_chat(src, "[A.alarm_name()]! ([(cameratext)? cameratext : "No Camera"])") - -/mob/living/silicon/adjustToxLoss(var/amount) - return STATUS_UPDATE_NONE - -/mob/living/silicon/get_access() - return IGNORE_ACCESS //silicons always have access - -/mob/living/silicon/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash/noise) - if(affect_silicon) - return ..() - -/mob/living/silicon/is_mechanical() - return 1 - -/mob/living/silicon/is_literate() - return 1 - -/////////////////////////////////// EAR DAMAGE //////////////////////////////////// -/mob/living/silicon/can_hear() - . = TRUE - +/mob/living/silicon/bullet_act(var/obj/item/projectile/Proj) + + + if(!Proj.nodamage) + switch(Proj.damage_type) + if(BRUTE) + adjustBruteLoss(Proj.damage) + if(BURN) + adjustFireLoss(Proj.damage) + + Proj.on_hit(src,2) + + return 2 + +/mob/living/silicon/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/negate_armor = 0) + return 0//The only effect that can hit them atm is flashes and they still directly edit so this works for now +/* + if(!effect || (blocked >= 2)) return 0 + switch(effecttype) + if(STUN) + Stun(effect / (blocked + 1)) + if(WEAKEN) + Weaken(effect / (blocked + 1)) + if(PARALYZE) + Paralyse(effect / (blocked + 1)) + if(IRRADIATE) + radiation += min((effect - (effect*getarmor(null, "rad"))), 0)//Rads auto check armor + if(STUTTER) + stuttering = max(stuttering,(effect/(blocked+1))) + if(EYE_BLUR) + eye_blurry = max(eye_blurry,(effect/(blocked+1))) + if(DROWSY) + drowsyness = max(drowsyness,(effect/(blocked+1))) + updatehealth() + return 1*/ + +/proc/islinked(var/mob/living/silicon/robot/bot, var/mob/living/silicon/ai/ai) + if(!istype(bot) || !istype(ai)) + return 0 + if(bot.connected_ai == ai) + return 1 + return 0 + + +// this function shows the health of the pAI in the Status panel +/mob/living/silicon/proc/show_system_integrity() + if(!src.stat) + stat(null, text("System integrity: [round((health/maxHealth)*100)]%")) + else + stat(null, text("Systems nonfunctional")) + + +// This adds the basic clock, shuttle recall timer, and malf_ai info to all silicon lifeforms +/mob/living/silicon/Stat() + ..() + if(statpanel("Status")) + show_stat_emergency_shuttle_eta() + show_system_integrity() + +//Silicon mob language procs + +/mob/living/silicon/can_speak_language(datum/language/speaking) + return universal_speak || (speaking in src.speech_synthesizer_langs) //need speech synthesizer support to vocalize a language + +/mob/living/silicon/add_language(var/language, var/can_speak=1) + if(..(language) && can_speak) + speech_synthesizer_langs.Add(GLOB.all_languages[language]) + return 1 + +/mob/living/silicon/remove_language(var/rem_language) + ..(rem_language) + + for(var/datum/language/L in speech_synthesizer_langs) + if(L.name == rem_language) + speech_synthesizer_langs -= L + +/mob/living/silicon/check_lang_data() + . = "" + + if(default_language) + . += "Current default language: [default_language] - reset

" + + for(var/datum/language/L in languages) + if(!(L.flags & NONGLOBAL)) + var/default_str + if(L == default_language) + default_str = " - default - reset" + else + default_str = " - set default" + + var/synth = (L in speech_synthesizer_langs) + . += "[L.name] (:[L.key])[synth ? default_str : null]
Speech Synthesizer: [synth ? "YES" : "NOT SUPPORTED"]
[L.desc]

" + + +// this function displays the stations manifest in a separate window +/mob/living/silicon/proc/show_station_manifest() + var/dat + dat += "

Crew Manifest

" + if(data_core) + dat += data_core.get_manifest(1) // make it monochrome + dat += "
" + src << browse(dat, "window=airoster") + onclose(src, "airoster") + +/mob/living/silicon/assess_threat() //Secbots won't hunt silicon units + return -10 + +/mob/living/silicon/verb/pose() + set name = "Set Pose" + set desc = "Sets a description which will be shown when someone examines you." + set category = "IC" + + pose = sanitize(copytext(input(usr, "This is [src]. It is...", "Pose", null) as text, 1, MAX_MESSAGE_LEN)) + +/mob/living/silicon/verb/set_flavor() + set name = "Set Flavour Text" + set desc = "Sets an extended description of your character's features." + set category = "IC" + + update_flavor_text() + +/mob/living/silicon/binarycheck() + return 1 + +/mob/living/silicon/proc/remove_med_sec_hud() + var/datum/atom_hud/secsensor = huds[sec_hud] + var/datum/atom_hud/medsensor = huds[med_hud] + for(var/datum/atom_hud/data/diagnostic/diagsensor in huds) + diagsensor.remove_hud_from(src) + secsensor.remove_hud_from(src) + medsensor.remove_hud_from(src) + + +/mob/living/silicon/proc/add_sec_hud() + var/datum/atom_hud/secsensor = huds[sec_hud] + secsensor.add_hud_to(src) + +/mob/living/silicon/proc/add_med_hud() + var/datum/atom_hud/medsensor = huds[med_hud] + medsensor.add_hud_to(src) + +/mob/living/silicon/proc/add_diag_hud() + for(var/datum/atom_hud/data/diagnostic/diagsensor in huds) + diagsensor.add_hud_to(src) + + +/mob/living/silicon/proc/toggle_sensor_mode() + var/sensor_type = input("Please select sensor type.", "Sensor Integration", null) in list("Security", "Medical","Diagnostic","Disable") + remove_med_sec_hud() + switch(sensor_type) + if("Security") + add_sec_hud() + to_chat(src, "Security records overlay enabled.") + if("Medical") + add_med_hud() + to_chat(src, "Life signs monitor overlay enabled.") + if("Diagnostic") + add_diag_hud() + to_chat(src, "Robotics diagnostic overlay enabled.") + if("Disable") + to_chat(src, "Sensor augmentations disabled.") + +/mob/living/silicon/proc/receive_alarm(var/datum/alarm_handler/alarm_handler, var/datum/alarm/alarm, was_raised) + if(!next_alarm_notice) + next_alarm_notice = world.time + SecondsToTicks(10) + + var/list/alarms = queued_alarms[alarm_handler] + if(was_raised) + // Raised alarms are always set + alarms[alarm] = 1 + else + // Alarms that were raised but then cleared before the next notice are instead removed + if(alarm in alarms) + alarms -= alarm + // And alarms that have only been cleared thus far are set as such + else + alarms[alarm] = -1 + +/mob/living/silicon/proc/process_queued_alarms() + if(next_alarm_notice && (world.time > next_alarm_notice)) + next_alarm_notice = 0 + + var/alarm_raised = 0 + for(var/datum/alarm_handler/AH in queued_alarms) + var/list/alarms = queued_alarms[AH] + var/reported = 0 + for(var/datum/alarm/A in alarms) + if(alarms[A] == 1) + if(!reported) + reported = 1 + to_chat(src, "--- [AH.category] Detected ---") + raised_alarm(A) + + for(var/datum/alarm_handler/AH in queued_alarms) + var/list/alarms = queued_alarms[AH] + var/reported = 0 + for(var/datum/alarm/A in alarms) + if(alarms[A] == -1) + if(!reported) + reported = 1 + to_chat(src, "--- [AH.category] Cleared ---") + to_chat(src, "\The [A.alarm_name()].") + + if(alarm_raised) + to_chat(src, "\[Show Alerts\]") + + for(var/datum/alarm_handler/AH in queued_alarms) + var/list/alarms = queued_alarms[AH] + alarms.Cut() + +/mob/living/silicon/proc/raised_alarm(var/datum/alarm/A) + to_chat(src, "[A.alarm_name()]!") + +/mob/living/silicon/ai/raised_alarm(var/datum/alarm/A) + var/cameratext = "" + for(var/obj/machinery/camera/C in A.cameras()) + cameratext += "[(cameratext == "")? "" : "|"][C.c_tag]" + to_chat(src, "[A.alarm_name()]! ([(cameratext)? cameratext : "No Camera"])") + +/mob/living/silicon/adjustToxLoss(var/amount) + return STATUS_UPDATE_NONE + +/mob/living/silicon/get_access() + return IGNORE_ACCESS //silicons always have access + +/mob/living/silicon/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash/noise) + if(affect_silicon) + return ..() + +/mob/living/silicon/is_mechanical() + return 1 + +/mob/living/silicon/is_literate() + return 1 + +/////////////////////////////////// EAR DAMAGE //////////////////////////////////// +/mob/living/silicon/can_hear() + . = TRUE +