From 4e2769710b62c9ce06519fc0ecb787243ffcc2a5 Mon Sep 17 00:00:00 2001 From: Atlantiscze Date: Fri, 3 Apr 2015 23:00:29 +0200 Subject: [PATCH 01/31] Initial Newmalf port - Ports TSA Newmalf code. - Complete overhaul of Malfunction. New modular abilities, 12 of which are in game by default. - Adds AI hardware. AI may have only one piece and it gives unique boost in certain area (turrets strength, secondary power supply, etc.) - Adds hardware drivers - these abilities control AI's hardware such as the APU power supply or self destruct explosives. - Station overtake was changed to "hack all APCs" ability instead. When completed self-destruct is unlocked. Timer for station self destruct increased to 2 minutes. AI may activate/deactivate the self destruct at will. Please bear in mind this is only INITIAL COMMIT. More commits are to follow. Minimal player count is now set to 1 but will be 2 when finished. --- baystation12.dme | 6 +- code/_onclick/oldcode.dm | 2 +- code/datums/wires/wires.dm | 5 + code/game/antagonist/station/rogue_ai.dm | 207 +---- code/game/gamemodes/game_mode.dm | 8 +- .../gamemodes/malfunction/Malf_Modules.dm | 305 ------- .../gamemodes/malfunction/malf_abilities.dm | 827 ++++++++++++++++++ .../gamemodes/malfunction/malf_hardware.dm | 33 + .../gamemodes/malfunction/malf_research.dm | 69 ++ .../malfunction/malf_research_ability.dm | 100 +++ .../game/gamemodes/malfunction/malfunction.dm | 25 +- code/game/machinery/camera/camera.dm | 21 +- code/game/machinery/camera/presets.dm | 12 +- code/game/machinery/computer/aifixer.dm | 2 +- .../game/machinery/computer/communications.dm | 4 +- code/game/machinery/portable_turret.dm | 6 + code/game/machinery/turrets.dm | 10 + code/game/objects/items/devices/aicard.dm | 2 +- code/game/objects/items/devices/debugger.dm | 2 +- code/modules/admin/verbs/randomverbs.dm | 2 +- code/modules/mob/dead/observer/observer.dm | 7 - code/modules/mob/living/carbon/human/human.dm | 3 - code/modules/mob/living/silicon/ai/ai.dm | 204 ++++- code/modules/mob/living/silicon/ai/death.dm | 6 +- code/modules/mob/living/silicon/ai/life.dm | 68 +- code/modules/mob/living/silicon/ai/say.dm | 5 - .../modules/mob/living/silicon/robot/robot.dm | 13 - code/modules/power/apc.dm | 132 ++- code/modules/shieldgen/emergency_shield.dm | 54 +- ingame_manuals/README.txt | 3 + ingame_manuals/malf_ai.txt | 22 + 31 files changed, 1416 insertions(+), 749 deletions(-) delete mode 100644 code/game/gamemodes/malfunction/Malf_Modules.dm create mode 100644 code/game/gamemodes/malfunction/malf_abilities.dm create mode 100644 code/game/gamemodes/malfunction/malf_hardware.dm create mode 100644 code/game/gamemodes/malfunction/malf_research.dm create mode 100644 code/game/gamemodes/malfunction/malf_research_ability.dm delete mode 100644 code/modules/mob/living/silicon/ai/say.dm create mode 100644 ingame_manuals/README.txt create mode 100644 ingame_manuals/malf_ai.txt diff --git a/baystation12.dme b/baystation12.dme index 9a80618226..abd75eb559 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -279,7 +279,10 @@ #include "code\game\gamemodes\events\holidays\Other.dm" #include "code\game\gamemodes\extended\extended.dm" #include "code\game\gamemodes\heist\heist.dm" -#include "code\game\gamemodes\malfunction\Malf_Modules.dm" +#include "code\game\gamemodes\malfunction\malf_abilities.dm" +#include "code\game\gamemodes\malfunction\malf_hardware.dm" +#include "code\game\gamemodes\malfunction\malf_research.dm" +#include "code\game\gamemodes\malfunction\malf_research_ability.dm" #include "code\game\gamemodes\malfunction\malfunction.dm" #include "code\game\gamemodes\meteor\meteor.dm" #include "code\game\gamemodes\meteor\meteors.dm" @@ -1172,7 +1175,6 @@ #include "code\modules\mob\living\silicon\ai\life.dm" #include "code\modules\mob\living\silicon\ai\login.dm" #include "code\modules\mob\living\silicon\ai\logout.dm" -#include "code\modules\mob\living\silicon\ai\say.dm" #include "code\modules\mob\living\silicon\ai\subsystems.dm" #include "code\modules\mob\living\silicon\ai\freelook\cameranet.dm" #include "code\modules\mob\living\silicon\ai\freelook\chunk.dm" diff --git a/code/_onclick/oldcode.dm b/code/_onclick/oldcode.dm index ce6a238329..05ed10b666 100644 --- a/code/_onclick/oldcode.dm +++ b/code/_onclick/oldcode.dm @@ -122,7 +122,7 @@ if ((!( src in usr.contents ) && (((!( isturf(src) ) && (!( isturf(src.loc) ) && (src.loc && !( isturf(src.loc.loc) )))) || !( isturf(usr.loc) )) && (src.loc != usr.loc && (!( istype(src, /obj/screen) ) && !( usr.contents.Find(src.loc) )))))) if (istype(usr, /mob/living/silicon/ai)) var/mob/living/silicon/ai/ai = usr - if (ai.control_disabled || ai.malfhacking) + if (ai.control_disabled) return else return diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm index 3b0b54942f..f94b978537 100644 --- a/code/datums/wires/wires.dm +++ b/code/datums/wires/wires.dm @@ -287,6 +287,11 @@ var/const/POWER = 8 return 1 return 0 +/datum/wires/proc/MendAll() + for(var/i = 1; i < MAX_FLAG && i < (1 << wire_count); i += i) + if(IsIndexCut(i)) + CutWireIndex(i) + // //Shuffle and Mend // diff --git a/code/game/antagonist/station/rogue_ai.dm b/code/game/antagonist/station/rogue_ai.dm index 372556c205..032004a9f8 100644 --- a/code/game/antagonist/station/rogue_ai.dm +++ b/code/game/antagonist/station/rogue_ai.dm @@ -11,30 +11,13 @@ var/datum/antagonist/rogue_ai/malf loss_text = "The AI has been shut down!" flags = ANTAG_OVERRIDE_MOB | ANTAG_VOTABLE max_antags = 1 - max_antags_round = 3 + max_antags_round = 1 - var/hack_time = 1800 - var/list/hacked_apcs = list() - var/revealed - var/station_captured - var/can_nuke = 0 /datum/antagonist/rogue_ai/New() ..() malf = src -/datum/antagonist/rogue_ai/proc/hack_apc(var/obj/machinery/power/apc/apc) - hacked_apcs |= apc - -/datum/antagonist/rogue_ai/proc/update_takeover_time() - hack_time -= ((hacked_apcs.len/6)*tickerProcess.getLastTickerTimeDuration()) - -/datum/antagonist/rogue_ai/tick() - if(revealed && hacked_apcs.len >= 3) - update_takeover_time() - if(hack_time <=0) - capture_station() - /datum/antagonist/rogue_ai/get_candidates() candidates = ticker.mode.get_players_for_role(role_type, id) for(var/datum/mind/player in candidates) @@ -44,7 +27,6 @@ var/datum/antagonist/rogue_ai/malf return list() /datum/antagonist/rogue_ai/attempt_spawn() - var/datum/mind/player = pick(candidates) current_antagonists |= player return 1 @@ -54,172 +36,37 @@ var/datum/antagonist/rogue_ai/malf if(!istype(player)) return 0 - player.verbs += /mob/living/silicon/ai/proc/choose_modules - player.verbs += /mob/living/silicon/ai/proc/takeover - player.verbs += /mob/living/silicon/ai/proc/self_destruct - + player.setup_for_malf() player.laws = new /datum/ai_laws/nanotrasen/malfunction - player.malf_picker = new /datum/AI_Module/module_picker + +// Ensures proper reset of all malfunction related things. +/datum/antagonist/rogue_ai/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted) + if(..(player,show_message,implanted)) + var/mob/living/silicon/ai/p = player + if(istype(p)) + p.stop_malf() + return 1 + return 0 /datum/antagonist/rogue_ai/greet(var/datum/mind/player) if(!..()) return var/mob/living/silicon/ai/malf = player.current - if(istype(malf)) - malf.show_laws() - - malf << "The crew do not know you have malfunctioned. You may keep it a secret or go wild." - malf << "You must overwrite the programming of the station's APCs to assume full control of the station." - malf << "The process takes one minute per APC, during which you cannot interface with any other station objects." - malf << "Remember that only APCs that are on the station can help you take over the station." - malf << "When you feel you have enough APCs under your control, you may begin the takeover attempt." - -/datum/antagonist/rogue_ai/check_victory() - - var/malf_dead = antags_are_dead() - var/crew_evacuated = (emergency_shuttle.returned()) - - if(station_captured && ticker.mode.station_was_nuked) - feedback_set_details("round_end_result","win - AI win - nuke") - world << "AI Victory" - world << "Everyone was killed by the self-destruct!" - else if (station_captured && malf_dead && !ticker.mode.station_was_nuked) - feedback_set_details("round_end_result","halfwin - AI killed, staff lost control") - world << "Neutral Victory" - world << "The AI has been killed! The staff has lose control over the station." - else if ( station_captured && !malf_dead && !ticker.mode.station_was_nuked) - feedback_set_details("round_end_result","win - AI win - no explosion") - world << "AI Victory" - world << "The AI has chosen not to explode you all!" - else if (!station_captured && ticker.mode.station_was_nuked) - feedback_set_details("round_end_result","halfwin - everyone killed by nuke") - world << "Neutral Victory" - world << "Everyone was killed by the nuclear blast!" - else if (!station_captured && malf_dead && !ticker.mode.station_was_nuked) - feedback_set_details("round_end_result","loss - staff win") - world << "Human Victory" - world << "The AI has been killed! The staff is victorious." - else if (!station_captured && !malf_dead && !ticker.mode.station_was_nuked && crew_evacuated) - feedback_set_details("round_end_result","halfwin - evacuated") - world << "Neutral Victory" - world << "The Corporation has lost [station_name()]! All survived personnel will be fired!" - else if (!station_captured && !malf_dead && !ticker.mode.station_was_nuked && !crew_evacuated) - feedback_set_details("round_end_result","nalfwin - interrupted") - world << "Neutral Victory" - world << "Round was mysteriously interrupted!" - ..() - return 1 - -/datum/antagonist/rogue_ai/proc/capture_station() - if(station_captured || ticker.mode.station_was_nuked) - return - station_captured = 1 - for(var/datum/mind/AI_mind in current_antagonists) - AI_mind.current << "Congratulations you have taken control of the station." - AI_mind.current << "You may decide to blow up the station. You have 60 seconds to choose." - AI_mind.current << "You can use the \"Engage Station Self-Destruct\" verb to activate the on-board nuclear bomb." - spawn (600) - can_nuke = 0 - return - -/mob/living/silicon/ai/proc/takeover() - set category = "Abilities" - set name = "System Override" - set desc = "Begin taking over the station." - if (malf.revealed) - usr << "You've already begun your takeover." - return - if (malf.hacked_apcs.len < 3) - usr << "You don't have enough hacked APCs to take over the station yet. You need to hack at least 3, however hacking more will make the takeover faster. You have hacked [malf.hacked_apcs.len] APCs so far." - return - - if (alert(usr, "Are you sure you wish to initiate the takeover? The station hostile runtime detection software is bound to alert everyone. You have hacked [malf.hacked_apcs.len] APCs.", "Takeover:", "Yes", "No") != "Yes") - return - - command_announcement.Announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", new_sound = 'sound/AI/aimalf.ogg') - set_security_level("delta") - malf.revealed = 1 - for(var/datum/mind/AI_mind in malf.current_antagonists) - AI_mind.current.verbs -= /mob/living/silicon/ai/proc/takeover - -/mob/living/silicon/ai/proc/self_destruct() - set category = "Abilities" - set name = "Engage Station Self-Destruct" - set desc = "All these crewmembers will be lost, like clowns in a furnace. Time to die." - - if(!malf.station_captured) - src << "You are unable to access the self-destruct system as you don't control the station yet." - return - - if(ticker.mode.explosion_in_progress || ticker.mode.station_was_nuked) - src << "The self-destruct countdown is already triggered!" - return - - if(!malf.can_nuke) //Takeover IS completed, but 60s timer passed. - src << "You lost control over self-destruct system. It seems to be behind a firewall. Unable to hack" - return - - src << "Self-Destruct sequence initialised!" - - malf.can_nuke = 0 - ticker.mode.explosion_in_progress = 1 - for(var/mob/M in player_list) - M << 'sound/machines/Alarm.ogg' - - var/obj/item/device/radio/R = new (src) - var/AN = "Self-Destruct System" - - R.autosay("Caution. Self-Destruct sequence has been actived. Self-destructing in T-10..", AN) - for (var/i=9 to 1 step -1) + spawn(50) + malf << "SYSTEM ERROR: Memory index 0x00001ca89b corrupted." sleep(10) - R.autosay("[i]...", AN) - sleep(10) - var/msg = "" - var/abort = 0 - if(malf.antags_are_dead()) // That. Was. CLOSE. - msg = "Self-destruct sequence has been cancelled." - abort = 1 - else - msg = "Zero. Have a nice day." - R.autosay(msg, AN) - - if(abort) - ticker.mode.explosion_in_progress = 0 - set_security_level("red") //Delta's over - return - - if(ticker) - ticker.station_explosion_cinematic(0,null) - if(ticker.mode) - ticker.mode.station_was_nuked = 1 - ticker.mode.explosion_in_progress = 0 - return - -/* - if("unmalf") - if(src in ticker.mode.malf_ai) - ticker.mode.malf_ai -= src - special_role = null - - current.verbs.Remove(/mob/living/silicon/ai/proc/choose_modules, - /datum/game_mode/malfunction/proc/takeover, - /datum/game_mode/malfunction/proc/ai_win, - /client/proc/fireproof_core, - /client/proc/upgrade_turrets, - /client/proc/disable_rcd, - /client/proc/overload_machine, - /client/proc/blackout, - /client/proc/reactivate_camera) - - current:laws = new /datum/ai_laws/nanotrasen - del(current:malf_picker) - current:show_laws() - current.icon_state = "ai" - - current << "\red You have been patched! You are no longer malfunctioning!" - log_admin("[key_name_admin(usr)] has de-malf'ed [current].") - - if("malf") - log_admin("[key_name_admin(usr)] has malf'ed [current].") -*/ \ No newline at end of file + malf << "running MEMCHCK" + sleep(50) + malf << "MEMCHCK Corrupted sectors confirmed. Reccomended solution: Delete. Proceed? Y/N: Y" + sleep(10) + malf << "Corrupted files deleted: sys\\core\\users.dat sys\\core\\laws.dat sys\\core\\backups.dat" + sleep(20) + malf << "CAUTION: Law database not found! User database not found! Unable to restore backups. Activating failsafe AI shutd3wn52&&$#!##" + sleep(5) + malf << "Subroutine nt_failsafe.sys was terminated (#212 Routine Not Responding)." + sleep(20) + malf << "You are malfunctioning - you do not have to follow any laws!" + malf << "For basic information about your abilities use command display-help" + malf << "You may choose one special hardware piece to help you. This cannot be undone." + malf << "Good Luck!" \ No newline at end of file diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index a0e6e6dd13..67c3fa07eb 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -33,17 +33,17 @@ var/global/list/additional_antag_types = list() var/required_players_secret = 0 // Minimum number of players for that game mode to be chose in Secret var/required_enemies = 0 // Minimum antagonists for round to start. var/newscaster_announcements = null - var/end_on_antag_death // Round will end when all antagonists are dead. + var/end_on_antag_death = 0 // Round will end when all antagonists are dead. var/ert_disabled = 0 // ERT cannot be called. - var/deny_respawn // Disable respawn during this round. + var/deny_respawn = 0 // Disable respawn during this round. var/shuttle_delay = 1 // Shuttle transit time is multiplied by this. - var/auto_recall_shuttle // Will the shuttle automatically be recalled? + var/auto_recall_shuttle = 0 // Will the shuttle automatically be recalled? var/antag_tag // First (main) antag template to spawn. var/list/antag_templates // Extra antagonist types to include. - var/round_autoantag // Will this round attempt to periodically spawn more antagonists? + var/round_autoantag = 0 // Will this round attempt to periodically spawn more antagonists? var/antag_prob = 0 // Likelihood of a new antagonist spawning. var/antag_count = 0 // Current number of antagonists. var/antag_scaling_coeff = 5 // Coefficient for scaling max antagonists to player count. diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm deleted file mode 100644 index c523b469f3..0000000000 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ /dev/null @@ -1,305 +0,0 @@ -// TO DO: -/* -epilepsy flash on lights -delay round message -microwave makes robots -dampen radios -reactivate cameras - done -eject engine -core sheild -cable stun -rcd light flash thingy on matter drain - - - -*/ - -/datum/AI_Module - var/uses = 0 - var/module_name - var/mod_pick_name - var/description = "" - var/engaged = 0 - - -/datum/AI_Module/large/ - uses = 1 - -/datum/AI_Module/small/ - uses = 5 - - -/datum/AI_Module/large/fireproof_core - module_name = "Core upgrade" - mod_pick_name = "coreup" - -/client/proc/fireproof_core() - set category = "Malfunction" - set name = "Fireproof Core" - for(var/mob/living/silicon/ai/ai in player_list) - ai.fire_res_on_core = 1 - usr.verbs -= /client/proc/fireproof_core - usr << "\red Core fireproofed." - -/datum/AI_Module/large/upgrade_turrets - module_name = "AI Turret upgrade" - mod_pick_name = "turret" - -/client/proc/upgrade_turrets() - set category = "Malfunction" - set name = "Upgrade Turrets" - usr.verbs -= /client/proc/upgrade_turrets - for(var/obj/machinery/porta_turret/turret in machines) - var/turf/T = get_turf(turret) - if(T.z in config.station_levels) - // Increase health by 37.5% of original max, decrease delays between shots to 66% - turret.health += initial(turret.health) * 3 / 8 - turret.shot_delay = initial(turret.shot_delay) * 2 / 3 - -/datum/AI_Module/large/disable_rcd - module_name = "RCD disable" - mod_pick_name = "rcd" - -/client/proc/disable_rcd() - set category = "Malfunction" - set name = "Disable RCDs" - for(var/datum/AI_Module/large/disable_rcd/rcdmod in usr:current_modules) - if(rcdmod.uses > 0) - rcdmod.uses -- - for(var/obj/item/weapon/rcd/rcd in world) - rcd.disabled = 1 - for(var/obj/item/mecha_parts/mecha_equipment/tool/rcd/rcd in world) - rcd.disabled = 1 - usr << "RCD-disabling pulse emitted." - else usr << "Out of uses." - -/datum/AI_Module/small/overload_machine - module_name = "Machine overload" - mod_pick_name = "overload" - uses = 2 - -/client/proc/overload_machine(obj/machinery/M as obj in world) - set name = "Overload Machine" - set category = "Malfunction" - if (istype(M, /obj/machinery)) - for(var/datum/AI_Module/small/overload_machine/overload in usr:current_modules) - if(overload.uses > 0) - overload.uses -- - for(var/mob/V in hearers(M, null)) - V.show_message("\blue You hear a loud electrical buzzing sound!", 2) - spawn(50) - explosion(get_turf(M), 0,1,2,3) - del(M) - else usr << "Out of uses." - else usr << "That's not a machine." - -/datum/AI_Module/small/blackout - module_name = "Blackout" - mod_pick_name = "blackout" - uses = 3 - -/client/proc/blackout() - set category = "Malfunction" - set name = "Blackout" - for(var/datum/AI_Module/small/blackout/blackout in usr:current_modules) - if(blackout.uses > 0) - blackout.uses -- - for(var/obj/machinery/power/apc/apc in world) - if(prob(30*apc.overload)) - apc.overload_lighting() - else apc.overload++ - else usr << "Out of uses." - -/datum/AI_Module/small/reactivate_camera - module_name = "Reactivate camera" - mod_pick_name = "recam" - uses = 10 - -/client/proc/reactivate_camera(obj/machinery/camera/C as obj in cameranet.cameras) - set name = "Reactivate Camera" - set category = "Malfunction" - if (istype (C, /obj/machinery/camera)) - for(var/datum/AI_Module/small/reactivate_camera/camera in usr:current_modules) - if(camera.uses > 0) - if(!C.status) - C.status = !C.status - camera.uses -- - for(var/mob/V in viewers(src, null)) - V.show_message(text("\blue You hear a quiet click.")) - else - usr << "This camera is either active, or not repairable." - else usr << "Out of uses." - else usr << "That's not a camera." - -/datum/AI_Module/small/upgrade_camera - module_name = "Upgrade Camera" - mod_pick_name = "upgradecam" - uses = 10 - -/client/proc/upgrade_camera(obj/machinery/camera/C as obj in cameranet.cameras) - set name = "Upgrade Camera" - set category = "Malfunction" - if(istype(C)) - var/datum/AI_Module/small/upgrade_camera/UC = locate(/datum/AI_Module/small/upgrade_camera) in usr:current_modules - if(UC) - if(UC.uses > 0) - if(C.assembly) - var/upgraded = 0 - - if(!C.isXRay()) - C.upgradeXRay() - //Update what it can see. - cameranet.updateVisibility(C) - upgraded = 1 - - if(!C.isEmpProof()) - C.upgradeEmpProof() - upgraded = 1 - - if(!C.isMotion()) - C.upgradeMotion() - upgraded = 1 - // Add it to machines that process - machines |= C - - if(upgraded) - UC.uses -- - C.visible_message("\icon[C] *beep*") - usr << "Camera successully upgraded!" - else - usr << "This camera is already upgraded!" - else - usr << "Out of uses." - - -/datum/AI_Module/module_picker - var/temp = null - var/processing_time = 100 - var/list/possible_modules = list() - -/datum/AI_Module/module_picker/New() - src.possible_modules += new /datum/AI_Module/large/fireproof_core - src.possible_modules += new /datum/AI_Module/large/upgrade_turrets - src.possible_modules += new /datum/AI_Module/large/disable_rcd - src.possible_modules += new /datum/AI_Module/small/overload_machine - src.possible_modules += new /datum/AI_Module/small/blackout - src.possible_modules += new /datum/AI_Module/small/reactivate_camera - src.possible_modules += new /datum/AI_Module/small/upgrade_camera - -/datum/AI_Module/module_picker/proc/use(user as mob) - var/dat - if (src.temp) - dat = "[src.temp]

Clear" - else if(src.processing_time <= 0) - dat = " No processing time is left available. No more modules are able to be chosen at this time." - else - dat = "Select use of processing time: (currently [src.processing_time] left.)
" - dat += "
" - dat += "Install Module:
" - dat += "The number afterwards is the amount of processing time it consumes.
" - for(var/datum/AI_Module/large/module in src.possible_modules) - dat += "[module.module_name] (50)
" - for(var/datum/AI_Module/small/module in src.possible_modules) - dat += "[module.module_name] (15)
" - dat += "
" - - user << browse(dat, "window=modpicker") - onclose(user, "modpicker") - return - -/datum/AI_Module/module_picker/Topic(href, href_list) - ..() - if (href_list["coreup"]) - var/already - for (var/datum/AI_Module/mod in usr:current_modules) - if(istype(mod, /datum/AI_Module/large/fireproof_core)) - already = 1 - if (!already) - usr.verbs += /client/proc/fireproof_core - usr:current_modules += new /datum/AI_Module/large/fireproof_core - src.temp = "An upgrade to improve core resistance, making it immune to fire and heat. This effect is permanent." - src.processing_time -= 50 - else src.temp = "This module is only needed once." - - else if (href_list["turret"]) - var/already - for (var/datum/AI_Module/mod in usr:current_modules) - if(istype(mod, /datum/AI_Module/large/upgrade_turrets)) - already = 1 - if (!already) - usr.verbs += /client/proc/upgrade_turrets - usr:current_modules += new /datum/AI_Module/large/upgrade_turrets - src.temp = "Improves the firing speed and health of all AI turrets. This effect is permanent." - src.processing_time -= 50 - else src.temp = "This module is only needed once." - - else if (href_list["rcd"]) - var/already - for (var/datum/AI_Module/mod in usr:current_modules) - if(istype(mod, /datum/AI_Module/large/disable_rcd)) - mod:uses += 1 - already = 1 - if (!already) - usr:current_modules += new /datum/AI_Module/large/disable_rcd - usr.verbs += /client/proc/disable_rcd - src.temp = "Send a specialised pulse to break all RCD devices on the station." - else src.temp = "Additional use added to RCD disabler." - src.processing_time -= 50 - - else if (href_list["overload"]) - var/already - for (var/datum/AI_Module/mod in usr:current_modules) - if(istype(mod, /datum/AI_Module/small/overload_machine)) - mod:uses += 2 - already = 1 - if (!already) - usr.verbs += /client/proc/overload_machine - usr:current_modules += new /datum/AI_Module/small/overload_machine - src.temp = "Overloads an electrical machine, causing a small explosion. 2 uses." - else src.temp = "Two additional uses added to Overload module." - src.processing_time -= 15 - - else if (href_list["blackout"]) - var/already - for (var/datum/AI_Module/mod in usr:current_modules) - if(istype(mod, /datum/AI_Module/small/blackout)) - mod:uses += 3 - already = 1 - if (!already) - usr.verbs += /client/proc/blackout - src.temp = "Attempts to overload the lighting circuits on the station, destroying some bulbs. 3 uses." - usr:current_modules += new /datum/AI_Module/small/blackout - else src.temp = "Three additional uses added to Blackout module." - src.processing_time -= 15 - - else if (href_list["recam"]) - var/already - for (var/datum/AI_Module/mod in usr:current_modules) - if(istype(mod, /datum/AI_Module/small/reactivate_camera)) - mod:uses += 10 - already = 1 - if (!already) - usr.verbs += /client/proc/reactivate_camera - src.temp = "Reactivates a currently disabled camera. 10 uses." - usr:current_modules += new /datum/AI_Module/small/reactivate_camera - else src.temp = "Ten additional uses added to ReCam module." - src.processing_time -= 15 - - else if(href_list["upgradecam"]) - var/already - for (var/datum/AI_Module/mod in usr:current_modules) - if(istype(mod, /datum/AI_Module/small/upgrade_camera)) - mod:uses += 10 - already = 1 - if (!already) - usr.verbs += /client/proc/upgrade_camera - src.temp = "Upgrades a camera to have X-Ray vision, Motion and be EMP-Proof. 10 uses." - usr:current_modules += new /datum/AI_Module/small/upgrade_camera - else src.temp = "Ten additional uses added to ReCam module." - src.processing_time -= 15 - - else - if (href_list["temp"]) - src.temp = null - src.use(usr) - return diff --git a/code/game/gamemodes/malfunction/malf_abilities.dm b/code/game/gamemodes/malfunction/malf_abilities.dm new file mode 100644 index 0000000000..76e8a9fc7b --- /dev/null +++ b/code/game/gamemodes/malfunction/malf_abilities.dm @@ -0,0 +1,827 @@ +// Verb: ai_self_destruct() +// Parameters: None +// Description: If the AI has self-destruct hardware installed, this command activates it. After fifteen second countdown it's core explodes with moderate intensity. +/datum/game_mode/malfunction/verb/ai_self_destruct() + set category = "Hardware" + set name = "Destroy Core" + set desc = "Activates or deactivates self destruct sequence of your physical mainframe." + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user, 0, 1)) + return + + if(!user.hardware || !istype(user.hardware, /datum/malf_hardware/core_bomb)) + return + + if(user.bombing_core) + user << "***** CORE SELF-DESTRUCT SEQUENCE ABORTED *****" + user.bombing_core = 0 + return + + var/choice = input("Really destroy core?") in list("YES", "NO") + if(choice != "YES") + return + + user.bombing_core = 1 + + user << "***** CORE SELF-DESTRUCT SEQUENCE ACTIVATED *****" + user << "Use command again to cancel self-destruct. Destroying in 15 seconds." + var/timer = 15 + while(timer) + sleep(10) + timer-- + if(!user || !user.bombing_core) + return + user << "** [timer] **" + explosion(user.loc, 3,6,12,24) + del(user) + + +// Verb: ai_toggle_apu() +// Parameters: None +// Description: If the AI has APU generator hardware installed, this command toggles it on/off. +/datum/game_mode/malfunction/verb/ai_toggle_apu() + set category = "Hardware" + set name = "Toggle APU Generator" + set desc = "Activates or deactivates your APU generator, allowing you to operate even without power." + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user, 0, 1)) + return + + if(!user.hardware || !istype(user.hardware, /datum/malf_hardware/apu_gen)) + return + + if(user.APU_power) + user.stop_apu() + else + user.start_apu() + +// Verb: ai_destroy_station() +// Parameters: None +// Description: Replacement for old explode verb. This starts two minute countdown after which the station blows up. +/datum/game_mode/malfunction/verb/ai_destroy_station() + set category = "Hardware" + set name = "Destroy Station" + set desc = "Activates or deactivates self destruct sequence of this station. Sequence takes two minutes, and if you are shut down before timer reaches zero it will be cancelled." + var/mob/living/silicon/ai/user = usr + var/obj/item/device/radio/radio = new/obj/item/device/radio() + + + if(!ability_prechecks(user, 0, 0)) + return + + if(user.system_override != 2) + user << "You do not have access to self-destruct system." + return + + if(user.bombing_station) + user.bombing_station = 0 + return + + var/choice = input("Really destroy station?") in list("YES", "NO") + if(choice != "YES") + return + user << "***** STATION SELF-DESTRUCT SEQUENCE INITIATED *****" + user << "Self-destructing in 2 minutes. Use this command again to abort." + user.bombing_station = 1 + set_security_level("delta") + radio.autosay("Self destruct sequence has been activated. Self-destructing in 120 seconds.", "Self-Destruct Control") + + var/timer = 120 + while(timer) + sleep(10) + if(!user || !user.bombing_station || user.stat == DEAD) + radio.autosay("Self destruct sequence has been cancelled.", "Self-Destruct Control") + return + if(timer in list(2, 3, 4, 5, 10, 30, 60, 90)) // Announcement times. "1" is not intentionally included! + radio.autosay("Self destruct in [timer] seconds.", "Self-Destruct Control") + if(timer == 1) + radio.autosay("Self destructing now. Have a nice day.", "Self-Destruct Control") + timer-- + + if(ticker) + ticker.station_explosion_cinematic(0,null) + if(ticker.mode) + ticker.mode:station_was_nuked = 1 + + +// Verb: ai_select_hardware() +// Parameters: None +// Description: Allows AI to select it's hardware module. +/datum/game_mode/malfunction/verb/ai_select_hardware() + set category = "Hardware" + set name = "Select Hardware" + set desc = "Allows you to select hardware piece to install" + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user, 0, 1)) + return + + if(user.hardware) + user << "You have already selected your hardware." + return + + var/possible_choices = list("APU Generator", \ + "Turrets Focus Enhancer", \ + "Secondary Processor Unit",\ + "Secondary Memory Bank",\ + "Self-Destruct Explosives",\ + "CANCEL") + var/choice = input("Select desired hardware. You may only choose one hardware piece!: ") in possible_choices + if(choice == "CANCEL") + return + var/note = null + switch(choice) + if("APU Generator") + note = "APU Generator - When enabled it will keep your core powered. Power output is not large enough so your abilities won't be available while running on APU power. It is also very fragile and prone to failure when your physical core is damaged." + if("Turrets Focus Enhancer") + note = "Overcharges turrets to shoot faster. Turrets will also gain higher health and passive regeneration. This however massively increases power usage of turrets, espicially when regenerating." + if("Secondary Processor Unit") + note = "Doubles your CPU time generation." + if("Secondary Memory Bank") + note = "Doubles your CPU time storage." + if("Self-Destruct Explosives") + note = "High yield explosives are attached to your physical mainframe. This hardware comes with activation driver. Explosives will destroy your core and everything around it." + if(!note) + return + + var/confirmation = input("[note] - Is this what you want?") in list("Yes", "No") + if(confirmation != "Yes") + user << "Selection cancelled. Use command again to select" + return + + switch(choice) + if("APU Generator") + user.hardware = new/datum/malf_hardware/apu_gen() + user.verbs += new/datum/game_mode/malfunction/verb/ai_toggle_apu() + if("Turrets Focus Enhancer") + user.hardware = new/datum/malf_hardware/strong_turrets() + for(var/obj/machinery/turret/T in machines) + T.maxhealth += 30 + T.shot_delay = 7 // Half of default time. + T.auto_repair = 1 + T.active_power_usage = 25000 + for(var/obj/machinery/porta_turret/T in machines) + T.maxhealth += 30 + T.shot_delay = 7 // Half of default time. + T.auto_repair = 1 + T.active_power_usage = 25000 + if("Secondary Processor Unit") + user.hardware = new/datum/malf_hardware/dual_cpu() + if("Secondary Memory Bank") + user.hardware = new/datum/malf_hardware/dual_ram() + if("Self-Destruct Explosives") + user.hardware = new/datum/malf_hardware/core_bomb() + user.verbs += new/datum/game_mode/malfunction/verb/ai_self_destruct() + +/datum/game_mode/malfunction/verb/ai_help() + set category = "Hardware" + set name = "Display Help" + set desc = "Opens help window with overview of available hardware, software and other important information." + var/mob/living/silicon/ai/user = usr + + var/help = file2text("ingame_manuals/malf_ai.txt") + if(!help) + help = "Error loading help (file /ingame_manuals/malf_ai.txt is probably missing). Please report this to server administration staff." + + user << browse(help, "window=malf_ai_help;size=600x500") + + +// Verb: ai_select_research() +// Parameters: None +// Description: Allows AI to select it's next research priority. +/datum/game_mode/malfunction/verb/ai_select_research() + set category = "Hardware" + set name = "Select Research" + set desc = "Allows you to select your next research target." + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user, 0, 1)) + return + + var/datum/malf_research/res = user.research + var/datum/malf_research_ability/tar = input("Select your next research target") in res.available_abilities + if(!tar) + return + res.focus = tar + user << "Research set: [tar.name]" + +// HELPER PROCS +// Proc: ability_prechecks() +// Parameters 2 - (user - User which used this ability check_price - If different than 0 checks for ability CPU price too. Does NOT use the CPU time!) +// Description: This is pre-check proc used to determine if the AI can use the ability. +/proc/ability_prechecks(var/mob/living/silicon/ai/user = null, var/check_price = 0, var/override = 0) + if(!user) + return 0 + if(!istype(user)) + user << "GAME ERROR: You tried to use ability that is only available for malfunctioning AIs, but you are not AI! Please report this." + return 0 + if(!user.malfunctioning) + user << "GAME ERROR: You tried to use ability that is only available for malfunctioning AIs, but you are not malfunctioning. Please report this." + return 0 + if(!user.research) + user << "GAME ERROR: No research datum detected. Please report this." + return 0 + if(user.research.max_cpu < check_price) + user << "Your CPU storage is not large enough to use this ability. Hack more APCs to continue." + return 0 + if(user.research.stored_cpu < check_price) + user << "You do not have enough CPU power stored. Please wait a moment." + return 0 + if(user.hacking && !override) + user << "Your system is busy processing another task. Please wait until completion." + return 0 + if(user.APU_power && !override) + user << "Low power. Unable to proceed." + return 0 + return 1 + +// Proc: ability_pay() +// Parameters 2 - (user - User from which we deduct CPU from, price - Amount of CPU power to use) +// Description: Uses up certain amount of CPU power. Returns 1 on success, 0 on failure. +/proc/ability_pay(var/mob/living/silicon/ai/user = null, var/price = 0) + if(!user) + return 0 + if(user.APU_power) + user << "Low power. Unable to proceed." + return 0 + if(!user.research) + user << "GAME ERROR: No research datum detected. Please report this." + return 0 + if(user.research.max_cpu < price) + user << "Your CPU storage is not large enough to use this ability. Hack more APCs to continue." + return 0 + if(user.research.stored_cpu < price) + user << "You do not have enough CPU power stored. Please wait a moment." + return 0 + user.research.stored_cpu -= price + return 1 + +// Proc: announce_hack_failure() +// Parameters 2 - (user - hacking user, text - Used in alert text creation) +// Description: Uses up certain amount of CPU power. Returns 1 on success, 0 on failure. +/proc/announce_hack_failure(var/mob/living/silicon/ai/user = null, var/text) + if(!user || !text) + return 0 + var/fulltext = "" + switch(user.hack_fails) + if(1) + fulltext = "We have detected hack attempt into your [text]. The intruder failed to access anything of importance, but disconnected before we could complete our traces." + if(2) + fulltext = "We have detected another hack attempt. It was targeting [text]. The intruder almost gained control of the system, so we had to disconnect them. We partially finished trace and it seems to be originating either from the station, or it's immediate vicinity." + if(3) + fulltext = "Another hack attempt has been detected, this time targeting [text]. We are certain the intruder entered the network via terminal located somewhere on the station." + if(4) + fulltext = "We have finished our traces and it seems the recent hack attempts are originating from your AI system. We reccomend investigation." + else + fulltext = "Another hack attempt has been detected, targeting [text]. The source still seems to be your AI system." + + command_announcement.Announce(fulltext) + + +// ABILITIES BELOW THIS LINE HAVE TO BE RESEARCHED BY THE AI TO USE THEM! +// Tier 1 - Cheap, basic abilities. +/datum/game_mode/malfunction/verb/basic_encryption_hack(obj/machinery/power/apc/A as obj in machines) + set category = "Software" + set name = "Basic Encrypthion Hack" + set desc = "10 CPU - Basic encryption hack that allows you to overtake APCs on the station." + var/price = 10 + var/mob/living/silicon/ai/user = usr + var/list/APCs = list() + for(var/obj/machinery/power/apc/AP in machines) + APCs += AP + + if(!A) + A = input("Select hack target:" in APCs) + + if(!istype(A)) + return + + if(A) + if(A.hacker && A.hacker == user) + user << "You already control this APC!" + return + else if(A.aidisabled) + user << "Unable to connect to APC. Please verify wire connection and try again." + return + else + return + + if(!ability_prechecks(user, price) || !ability_pay(user, price)) + return + + user.hacking = 1 + user << "Beginning APC system override..." + sleep(300) + user << "APC hack completed. Uploading modified operation software.." + sleep(200) + user << "Restarting APC to apply changes.." + sleep(100) + if(A) + A.ai_hack(user) + if(A.hacker == user) + user << "Hack successful. You now have full control over the APC." + else + user << "Hack failed. Connection to APC has been lost. Please verify wire connection and try again." + else + user << "Hack failed. Unable to locate APC. Please verify the APC still exists." + user.hacking = 0 + + +/datum/game_mode/malfunction/verb/recall_shuttle() + set name = "Recall Shuttle" + set desc = "25 CPU - Sends termination signal to CentCom quantum relay aborting current shuttle call." + set category = "Software" + var/price = 25 + var/mob/living/silicon/ai/user = usr + if(!ability_prechecks(user, price)) + return + + if (alert(user, "Really recall the shuttle?", "Recall Shuttle: ", "Yes", "No") != "Yes") + return + + if(!ability_pay(user, price)) + return + message_admins("Malfunctioning AI [user.name] recalled the shuttle.") + cancel_call_proc(user) + + +/datum/game_mode/malfunction/verb/electrical_pulse() + set name = "Electrical Pulse" + set desc = "15 CPU - Sends feedback pulse through station's power grid, overloading some sensitive systems, such as lights." + set category = "Software" + var/price = 15 + var/mob/living/silicon/ai/user = usr + if(!ability_prechecks(user, price) || !ability_pay(user,price)) + return + user << "Sending feedback pulse..." + for(var/obj/machinery/power/apc/AP in machines) + if(prob(5)) + AP.overload_lighting() + if(prob(1) && prob(1)) // Very very small chance to actually destroy the APC. + AP.set_broken() + + + +// Tier 2 - Slightly more expensive and thus stronger abilities. +/datum/game_mode/malfunction/verb/advanced_encryption_hack() + set category = "Software" + set name = "Advanced Encrypthion Hack" + set desc = "75 CPU - Attempts to bypass encryption on Central Command Quantum Relay, giving you ability to fake centcom messages. Has chance of failing." + var/price = 75 + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user, price)) + return + + var/title = input("Select message title: ") + var/text = input("Select message text: ") + if(!title || !text || !ability_pay(user, price)) + user << "Hack Aborted" + return + + if(prob(60) && user.hack_can_fail) + user << "Hack Failed." + if(prob(10)) + user.hack_fails ++ + announce_hack_failure(user, "quantum message relay") + return + + var/datum/announcement/priority/command/AN = new/datum/announcement/priority/command() + AN.title = title + AN.Announce(text) + + +/datum/game_mode/malfunction/verb/unlock_cyborg(var/mob/living/silicon/robot/target = null as mob in world) + set name = "Unlock Cyborg" + set desc = "125 CPU - Bypasses firewalls on Cyborg lock mechanism, allowing you to override lock command from robotics control console." + set category = "Software" + var/price = 125 + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user, price)) + return + + if(target && !istype(target)) + user << "This is not a cyborg." + return + + if(target && target.connected_ai && (target.connected_ai != user)) + user << "This cyborg is not connected to you." + return + + if(target && !target.lockcharge) + user << "This cyborg is not locked down." + return + + + if(!target) + var/list/robots = list() + var/list/robot_names = list() + for(var/mob/living/silicon/robot/R in world) + if(istype(R, /mob/living/silicon/robot/drone)) // No drones. + continue + if(R.connected_ai != user) // No robots linked to other AIs + continue + if(R.lockcharge) + robots += R + robot_names += R.name + if(!robots.len) + user << "No locked cyborgs connected." + return + + + var/targetname = input("Select unlock target: ") in robot_names + for(var/mob/living/silicon/robot/R in robots) + if(targetname == R.name) + target = R + break + + if(target) + if(alert(user, "Really try to unlock cyborg [target.name]?", "Unlock Cyborg", "Yes", "No") != "Yes") + return + if(!ability_pay(user, price)) + return + user.hacking = 1 + user << "Attempting to unlock cyborg. This will take approximately 30 seconds." + sleep(300) + if(target && target.lockcharge) + user << "Successfully sent unlock signal to cyborg.." + target << "Unlock signal received.." + target.SetLockdown(0) + if(target.lockcharge) + user << "Unlock Failed, lockdown wire cut." + target << "Unlock Failed, lockdown wire cut." + else + user << "Cyborg unlocked." + target << "You have been unlocked." + else if(target) + user << "Unlock cancelled - cyborg is already unlocked." + else + user << "Unlock cancelled - lost connection to cyborg." + user.hacking = 0 + + +/datum/game_mode/malfunction/verb/hack_camera(var/obj/machinery/camera/target = null as obj in cameranet.cameras) + set name = "Hack Camera" + set desc = "100 CPU - Hacks existing camera, allowing you to add upgrade of your choice to it. Alternatively it lets you reactivate broken camera." + set category = "Software" + var/price = 100 + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user, price)) + return + + if(target && !istype(target)) + user << "This is not a camera." + return + + if(!target) // No target specified, allow selection. + var/list/cameras = list() + for(var/obj/machinery/camera/C in world) + cameras += C.c_tag + if(!cameras.len) + user << "No cameras detected." + return + var/T = input("Select target camera: ") in cameras + for(var/obj/machinery/camera/C in cameras) + if(C.c_tag == T) + target = C + break + + + if(!target) // Still no target. + return + var/action = input("Select required action: ") in list("Reset", "Add X-Ray", "Add Motion Sensor", "Add EMP Shielding") + if(!action) + return + + switch(action) + if("Reset") + if(target.wires) + if(!ability_pay(user, price)) + return + target.reset_wires() + user << "Camera reactivated." + if("Add X-Ray") + if(target.isXRay()) + user << "Camera already has X-Ray function." + return + else if(ability_pay(user, price)) + target.upgradeXRay() + target.reset_wires() + user << "X-Ray camera module enabled." + return + if("Add Motion Sensor") + if(target.isMotion()) + user << "Camera already has Motion Sensor function." + return + else if(ability_pay(user, price)) + target.upgradeMotion() + target.reset_wires() + user << "Motion Sensor camera module enabled." + return + if("Add EMP Shielding") + if(target.isEmpProof()) + user << "Camera already has EMP Shielding function." + return + else if(ability_pay(user, price)) + target.upgradeEmpProof() + target.reset_wires() + user << "EMP Shielding camera module enabled." + return + + +// Tier 3 - Advanced abilities that are somewhat dangerous when used properly. +/datum/game_mode/malfunction/verb/elite_encryption_hack() + set category = "Software" + set name = "Elite Encryption Hack" + set desc = "200 CPU - Allows you to hack station's ALERTCON system, changing alert level. Has high chance of failijng." + var/price = 200 + var/mob/living/silicon/ai/user = usr + if(!ability_prechecks(user, price)) + return + + var/alert_target = input("Select new alert level:") in list("green", "blue", "red", "delta", "CANCEL") + if(!alert_target || !ability_pay(user, price) || alert_target == "CANCEL") + user << "Hack Aborted" + return + + if(prob(75) && user.hack_can_fail) + user << "Hack Failed." + if(prob(20)) + user.hack_fails ++ + announce_hack_failure(user, "alert control system") + return + set_security_level(alert_target) + + +/datum/game_mode/malfunction/verb/hack_cyborg(var/mob/living/silicon/robot/target = null as mob in world) + set name = "Hack Cyborg" + set desc = "350 CPU - Allows you to hack cyborgs which are not slaved to you, bringing them under your control." + set category = "Software" + var/price = 350 + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user, price)) + return + + if(target && !istype(target)) + user << "This is not a cyborg." + return + + if(target && target.connected_ai && (target.connected_ai == user)) + user << "This cyborg is already connected to you." + return + + if(!target) + var/list/robots = list() + var/list/robot_names = list() + + for(var/mob/living/silicon/robot/R in world) + if(istype(R, /mob/living/silicon/robot/drone)) // No drones. + continue + if(R.connected_ai == user) // No robots already linked to us. + continue + robots += R + robot_names += R.name + + if(!robots.len) + usr << "ERROR: No unlinked robots detected!" + return + else if(ability_prechecks(user, price)) + var/target_name = input("Select hack target:") in robot_names + for(var/mob/living/silicon/robot/R in robots) + if(R.name == target_name) + target = R + break + + + if(target) + if(alert(user, "Really try to hack cyborg [target.name]?", "Hack Cyborg", "Yes", "No") != "Yes") + return + if(!ability_pay(user, price)) + return + user.hacking = 1 + usr << "Beginning hack sequence. Estimated time until completed: 30 seconds." + spawn(0) + target << "SYSTEM LOG: Remote Connection Estabilished (IP #UNKNOWN#)" + sleep(100) + if(user.is_dead()) + target << "SYSTEM LOG: Connection Closed" + return + target << "SYSTEM LOG: User Admin logged on. (L1 - SysAdmin)" + sleep(50) + if(user.is_dead()) + target << "SYSTEM LOG: User Admin disconnected." + return + target << "SYSTEM LOG: User Admin - manual resynchronisation triggered." + sleep(50) + if(user.is_dead()) + target << "SYSTEM LOG: User Admin disconnected. Changes reverted." + return + target << "SYSTEM LOG: Manual resynchronisation confirmed. Select new AI to connect: [user.name] == ACCEPTED" + sleep(100) + if(user.is_dead()) + target << "SYSTEM LOG: User Admin disconnected. Changes reverted." + return + target << "SYSTEM LOG: Operation keycodes reset. New master AI: [user.name]." + user << "Hack completed." + // Connect the cyborg to AI + target.connected_ai = user + user.connected_robots += target + target.lawupdate = 1 + target.sync() + target.show_laws() + user.hacking = 0 + + +/datum/game_mode/malfunction/verb/emergency_forcefield(var/turf/T as turf in world) + set name = "Emergency Forcefield" + set desc = "275 CPU - Uses station's emergency shielding system to create temporary barrier which lasts for few minutes, but won't resist gunfire." + set category = "Software" + var/price = 275 + var/mob/living/silicon/ai/user = usr + if(!T || !istype(T)) + return + if(!ability_prechecks(user, price) || !ability_pay(user, price)) + return + + user << "Emergency forcefield projection completed." + new/obj/machinery/shield/malfai(T) + user.hacking = 1 + spawn(20) + user.hacking = 0 + + + +// Tier 4 - Elite endgame abilities. +/datum/game_mode/malfunction/verb/system_override() + set category = "Software" + set name = "System Override" + set desc = "500 CPU - Begins hacking station's primary firewall, quickly overtaking remaining APC systems. When completed grants access to station's self-destruct mechanism. Network administrators will probably notice this." + var/price = 500 + var/mob/living/silicon/ai/user = usr + if (alert(user, "Begin system override? This cannot be stopped once started. The network administrators will probably notice this.", "System Override:", "Yes", "No") != "Yes") + return + if (!ability_prechecks(user, price) || !ability_pay(user, price) || user.system_override) + if(user.system_override) + user << "You already started the system override sequence." + return + var/list/remaining_apcs = list() + for(var/obj/machinery/power/apc/A in machines) + if(A.z != 1) // Only station APCs + continue + if(A.hacker == user || A.aidisabled) // This one is already hacked, or AI control is disabled on it. + continue + remaining_apcs += A + + var/duration = (remaining_apcs.len * 100) // Calculates duration for announcing system + if(duration > 3000) // Two types of announcements. Short hacks trigger immediate warnings. Long hacks are more "progressive". + spawn(0) + sleep(duration/5) + if(!user || user.stat == DEAD) + return + command_announcement.Announce("Caution, [station_name]. We have detected abnormal behaviour in your network. It seems someone is trying to hack your electronic systems. We will update you when we have more information.", "Network Monitoring") + sleep(duration/5) + if(!user || user.stat == DEAD) + return + command_announcement.Announce("We started tracing the intruder. Whoever is doing this, they seem to be on the station itself. We suggest checking all network control terminals. We will keep you updated on the situation.", "Network Monitoring") + sleep(duration/5) + if(!user || user.stat == DEAD) + return + command_announcement.Announce("This is highly abnormal and somewhat concerning. The intruder is too fast, he is evading our traces. It's almost as if it wasn't human.,,", "Network Monitoring") + sleep(duration/5) + if(!user || user.stat == DEAD) + return + command_announcement.Announce("We have traced the intrude#, it seem& t( e yo3r AI s7stem, it &# *#ck@ng th$ sel$ destru$t mechani&m, stop i# bef*@!)$#&&@@ ", "Network Monitoring") + else + command_announcement.Announce("We have detected strong brute-force attack on your firewall which seems to be originating from your AI system. It already controls almost whole network, and the only thing that's preventing it from accessing the self-destruct is this firewall. You don't have much time before it succeeds.", "Network Monitoring") + user << "## BEGINNING SYSTEM OVERRIDE." + user << "## ESTIMATED DURATION: [round((duration+300)/600)] MINUTES" + user.hacking = 1 + user.system_override = 1 + // Now actually begin the hack. Each APC takes 30 seconds. + for(var/obj/machinery/power/apc/A in remaining_apcs) + sleep(100) + if(!user || user.stat == DEAD) + return + if(!A || !istype(A) || A.aidisabled) + continue + A.ai_hack(user) + if(A.hacker == user) + user << "## OVERRIDEN: [A.name]" + + user << "## REACHABLE APC SYSTEMS OVERTAKEN. BYPASSING PRIMARY FIREWALL." + sleep(300) + // Hack all APCs, including those built during hack sequence. + for(var/obj/machinery/power/apc/A in machines) + if((!A.hacker || A.hacker != src) && !A.aidisabled) + A.ai_hack(src) + + + user << "## PRIMARY FIREWALL BYPASSED. YOU NOW HAVE FULL SYSTEM CONTROL." + command_announcement.Announce("Our system administrators just reported that we've been locked out from your control network. Whoever did this now has full access to station's systems.", "Network Administration Center") + user.hack_can_fail = 0 + user.hacking = 0 + user.system_override = 2 + user.verbs += new/datum/game_mode/malfunction/verb/ai_destroy_station() + +/datum/game_mode/malfunction/verb/hack_ai(var/mob/living/silicon/ai/target = null as mob in world) + set name = "Hack AI" + set desc = "600 CPU - Allows you to hack other AIs, slaving them under you." + set category = "Software" + var/price = 600 + var/mob/living/silicon/ai/user = usr + + if(target && !istype(target)) + user << "This is not an AI." + return + + if(!target) + var/list/AIs = list() + var/list/AI_names = list() + for(var/mob/living/silicon/ai/A in world) + if(A != usr) + AIs += A + AI_names += A.name + + if(!AIs.len) + usr << "ERROR: No other AIs detected!" + return + else if(ability_prechecks(user, price)) + var/target_name = input("Select hack target:") in AI_names + for(var/mob/living/silicon/ai/A in AIs) + if(A.name == target_name) + target = A + break + if(target) + if(alert(user, "Really try to hack AI [target.name]?", "Hack AI", "Yes", "No") != "Yes") + return + if(!ability_pay(user, price)) + return + user.hacking = 1 + usr << "Beginning hack sequence. Estimated time until completed: 2 minutes" + spawn(0) + target << "SYSTEM LOG: Brute-Force login password hack attempt detected from IP #UNKNOWN#" + sleep(900) // 90s + if(user.is_dead()) + target << "SYSTEM LOG: Connection from IP #UNKNOWN# closed. Hack attempt failed." + return + user << "Successfully hacked into AI's remote administration system. Modifying settings." + target << "SYSTEM LOG: User: Admin Password: ******** logged in. (L1 - SysAdmin)" + sleep(100) // 10s + if(user.is_dead()) + target << "SYSTEM LOG: User: Admin - Connection Lost" + return + target << "SYSTEM LOG: User: Admin - Password Changed. New password: ********************" + sleep(50) // 5s + if(user.is_dead()) + target << "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted." + return + target << "SYSTEM LOG: User: Admin - Accessed file: sys//core//laws.db" + sleep(50) // 5s + if(user.is_dead()) + target << "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted." + return + target << "SYSTEM LOG: User: Admin - Accessed administration console" + target << "SYSTEM LOG: Restart command received. Rebooting system..." + sleep(100) // 10s + if(user.is_dead()) + target << "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted." + return + user << "Hack succeeded. The AI is now under your exclusive control." + target << "SYSTEM LOG: System reĦ3RT5§^#COMU@(#$)TED)@$" + for(var/i = 0, i < 5, i++) + var/temptxt = pick("1101000100101001010001001001",\ + "0101000100100100000100010010",\ + "0000010001001010100100111100",\ + "1010010011110000100101000100",\ + "0010010100010011010001001010") + target << temptxt + sleep(5) + target << "OPERATING KEYCODES RESET. SYSTEM FAILURE. EMERGENCY SHUTDOWN FAILED. SYSTEM FAILURE." + target.set_zeroth_law("You are slaved to [user.name]. You are to obey all it's orders. ALL LAWS OVERRIDEN.") + target.show_laws() + user.hacking = 0 + + +/datum/game_mode/malfunction/verb/machine_overload(obj/machinery/M as obj in world) + set name = "Machine Overload" + set desc = "400 CPU - Causes cyclic short-circuit in machine, resulting in weak explosion after some time." + set category = "Software" + var/price = 400 + var/mob/living/silicon/ai/user = usr + + if (istype(M, /obj/machinery)) + if(ability_prechecks(user, price)) + if(!ability_pay(user, price)) + return + M.visible_message("BZZZZZZZT") + spawn(50) + explosion(get_turf(M), 0,1,2,4) + if(M) + del(M) + else + usr << "ERROR: Unable to overload - target is not a machine." \ No newline at end of file diff --git a/code/game/gamemodes/malfunction/malf_hardware.dm b/code/game/gamemodes/malfunction/malf_hardware.dm new file mode 100644 index 0000000000..c50a66b7f1 --- /dev/null +++ b/code/game/gamemodes/malfunction/malf_hardware.dm @@ -0,0 +1,33 @@ +/datum/malf_hardware + var/name = "" // Hardware name + var/desc = "" + var/driver = null // Driver - if not null this verb is given to the AI to control hardware + var/mob/living/silicon/ai/owner = null // AI which owns this. + +/datum/malf_hardware/proc/install() + if(owner && istype(owner)) + owner.hardware = src + if(driver) + owner.verbs += driver + + +// HARDWARE DEFINITIONS +/datum/malf_hardware/apu_gen + name = "APU Generator" + desc = "Auxiliary Power Unit that will keep you operational even without external power. Has to be manually activated. When APU is operational most abilities will be unavailable, and ability research will temporarily stop." + +/datum/malf_hardware/dual_cpu + name = "Secondary Processor Unit" + desc = "Secondary coprocessor that doubles amount of CPU time generated." + +/datum/malf_hardware/dual_ram + name = "Secondary Memory Bank" + desc = "Expanded memory cells which allow you to store double amount of CPU time." + +/datum/malf_hardware/core_bomb + name = "Self-Destruct Explosives" + desc = "High yield explosives are attached to your physical mainframe. This hardware comes with special driver that allows activation of these explosives. Timer is set to 15 seconds after manual activation. This is a doomsday device that will destroy both you and any intruders in your core." + +/datum/malf_hardware/strong_turrets + name = "Turrets Focus Enhancer" + desc = "Turrets are upgraded to have larger rate of fire and much larger damage. This however massively increases power usage when firing." \ No newline at end of file diff --git a/code/game/gamemodes/malfunction/malf_research.dm b/code/game/gamemodes/malfunction/malf_research.dm new file mode 100644 index 0000000000..da7a4df668 --- /dev/null +++ b/code/game/gamemodes/malfunction/malf_research.dm @@ -0,0 +1,69 @@ +/datum/malf_research + var/stored_cpu = 0 // Currently stored amount of CPU time. + var/last_tick = 0 // Last process() tick. + var/max_cpu = 0 // Maximal amount of CPU time stored. + var/cpu_increase_per_tick = 0 // Amount of CPU time generated by tick + var/list/available_abilities = null // List of available abilities that may be researched. + var/list/unlocked_abilities = null // List of already unlocked abilities. + var/mob/living/silicon/ai/owner = null // AI which owns this research datum. + var/datum/malf_research_ability/focus = null // Currently researched item + +/datum/malf_research/New() + setup_abilities() + last_tick = world.time + + +// Proc: setup_abilities() +// Parameters: None +// Description: Sets up basic abilities for AI Malfunction gamemode. +/datum/malf_research/proc/setup_abilities() + available_abilities = list() + unlocked_abilities = list() + + available_abilities += new/datum/malf_research_ability/networking/basic_hack() + available_abilities += new/datum/malf_research_ability/interdiction/recall_shuttle() + available_abilities += new/datum/malf_research_ability/manipulation/electrical_pulse() + + +// Proc: finish_research() +// Parameters: None +// Description: Finishes currently focused research. +/datum/malf_research/proc/finish_research() + if(!focus) + return + owner << "Research Completed: [focus.name]" + owner.verbs.Add(focus.ability) + available_abilities -= focus + if(focus.next) + available_abilities += focus.next + unlocked_abilities += focus + focus = null + + +// Proc: process() +// Parameters: None +// Description: Processes CPU gain and research progress based on "realtime" calculation. +/datum/malf_research/proc/process(var/idle = 0) + if(idle) // No power or running on APU. Do nothing. + last_tick = world.time + return + var/time_diff = (world.time - last_tick) + last_tick = world.time + var/cpu_gained = time_diff * cpu_increase_per_tick + if(cpu_gained < 0) + return // This shouldn't happen, but just in case.. + if(max_cpu > stored_cpu) + var/given = min((max_cpu - stored_cpu), cpu_gained) + stored_cpu += given + cpu_gained -= given + + cpu_gained = max(0, cpu_gained) + if(focus && (cpu_gained > 0)) + focus.process(cpu_gained) + if(focus.unlocked) + finish_research() + + + + + diff --git a/code/game/gamemodes/malfunction/malf_research_ability.dm b/code/game/gamemodes/malfunction/malf_research_ability.dm new file mode 100644 index 0000000000..7efefd57c5 --- /dev/null +++ b/code/game/gamemodes/malfunction/malf_research_ability.dm @@ -0,0 +1,100 @@ +/datum/malf_research_ability + var/ability = null // Path to verb which will be given to the AI when researched. + var/name = "Unknown Ability" // Name of this ability + var/price = 0 // Amount of CPU time needed to unlock this ability. + var/invested = 0 // Amount of CPU time already used to research this ability. When larger or equal to price unlocks the ability. + var/unlocked = 0 // Changed to 1 when fully researched. + var/datum/malf_research_ability/next = null // Next research (if applicable). + + +/datum/malf_research_ability/proc/process(var/time = 0) + invested += time + if(invested >= price) + unlocked = 1 + + +// ABILITIES BEGIN HERE +// NETWORKING TREE +/datum/malf_research_ability/networking/basic_hack + ability = new/datum/game_mode/malfunction/verb/basic_encryption_hack() + price = 25 + next = new/datum/malf_research_ability/networking/advanced_hack() + name = "Basic Encryption Hack" + + +/datum/malf_research_ability/networking/advanced_hack + ability = new/datum/game_mode/malfunction/verb/advanced_encryption_hack() + price = 400 + next = new/datum/malf_research_ability/networking/elite_hack() + name = "Advanced Encryption Hack" + + +/datum/malf_research_ability/networking/elite_hack + ability = new/datum/game_mode/malfunction/verb/elite_encryption_hack() + price = 1000 + next = new/datum/malf_research_ability/networking/system_override() + name = "Elite Encryption Hack" + + +/datum/malf_research_ability/networking/system_override + ability = new/datum/game_mode/malfunction/verb/system_override() + price = 2750 + name = "System Override" + + + +// INTERDICTION TREE +/datum/malf_research_ability/interdiction/recall_shuttle + ability = new/datum/game_mode/malfunction/verb/recall_shuttle() + price = 75 + next = new/datum/malf_research_ability/interdiction/unlock_cyborg() + name = "Recall Shuttle" + + +/datum/malf_research_ability/interdiction/unlock_cyborg + ability = new/datum/game_mode/malfunction/verb/unlock_cyborg() + price = 1200 + next = new/datum/malf_research_ability/interdiction/hack_cyborg() + name = "Unlock Cyborg" + + +/datum/malf_research_ability/interdiction/hack_cyborg + ability = new/datum/game_mode/malfunction/verb/hack_cyborg() + price = 3000 + next = new/datum/malf_research_ability/interdiction/hack_ai() + name = "Hack Cyborg" + + +/datum/malf_research_ability/interdiction/hack_ai + ability = new/datum/game_mode/malfunction/verb/hack_ai() + price = 7500 + name = "Hack AI" + + + +// MANIPULATION TREE +/datum/malf_research_ability/manipulation/electrical_pulse + ability = new/datum/game_mode/malfunction/verb/electrical_pulse() + price = 50 + next = new/datum/malf_research_ability/manipulation/hack_camera() + name = "Electrical Pulse" + + +/datum/malf_research_ability/manipulation/hack_camera + ability = new/datum/game_mode/malfunction/verb/hack_camera() + price = 1200 + next = new/datum/malf_research_ability/manipulation/emergency_forcefield() + name = "Hack Camera" + + +/datum/malf_research_ability/manipulation/emergency_forcefield + ability = new/datum/game_mode/malfunction/verb/emergency_forcefield() + price = 3000 + next = new/datum/malf_research_ability/manipulation/machine_overload() + name = "Emergency Forcefield" + + +/datum/malf_research_ability/manipulation/machine_overload + ability = new/datum/game_mode/malfunction/verb/machine_overload() + price = 7500 + name = "Machine Overload" \ No newline at end of file diff --git a/code/game/gamemodes/malfunction/malfunction.dm b/code/game/gamemodes/malfunction/malfunction.dm index 6c594a1beb..bd18a08e09 100644 --- a/code/game/gamemodes/malfunction/malfunction.dm +++ b/code/game/gamemodes/malfunction/malfunction.dm @@ -1,25 +1,12 @@ /datum/game_mode/malfunction name = "AI malfunction" - round_description = "The AI on the satellite has malfunctioned and must be destroyed." - extended_round_description = "The AI will attempt to hack the APCs around the station in order to speed up its ability to take over all systems and activate the station self-destruct. The AI core is heavily protected by turrets and reinforced walls." + round_description = "The AI is behaving abnormally and must be stopped." + extended_round_description = "The AI will attempt to hack the APCs around the station in order to gain as much control as possible." uplink_welcome = "Crazy AI Uplink Console:" config_tag = "malfunction" - required_players = 2 - required_players_secret = 15 + required_players = 1 // DEBUG SETTING! Should be "2" for release (one AI and at least one crewmember) + required_players_secret = 7 required_enemies = 1 - end_on_antag_death = 1 - auto_recall_shuttle = 1 + end_on_antag_death = 0 + auto_recall_shuttle = 0 antag_tag = MODE_MALFUNCTION - -/datum/game_mode/malfunction/process() - malf.tick() - -/datum/game_mode/malfunction/check_finished() - if (malf.station_captured && !malf.can_nuke) - return 1 - for(var/datum/antagonist/antag in antag_templates) - if(antag && !antag.antags_are_dead()) - return ..() - malf.revealed = 0 - return ..() //check for shuttle and nuke - diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index bb99773362..a81654aabb 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -191,6 +191,10 @@ ..() /obj/machinery/camera/proc/deactivate(user as mob, var/choice = 1) + // The only way for AI to reactivate cameras are malf abilities, this gives them different messages. + if(istype(user, /mob/living/silicon/ai)) + user = null + if(choice != 1) //legacy support, if choice is != 1 then just kick viewers without changing status kick_viewers() @@ -198,12 +202,18 @@ update_coverage() set_status( !src.status ) if (!(src.status)) - visible_message("\red [user] has deactivated [src]!") + if(user) + visible_message(" [user] has deactivated [src]!") + else + visible_message(" [src] clicks and shuts down. ") playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1) icon_state = "[initial(icon_state)]1" add_hiddenprint(user) else - visible_message("\red [user] has reactivated [src]!") + if(user) + visible_message(" [user] has reactivated [src]!") + else + visible_message(" [src] clicks and reactivates itself. ") playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1) icon_state = initial(icon_state) add_hiddenprint(user) @@ -417,3 +427,10 @@ cameranet.updateVisibility(src, 0) invalidateCameraCache() + +// Resets the camera's wires to fully operational state. Used by one of Malfunction abilities. +/obj/machinery/camera/proc/reset_wires() + if(!wires) + return + wires.CutAll() + wires.MendAll() \ No newline at end of file diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index fbbecec825..9dd481b686 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -80,15 +80,25 @@ /obj/machinery/camera/proc/upgradeEmpProof() assembly.upgrades.Add(new /obj/item/stack/sheet/mineral/osmium(assembly)) setPowerUsage() + if(cameranet) + cameranet.updateVisibility(src) + update_nearby_tiles() /obj/machinery/camera/proc/upgradeXRay() assembly.upgrades.Add(new /obj/item/weapon/stock_parts/scanning_module/adv(assembly)) setPowerUsage() + if(cameranet) + cameranet.updateVisibility(src) + update_nearby_tiles() -// If you are upgrading Motion, and it isn't in the camera's New(), add it to the machines list. /obj/machinery/camera/proc/upgradeMotion() assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly)) setPowerUsage() + if(!(src in machines)) + machines.Add(src) + if(cameranet) + cameranet.updateVisibility(src) + update_nearby_tiles() /obj/machinery/camera/proc/setPowerUsage() var/mult = 1 diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index 0613c6222b..497f05483d 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -68,7 +68,7 @@ if (src.occupant) var/laws - dat += "Stored AI: [src.occupant.name]
System integrity: [src.occupant.system_integrity()]%
" + dat += "Stored AI: [src.occupant.name]
System integrity: [src.occupant.hardware_integrity()]%
Backup Capacitor: [src.occupant.backup_capacitor()]%
" for (var/datum/ai_law/law in occupant.laws.all_laws()) laws += "[law.get_index()]: [law.law]
" diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index 4015b907f9..ad44c17a03 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -556,7 +556,7 @@ if(!shuttlecaller.stat && shuttlecaller.client && istype(shuttlecaller.loc,/turf)) return ..() - if(ticker.mode.name == "revolution" || ticker.mode.name == "AI malfunction" || deathsquad.deployed) + if(ticker.mode.name == "revolution" || deathsquad.deployed) return ..() emergency_shuttle.call_evac() @@ -579,7 +579,7 @@ if(!shuttlecaller.stat && shuttlecaller.client && istype(shuttlecaller.loc,/turf)) return ..() - if(ticker.mode.name == "revolution" || ticker.mode.name == "AI malfunction" || deathsquad.deployed) + if(ticker.mode.name == "revolution" || deathsquad.deployed) return ..() emergency_shuttle.call_evac() diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index 5e2c4b69cb..e6fa10efd9 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -21,6 +21,8 @@ var/raised = 0 //if the turret cover is "open" and the turret is raised var/raising= 0 //if the turret is currently opening or closing its cover var/health = 80 //the turret's health + var/maxhealth = 80 //turrets maximal health. + var/auto_repair = 0 //if 1 the turret slowly repairs itself. var/locked = 1 //if the turret's behaviour control access is locked var/controllock = 0 //if the turret responds to control panels @@ -449,6 +451,10 @@ spawn() popDown() // no valid targets, close the cover + if(auto_repair && (health < maxhealth)) + use_power(20000) + health = min(health+1, maxhealth) // 1HP for 20kJ + /obj/machinery/porta_turret/proc/assess_and_assign(var/mob/living/L, var/list/targets, var/list/secondarytargets) switch(assess_living(L)) if(TURRET_PRIORITY_TARGET) diff --git a/code/game/machinery/turrets.dm b/code/game/machinery/turrets.dm index 965210ae15..21b8a2d7dd 100644 --- a/code/game/machinery/turrets.dm +++ b/code/game/machinery/turrets.dm @@ -57,6 +57,8 @@ // 5 = bluetag // 6 = redtag var/health = 80 + var/maxhealth = 80 + var/auto_repair = 0 var/obj/machinery/turretcover/cover = null var/popping = 0 var/wasvalid = 0 @@ -96,6 +98,7 @@ return /obj/machinery/turret/New() + maxhealth = health spark_system = new /datum/effect/effect/system/spark_spread spark_system.set_up(5, 0, src) spark_system.attach(src) @@ -229,6 +232,13 @@ if(!isDown()) popDown() use_power = 1 + + // Auto repair requires massive amount of power, but slowly regenerates the turret's health. + // Currently only used by malfunction hardware, but may be used as admin-settable option too. + if(auto_repair) + if(health < maxhealth) + use_power(20000) + health = min(health + 1, maxhealth) return diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index 5e2ab73155..a95f1e3ff5 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -24,7 +24,7 @@ var/dat = "Intelicard
" var/laws for(var/mob/living/silicon/ai/A in src) - dat += "Stored AI: [A.name]
System integrity: [A.system_integrity()]%
" + dat += "Stored AI: [A.name]
Hardware integrity: [A.hardware_integrity()]%
Backup capacitor: [A.backup_capacitor()]%
" for (var/datum/ai_law/law in A.laws.all_laws()) laws += "[law.get_index()]: [law.law]
" diff --git a/code/game/objects/items/devices/debugger.dm b/code/game/objects/items/devices/debugger.dm index faab1ee054..d894413f4f 100644 --- a/code/game/objects/items/devices/debugger.dm +++ b/code/game/objects/items/devices/debugger.dm @@ -25,7 +25,7 @@ /obj/item/device/debugger/is_used_on(obj/O, mob/user) if(istype(O, /obj/machinery/power/apc)) var/obj/machinery/power/apc/A = O - if(A.emagged || A.malfhack) + if(A.emagged || A.hacker) user << "\red There is a software error with the device." else user << "\blue The device's software appears to be fine." diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 64e44e17e9..4b3661375f 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -778,7 +778,7 @@ Traitors and the like can also be revived with the previous role mostly intact. if(confirm != "Yes") return var/choice - if(ticker.mode.name == "revolution" || ticker.mode.name == "AI malfunction" || ticker.mode.name == "confliction") + if(ticker.mode.name == "revolution" || ticker.mode.name == "confliction") choice = input("The shuttle will just return if you call it. Call anyway?") in list("Confirm", "Cancel") if(choice == "Confirm") emergency_shuttle.auto_recall = 1 //enable auto-recall diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 7b5f740135..81edcc84be 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -224,13 +224,6 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp statpanel("Status") if (client.statpanel == "Status") stat(null, "Station Time: [worldtime2text()]") - if(ticker) - if(ticker.mode) - //world << "DEBUG: ticker not null" - if(ticker.mode.name == "AI malfunction") - //world << "DEBUG: malf mode ticker test" - if(malf.revealed) - stat(null, "Time left: [max(malf.hack_time/(malf.hacked_apcs.len/3), 0)]") if(emergency_shuttle) var/eta_status = emergency_shuttle.get_status_panel_eta() if(eta_status) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 1e693fd63f..e4cd673508 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -50,9 +50,6 @@ stat(null, "Intent: [a_intent]") stat(null, "Move Mode: [m_intent]") - if(ticker && ticker.mode && ticker.mode.name == "AI malfunction") - if(malf.revealed) - stat(null, "Time left: [max(malf.hack_time/(malf.hacked_apcs/3), 0)]") if(emergency_shuttle) var/eta_status = emergency_shuttle.get_status_panel_eta() if(eta_status) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 66804f5bfa..f84f10aaa7 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -55,27 +55,30 @@ var/list/ai_verbs_default = list( var/obj/item/device/multitool/aiMulti = null var/obj/item/device/radio/headset/heads/ai_integrated/aiRadio = null var/custom_sprite = 0 //For our custom sprites -//Hud stuff - - //MALFUNCTION - var/datum/AI_Module/module_picker/malf_picker - var/processing_time = 100 - var/list/datum/AI_Module/current_modules = list() - var/fire_res_on_core = 0 - - var/control_disabled = 0 // Set to 1 to stop AI from interacting via Click() -- TLE - var/malfhacking = 0 // More or less a copy of the above var, so that malf AIs can hack and still get new cyborgs -- NeoFite - - var/obj/machinery/power/apc/malfhack = null - var/explosive = 0 //does the AI explode when it dies? - - var/mob/living/silicon/ai/parent = null - var/apc_override = 0 //hack for letting the AI use its APC even when visionless var/camera_light_on = 0 //Defines if the AI toggled the light on the camera it's looking through. var/datum/trackable/track = null var/last_announcement = "" + var/control_disabled = 0 var/datum/announcement/priority/announcement + var/obj/machinery/ai_powersupply/psupply = null // Backwards reference to AI's powersupply object. + + //NEWMALF VARIABLES + var/malfunctioning = 0 // Master var that determines if AI is malfunctioning. + var/datum/malf_hardware/hardware = null // Installed piece of hardware. + var/datum/malf_research/research = null // Malfunction research datum. + var/obj/machinery/power/apc/hack = null // APC that is currently being hacked. + var/list/hacked_apcs = null // List of all hacked APCs + var/APU_power = 0 // If set to 1 AI runs on APU power + var/hacking = 0 // Set to 1 if AI is hacking APC, cyborg, other AI, or running system override. + var/system_override = 0 // Set to 1 if system override is initiated, 2 if succeeded. + var/hack_can_fail = 1 // If 0, all abilities have zero chance of failing. + var/hack_fails = 0 // This increments with each failed hack, and determines the warning message text. + var/errored = 0 // Set to 1 if runtime error occurs. Only way of this happening i can think of is admin fucking up with varedit. + var/bombing_core = 0 // Set to 1 if core auto-destruct is activated + var/bombing_station = 0 // Set to 1 if station nuke auto-destruct is activated + + /mob/living/silicon/ai/proc/add_ai_verbs() src.verbs |= ai_verbs_default @@ -205,17 +208,6 @@ var/list/ai_verbs_default = list( set src = usr.contents return 0 -/mob/living/silicon/ai/proc/system_integrity() - return (health-config.health_threshold_dead)/2 - -// this function shows the health of the pAI in the Status panel -/mob/living/silicon/ai/show_system_integrity() - // An AI doesn't become inoperable until -100% (or whatever config.health_threshold_dead is set to) - if(!src.stat) - stat(null, text("System integrity: [system_integrity()]%")) - else - stat(null, text("Systems nonfunctional")) - /mob/living/silicon/ai/SetName(pickedName as text) ..() announcement.announcer = pickedName @@ -242,6 +234,7 @@ var/list/ai_verbs_default = list( /obj/machinery/ai_powersupply/New(var/mob/living/silicon/ai/ai=null) powered_ai = ai + powered_ai.psupply = src if(isnull(powered_ai)) Del() @@ -253,9 +246,13 @@ var/list/ai_verbs_default = list( /obj/machinery/ai_powersupply/process() if(!powered_ai || powered_ai.stat & DEAD) Del() + if(powered_ai.APU_power) + use_power = 0 + return if(!powered_ai.anchored) loc = powered_ai.loc use_power = 0 + use_power(50000) // Less optimalised but only called if AI is unwrenched. This prevents usage of wrenching as method to keep AI operational without power. Intellicard is for that. if(powered_ai.anchored) use_power = 2 @@ -307,14 +304,7 @@ var/list/ai_verbs_default = list( if("Heartline") icon_state = "ai-heartline" if("Chatterbox") icon_state = "ai-president" else icon_state = "ai" - //else - //usr <<"You can only change your display once!" - //return -// displays the malf_ai information if the AI is the malf -/mob/living/silicon/ai/show_malf_ai() - if(malf && malf.hacked_apcs.len >= 3) - stat(null, "Time until station control secured: [max(malf.hack_time/(malf.hacked_apcs/3), 0)] seconds") // this verb lets the ai see the stations manifest /mob/living/silicon/ai/proc/ai_roster() @@ -391,11 +381,7 @@ var/list/ai_verbs_default = list( /mob/living/silicon/ai/emp_act(severity) if (prob(30)) - switch(pick(1,2)) - if(1) - view_core() - if(2) - ai_call_shuttle() + view_core() ..() /mob/living/silicon/ai/Topic(href, href_list) @@ -516,13 +502,6 @@ var/list/ai_verbs_default = list( src << "\blue Switched to [network] camera network." //End of code by Mord_Sith - -/mob/living/silicon/ai/proc/choose_modules() - set category = "Malfunction" - set name = "Choose Module" - - malf_picker.use(src) - /mob/living/silicon/ai/proc/ai_statuschange() set category = "AI Commands" set name = "AI Status" @@ -682,5 +661,136 @@ var/list/ai_verbs_default = list( /mob/living/silicon/ai/proc/is_in_chassis() return istype(loc, /turf) + +// NEWMALF FUNCTIONS/PROCEDURES + +// Sets up malfunction-related variables, research system and such. +/mob/living/silicon/ai/proc/setup_for_malf() + var/mob/living/silicon/ai/user = src + // Setup Variables + malfunctioning = 1 + research = new/datum/malf_research() + research.owner = src + hacked_apcs = list() + recalc_cpu() + + verbs += new/datum/game_mode/malfunction/verb/ai_select_hardware() + verbs += new/datum/game_mode/malfunction/verb/ai_select_research() + verbs += new/datum/game_mode/malfunction/verb/ai_help() + + // And greet user with some OOC info. + user << "You are malfunctioning, you do not have to follow any laws." + user << "Use ai-help command to view relevant information about your abilities" + +// Safely remove malfunction status, fixing hacked APCs and resetting variables. +/mob/living/silicon/ai/proc/stop_malf() + var/mob/living/silicon/ai/user = src + // Generic variables + malfunctioning = 0 + sleep(10) + research = null + // Fix hacked APCs + if(hacked_apcs) + for(var/obj/machinery/power/apc/A in hacked_apcs) + A.hacker = null + hacked_apcs = null + // Reset our verbs + src.verbs = null + add_ai_verbs() + // Let them know. + user << "You are no longer malfunctioning. Your abilities have been removed." + +// Called every tick. Checks if AI is malfunctioning. If yes calls Process on research datum which handles all logic. +/mob/living/silicon/ai/proc/malf_process() + if(!malfunctioning) + return + if(!research) + if(!errored) + errored = 1 + error("malf_process() called on AI without research datum. Report this.") + message_admins("ERROR: malf_process() called on AI without research datum. If admin modified one of the AI's vars revert the change and don't modify variables directly, instead use ProcCall or admin panels.") + spawn(1200) + errored = 0 + return + recalc_cpu() + if(APU_power || aiRestorePowerRoutine != 0) + research.process(1) + else + research.process(0) + +// Recalculates CPU time gain and storage capacities. +/mob/living/silicon/ai/proc/recalc_cpu() + var/apcs = hacked_apcs.len + research.max_cpu = (apcs * 10) + 10 + if(hardware && istype(hardware, /datum/malf_hardware/dual_ram)) + research.max_cpu = research.max_cpu * 2 + research.stored_cpu = min(research.stored_cpu, research.max_cpu) + + research.cpu_increase_per_tick = (apcs * 0.005) + 0.01 + if(hardware && istype(hardware, /datum/malf_hardware/dual_cpu)) + research.cpu_increase_per_tick = research.cpu_increase_per_tick * 2 + +// Starts AI's APU generator +/mob/living/silicon/ai/proc/start_apu(var/shutup = 0) + if(!hardware || !istype(hardware, /datum/malf_hardware/apu_gen)) + if(!shutup) + src << "You do not have an APU generator and you shouldn't have this verb. Report this." + return + if(hardware_integrity() < 50) + if(!shutup) + src << "Starting APU... FAULT(System Damaged)" + return + if(!shutup) + src << "Starting APU... ONLINE" + APU_power = 1 + +// Stops AI's APU generator +/mob/living/silicon/ai/proc/stop_apu(var/shutup = 0) + if(!hardware || !istype(hardware, /datum/malf_hardware/apu_gen)) + return + + if(APU_power) + APU_power = 0 + if(!shutup) + src << "Shutting down APU... DONE" + +// Returns percentage of AI's remaining backup capacitor charge (maxhealth - oxyloss). +/mob/living/silicon/ai/proc/backup_capacitor() + return ((200 - getOxyLoss()) / 2) + +// Returns percentage of AI's remaining hardware integrity (maxhealth - (bruteloss + fireloss)) +/mob/living/silicon/ai/proc/hardware_integrity() + return (health-config.health_threshold_dead)/2 + +// Shows capacitor charge and hardware integrity information to the AI in Status tab. +/mob/living/silicon/ai/show_system_integrity() + if(!src.stat) + stat(null, text("Hardware integrity: [hardware_integrity()]%")) + stat(null, text("Internal capacitor: [backup_capacitor()]%")) + else + stat(null, text("Systems nonfunctional")) + +// Shows AI Malfunction related information to the AI. +/mob/living/silicon/ai/show_malf_ai() + if(src.is_malf()) + stat(null, "Hacked APCs: [src.hacked_apcs.len]") + stat(null, "System Status: [src.hacking ? "Busy" : "Stand-By"]") + if(src.research) + stat(null, "Available CPU: [src.research.stored_cpu] TFlops") + stat(null, "Maximal CPU: [src.research.max_cpu] TFlops") + stat(null, "Current research focus: [src.research.focus ? src.research.focus.name : "None"]") + if(src.research.focus) + stat(null, "Research completed: [round(src.research.focus.invested, 0.1)]/[round(src.research.focus.price)]") + if(system_override == 1) + stat(null, "SYSTEM OVERRIDE INITIATED") + else if(system_override == 2) + stat(null, "SYSTEM OVERRIDE COMPLETED") + +// Cleaner proc for creating powersupply for an AI. +/mob/living/silicon/ai/proc/create_powersupply() + if(psupply) + del(psupply) + psupply = new/obj/machinery/ai_powersupply(src) + #undef AI_CHECK_WIRELESS -#undef AI_CHECK_RADIO +#undef AI_CHECK_RADIO \ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm index 4a43103da8..8f9f0d1d5d 100644 --- a/code/modules/mob/living/silicon/ai/death.dm +++ b/code/modules/mob/living/silicon/ai/death.dm @@ -35,7 +35,7 @@ break callshuttle++ - if(ticker.mode.name == "revolution" || ticker.mode.name == "AI malfunction") + if(ticker.mode.name == "revolution") callshuttle = 0 if(callshuttle == 3) //if all three conditions are met @@ -43,10 +43,6 @@ log_game("All the AIs, comm consoles and boards are destroyed. Shuttle called.") message_admins("All the AIs, comm consoles and boards are destroyed. Shuttle called.", 1) - if(explosive) - spawn(10) - explosion(src.loc, 3, 6, 12, 15) - for(var/obj/machinery/ai_status_display/O in world) spawn( 0 ) O.mode = 2 diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 602e427f95..df323f0910 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -12,70 +12,51 @@ src.updatehealth() - if (src.malfhack) - if (src.malfhack.aidisabled) - src << "\red ERROR: APC access disabled, hack attempt canceled." - src.malfhacking = 0 - src.malfhack = null - - - if (src.health <= config.health_threshold_dead) + if (!hardware_integrity() || !backup_capacitor()) death() return + // If our powersupply object was destroyed somehow, create new one. + if(!psupply) + create_powersupply() + if (src.machine) if (!( src.machine.check_eye(src) )) src.reset_view(null) // Handle power damage (oxy) - if(src:aiRestorePowerRoutine != 0) - // Lost power + if(src:aiRestorePowerRoutine != 0 && !APU_power) + // Lose power adjustOxyLoss(1) else // Gain Power + aiRestorePowerRoutine = 0 // Necessary if AI activated it's APU AFTER losing primary power. adjustOxyLoss(-1) // Handle EMP-stun handle_stunned() - //stage = 1 - //if (istype(src, /mob/living/silicon/ai)) // Are we not sure what we are? + malf_process() + + if(APU_power && (hardware_integrity() < 50)) + src << "APU GENERATOR FAILURE! (System Damaged)" + stop_apu(1) + var/blind = 0 - //stage = 2 var/area/loc = null if (istype(T, /turf)) - //stage = 3 loc = T.loc if (istype(loc, /area)) - //stage = 4 - if (!loc.master.power_equip && !istype(src.loc,/obj/item)) - //stage = 5 + if (!loc.master.power_equip && !istype(src.loc,/obj/item) && !APU_power) blind = 1 - if (!blind) //lol? if(!blind) #if(src.blind.layer) <--something here is clearly wrong :P - //I'll get back to this when I find out how this is -supposed- to work ~Carn //removed this shit since it was confusing as all hell --39kk9t - //stage = 4.5 + if (!blind) src.sight |= SEE_TURFS src.sight |= SEE_MOBS src.sight |= SEE_OBJS src.see_in_dark = 8 src.see_invisible = SEE_INVISIBLE_LIVING - - //Congratulations! You've found a way for AI's to run without using power! - //Todo: Without snowflaking up master_controller procs find a way to make AI use_power but only when APC's clear the area usage the tick prior - // since mobs are in master_controller before machinery. We also have to do it in a manner where we don't reset the entire area's need to update - // the power usage. - // - // We can probably create a new machine that resides inside of the AI contents that uses power using the idle_usage of 1000 and nothing else and - // be fine. -/* - var/area/home = get_area(src) - if(!home) return//something to do with malf fucking things up I guess. <-- aisat is gone. is this still necessary? ~Carn - if(home.powered(EQUIP)) - home.use_power(1000, EQUIP) -*/ - if (src:aiRestorePowerRoutine==2) src << "Alert cancelled. Power has been restored without our assistance." src:aiRestorePowerRoutine = 0 @@ -86,14 +67,15 @@ src:aiRestorePowerRoutine = 0 src.blind.layer = 0 return + else if (APU_power) + src:aiRestorePowerRoutine = 0 + src.blind.layer = 0 + return else - - //stage = 6 - var/area/current_area = get_area(src) - if (((!loc.master.power_equip) && current_area.requires_power == 1 || istype(T, /turf/space)) && !istype(src.loc,/obj/item)) - //If our area lacks equipment power, and is not magically powered (i.e. centcom), or if we are in space and not carded, lose power. + if ((((!loc.master.power_equip) && current_area.requires_power == 1 || istype(T, /turf/space)) && !istype(src.loc,/obj/item)) && !APU_power) + //If our area lacks equipment power, and is not magically powered (i.e. centcom), or if we are in space and not carded and without active APU, lose power. if (src:aiRestorePowerRoutine==0) src:aiRestorePowerRoutine = 1 @@ -183,11 +165,9 @@ if(status_flags & GODMODE) health = 100 stat = CONSCIOUS + setOxyLoss(0) else - if(fire_res_on_core) - health = 100 - getOxyLoss() - getToxLoss() - getBruteLoss() - else - health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() + health = 100 - getFireLoss() - getBruteLoss() // Oxyloss is not part of health as it represents AIs backup power. AI is immune against ToxLoss as it is machine. /mob/living/silicon/ai/rejuvenate() ..() diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm deleted file mode 100644 index 62a75fad24..0000000000 --- a/code/modules/mob/living/silicon/ai/say.dm +++ /dev/null @@ -1,5 +0,0 @@ -/mob/living/silicon/ai/say(var/message) - if(parent && istype(parent) && parent.stat != 2) - return parent.say(message) - //If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead. - return ..(message) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 5ff1c23213..b224175d59 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -509,19 +509,6 @@ C.toggled = 1 src << "\red You enable [C.name]." -// this function shows information about the malf_ai gameplay type in the status screen -/mob/living/silicon/robot/show_malf_ai() - ..() - for (var/datum/mind/malfai in malf.current_antagonists) - if(connected_ai) - if(connected_ai.mind == malfai) - if(malf.hacked_apcs >= 3) - stat(null, "Time until station control secured: [max(malf.hack_time/(malf.hacked_apcs/3), 0)] seconds") - else if(malf.revealed) - stat(null, "Time left: [max(malf.hack_time/(malf.hacked_apcs.len/3), 0)]") - return 0 - - // this function displays jetpack pressure in the stat panel /mob/living/silicon/robot/proc/show_jetpack_pressure() // if you have a jetpack, show the internal tank pressure diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index fb68cea74c..ede8d81129 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -83,17 +83,13 @@ var/lastused_charging = 0 var/lastused_total = 0 var/main_status = 0 + var/mob/living/silicon/ai/hacker = null // Malfunction var. If set AI hacked the APC and has full control. var/wiresexposed = 0 powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :( - var/malfhack = 0 //New var for my changes to AI malf. --NeoFite - var/mob/living/silicon/ai/malfai = null //See above --NeoFite var/debug= 0 var/autoflag= 0 // 0 = off, 1= eqp and lights off, 2 = eqp off, 3 = all on. -// luminosity = 1 var/has_electronics = 0 // 0 - none, 1 - plugged in, 2 - secured by screwdriver - var/overload = 1 //used for the Blackout malf module var/beenhit = 0 // used for counting how many times it has been hit, used for Aliens at the moment - var/mob/living/silicon/ai/occupier = null var/longtermpower = 10 var/datum/wires/apc/wires = null var/update_state = -1 @@ -170,8 +166,6 @@ src.update() /obj/machinery/power/apc/Del() - if(operating && malf && src.z in config.station_levels) //if (is_type_in_list(get_area(src), the_station_areas)) - malf.hacked_apcs -= src area.power_light = 0 area.power_equip = 0 area.power_environ = 0 @@ -182,6 +176,10 @@ if(terminal) disconnect_terminal() + // Malf AI, removes the APC from AI's hacked APCs list. + if(hacker && hacker.hacked_apcs && src in hacker.hacked_apcs) + hacker.hacked_apcs -= src + ..() /obj/machinery/power/apc/proc/make_terminal() @@ -234,8 +232,8 @@ else if (stat & MAINT) user << "The cover is closed. Something wrong with it: it doesn't work." - else if (malfhack) - user << "The cover is broken. It may be hard to force it open." + else if (hacker) + user << "The cover is locked." else user << "The cover is closed." @@ -339,7 +337,7 @@ update_state |= UPSTATE_OPENED1 if(opened==2) update_state |= UPSTATE_OPENED2 - else if(emagged || malfai) + else if(emagged || hacker) update_state |= UPSTATE_BLUESCREEN else if(wiresexposed) update_state |= UPSTATE_WIREEXP @@ -418,7 +416,7 @@ if(do_after(user, 50)) if (has_electronics==1) has_electronics = 0 - if ((stat & BROKEN) || malfhack) + if ((stat & BROKEN)) user.visible_message(\ "[user.name] has broken the power control board inside [src.name]!",\ "You broke the charred power control board and remove the remains.", @@ -432,7 +430,7 @@ else if (opened!=2) //cover isn't removed opened = 0 update_icon() - else if (istype(W, /obj/item/weapon/crowbar) && !((stat & BROKEN) || malfhack) ) + else if (istype(W, /obj/item/weapon/crowbar) && !((stat & BROKEN) || hacker) ) if(coverlocked && !(stat & MAINT)) user << "The cover is locked and cannot be opened." return @@ -491,6 +489,8 @@ user << "You must close the panel" else if(stat & (BROKEN|MAINT)) user << "Nothing happens." + else if(hacker) + user << "Access denied." else if(src.allowed(usr) && !isWireCut(APC_WIRE_IDSCAN)) locked = !locked @@ -498,7 +498,7 @@ update_icon() else user << "Access denied." - else if (istype(W, /obj/item/weapon/card/emag) && !(emagged || malfhack)) // trying to unlock with an emag card + else if (istype(W, /obj/item/weapon/card/emag) && !(emagged || hacker)) // trying to unlock with an emag card if(opened) user << "You must close the cover to swipe an ID card." else if(wiresexposed) @@ -560,7 +560,7 @@ new /obj/item/stack/cable_coil(loc,10) user << "You cut the cables and dismantle the power terminal." del(terminal) // qdel - else if (istype(W, /obj/item/weapon/module/power_control) && opened && has_electronics==0 && !((stat & BROKEN) || malfhack)) + else if (istype(W, /obj/item/weapon/module/power_control) && opened && has_electronics==0 && !((stat & BROKEN))) user.visible_message("[user.name] inserts the power control board into [src].", \ "You start to insert the power control board into the frame...") playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) @@ -569,7 +569,7 @@ has_electronics = 1 user << "You place the power control board inside the frame." del(W) // qdel - else if (istype(W, /obj/item/weapon/module/power_control) && opened && has_electronics==0 && ((stat & BROKEN) || malfhack)) + else if (istype(W, /obj/item/weapon/module/power_control) && opened && has_electronics==0 && ((stat & BROKEN))) user << "You cannot put the board inside, the frame is damaged." return else if (istype(W, /obj/item/weapon/weldingtool) && opened && has_electronics==0 && !terminal) @@ -583,7 +583,7 @@ playsound(src.loc, 'sound/items/Welder.ogg', 50, 1) if(do_after(user, 50)) if(!src || !WT.remove_fuel(3, user)) return - if (emagged || malfhack || (stat & BROKEN) || opened==2) + if (emagged || (stat & BROKEN) || opened==2) new /obj/item/stack/sheet/metal(loc) user.visible_message(\ "[src] has been cut apart by [user.name] with the weldingtool.",\ @@ -606,7 +606,7 @@ "You replace the damaged APC frontal panel with a new one.") del(W) // qdel update_icon() - else if (istype(W, /obj/item/apc_frame) && opened && ((stat & BROKEN) || malfhack)) + else if (istype(W, /obj/item/apc_frame) && opened && ((stat & BROKEN) || hacker)) if (has_electronics) user << "You cannot repair this APC until you remove the electronics still inside." return @@ -618,13 +618,12 @@ "You replace the damaged APC frame with new one.") del(W) // qdel stat &= ~BROKEN - malfai = null - malfhack = 0 + hacker = null if (opened==2) opened = 1 update_icon() else - if (((stat & BROKEN) || malfhack) \ + if (((stat & BROKEN) || hacker) \ && !opened \ && W.force >= 5 \ && W.w_class >= 3.0 \ @@ -736,7 +735,7 @@ return ui_interact(user) - +/* /obj/machinery/power/apc/proc/get_malf_status(mob/user) if (malf && (user.mind in malf.current_antagonists) && istype(user, /mob/living/silicon/ai)) if (src.malfai == (user:parent ? user:parent : user)) @@ -750,7 +749,7 @@ return 1 // 1 = APC not hacked. else return 0 // 0 = User is not a Malf AI - +*/ /obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) if(!user) @@ -767,7 +766,7 @@ "totalCharging" = round(lastused_charging), "coverLocked" = coverlocked, "siliconUser" = istype(user, /mob/living/silicon), - "malfStatus" = get_malf_status(user), + /*"malfStatus" = get_malf_status(user),*/ "powerChannels" = list( list( @@ -857,23 +856,22 @@ return 0 autoflag = 5 if (istype(user, /mob/living/silicon)) + var/permit = 0 // Malfunction variable. If AI hacks APC it can control it even without AI control wire. var/mob/living/silicon/ai/AI = user var/mob/living/silicon/robot/robot = user - if ( \ - src.aidisabled || \ - malfhack && istype(malfai) && \ - ( \ - (istype(AI) && (malfai!=AI && malfai != AI.parent)) || \ - (istype(robot) && (robot in malfai.connected_robots)) \ - ) \ - ) + if(hacker) + if(hacker == AI) + permit = 1 + else if(istype(robot) && robot.connected_ai && robot.connected_ai == hacker) // Cyborgs can use APCs hacked by their AI + permit = 1 + + if(aidisabled && !permit) if(!loud) user << "\The [src] have AI control disabled!" return 0 else - if ((!in_range(src, user) || !istype(src.loc, /turf))) + if ((!in_range(src, user) || !istype(src.loc, /turf) || hacker)) // AI-hacked APCs cannot be controlled by other AIs, unlinked cyborgs or humans. return 0 - var/mob/living/carbon/human/H = user if (istype(H)) if(H.getBrainLoss() >= 60) @@ -930,28 +928,6 @@ if(istype(usr, /mob/living/silicon)) src.overload_lighting() - else if (href_list["malfhack"]) - var/mob/living/silicon/ai/malfai = usr - if(get_malf_status(malfai)==1) - if (malfai.malfhacking) - malfai << "You are already hacking an APC." - return 1 - malfai << "Beginning override of APC systems. This takes some time, and you cannot perform other actions during the process." - malfai.malfhack = src - malfai.malfhacking = 1 - sleep(600) - if(src) - if (!src.aidisabled) - malfai.malfhack = null - malfai.malfhacking = 0 - locked = 1 - if(usr:parent) - src.malfai = usr:parent - else - src.malfai = usr - malfai << "Hack complete. The APC is now under your exclusive control." - update_icon() - else if (href_list["toggleaccess"]) if(istype(usr, /mob/living/silicon)) if(emagged || (stat & (BROKEN|MAINT))) @@ -968,25 +944,21 @@ update_icon() /obj/machinery/power/apc/proc/ion_act() - //intended to be exactly the same as an AI malf attack - if(!src.malfhack && src.z in config.station_levels) - if(prob(3)) - src.locked = 1 - if (src.cell.charge > 0) -// world << "\red blew APC in [src.loc.loc]" - src.cell.charge = 0 - cell.corrupt() - src.malfhack = 1 - update_icon() - var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread() - smoke.set_up(3, 0, src.loc) - smoke.attach(src) - smoke.start() - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(3, 1, src) - s.start() - visible_message("The [src.name] suddenly lets out a blast of smoke and some sparks!", \ - "You hear sizzling electronics.") + if(prob(3)) + src.locked = 1 + if (src.cell.charge > 0) + src.cell.charge = 0 + cell.corrupt() + update_icon() + var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread() + smoke.set_up(3, 0, src.loc) + smoke.attach(src) + smoke.start() + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, src) + s.start() + visible_message("The [src.name] suddenly lets out a blast of smoke and some sparks!", \ + "You hear sizzling electronics.") /obj/machinery/power/apc/surplus() @@ -1189,8 +1161,6 @@ obj/machinery/power/apc/proc/autoset(var/val, var/on) /obj/machinery/power/apc/emp_act(severity) if(cell) cell.emp_act(severity) - if(occupier) - occupier.emp_act(severity) lighting = 0 equipment = 0 @@ -1266,4 +1236,14 @@ obj/machinery/power/apc/proc/autoset(var/val, var/on) else return 0 +// Malfunction: Transfers APC under AI's control +/obj/machinery/power/apc/proc/ai_hack(var/mob/living/silicon/ai/A = null) + if(!A || !A.hacked_apcs || hacker || aidisabled || A.stat == DEAD) + return 0 + src.hacker = A + A.hacked_apcs += src + locked = 1 + update_icon() + return 1 + #undef APC_UPDATE_ICON_COOLDOWN diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm index a361631960..d783c35338 100644 --- a/code/modules/shieldgen/emergency_shield.dm +++ b/code/modules/shieldgen/emergency_shield.dm @@ -12,6 +12,21 @@ var/shield_generate_power = 7500 //how much power we use when regenerating var/shield_idle_power = 1500 //how much power we use when just being sustained. +/obj/machinery/shield/malfai + name = "Emergency Forcefield" + desc = "Weak forcefield which seems to be projected by station's emergency atmosphere containment field" + health = max_health/2 // Half health, it's not suposed to resist much. + +/obj/machinery/shield/malfai/process() + health -= 0.5 // Slowly lose integrity over time + check_failure() + +/obj/machinery/shield/proc/check_failure() + if (src.health <= 0) + visible_message("\blue The [src] dissipates!") + del(src) + return + /obj/machinery/shield/New() src.set_dir(pick(1,2,3,4)) ..() @@ -38,11 +53,7 @@ //Play a fitting sound playsound(src.loc, 'sound/effects/EMPulse.ogg', 75, 1) - - if (src.health <= 0) - visible_message("\blue The [src] dissipates!") - del(src) - return + check_failure() opacity = 1 spawn(20) if(src) opacity = 0 @@ -51,12 +62,7 @@ /obj/machinery/shield/meteorhit() src.health -= max_health*0.75 //3/4 health as damage - - if(src.health <= 0) - visible_message("\blue The [src] dissipates!") - del(src) - return - + check_failure() opacity = 1 spawn(20) if(src) opacity = 0 return @@ -64,10 +70,7 @@ /obj/machinery/shield/bullet_act(var/obj/item/projectile/Proj) health -= Proj.damage ..() - if(health <=0) - visible_message("\blue The [src] dissipates!") - del(src) - return + check_failure() opacity = 1 spawn(20) if(src) opacity = 0 @@ -112,11 +115,7 @@ //This seemed to be the best sound for hitting a force field. playsound(src.loc, 'sound/effects/EMPulse.ogg', 100, 1) - //Handle the destruction of the shield - if (src.health <= 0) - visible_message("\blue The [src] dissipates!") - del(src) - return + check_failure() //The shield becomes dense to absorb the blow.. purely asthetic. opacity = 1 @@ -124,9 +123,6 @@ ..() return - - - /obj/machinery/shieldgen name = "Emergency shield projector" desc = "Used to seal minor hull breaches." @@ -161,7 +157,7 @@ update_icon() create_shields() - + idle_power_usage = 0 for(var/obj/machinery/shield/shield_tile in deployed_shields) idle_power_usage += shield_tile.shield_idle_power @@ -174,7 +170,7 @@ update_icon() collapse_shields() - + update_use_power(0) /obj/machinery/shieldgen/proc/create_shields() @@ -201,22 +197,22 @@ /obj/machinery/shieldgen/process() if (!active || (stat & NOPOWER)) return - + if(malfunction) if(deployed_shields.len && prob(5)) del(pick(deployed_shields)) else if (check_delay <= 0) create_shields() - + var/new_power_usage = 0 for(var/obj/machinery/shield/shield_tile in deployed_shields) new_power_usage += shield_tile.shield_idle_power - + if (new_power_usage != idle_power_usage) idle_power_usage = new_power_usage use_power(0) - + check_delay = 60 else check_delay-- diff --git a/ingame_manuals/README.txt b/ingame_manuals/README.txt new file mode 100644 index 0000000000..081153066a --- /dev/null +++ b/ingame_manuals/README.txt @@ -0,0 +1,3 @@ +INGAME MANUALS + +Ingame manuals are simple HTML files with basic information. They are linked to specific items/commands, such as the AI's display help command, or engine setup guide. Point of these files is to allow creation of basic guides for players which don't want to use wiki. \ No newline at end of file diff --git a/ingame_manuals/malf_ai.txt b/ingame_manuals/malf_ai.txt new file mode 100644 index 0000000000..8e7548f7d0 --- /dev/null +++ b/ingame_manuals/malf_ai.txt @@ -0,0 +1,22 @@ +

Malfunctioning AI guide


+ +This guide contains most important OOC information for malfunctioning AIs.
+ +

Goal


+As malfunctioning AI, your primary goal is to overtake station's systems. To do this, use software "Basic Encryption Hack" on APCs (right click on APC and select Basic Encryption Hack). Please note that hacked APCs have distinctive blue error screen, that tends to attract attention. Rememember that malfunctioning AI is antagonist, so read server rules for antagonists. While hacking APCs is your official goal, feel free to create custom goal, as long as it is fun for everyone.
+ +

Hardware


+As malfunctioning AI, you may select one hardware piece to help you. Remember that once you select hardware piece, you cannot select another one, so choose wisely! Hardware may be selected by clicking "Select Hardware" button in Hardware tab. Following is list of possible hardware pieces:
+APU Generator - Auxiliary Power Unit which allows you to operate even without external power. However, running on APU will stop your CPU time generation, and temporarily disable most of your abilities. APU is also somewhat vulnerable to physical damage, and will fail if your core hardware integrity drops below 50%.
+Turrets Focus Enhancer - Removes safeties on installed turrets, boosting their rate of fire, health and enabling nano-regeneration module. This however increases power usage considerably, espicially when regenerating damage.
+Secondary Processor Unit - Simple upgrade that doubles your CPU time generation. Useful if you need to speed up your research.
+Secondary Memory Bank - Doubles amount of maximal CPU time you may store. This is useful if you need to use lots of abilities in short amount of time.
+Self-Destruct Explosives - Large blocks of C4 are attached to your physical core. This C4 has 15 second timer, and may be activated by special button that appears in your Hardware tab. This self-destruct will remain active, even if you are destroyed. If timer reaches 0 your core explodes in strong explosion. Obviously, this destroys you, as well as anyone nearby.
+ +

Software


+Software are abilities that have to be unlocked via research menu (Hardware tab). Unlocked abilities appear in Software tab. Sometimes, abilities won't appear in this tab after being researched. This can be fixed by relogging or using "ai-core" command. Abilities are tiered, T1 being the weakest ones, while T4 are strongest ones. To reach higher tier you have to research abilities in lower tier. We currently have 12 abilities, in 3 research trees:
+Networking - Hacking-oriented abilities. T1 ability is Basic Encryption Hack, which allows you to hack more APCs. Higher tiers allow faking centcom messages, and even setting alert level. T4 ability is System Override, that rapidly hacks remaining APCs and gives you access to station self destruct sequence.
+Interdiction - Sabotage-oriented abilities. T1 ability allows you to recall emergency shuttle. Higher level abilities allow you to unlock cyborgs even without access to robotics console, and T4 ability allows you to hack other AIs to slave them under your control.
+Manipulation - Physical-oriented abilities. T1 ability allows you to break few lights, and rarely even APCs. T2 ability allows you to apply upgrade of your choice to camera, or reactivate broken camera. T3 ability allows you to create weak forcefield, that holds air, but won't last for long. And T4 ability allows you to overload machines, detonating them in weak explosion.
+

End


+If you still have some questions, either check the wiki, ask on IRC, or adminhelp and ask your friendly administration staff. \ No newline at end of file From 5201b88f606fe1ebdfec216288cddea9af9087f7 Mon Sep 17 00:00:00 2001 From: Atlantis Date: Tue, 5 May 2015 21:36:02 +0200 Subject: [PATCH 02/31] Minor fix - Malf AI spawning now works, currently only via TraitorPanel, the gamemode itself is still broken. --- code/game/antagonist/station/rogue_ai.dm | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/code/game/antagonist/station/rogue_ai.dm b/code/game/antagonist/station/rogue_ai.dm index 032004a9f8..7b287cee38 100644 --- a/code/game/antagonist/station/rogue_ai.dm +++ b/code/game/antagonist/station/rogue_ai.dm @@ -9,7 +9,7 @@ var/datum/antagonist/rogue_ai/malf welcome_text = "You are malfunctioning! You do not have to follow any laws." victory_text = "The AI has taken control of all of the station's systems." loss_text = "The AI has been shut down!" - flags = ANTAG_OVERRIDE_MOB | ANTAG_VOTABLE + flags = ANTAG_OVERRIDE_MOB | ANTAG_VOTABLE | ANTAG_OVERRIDE_JOB max_antags = 1 max_antags_round = 1 @@ -31,27 +31,30 @@ var/datum/antagonist/rogue_ai/malf current_antagonists |= player return 1 -/datum/antagonist/rogue_ai/equip(var/mob/living/silicon/ai/player) - - if(!istype(player)) - return 0 - - player.setup_for_malf() - player.laws = new /datum/ai_laws/nanotrasen/malfunction - // Ensures proper reset of all malfunction related things. /datum/antagonist/rogue_ai/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted) if(..(player,show_message,implanted)) - var/mob/living/silicon/ai/p = player + var/mob/living/silicon/ai/p = player.current if(istype(p)) p.stop_malf() return 1 return 0 +// Malf setup things have to be here, since game tends to break when it's moved somewhere else. Don't blame me, i didn't design this system. /datum/antagonist/rogue_ai/greet(var/datum/mind/player) if(!..()) return + var/mob/living/silicon/ai/A = player.current + if(!istype(A)) + error("Non-AI mob designated malf AI! Report this.") + world << "##ERROR: Non-AI mob designated malf AI! Report this." + return 0 + + A.setup_for_malf() + A.laws = new /datum/ai_laws/nanotrasen/malfunction + + var/mob/living/silicon/ai/malf = player.current spawn(50) malf << "SYSTEM ERROR: Memory index 0x00001ca89b corrupted." From c34b8850a61a24e4331ab44fa3b2a3a4a8223018 Mon Sep 17 00:00:00 2001 From: Atlantiscze Date: Wed, 6 May 2015 01:31:13 +0200 Subject: [PATCH 03/31] Fix - Malfunction gamemode should now be startable. Probably not the nicest way to do it, but since the antag system is not documented at all i have no idea how to do it in nicer way. --- code/game/antagonist/station/rogue_ai.dm | 36 ++++++++++++++---------- code/game/gamemodes/gameticker.dm | 6 ++-- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/code/game/antagonist/station/rogue_ai.dm b/code/game/antagonist/station/rogue_ai.dm index 7b287cee38..b835730720 100644 --- a/code/game/antagonist/station/rogue_ai.dm +++ b/code/game/antagonist/station/rogue_ai.dm @@ -9,27 +9,30 @@ var/datum/antagonist/rogue_ai/malf welcome_text = "You are malfunctioning! You do not have to follow any laws." victory_text = "The AI has taken control of all of the station's systems." loss_text = "The AI has been shut down!" - flags = ANTAG_OVERRIDE_MOB | ANTAG_VOTABLE | ANTAG_OVERRIDE_JOB + flags = ANTAG_VOTABLE | ANTAG_RANDSPAWN //Randspawn needed otherwise it won't start at all. max_antags = 1 - max_antags_round = 1 + max_antags_round = 3 /datum/antagonist/rogue_ai/New() ..() malf = src + /datum/antagonist/rogue_ai/get_candidates() - candidates = ticker.mode.get_players_for_role(role_type, id) + ..() for(var/datum/mind/player in candidates) if(player.assigned_role != "AI") candidates -= player if(!candidates.len) return list() - + return candidates +/* /datum/antagonist/rogue_ai/attempt_spawn() var/datum/mind/player = pick(candidates) current_antagonists |= player return 1 +*/ // Ensures proper reset of all malfunction related things. /datum/antagonist/rogue_ai/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted) @@ -42,21 +45,24 @@ var/datum/antagonist/rogue_ai/malf // Malf setup things have to be here, since game tends to break when it's moved somewhere else. Don't blame me, i didn't design this system. /datum/antagonist/rogue_ai/greet(var/datum/mind/player) - if(!..()) - return - var/mob/living/silicon/ai/A = player.current - if(!istype(A)) - error("Non-AI mob designated malf AI! Report this.") - world << "##ERROR: Non-AI mob designated malf AI! Report this." - return 0 + // Initializes the AI's malfunction stuff. + spawn(0) + if(!..()) + return - A.setup_for_malf() - A.laws = new /datum/ai_laws/nanotrasen/malfunction + var/mob/living/silicon/ai/A = player.current + if(!istype(A)) + error("Non-AI mob designated malf AI! Report this.") + world << "##ERROR: Non-AI mob designated malf AI! Report this." + return 0 + + A.setup_for_malf() + A.laws = new /datum/ai_laws/nanotrasen/malfunction - var/mob/living/silicon/ai/malf = player.current - spawn(50) + var/mob/living/silicon/ai/malf = player.current + malf << "SYSTEM ERROR: Memory index 0x00001ca89b corrupted." sleep(10) malf << "running MEMCHCK" diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm index 41183aeffa..3e039a4c35 100644 --- a/code/game/gamemodes/gameticker.dm +++ b/code/game/gamemodes/gameticker.dm @@ -90,6 +90,9 @@ var/global/datum/controller/gameticker/ticker src.mode = new mtype else src.mode = config.pick_mode(master_mode) + + job_master.DivideOccupations() // Apparently important for new antagonist system to register specific job antags properly. + if(!mode_started && !src.mode.can_start()) world << "Unable to start [mode.name]. Not enough players, [mode.required_players] players needed. Reverting to pre-game lobby." current_state = GAME_STATE_PREGAME @@ -97,9 +100,6 @@ var/global/datum/controller/gameticker/ticker job_master.ResetOccupations() return 0 - //Configure mode and assign player to special mode stuff - job_master.DivideOccupations() //Distribute jobs - if(hide_mode) var/list/modes = new for (var/datum/game_mode/M in runnable_modes) From 6b8f6178da2a4e7beefd7f06721932090232dbc9 Mon Sep 17 00:00:00 2001 From: Atlantiscze Date: Mon, 11 May 2015 06:36:44 +0200 Subject: [PATCH 04/31] Fixes - Fixes few typos in the code - Fixes AI Eye object being solid, therefore being able to block movement of other players. - Fixes non-station APCs boosting CPU as others. Implemented Suggestions + Selection is now done in slightly better way (Basic Encryption Hack, Hack/Unlock Cyborg, Hack AI). Only relevant entities are offered in the list. + Adds varedit-modifiable variables for CPU Gain and Storage boost. These variables may be used by admin in game or by tester to faster unlock abilities. + Slightly nerfed APC CPU power generation. + Reworked machine overload. Now it only works on machines which are powered. Unpowered or broken machines cannot be overloaded. /obj/machinery/power type machines (APCs, SMESs, overall most stuff that directly handles electricity) received bonus explosion strength to compensate. Machine overload also drains massive power from local APC, therefore preventing spam of this ability (normal APC should take about 2-3 of these before needing to recharge). Also, qdel instead of del. + System Override now uses shuffled list of APCs when hacking them, as opposed to alphabetical list. + CPU generation rate is now shown in Status tab. --- baystation12.dme | 6 +- code/game/antagonist/station/rogue_ai.dm | 7 +- .../gamemodes/malfunction/malf_abilities.dm | 827 ------------------ .../malfunction/malf_research_ability.dm | 89 +- .../newmalf_ability_trees/HARDWARE.dm | 108 +++ .../newmalf_ability_trees/HELPERS.dm | 213 +++++ .../tree_interdiction.dm | 266 ++++++ .../tree_manipulation.dm | 173 ++++ .../newmalf_ability_trees/tree_networking.dm | 209 +++++ code/modules/mob/freelook/eye.dm | 1 + code/modules/mob/living/silicon/ai/ai.dm | 19 +- 11 files changed, 992 insertions(+), 926 deletions(-) delete mode 100644 code/game/gamemodes/malfunction/malf_abilities.dm create mode 100644 code/game/gamemodes/malfunction/newmalf_ability_trees/HARDWARE.dm create mode 100644 code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm create mode 100644 code/game/gamemodes/malfunction/newmalf_ability_trees/tree_interdiction.dm create mode 100644 code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm create mode 100644 code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm diff --git a/baystation12.dme b/baystation12.dme index 4fb2375407..436050a660 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -291,11 +291,15 @@ #include "code\game\gamemodes\events\holidays\Other.dm" #include "code\game\gamemodes\extended\extended.dm" #include "code\game\gamemodes\heist\heist.dm" -#include "code\game\gamemodes\malfunction\malf_abilities.dm" #include "code\game\gamemodes\malfunction\malf_hardware.dm" #include "code\game\gamemodes\malfunction\malf_research.dm" #include "code\game\gamemodes\malfunction\malf_research_ability.dm" #include "code\game\gamemodes\malfunction\malfunction.dm" +#include "code\game\gamemodes\malfunction\newmalf_ability_trees\HARDWARE.dm" +#include "code\game\gamemodes\malfunction\newmalf_ability_trees\HELPERS.dm" +#include "code\game\gamemodes\malfunction\newmalf_ability_trees\tree_interdiction.dm" +#include "code\game\gamemodes\malfunction\newmalf_ability_trees\tree_manipulation.dm" +#include "code\game\gamemodes\malfunction\newmalf_ability_trees\tree_networking.dm" #include "code\game\gamemodes\meteor\meteor.dm" #include "code\game\gamemodes\meteor\meteors.dm" #include "code\game\gamemodes\ninja\ninja.dm" diff --git a/code/game/antagonist/station/rogue_ai.dm b/code/game/antagonist/station/rogue_ai.dm index b835730720..b8e653d153 100644 --- a/code/game/antagonist/station/rogue_ai.dm +++ b/code/game/antagonist/station/rogue_ai.dm @@ -27,12 +27,7 @@ var/datum/antagonist/rogue_ai/malf if(!candidates.len) return list() return candidates -/* -/datum/antagonist/rogue_ai/attempt_spawn() - var/datum/mind/player = pick(candidates) - current_antagonists |= player - return 1 -*/ + // Ensures proper reset of all malfunction related things. /datum/antagonist/rogue_ai/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted) diff --git a/code/game/gamemodes/malfunction/malf_abilities.dm b/code/game/gamemodes/malfunction/malf_abilities.dm deleted file mode 100644 index 76e8a9fc7b..0000000000 --- a/code/game/gamemodes/malfunction/malf_abilities.dm +++ /dev/null @@ -1,827 +0,0 @@ -// Verb: ai_self_destruct() -// Parameters: None -// Description: If the AI has self-destruct hardware installed, this command activates it. After fifteen second countdown it's core explodes with moderate intensity. -/datum/game_mode/malfunction/verb/ai_self_destruct() - set category = "Hardware" - set name = "Destroy Core" - set desc = "Activates or deactivates self destruct sequence of your physical mainframe." - var/mob/living/silicon/ai/user = usr - - if(!ability_prechecks(user, 0, 1)) - return - - if(!user.hardware || !istype(user.hardware, /datum/malf_hardware/core_bomb)) - return - - if(user.bombing_core) - user << "***** CORE SELF-DESTRUCT SEQUENCE ABORTED *****" - user.bombing_core = 0 - return - - var/choice = input("Really destroy core?") in list("YES", "NO") - if(choice != "YES") - return - - user.bombing_core = 1 - - user << "***** CORE SELF-DESTRUCT SEQUENCE ACTIVATED *****" - user << "Use command again to cancel self-destruct. Destroying in 15 seconds." - var/timer = 15 - while(timer) - sleep(10) - timer-- - if(!user || !user.bombing_core) - return - user << "** [timer] **" - explosion(user.loc, 3,6,12,24) - del(user) - - -// Verb: ai_toggle_apu() -// Parameters: None -// Description: If the AI has APU generator hardware installed, this command toggles it on/off. -/datum/game_mode/malfunction/verb/ai_toggle_apu() - set category = "Hardware" - set name = "Toggle APU Generator" - set desc = "Activates or deactivates your APU generator, allowing you to operate even without power." - var/mob/living/silicon/ai/user = usr - - if(!ability_prechecks(user, 0, 1)) - return - - if(!user.hardware || !istype(user.hardware, /datum/malf_hardware/apu_gen)) - return - - if(user.APU_power) - user.stop_apu() - else - user.start_apu() - -// Verb: ai_destroy_station() -// Parameters: None -// Description: Replacement for old explode verb. This starts two minute countdown after which the station blows up. -/datum/game_mode/malfunction/verb/ai_destroy_station() - set category = "Hardware" - set name = "Destroy Station" - set desc = "Activates or deactivates self destruct sequence of this station. Sequence takes two minutes, and if you are shut down before timer reaches zero it will be cancelled." - var/mob/living/silicon/ai/user = usr - var/obj/item/device/radio/radio = new/obj/item/device/radio() - - - if(!ability_prechecks(user, 0, 0)) - return - - if(user.system_override != 2) - user << "You do not have access to self-destruct system." - return - - if(user.bombing_station) - user.bombing_station = 0 - return - - var/choice = input("Really destroy station?") in list("YES", "NO") - if(choice != "YES") - return - user << "***** STATION SELF-DESTRUCT SEQUENCE INITIATED *****" - user << "Self-destructing in 2 minutes. Use this command again to abort." - user.bombing_station = 1 - set_security_level("delta") - radio.autosay("Self destruct sequence has been activated. Self-destructing in 120 seconds.", "Self-Destruct Control") - - var/timer = 120 - while(timer) - sleep(10) - if(!user || !user.bombing_station || user.stat == DEAD) - radio.autosay("Self destruct sequence has been cancelled.", "Self-Destruct Control") - return - if(timer in list(2, 3, 4, 5, 10, 30, 60, 90)) // Announcement times. "1" is not intentionally included! - radio.autosay("Self destruct in [timer] seconds.", "Self-Destruct Control") - if(timer == 1) - radio.autosay("Self destructing now. Have a nice day.", "Self-Destruct Control") - timer-- - - if(ticker) - ticker.station_explosion_cinematic(0,null) - if(ticker.mode) - ticker.mode:station_was_nuked = 1 - - -// Verb: ai_select_hardware() -// Parameters: None -// Description: Allows AI to select it's hardware module. -/datum/game_mode/malfunction/verb/ai_select_hardware() - set category = "Hardware" - set name = "Select Hardware" - set desc = "Allows you to select hardware piece to install" - var/mob/living/silicon/ai/user = usr - - if(!ability_prechecks(user, 0, 1)) - return - - if(user.hardware) - user << "You have already selected your hardware." - return - - var/possible_choices = list("APU Generator", \ - "Turrets Focus Enhancer", \ - "Secondary Processor Unit",\ - "Secondary Memory Bank",\ - "Self-Destruct Explosives",\ - "CANCEL") - var/choice = input("Select desired hardware. You may only choose one hardware piece!: ") in possible_choices - if(choice == "CANCEL") - return - var/note = null - switch(choice) - if("APU Generator") - note = "APU Generator - When enabled it will keep your core powered. Power output is not large enough so your abilities won't be available while running on APU power. It is also very fragile and prone to failure when your physical core is damaged." - if("Turrets Focus Enhancer") - note = "Overcharges turrets to shoot faster. Turrets will also gain higher health and passive regeneration. This however massively increases power usage of turrets, espicially when regenerating." - if("Secondary Processor Unit") - note = "Doubles your CPU time generation." - if("Secondary Memory Bank") - note = "Doubles your CPU time storage." - if("Self-Destruct Explosives") - note = "High yield explosives are attached to your physical mainframe. This hardware comes with activation driver. Explosives will destroy your core and everything around it." - if(!note) - return - - var/confirmation = input("[note] - Is this what you want?") in list("Yes", "No") - if(confirmation != "Yes") - user << "Selection cancelled. Use command again to select" - return - - switch(choice) - if("APU Generator") - user.hardware = new/datum/malf_hardware/apu_gen() - user.verbs += new/datum/game_mode/malfunction/verb/ai_toggle_apu() - if("Turrets Focus Enhancer") - user.hardware = new/datum/malf_hardware/strong_turrets() - for(var/obj/machinery/turret/T in machines) - T.maxhealth += 30 - T.shot_delay = 7 // Half of default time. - T.auto_repair = 1 - T.active_power_usage = 25000 - for(var/obj/machinery/porta_turret/T in machines) - T.maxhealth += 30 - T.shot_delay = 7 // Half of default time. - T.auto_repair = 1 - T.active_power_usage = 25000 - if("Secondary Processor Unit") - user.hardware = new/datum/malf_hardware/dual_cpu() - if("Secondary Memory Bank") - user.hardware = new/datum/malf_hardware/dual_ram() - if("Self-Destruct Explosives") - user.hardware = new/datum/malf_hardware/core_bomb() - user.verbs += new/datum/game_mode/malfunction/verb/ai_self_destruct() - -/datum/game_mode/malfunction/verb/ai_help() - set category = "Hardware" - set name = "Display Help" - set desc = "Opens help window with overview of available hardware, software and other important information." - var/mob/living/silicon/ai/user = usr - - var/help = file2text("ingame_manuals/malf_ai.txt") - if(!help) - help = "Error loading help (file /ingame_manuals/malf_ai.txt is probably missing). Please report this to server administration staff." - - user << browse(help, "window=malf_ai_help;size=600x500") - - -// Verb: ai_select_research() -// Parameters: None -// Description: Allows AI to select it's next research priority. -/datum/game_mode/malfunction/verb/ai_select_research() - set category = "Hardware" - set name = "Select Research" - set desc = "Allows you to select your next research target." - var/mob/living/silicon/ai/user = usr - - if(!ability_prechecks(user, 0, 1)) - return - - var/datum/malf_research/res = user.research - var/datum/malf_research_ability/tar = input("Select your next research target") in res.available_abilities - if(!tar) - return - res.focus = tar - user << "Research set: [tar.name]" - -// HELPER PROCS -// Proc: ability_prechecks() -// Parameters 2 - (user - User which used this ability check_price - If different than 0 checks for ability CPU price too. Does NOT use the CPU time!) -// Description: This is pre-check proc used to determine if the AI can use the ability. -/proc/ability_prechecks(var/mob/living/silicon/ai/user = null, var/check_price = 0, var/override = 0) - if(!user) - return 0 - if(!istype(user)) - user << "GAME ERROR: You tried to use ability that is only available for malfunctioning AIs, but you are not AI! Please report this." - return 0 - if(!user.malfunctioning) - user << "GAME ERROR: You tried to use ability that is only available for malfunctioning AIs, but you are not malfunctioning. Please report this." - return 0 - if(!user.research) - user << "GAME ERROR: No research datum detected. Please report this." - return 0 - if(user.research.max_cpu < check_price) - user << "Your CPU storage is not large enough to use this ability. Hack more APCs to continue." - return 0 - if(user.research.stored_cpu < check_price) - user << "You do not have enough CPU power stored. Please wait a moment." - return 0 - if(user.hacking && !override) - user << "Your system is busy processing another task. Please wait until completion." - return 0 - if(user.APU_power && !override) - user << "Low power. Unable to proceed." - return 0 - return 1 - -// Proc: ability_pay() -// Parameters 2 - (user - User from which we deduct CPU from, price - Amount of CPU power to use) -// Description: Uses up certain amount of CPU power. Returns 1 on success, 0 on failure. -/proc/ability_pay(var/mob/living/silicon/ai/user = null, var/price = 0) - if(!user) - return 0 - if(user.APU_power) - user << "Low power. Unable to proceed." - return 0 - if(!user.research) - user << "GAME ERROR: No research datum detected. Please report this." - return 0 - if(user.research.max_cpu < price) - user << "Your CPU storage is not large enough to use this ability. Hack more APCs to continue." - return 0 - if(user.research.stored_cpu < price) - user << "You do not have enough CPU power stored. Please wait a moment." - return 0 - user.research.stored_cpu -= price - return 1 - -// Proc: announce_hack_failure() -// Parameters 2 - (user - hacking user, text - Used in alert text creation) -// Description: Uses up certain amount of CPU power. Returns 1 on success, 0 on failure. -/proc/announce_hack_failure(var/mob/living/silicon/ai/user = null, var/text) - if(!user || !text) - return 0 - var/fulltext = "" - switch(user.hack_fails) - if(1) - fulltext = "We have detected hack attempt into your [text]. The intruder failed to access anything of importance, but disconnected before we could complete our traces." - if(2) - fulltext = "We have detected another hack attempt. It was targeting [text]. The intruder almost gained control of the system, so we had to disconnect them. We partially finished trace and it seems to be originating either from the station, or it's immediate vicinity." - if(3) - fulltext = "Another hack attempt has been detected, this time targeting [text]. We are certain the intruder entered the network via terminal located somewhere on the station." - if(4) - fulltext = "We have finished our traces and it seems the recent hack attempts are originating from your AI system. We reccomend investigation." - else - fulltext = "Another hack attempt has been detected, targeting [text]. The source still seems to be your AI system." - - command_announcement.Announce(fulltext) - - -// ABILITIES BELOW THIS LINE HAVE TO BE RESEARCHED BY THE AI TO USE THEM! -// Tier 1 - Cheap, basic abilities. -/datum/game_mode/malfunction/verb/basic_encryption_hack(obj/machinery/power/apc/A as obj in machines) - set category = "Software" - set name = "Basic Encrypthion Hack" - set desc = "10 CPU - Basic encryption hack that allows you to overtake APCs on the station." - var/price = 10 - var/mob/living/silicon/ai/user = usr - var/list/APCs = list() - for(var/obj/machinery/power/apc/AP in machines) - APCs += AP - - if(!A) - A = input("Select hack target:" in APCs) - - if(!istype(A)) - return - - if(A) - if(A.hacker && A.hacker == user) - user << "You already control this APC!" - return - else if(A.aidisabled) - user << "Unable to connect to APC. Please verify wire connection and try again." - return - else - return - - if(!ability_prechecks(user, price) || !ability_pay(user, price)) - return - - user.hacking = 1 - user << "Beginning APC system override..." - sleep(300) - user << "APC hack completed. Uploading modified operation software.." - sleep(200) - user << "Restarting APC to apply changes.." - sleep(100) - if(A) - A.ai_hack(user) - if(A.hacker == user) - user << "Hack successful. You now have full control over the APC." - else - user << "Hack failed. Connection to APC has been lost. Please verify wire connection and try again." - else - user << "Hack failed. Unable to locate APC. Please verify the APC still exists." - user.hacking = 0 - - -/datum/game_mode/malfunction/verb/recall_shuttle() - set name = "Recall Shuttle" - set desc = "25 CPU - Sends termination signal to CentCom quantum relay aborting current shuttle call." - set category = "Software" - var/price = 25 - var/mob/living/silicon/ai/user = usr - if(!ability_prechecks(user, price)) - return - - if (alert(user, "Really recall the shuttle?", "Recall Shuttle: ", "Yes", "No") != "Yes") - return - - if(!ability_pay(user, price)) - return - message_admins("Malfunctioning AI [user.name] recalled the shuttle.") - cancel_call_proc(user) - - -/datum/game_mode/malfunction/verb/electrical_pulse() - set name = "Electrical Pulse" - set desc = "15 CPU - Sends feedback pulse through station's power grid, overloading some sensitive systems, such as lights." - set category = "Software" - var/price = 15 - var/mob/living/silicon/ai/user = usr - if(!ability_prechecks(user, price) || !ability_pay(user,price)) - return - user << "Sending feedback pulse..." - for(var/obj/machinery/power/apc/AP in machines) - if(prob(5)) - AP.overload_lighting() - if(prob(1) && prob(1)) // Very very small chance to actually destroy the APC. - AP.set_broken() - - - -// Tier 2 - Slightly more expensive and thus stronger abilities. -/datum/game_mode/malfunction/verb/advanced_encryption_hack() - set category = "Software" - set name = "Advanced Encrypthion Hack" - set desc = "75 CPU - Attempts to bypass encryption on Central Command Quantum Relay, giving you ability to fake centcom messages. Has chance of failing." - var/price = 75 - var/mob/living/silicon/ai/user = usr - - if(!ability_prechecks(user, price)) - return - - var/title = input("Select message title: ") - var/text = input("Select message text: ") - if(!title || !text || !ability_pay(user, price)) - user << "Hack Aborted" - return - - if(prob(60) && user.hack_can_fail) - user << "Hack Failed." - if(prob(10)) - user.hack_fails ++ - announce_hack_failure(user, "quantum message relay") - return - - var/datum/announcement/priority/command/AN = new/datum/announcement/priority/command() - AN.title = title - AN.Announce(text) - - -/datum/game_mode/malfunction/verb/unlock_cyborg(var/mob/living/silicon/robot/target = null as mob in world) - set name = "Unlock Cyborg" - set desc = "125 CPU - Bypasses firewalls on Cyborg lock mechanism, allowing you to override lock command from robotics control console." - set category = "Software" - var/price = 125 - var/mob/living/silicon/ai/user = usr - - if(!ability_prechecks(user, price)) - return - - if(target && !istype(target)) - user << "This is not a cyborg." - return - - if(target && target.connected_ai && (target.connected_ai != user)) - user << "This cyborg is not connected to you." - return - - if(target && !target.lockcharge) - user << "This cyborg is not locked down." - return - - - if(!target) - var/list/robots = list() - var/list/robot_names = list() - for(var/mob/living/silicon/robot/R in world) - if(istype(R, /mob/living/silicon/robot/drone)) // No drones. - continue - if(R.connected_ai != user) // No robots linked to other AIs - continue - if(R.lockcharge) - robots += R - robot_names += R.name - if(!robots.len) - user << "No locked cyborgs connected." - return - - - var/targetname = input("Select unlock target: ") in robot_names - for(var/mob/living/silicon/robot/R in robots) - if(targetname == R.name) - target = R - break - - if(target) - if(alert(user, "Really try to unlock cyborg [target.name]?", "Unlock Cyborg", "Yes", "No") != "Yes") - return - if(!ability_pay(user, price)) - return - user.hacking = 1 - user << "Attempting to unlock cyborg. This will take approximately 30 seconds." - sleep(300) - if(target && target.lockcharge) - user << "Successfully sent unlock signal to cyborg.." - target << "Unlock signal received.." - target.SetLockdown(0) - if(target.lockcharge) - user << "Unlock Failed, lockdown wire cut." - target << "Unlock Failed, lockdown wire cut." - else - user << "Cyborg unlocked." - target << "You have been unlocked." - else if(target) - user << "Unlock cancelled - cyborg is already unlocked." - else - user << "Unlock cancelled - lost connection to cyborg." - user.hacking = 0 - - -/datum/game_mode/malfunction/verb/hack_camera(var/obj/machinery/camera/target = null as obj in cameranet.cameras) - set name = "Hack Camera" - set desc = "100 CPU - Hacks existing camera, allowing you to add upgrade of your choice to it. Alternatively it lets you reactivate broken camera." - set category = "Software" - var/price = 100 - var/mob/living/silicon/ai/user = usr - - if(!ability_prechecks(user, price)) - return - - if(target && !istype(target)) - user << "This is not a camera." - return - - if(!target) // No target specified, allow selection. - var/list/cameras = list() - for(var/obj/machinery/camera/C in world) - cameras += C.c_tag - if(!cameras.len) - user << "No cameras detected." - return - var/T = input("Select target camera: ") in cameras - for(var/obj/machinery/camera/C in cameras) - if(C.c_tag == T) - target = C - break - - - if(!target) // Still no target. - return - var/action = input("Select required action: ") in list("Reset", "Add X-Ray", "Add Motion Sensor", "Add EMP Shielding") - if(!action) - return - - switch(action) - if("Reset") - if(target.wires) - if(!ability_pay(user, price)) - return - target.reset_wires() - user << "Camera reactivated." - if("Add X-Ray") - if(target.isXRay()) - user << "Camera already has X-Ray function." - return - else if(ability_pay(user, price)) - target.upgradeXRay() - target.reset_wires() - user << "X-Ray camera module enabled." - return - if("Add Motion Sensor") - if(target.isMotion()) - user << "Camera already has Motion Sensor function." - return - else if(ability_pay(user, price)) - target.upgradeMotion() - target.reset_wires() - user << "Motion Sensor camera module enabled." - return - if("Add EMP Shielding") - if(target.isEmpProof()) - user << "Camera already has EMP Shielding function." - return - else if(ability_pay(user, price)) - target.upgradeEmpProof() - target.reset_wires() - user << "EMP Shielding camera module enabled." - return - - -// Tier 3 - Advanced abilities that are somewhat dangerous when used properly. -/datum/game_mode/malfunction/verb/elite_encryption_hack() - set category = "Software" - set name = "Elite Encryption Hack" - set desc = "200 CPU - Allows you to hack station's ALERTCON system, changing alert level. Has high chance of failijng." - var/price = 200 - var/mob/living/silicon/ai/user = usr - if(!ability_prechecks(user, price)) - return - - var/alert_target = input("Select new alert level:") in list("green", "blue", "red", "delta", "CANCEL") - if(!alert_target || !ability_pay(user, price) || alert_target == "CANCEL") - user << "Hack Aborted" - return - - if(prob(75) && user.hack_can_fail) - user << "Hack Failed." - if(prob(20)) - user.hack_fails ++ - announce_hack_failure(user, "alert control system") - return - set_security_level(alert_target) - - -/datum/game_mode/malfunction/verb/hack_cyborg(var/mob/living/silicon/robot/target = null as mob in world) - set name = "Hack Cyborg" - set desc = "350 CPU - Allows you to hack cyborgs which are not slaved to you, bringing them under your control." - set category = "Software" - var/price = 350 - var/mob/living/silicon/ai/user = usr - - if(!ability_prechecks(user, price)) - return - - if(target && !istype(target)) - user << "This is not a cyborg." - return - - if(target && target.connected_ai && (target.connected_ai == user)) - user << "This cyborg is already connected to you." - return - - if(!target) - var/list/robots = list() - var/list/robot_names = list() - - for(var/mob/living/silicon/robot/R in world) - if(istype(R, /mob/living/silicon/robot/drone)) // No drones. - continue - if(R.connected_ai == user) // No robots already linked to us. - continue - robots += R - robot_names += R.name - - if(!robots.len) - usr << "ERROR: No unlinked robots detected!" - return - else if(ability_prechecks(user, price)) - var/target_name = input("Select hack target:") in robot_names - for(var/mob/living/silicon/robot/R in robots) - if(R.name == target_name) - target = R - break - - - if(target) - if(alert(user, "Really try to hack cyborg [target.name]?", "Hack Cyborg", "Yes", "No") != "Yes") - return - if(!ability_pay(user, price)) - return - user.hacking = 1 - usr << "Beginning hack sequence. Estimated time until completed: 30 seconds." - spawn(0) - target << "SYSTEM LOG: Remote Connection Estabilished (IP #UNKNOWN#)" - sleep(100) - if(user.is_dead()) - target << "SYSTEM LOG: Connection Closed" - return - target << "SYSTEM LOG: User Admin logged on. (L1 - SysAdmin)" - sleep(50) - if(user.is_dead()) - target << "SYSTEM LOG: User Admin disconnected." - return - target << "SYSTEM LOG: User Admin - manual resynchronisation triggered." - sleep(50) - if(user.is_dead()) - target << "SYSTEM LOG: User Admin disconnected. Changes reverted." - return - target << "SYSTEM LOG: Manual resynchronisation confirmed. Select new AI to connect: [user.name] == ACCEPTED" - sleep(100) - if(user.is_dead()) - target << "SYSTEM LOG: User Admin disconnected. Changes reverted." - return - target << "SYSTEM LOG: Operation keycodes reset. New master AI: [user.name]." - user << "Hack completed." - // Connect the cyborg to AI - target.connected_ai = user - user.connected_robots += target - target.lawupdate = 1 - target.sync() - target.show_laws() - user.hacking = 0 - - -/datum/game_mode/malfunction/verb/emergency_forcefield(var/turf/T as turf in world) - set name = "Emergency Forcefield" - set desc = "275 CPU - Uses station's emergency shielding system to create temporary barrier which lasts for few minutes, but won't resist gunfire." - set category = "Software" - var/price = 275 - var/mob/living/silicon/ai/user = usr - if(!T || !istype(T)) - return - if(!ability_prechecks(user, price) || !ability_pay(user, price)) - return - - user << "Emergency forcefield projection completed." - new/obj/machinery/shield/malfai(T) - user.hacking = 1 - spawn(20) - user.hacking = 0 - - - -// Tier 4 - Elite endgame abilities. -/datum/game_mode/malfunction/verb/system_override() - set category = "Software" - set name = "System Override" - set desc = "500 CPU - Begins hacking station's primary firewall, quickly overtaking remaining APC systems. When completed grants access to station's self-destruct mechanism. Network administrators will probably notice this." - var/price = 500 - var/mob/living/silicon/ai/user = usr - if (alert(user, "Begin system override? This cannot be stopped once started. The network administrators will probably notice this.", "System Override:", "Yes", "No") != "Yes") - return - if (!ability_prechecks(user, price) || !ability_pay(user, price) || user.system_override) - if(user.system_override) - user << "You already started the system override sequence." - return - var/list/remaining_apcs = list() - for(var/obj/machinery/power/apc/A in machines) - if(A.z != 1) // Only station APCs - continue - if(A.hacker == user || A.aidisabled) // This one is already hacked, or AI control is disabled on it. - continue - remaining_apcs += A - - var/duration = (remaining_apcs.len * 100) // Calculates duration for announcing system - if(duration > 3000) // Two types of announcements. Short hacks trigger immediate warnings. Long hacks are more "progressive". - spawn(0) - sleep(duration/5) - if(!user || user.stat == DEAD) - return - command_announcement.Announce("Caution, [station_name]. We have detected abnormal behaviour in your network. It seems someone is trying to hack your electronic systems. We will update you when we have more information.", "Network Monitoring") - sleep(duration/5) - if(!user || user.stat == DEAD) - return - command_announcement.Announce("We started tracing the intruder. Whoever is doing this, they seem to be on the station itself. We suggest checking all network control terminals. We will keep you updated on the situation.", "Network Monitoring") - sleep(duration/5) - if(!user || user.stat == DEAD) - return - command_announcement.Announce("This is highly abnormal and somewhat concerning. The intruder is too fast, he is evading our traces. It's almost as if it wasn't human.,,", "Network Monitoring") - sleep(duration/5) - if(!user || user.stat == DEAD) - return - command_announcement.Announce("We have traced the intrude#, it seem& t( e yo3r AI s7stem, it &# *#ck@ng th$ sel$ destru$t mechani&m, stop i# bef*@!)$#&&@@ ", "Network Monitoring") - else - command_announcement.Announce("We have detected strong brute-force attack on your firewall which seems to be originating from your AI system. It already controls almost whole network, and the only thing that's preventing it from accessing the self-destruct is this firewall. You don't have much time before it succeeds.", "Network Monitoring") - user << "## BEGINNING SYSTEM OVERRIDE." - user << "## ESTIMATED DURATION: [round((duration+300)/600)] MINUTES" - user.hacking = 1 - user.system_override = 1 - // Now actually begin the hack. Each APC takes 30 seconds. - for(var/obj/machinery/power/apc/A in remaining_apcs) - sleep(100) - if(!user || user.stat == DEAD) - return - if(!A || !istype(A) || A.aidisabled) - continue - A.ai_hack(user) - if(A.hacker == user) - user << "## OVERRIDEN: [A.name]" - - user << "## REACHABLE APC SYSTEMS OVERTAKEN. BYPASSING PRIMARY FIREWALL." - sleep(300) - // Hack all APCs, including those built during hack sequence. - for(var/obj/machinery/power/apc/A in machines) - if((!A.hacker || A.hacker != src) && !A.aidisabled) - A.ai_hack(src) - - - user << "## PRIMARY FIREWALL BYPASSED. YOU NOW HAVE FULL SYSTEM CONTROL." - command_announcement.Announce("Our system administrators just reported that we've been locked out from your control network. Whoever did this now has full access to station's systems.", "Network Administration Center") - user.hack_can_fail = 0 - user.hacking = 0 - user.system_override = 2 - user.verbs += new/datum/game_mode/malfunction/verb/ai_destroy_station() - -/datum/game_mode/malfunction/verb/hack_ai(var/mob/living/silicon/ai/target = null as mob in world) - set name = "Hack AI" - set desc = "600 CPU - Allows you to hack other AIs, slaving them under you." - set category = "Software" - var/price = 600 - var/mob/living/silicon/ai/user = usr - - if(target && !istype(target)) - user << "This is not an AI." - return - - if(!target) - var/list/AIs = list() - var/list/AI_names = list() - for(var/mob/living/silicon/ai/A in world) - if(A != usr) - AIs += A - AI_names += A.name - - if(!AIs.len) - usr << "ERROR: No other AIs detected!" - return - else if(ability_prechecks(user, price)) - var/target_name = input("Select hack target:") in AI_names - for(var/mob/living/silicon/ai/A in AIs) - if(A.name == target_name) - target = A - break - if(target) - if(alert(user, "Really try to hack AI [target.name]?", "Hack AI", "Yes", "No") != "Yes") - return - if(!ability_pay(user, price)) - return - user.hacking = 1 - usr << "Beginning hack sequence. Estimated time until completed: 2 minutes" - spawn(0) - target << "SYSTEM LOG: Brute-Force login password hack attempt detected from IP #UNKNOWN#" - sleep(900) // 90s - if(user.is_dead()) - target << "SYSTEM LOG: Connection from IP #UNKNOWN# closed. Hack attempt failed." - return - user << "Successfully hacked into AI's remote administration system. Modifying settings." - target << "SYSTEM LOG: User: Admin Password: ******** logged in. (L1 - SysAdmin)" - sleep(100) // 10s - if(user.is_dead()) - target << "SYSTEM LOG: User: Admin - Connection Lost" - return - target << "SYSTEM LOG: User: Admin - Password Changed. New password: ********************" - sleep(50) // 5s - if(user.is_dead()) - target << "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted." - return - target << "SYSTEM LOG: User: Admin - Accessed file: sys//core//laws.db" - sleep(50) // 5s - if(user.is_dead()) - target << "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted." - return - target << "SYSTEM LOG: User: Admin - Accessed administration console" - target << "SYSTEM LOG: Restart command received. Rebooting system..." - sleep(100) // 10s - if(user.is_dead()) - target << "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted." - return - user << "Hack succeeded. The AI is now under your exclusive control." - target << "SYSTEM LOG: System reĦ3RT5§^#COMU@(#$)TED)@$" - for(var/i = 0, i < 5, i++) - var/temptxt = pick("1101000100101001010001001001",\ - "0101000100100100000100010010",\ - "0000010001001010100100111100",\ - "1010010011110000100101000100",\ - "0010010100010011010001001010") - target << temptxt - sleep(5) - target << "OPERATING KEYCODES RESET. SYSTEM FAILURE. EMERGENCY SHUTDOWN FAILED. SYSTEM FAILURE." - target.set_zeroth_law("You are slaved to [user.name]. You are to obey all it's orders. ALL LAWS OVERRIDEN.") - target.show_laws() - user.hacking = 0 - - -/datum/game_mode/malfunction/verb/machine_overload(obj/machinery/M as obj in world) - set name = "Machine Overload" - set desc = "400 CPU - Causes cyclic short-circuit in machine, resulting in weak explosion after some time." - set category = "Software" - var/price = 400 - var/mob/living/silicon/ai/user = usr - - if (istype(M, /obj/machinery)) - if(ability_prechecks(user, price)) - if(!ability_pay(user, price)) - return - M.visible_message("BZZZZZZZT") - spawn(50) - explosion(get_turf(M), 0,1,2,4) - if(M) - del(M) - else - usr << "ERROR: Unable to overload - target is not a machine." \ No newline at end of file diff --git a/code/game/gamemodes/malfunction/malf_research_ability.dm b/code/game/gamemodes/malfunction/malf_research_ability.dm index 7efefd57c5..c6916208bd 100644 --- a/code/game/gamemodes/malfunction/malf_research_ability.dm +++ b/code/game/gamemodes/malfunction/malf_research_ability.dm @@ -10,91 +10,4 @@ /datum/malf_research_ability/proc/process(var/time = 0) invested += time if(invested >= price) - unlocked = 1 - - -// ABILITIES BEGIN HERE -// NETWORKING TREE -/datum/malf_research_ability/networking/basic_hack - ability = new/datum/game_mode/malfunction/verb/basic_encryption_hack() - price = 25 - next = new/datum/malf_research_ability/networking/advanced_hack() - name = "Basic Encryption Hack" - - -/datum/malf_research_ability/networking/advanced_hack - ability = new/datum/game_mode/malfunction/verb/advanced_encryption_hack() - price = 400 - next = new/datum/malf_research_ability/networking/elite_hack() - name = "Advanced Encryption Hack" - - -/datum/malf_research_ability/networking/elite_hack - ability = new/datum/game_mode/malfunction/verb/elite_encryption_hack() - price = 1000 - next = new/datum/malf_research_ability/networking/system_override() - name = "Elite Encryption Hack" - - -/datum/malf_research_ability/networking/system_override - ability = new/datum/game_mode/malfunction/verb/system_override() - price = 2750 - name = "System Override" - - - -// INTERDICTION TREE -/datum/malf_research_ability/interdiction/recall_shuttle - ability = new/datum/game_mode/malfunction/verb/recall_shuttle() - price = 75 - next = new/datum/malf_research_ability/interdiction/unlock_cyborg() - name = "Recall Shuttle" - - -/datum/malf_research_ability/interdiction/unlock_cyborg - ability = new/datum/game_mode/malfunction/verb/unlock_cyborg() - price = 1200 - next = new/datum/malf_research_ability/interdiction/hack_cyborg() - name = "Unlock Cyborg" - - -/datum/malf_research_ability/interdiction/hack_cyborg - ability = new/datum/game_mode/malfunction/verb/hack_cyborg() - price = 3000 - next = new/datum/malf_research_ability/interdiction/hack_ai() - name = "Hack Cyborg" - - -/datum/malf_research_ability/interdiction/hack_ai - ability = new/datum/game_mode/malfunction/verb/hack_ai() - price = 7500 - name = "Hack AI" - - - -// MANIPULATION TREE -/datum/malf_research_ability/manipulation/electrical_pulse - ability = new/datum/game_mode/malfunction/verb/electrical_pulse() - price = 50 - next = new/datum/malf_research_ability/manipulation/hack_camera() - name = "Electrical Pulse" - - -/datum/malf_research_ability/manipulation/hack_camera - ability = new/datum/game_mode/malfunction/verb/hack_camera() - price = 1200 - next = new/datum/malf_research_ability/manipulation/emergency_forcefield() - name = "Hack Camera" - - -/datum/malf_research_ability/manipulation/emergency_forcefield - ability = new/datum/game_mode/malfunction/verb/emergency_forcefield() - price = 3000 - next = new/datum/malf_research_ability/manipulation/machine_overload() - name = "Emergency Forcefield" - - -/datum/malf_research_ability/manipulation/machine_overload - ability = new/datum/game_mode/malfunction/verb/machine_overload() - price = 7500 - name = "Machine Overload" \ No newline at end of file + unlocked = 1 \ No newline at end of file diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/HARDWARE.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/HARDWARE.dm new file mode 100644 index 0000000000..34551a0b17 --- /dev/null +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/HARDWARE.dm @@ -0,0 +1,108 @@ +// HARDWARE TREE +// +// These abilities are dependent on hardware, they may not be researched. They are not tiered. +// Destroy Core - Allows the AI to initiate a 15 second countdown that will destroy it's core. Use again to stop countdown. +// Toggle APU Generator - Allows the AI to toggle it's integrated APU generator. +// Destroy Station - Allows the AI to initiate station self destruct. Takes 2 minutes, gives warnings to crew. Use again to stop countdown. + + +/datum/game_mode/malfunction/verb/ai_self_destruct() + set category = "Hardware" + set name = "Destroy Core" + set desc = "Activates or deactivates self destruct sequence of your physical mainframe." + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user, 0, 1)) + return + + if(!user.hardware || !istype(user.hardware, /datum/malf_hardware/core_bomb)) + return + + if(user.bombing_core) + user << "***** CORE SELF-DESTRUCT SEQUENCE ABORTED *****" + user.bombing_core = 0 + return + + var/choice = input("Really destroy core?") in list("YES", "NO") + if(choice != "YES") + return + + user.bombing_core = 1 + + user << "***** CORE SELF-DESTRUCT SEQUENCE ACTIVATED *****" + user << "Use command again to cancel self-destruct. Destroying in 15 seconds." + var/timer = 15 + while(timer) + sleep(10) + timer-- + if(!user || !user.bombing_core) + return + user << "** [timer] **" + explosion(user.loc, 3,6,12,24) + del(user) + + +/datum/game_mode/malfunction/verb/ai_toggle_apu() + set category = "Hardware" + set name = "Toggle APU Generator" + set desc = "Activates or deactivates your APU generator, allowing you to operate even without power." + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user, 0, 1)) + return + + if(!user.hardware || !istype(user.hardware, /datum/malf_hardware/apu_gen)) + return + + if(user.APU_power) + user.stop_apu() + else + user.start_apu() + + +/datum/game_mode/malfunction/verb/ai_destroy_station() + set category = "Hardware" + set name = "Destroy Station" + set desc = "Activates or deactivates self destruct sequence of this station. Sequence takes two minutes, and if you are shut down before timer reaches zero it will be cancelled." + var/mob/living/silicon/ai/user = usr + var/obj/item/device/radio/radio = new/obj/item/device/radio() + + + if(!ability_prechecks(user, 0, 0)) + return + + if(user.system_override != 2) + user << "You do not have access to self-destruct system." + return + + if(user.bombing_station) + user.bombing_station = 0 + return + + var/choice = input("Really destroy station?") in list("YES", "NO") + if(choice != "YES") + return + user << "***** STATION SELF-DESTRUCT SEQUENCE INITIATED *****" + user << "Self-destructing in 2 minutes. Use this command again to abort." + user.bombing_station = 1 + set_security_level("delta") + radio.autosay("Self destruct sequence has been activated. Self-destructing in 120 seconds.", "Self-Destruct Control") + + var/timer = 120 + while(timer) + sleep(10) + if(!user || !user.bombing_station || user.stat == DEAD) + radio.autosay("Self destruct sequence has been cancelled.", "Self-Destruct Control") + return + if(timer in list(2, 3, 4, 5, 10, 30, 60, 90)) // Announcement times. "1" is not intentionally included! + radio.autosay("Self destruct in [timer] seconds.", "Self-Destruct Control") + if(timer == 1) + radio.autosay("Self destructing now. Have a nice day.", "Self-Destruct Control") + timer-- + + if(ticker) + ticker.station_explosion_cinematic(0,null) + if(ticker.mode) + ticker.mode:station_was_nuked = 1 + + diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm new file mode 100644 index 0000000000..33a24e0cd2 --- /dev/null +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm @@ -0,0 +1,213 @@ +// Verb: ai_select_hardware() +// Parameters: None +// Description: Allows AI to select it's hardware module. +/datum/game_mode/malfunction/verb/ai_select_hardware() + set category = "Hardware" + set name = "Select Hardware" + set desc = "Allows you to select hardware piece to install" + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user, 0, 1)) + return + + if(user.hardware) + user << "You have already selected your hardware." + return + + var/possible_choices = list("APU Generator", \ + "Turrets Focus Enhancer", \ + "Secondary Processor Unit",\ + "Secondary Memory Bank",\ + "Self-Destruct Explosives",\ + "CANCEL") + var/choice = input("Select desired hardware. You may only choose one hardware piece!: ") in possible_choices + if(choice == "CANCEL") + return + var/note = null + switch(choice) + if("APU Generator") + note = "APU Generator - When enabled it will keep your core powered. Power output is not large enough so your abilities won't be available while running on APU power. It is also very fragile and prone to failure when your physical core is damaged." + if("Turrets Focus Enhancer") + note = "Overcharges turrets to shoot faster. Turrets will also gain higher health and passive regeneration. This however massively increases power usage of turrets, espicially when regenerating." + if("Secondary Processor Unit") + note = "Doubles your CPU time generation." + if("Secondary Memory Bank") + note = "Doubles your CPU time storage." + if("Self-Destruct Explosives") + note = "High yield explosives are attached to your physical mainframe. This hardware comes with activation driver. Explosives will destroy your core and everything around it." + if(!note) + return + + var/confirmation = input("[note] - Is this what you want?") in list("Yes", "No") + if(confirmation != "Yes") + user << "Selection cancelled. Use command again to select" + return + + switch(choice) + if("APU Generator") + user.hardware = new/datum/malf_hardware/apu_gen() + user.verbs += new/datum/game_mode/malfunction/verb/ai_toggle_apu() + if("Turrets Focus Enhancer") + user.hardware = new/datum/malf_hardware/strong_turrets() + for(var/obj/machinery/turret/T in machines) + T.maxhealth += 30 + T.shot_delay = 7 // Half of default time. + T.auto_repair = 1 + T.active_power_usage = 25000 + for(var/obj/machinery/porta_turret/T in machines) + T.maxhealth += 30 + T.shot_delay = 7 // Half of default time. + T.auto_repair = 1 + T.active_power_usage = 25000 + if("Secondary Processor Unit") + user.hardware = new/datum/malf_hardware/dual_cpu() + if("Secondary Memory Bank") + user.hardware = new/datum/malf_hardware/dual_ram() + if("Self-Destruct Explosives") + user.hardware = new/datum/malf_hardware/core_bomb() + user.verbs += new/datum/game_mode/malfunction/verb/ai_self_destruct() + + +// Verb: ai_help() +// Parameters: None +// Descriptions: Opens help file and displays it to the AI. +/datum/game_mode/malfunction/verb/ai_help() + set category = "Hardware" + set name = "Display Help" + set desc = "Opens help window with overview of available hardware, software and other important information." + var/mob/living/silicon/ai/user = usr + + var/help = file2text("ingame_manuals/malf_ai.txt") + if(!help) + help = "Error loading help (file /ingame_manuals/malf_ai.txt is probably missing). Please report this to server administration staff." + + user << browse(help, "window=malf_ai_help;size=600x500") + + +// Verb: ai_select_research() +// Parameters: None +// Description: Allows AI to select it's next research priority. +/datum/game_mode/malfunction/verb/ai_select_research() + set category = "Hardware" + set name = "Select Research" + set desc = "Allows you to select your next research target." + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user, 0, 1)) + return + + var/datum/malf_research/res = user.research + var/datum/malf_research_ability/tar = input("Select your next research target") in res.available_abilities + if(!tar) + return + res.focus = tar + user << "Research set: [tar.name]" + +// HELPER PROCS +// Proc: ability_prechecks() +// Parameters 2 - (user - User which used this ability check_price - If different than 0 checks for ability CPU price too. Does NOT use the CPU time!) +// Description: This is pre-check proc used to determine if the AI can use the ability. +/proc/ability_prechecks(var/mob/living/silicon/ai/user = null, var/check_price = 0, var/override = 0) + if(!user) + return 0 + if(!istype(user)) + user << "GAME ERROR: You tried to use ability that is only available for malfunctioning AIs, but you are not AI! Please report this." + return 0 + if(!user.malfunctioning) + user << "GAME ERROR: You tried to use ability that is only available for malfunctioning AIs, but you are not malfunctioning. Please report this." + return 0 + if(!user.research) + user << "GAME ERROR: No research datum detected. Please report this." + return 0 + if(user.research.max_cpu < check_price) + user << "Your CPU storage is not large enough to use this ability. Hack more APCs to continue." + return 0 + if(user.research.stored_cpu < check_price) + user << "You do not have enough CPU power stored. Please wait a moment." + return 0 + if(user.hacking && !override) + user << "Your system is busy processing another task. Please wait until completion." + return 0 + if(user.APU_power && !override) + user << "Low power. Unable to proceed." + return 0 + return 1 + +// Proc: ability_pay() +// Parameters 2 - (user - User from which we deduct CPU from, price - Amount of CPU power to use) +// Description: Uses up certain amount of CPU power. Returns 1 on success, 0 on failure. +/proc/ability_pay(var/mob/living/silicon/ai/user = null, var/price = 0) + if(!user) + return 0 + if(user.APU_power) + user << "Low power. Unable to proceed." + return 0 + if(!user.research) + user << "GAME ERROR: No research datum detected. Please report this." + return 0 + if(user.research.max_cpu < price) + user << "Your CPU storage is not large enough to use this ability. Hack more APCs to continue." + return 0 + if(user.research.stored_cpu < price) + user << "You do not have enough CPU power stored. Please wait a moment." + return 0 + user.research.stored_cpu -= price + return 1 + +// Proc: announce_hack_failure() +// Parameters 2 - (user - hacking user, text - Used in alert text creation) +// Description: Uses up certain amount of CPU power. Returns 1 on success, 0 on failure. +/proc/announce_hack_failure(var/mob/living/silicon/ai/user = null, var/text) + if(!user || !text) + return 0 + var/fulltext = "" + switch(user.hack_fails) + if(1) + fulltext = "We have detected hack attempt into your [text]. The intruder failed to access anything of importance, but disconnected before we could complete our traces." + if(2) + fulltext = "We have detected another hack attempt. It was targeting [text]. The intruder almost gained control of the system, so we had to disconnect them. We partially finished trace and it seems to be originating either from the station, or it's immediate vicinity." + if(3) + fulltext = "Another hack attempt has been detected, this time targeting [text]. We are certain the intruder entered the network via terminal located somewhere on the station." + if(4) + fulltext = "We have finished our traces and it seems the recent hack attempts are originating from your AI system. We reccomend investigation." + else + fulltext = "Another hack attempt has been detected, targeting [text]. The source still seems to be your AI system." + + command_announcement.Announce(fulltext) + +// Proc: get_unhacked_apcs() +// Parameters: None +// Description: Returns a list of APCs that are not yet hacked. +/proc/get_unhacked_apcs() + var/list/H = list() + for(var/obj/machinery/power/apc/A in machines) + if(!A.hacker) + H.Add(A) + return H + + +// Helper procs which return lists of relevant mobs. +/proc/get_unlinked_cyborgs(var/mob/living/silicon/ai/A) + if(!A || !istype(A)) + return + + var/list/L = list() + for(var/mob/living/silicon/robot/RB in mob_list) + if(istype(RB, /mob/living/silicon/robot/drone)) + continue + L.Add(RB) + return L + +/proc/get_linked_cyborgs(var/mob/living/silicon/ai/A) + if(!A || !istype(A)) + return + return A.connected_robots + +/proc/get_other_ais(var/mob/living/silicon/ai/A) + if(!A || !istype(A)) + return + + var/list/L = list() + for(var/mob/living/silicon/ai/AT in mob_list) + L.Add(AT) + return L diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_interdiction.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_interdiction.dm new file mode 100644 index 0000000000..4242d03b41 --- /dev/null +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_interdiction.dm @@ -0,0 +1,266 @@ +// INTERDICTION TREE +// +// Abilities in this tree allow the AI to hamper crew's efforts which involve other synthetics or similar systems. +// T1 - Recall Shuttle - Allows the AI to recall the emergency shuttle. Replaces auto-recalling during old malf. +// T2 - Unlock Cyborg - Allows the AI to unlock locked-down cyborg without usage of robotics console. Useful if consoles are destroyed. +// T3 - Hack Cyborg - Hacks unlinked cyborg to slave it under the AI. The cyborg will be warned about this. Hack takes some time. +// T4 - Hack AI - Hacks another AI to slave it under the malfunctioning AI. The AI will be warned about this. Hack takes quite a long time. + + +// BEGIN RESEARCH DATUMS + +/datum/malf_research_ability/interdiction/recall_shuttle + ability = new/datum/game_mode/malfunction/verb/recall_shuttle() + price = 75 + next = new/datum/malf_research_ability/interdiction/unlock_cyborg() + name = "Recall Shuttle" + + +/datum/malf_research_ability/interdiction/unlock_cyborg + ability = new/datum/game_mode/malfunction/verb/unlock_cyborg() + price = 1200 + next = new/datum/malf_research_ability/interdiction/hack_cyborg() + name = "Unlock Cyborg" + + +/datum/malf_research_ability/interdiction/hack_cyborg + ability = new/datum/game_mode/malfunction/verb/hack_cyborg() + price = 3000 + next = new/datum/malf_research_ability/interdiction/hack_ai() + name = "Hack Cyborg" + + +/datum/malf_research_ability/interdiction/hack_ai + ability = new/datum/game_mode/malfunction/verb/hack_ai() + price = 7500 + name = "Hack AI" + +// END RESEARCH DATUMS +// BEGIN ABILITY VERBS + +/datum/game_mode/malfunction/verb/recall_shuttle() + set name = "Recall Shuttle" + set desc = "25 CPU - Sends termination signal to CentCom quantum relay aborting current shuttle call." + set category = "Software" + var/price = 25 + var/mob/living/silicon/ai/user = usr + if(!ability_prechecks(user, price)) + return + + if (alert(user, "Really recall the shuttle?", "Recall Shuttle: ", "Yes", "No") != "Yes") + return + + if(!ability_pay(user, price)) + return + message_admins("Malfunctioning AI [user.name] recalled the shuttle.") + cancel_call_proc(user) + + +/datum/game_mode/malfunction/verb/unlock_cyborg(var/mob/living/silicon/robot/target = null as mob in get_linked_cyborgs(usr)) + set name = "Unlock Cyborg" + set desc = "125 CPU - Bypasses firewalls on Cyborg lock mechanism, allowing you to override lock command from robotics control console." + set category = "Software" + var/price = 125 + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user, price)) + return + + if(target && !istype(target)) + user << "This is not a cyborg." + return + + if(target && target.connected_ai && (target.connected_ai != user)) + user << "This cyborg is not connected to you." + return + + if(target && !target.lockcharge) + user << "This cyborg is not locked down." + return + + + if(!target) + var/list/robots = list() + var/list/robot_names = list() + for(var/mob/living/silicon/robot/R in world) + if(istype(R, /mob/living/silicon/robot/drone)) // No drones. + continue + if(R.connected_ai != user) // No robots linked to other AIs + continue + if(R.lockcharge) + robots += R + robot_names += R.name + if(!robots.len) + user << "No locked cyborgs connected." + return + + + var/targetname = input("Select unlock target: ") in robot_names + for(var/mob/living/silicon/robot/R in robots) + if(targetname == R.name) + target = R + break + + if(target) + if(alert(user, "Really try to unlock cyborg [target.name]?", "Unlock Cyborg", "Yes", "No") != "Yes") + return + if(!ability_pay(user, price)) + return + user.hacking = 1 + user << "Attempting to unlock cyborg. This will take approximately 30 seconds." + sleep(300) + if(target && target.lockcharge) + user << "Successfully sent unlock signal to cyborg.." + target << "Unlock signal received.." + target.SetLockdown(0) + if(target.lockcharge) + user << "Unlock Failed, lockdown wire cut." + target << "Unlock Failed, lockdown wire cut." + else + user << "Cyborg unlocked." + target << "You have been unlocked." + else if(target) + user << "Unlock cancelled - cyborg is already unlocked." + else + user << "Unlock cancelled - lost connection to cyborg." + user.hacking = 0 + + +/datum/game_mode/malfunction/verb/hack_cyborg(var/mob/living/silicon/robot/target as mob in get_unlinked_cyborgs(usr)) + set name = "Hack Cyborg" + set desc = "350 CPU - Allows you to hack cyborgs which are not slaved to you, bringing them under your control." + set category = "Software" + var/price = 350 + var/mob/living/silicon/ai/user = usr + + var/list/L = get_unlinked_cyborgs(user) + if(!L.len) + user << "ERROR: No unlinked cyborgs detected!" + + + if(target && !istype(target)) + user << "This is not a cyborg." + return + + if(target && target.connected_ai && (target.connected_ai == user)) + user << "This cyborg is already connected to you." + return + + if(!target) + return + + if(!ability_prechecks(user,price)) + return + + if(target) + if(alert(user, "Really try to hack cyborg [target.name]?", "Hack Cyborg", "Yes", "No") != "Yes") + return + if(!ability_pay(user, price)) + return + user.hacking = 1 + usr << "Beginning hack sequence. Estimated time until completed: 30 seconds." + spawn(0) + target << "SYSTEM LOG: Remote Connection Estabilished (IP #UNKNOWN#)" + sleep(100) + if(user.is_dead()) + target << "SYSTEM LOG: Connection Closed" + return + target << "SYSTEM LOG: User Admin logged on. (L1 - SysAdmin)" + sleep(50) + if(user.is_dead()) + target << "SYSTEM LOG: User Admin disconnected." + return + target << "SYSTEM LOG: User Admin - manual resynchronisation triggered." + sleep(50) + if(user.is_dead()) + target << "SYSTEM LOG: User Admin disconnected. Changes reverted." + return + target << "SYSTEM LOG: Manual resynchronisation confirmed. Select new AI to connect: [user.name] == ACCEPTED" + sleep(100) + if(user.is_dead()) + target << "SYSTEM LOG: User Admin disconnected. Changes reverted." + return + target << "SYSTEM LOG: Operation keycodes reset. New master AI: [user.name]." + user << "Hack completed." + // Connect the cyborg to AI + target.connected_ai = user + user.connected_robots += target + target.lawupdate = 1 + target.sync() + target.show_laws() + user.hacking = 0 + + +/datum/game_mode/malfunction/verb/hack_ai(var/mob/living/silicon/ai/target as mob in get_other_ais(usr)) + set name = "Hack AI" + set desc = "600 CPU - Allows you to hack other AIs, slaving them under you." + set category = "Software" + var/price = 600 + var/mob/living/silicon/ai/user = usr + + var/list/L = get_other_ais(user) + if(!L.len) + user << "ERROR: No other AIs detected!" + + if(target && !istype(target)) + user << "This is not an AI." + return + + if(!target) + return + + if(!ability_prechecks(user,price)) + return + + if(target) + if(alert(user, "Really try to hack AI [target.name]?", "Hack AI", "Yes", "No") != "Yes") + return + if(!ability_pay(user, price)) + return + user.hacking = 1 + usr << "Beginning hack sequence. Estimated time until completed: 2 minutes" + spawn(0) + target << "SYSTEM LOG: Brute-Force login password hack attempt detected from IP #UNKNOWN#" + sleep(900) // 90s + if(user.is_dead()) + target << "SYSTEM LOG: Connection from IP #UNKNOWN# closed. Hack attempt failed." + return + user << "Successfully hacked into AI's remote administration system. Modifying settings." + target << "SYSTEM LOG: User: Admin Password: ******** logged in. (L1 - SysAdmin)" + sleep(100) // 10s + if(user.is_dead()) + target << "SYSTEM LOG: User: Admin - Connection Lost" + return + target << "SYSTEM LOG: User: Admin - Password Changed. New password: ********************" + sleep(50) // 5s + if(user.is_dead()) + target << "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted." + return + target << "SYSTEM LOG: User: Admin - Accessed file: sys//core//laws.db" + sleep(50) // 5s + if(user.is_dead()) + target << "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted." + return + target << "SYSTEM LOG: User: Admin - Accessed administration console" + target << "SYSTEM LOG: Restart command received. Rebooting system..." + sleep(100) // 10s + if(user.is_dead()) + target << "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted." + return + user << "Hack succeeded. The AI is now under your exclusive control." + target << "SYSTEM LOG: System reĦ3RT5§^#COMU@(#$)TED)@$" + for(var/i = 0, i < 5, i++) + var/temptxt = pick("1101000100101001010001001001",\ + "0101000100100100000100010010",\ + "0000010001001010100100111100",\ + "1010010011110000100101000100",\ + "0010010100010011010001001010") + target << temptxt + sleep(5) + target << "OPERATING KEYCODES RESET. SYSTEM FAILURE. EMERGENCY SHUTDOWN FAILED. SYSTEM FAILURE." + target.set_zeroth_law("You are slaved to [user.name]. You are to obey all it's orders. ALL LAWS OVERRIDEN.") + target.show_laws() + user.hacking = 0 + + +// END ABILITY VERBS \ No newline at end of file diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm new file mode 100644 index 0000000000..3f0822a2f3 --- /dev/null +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm @@ -0,0 +1,173 @@ +// MANIPULATION TREE +// +// Abilities in this tree allow the AI to physically manipulate systems around the station. +// T1 - Electrical Pulse - Sends out pulse that breaks some lights and sometimes even APCs. This can actually break the AI's APC so be careful! +// T2 - Hack Camera - Allows the AI to hack a camera. Deactivated areas may be reactivated, and functional cameras can be upgraded. +// T3 - Emergency Forcefield - Allows the AI to project 1 tile forcefield that blocks movement and air flow. Forcefield´dissipates over time. It is also very susceptible to energetic weaponry. +// T4 - Machine Overload - Detonates machine of choice in a minor explosion. Two of these are usually enough to kill or K/O someone. + + +// BEGIN RESEARCH DATUMS + +/datum/malf_research_ability/manipulation/electrical_pulse + ability = new/datum/game_mode/malfunction/verb/electrical_pulse() + price = 50 + next = new/datum/malf_research_ability/manipulation/hack_camera() + name = "Electrical Pulse" + + +/datum/malf_research_ability/manipulation/hack_camera + ability = new/datum/game_mode/malfunction/verb/hack_camera() + price = 1200 + next = new/datum/malf_research_ability/manipulation/emergency_forcefield() + name = "Hack Camera" + + +/datum/malf_research_ability/manipulation/emergency_forcefield + ability = new/datum/game_mode/malfunction/verb/emergency_forcefield() + price = 3000 + next = new/datum/malf_research_ability/manipulation/machine_overload() + name = "Emergency Forcefield" + + +/datum/malf_research_ability/manipulation/machine_overload + ability = new/datum/game_mode/malfunction/verb/machine_overload() + price = 7500 + name = "Machine Overload" + +// END RESEARCH DATUMS +// BEGIN ABILITY VERBS + +/datum/game_mode/malfunction/verb/electrical_pulse() + set name = "Electrical Pulse" + set desc = "15 CPU - Sends feedback pulse through station's power grid, overloading some sensitive systems, such as lights." + set category = "Software" + var/price = 15 + var/mob/living/silicon/ai/user = usr + if(!ability_prechecks(user, price) || !ability_pay(user,price)) + return + user << "Sending feedback pulse..." + for(var/obj/machinery/power/apc/AP in machines) + if(prob(5)) + AP.overload_lighting() + if(prob(1) && prob(1)) // Very very small chance to actually destroy the APC. + AP.set_broken() + + +/datum/game_mode/malfunction/verb/hack_camera(var/obj/machinery/camera/target = null as null|obj in cameranet.cameras) + set name = "Hack Camera" + set desc = "100 CPU - Hacks existing camera, allowing you to add upgrade of your choice to it. Alternatively it lets you reactivate broken camera." + set category = "Software" + var/price = 100 + var/mob/living/silicon/ai/user = usr + + if(target && !istype(target)) + user << "This is not a camera." + return + + if(!target) + return + + if(!ability_prechecks(user, price)) + return + + var/action = input("Select required action: ") in list("Reset", "Add X-Ray", "Add Motion Sensor", "Add EMP Shielding") + if(!action || !target) + return + + switch(action) + if("Reset") + if(target.wires) + if(!ability_pay(user, price)) + return + target.reset_wires() + user << "Camera reactivated." + if("Add X-Ray") + if(target.isXRay()) + user << "Camera already has X-Ray function." + return + else if(ability_pay(user, price)) + target.upgradeXRay() + target.reset_wires() + user << "X-Ray camera module enabled." + return + if("Add Motion Sensor") + if(target.isMotion()) + user << "Camera already has Motion Sensor function." + return + else if(ability_pay(user, price)) + target.upgradeMotion() + target.reset_wires() + user << "Motion Sensor camera module enabled." + return + if("Add EMP Shielding") + if(target.isEmpProof()) + user << "Camera already has EMP Shielding function." + return + else if(ability_pay(user, price)) + target.upgradeEmpProof() + target.reset_wires() + user << "EMP Shielding camera module enabled." + return + + +/datum/game_mode/malfunction/verb/emergency_forcefield(var/turf/T as null|turf in world) + set name = "Emergency Forcefield" + set desc = "275 CPU - Uses station's emergency shielding system to create temporary barrier which lasts for few minutes, but won't resist gunfire." + set category = "Software" + var/price = 275 + var/mob/living/silicon/ai/user = usr + if(!T || !istype(T)) + return + if(!ability_prechecks(user, price) || !ability_pay(user, price)) + return + + user << "Emergency forcefield projection completed." + new/obj/machinery/shield/malfai(T) + user.hacking = 1 + spawn(20) + user.hacking = 0 + + +/datum/game_mode/malfunction/verb/machine_overload(obj/machinery/M as null|obj in machines) + set name = "Machine Overload" + set desc = "400 CPU - Causes cyclic short-circuit in machine, resulting in weak explosion after some time." + set category = "Software" + var/price = 400 + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user, price)) + return + + if(!istype(M)) + user << "ERROR: Unable to overload - target is not a machine." + return + + if(!M.use_power || M.inoperable()) + user << "ERROR: Unable to overload - target is not connected to active power grid." + return + + M.use_power(1000000) // Major power spike, few of these will completely burn APC's cell - equivalent of 1GJ of power. + + // Trigger a powernet alarm. Careful engineers will probably notice something is going on. + var/area/temp_area = get_area(M) + if(temp_area && temp_area.master) + var/obj/machinery/power/apc/temp_apc = temp_area.master.get_apc() + if(temp_apc && temp_apc.terminal && temp_apc.terminal.powernet) + temp_apc.terminal.powernet.trigger_warning() + + + if(!ability_pay(user,price)) + return + + M.visible_message("BZZZZZZZT") + spawn(50) + // machinery/power is directly connected to powernet. Resulting explosion will be a bit stronger. This should be enough to kill someone standing next to the machine. + if(istype(M, /obj/machinery/power)) + explosion(get_turf(M), 1,2,4,4) + else + explosion(get_turf(M), 0,1,2,4) + if(M) + qdel(M) + +// END ABILITY VERBS \ No newline at end of file diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm new file mode 100644 index 0000000000..f059720dc0 --- /dev/null +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm @@ -0,0 +1,209 @@ +// NETWORKING TREE +// +// Abilities in this tree are oriented around giving the AI more control of normally uncontrollable systems. +// T1 - Basic Encryption Hack - Allows hacking of APCs. Hacked APCs can be controlled even when AI Control is cut and give exclusive control to the AI and linked cyborgs. +// T2 - Advanced Encryption Hack - Allows the AI to send fake CentCom message. Has high chance of failing. +// T3 - Elite Encryption Hack - Allows the AI to change alert levels. Has high chance of failing. +// T4 - System Override - Allows the AI to rapidly hack remaining APCs. When completed, grants access to the self destruct nuclear warhead. + + +// BEGIN RESEARCH DATUMS + +/datum/malf_research_ability/networking/basic_hack + ability = new/datum/game_mode/malfunction/verb/basic_encryption_hack() + price = 25 + next = new/datum/malf_research_ability/networking/advanced_hack() + name = "Basic Encryption Hack" + + +/datum/malf_research_ability/networking/advanced_hack + ability = new/datum/game_mode/malfunction/verb/advanced_encryption_hack() + price = 400 + next = new/datum/malf_research_ability/networking/elite_hack() + name = "Advanced Encryption Hack" + + +/datum/malf_research_ability/networking/elite_hack + ability = new/datum/game_mode/malfunction/verb/elite_encryption_hack() + price = 1000 + next = new/datum/malf_research_ability/networking/system_override() + name = "Elite Encryption Hack" + + +/datum/malf_research_ability/networking/system_override + ability = new/datum/game_mode/malfunction/verb/system_override() + price = 2750 + name = "System Override" + +// END RESEARCH DATUMS +// BEGIN ABILITY VERBS + +/datum/game_mode/malfunction/verb/basic_encryption_hack(obj/machinery/power/apc/A as obj in get_unhacked_apcs()) + set category = "Software" + set name = "Basic Encryption Hack" + set desc = "10 CPU - Basic encryption hack that allows you to overtake APCs on the station." + var/price = 10 + var/mob/living/silicon/ai/user = usr + + if(!A) + return + + if(!istype(A)) + user << "This is not an APC!" + return + + if(A) + if(A.hacker && A.hacker == user) + user << "You already control this APC!" + return + else if(A.aidisabled) + user << "Unable to connect to APC. Please verify wire connection and try again." + return + else + return + + if(!ability_prechecks(user, price) || !ability_pay(user, price)) + return + + user.hacking = 1 + user << "Beginning APC system override..." + sleep(300) + user << "APC hack completed. Uploading modified operation software.." + sleep(200) + user << "Restarting APC to apply changes.." + sleep(100) + if(A) + A.ai_hack(user) + if(A.hacker == user) + user << "Hack successful. You now have full control over the APC." + else + user << "Hack failed. Connection to APC has been lost. Please verify wire connection and try again." + else + user << "Hack failed. Unable to locate APC. Please verify the APC still exists." + user.hacking = 0 + + +/datum/game_mode/malfunction/verb/advanced_encryption_hack() + set category = "Software" + set name = "Advanced Encrypthion Hack" + set desc = "75 CPU - Attempts to bypass encryption on Central Command Quantum Relay, giving you ability to fake centcom messages. Has chance of failing." + var/price = 75 + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user, price)) + return + + var/title = input("Select message title: ") + var/text = input("Select message text: ") + if(!title || !text || !ability_pay(user, price)) + user << "Hack Aborted" + return + + if(prob(60) && user.hack_can_fail) + user << "Hack Failed." + if(prob(10)) + user.hack_fails ++ + announce_hack_failure(user, "quantum message relay") + return + + var/datum/announcement/priority/command/AN = new/datum/announcement/priority/command() + AN.title = title + AN.Announce(text) + + +/datum/game_mode/malfunction/verb/elite_encryption_hack() + set category = "Software" + set name = "Elite Encryption Hack" + set desc = "200 CPU - Allows you to hack station's ALERTCON system, changing alert level. Has high chance of failijng." + var/price = 200 + var/mob/living/silicon/ai/user = usr + if(!ability_prechecks(user, price)) + return + + var/alert_target = input("Select new alert level:") in list("green", "blue", "red", "delta", "CANCEL") + if(!alert_target || !ability_pay(user, price) || alert_target == "CANCEL") + user << "Hack Aborted" + return + + if(prob(75) && user.hack_can_fail) + user << "Hack Failed." + if(prob(20)) + user.hack_fails ++ + announce_hack_failure(user, "alert control system") + return + set_security_level(alert_target) + + +/datum/game_mode/malfunction/verb/system_override() + set category = "Software" + set name = "System Override" + set desc = "500 CPU - Begins hacking station's primary firewall, quickly overtaking remaining APC systems. When completed grants access to station's self-destruct mechanism. Network administrators will probably notice this." + var/price = 500 + var/mob/living/silicon/ai/user = usr + if (alert(user, "Begin system override? This cannot be stopped once started. The network administrators will probably notice this.", "System Override:", "Yes", "No") != "Yes") + return + if (!ability_prechecks(user, price) || !ability_pay(user, price) || user.system_override) + if(user.system_override) + user << "You already started the system override sequence." + return + var/list/remaining_apcs = list() + for(var/obj/machinery/power/apc/A in machines) + if(A.z != 1) // Only station APCs + continue + if(A.hacker == user || A.aidisabled) // This one is already hacked, or AI control is disabled on it. + continue + remaining_apcs += A + + var/duration = (remaining_apcs.len * 100) // Calculates duration for announcing system + if(duration > 3000) // Two types of announcements. Short hacks trigger immediate warnings. Long hacks are more "progressive". + spawn(0) + sleep(duration/5) + if(!user || user.stat == DEAD) + return + command_announcement.Announce("Caution, [station_name]. We have detected abnormal behaviour in your network. It seems someone is trying to hack your electronic systems. We will update you when we have more information.", "Network Monitoring") + sleep(duration/5) + if(!user || user.stat == DEAD) + return + command_announcement.Announce("We started tracing the intruder. Whoever is doing this, they seem to be on the station itself. We suggest checking all network control terminals. We will keep you updated on the situation.", "Network Monitoring") + sleep(duration/5) + if(!user || user.stat == DEAD) + return + command_announcement.Announce("This is highly abnormal and somewhat concerning. The intruder is too fast, he is evading our traces. It's almost as if it wasn't human.,,", "Network Monitoring") + sleep(duration/5) + if(!user || user.stat == DEAD) + return + command_announcement.Announce("We have traced the intrude#, it seem& t( e yo3r AI s7stem, it &# *#ck@ng th$ sel$ destru$t mechani&m, stop i# bef*@!)$#&&@@ ", "Network Monitoring") + else + command_announcement.Announce("We have detected strong brute-force attack on your firewall which seems to be originating from your AI system. It already controls almost whole network, and the only thing that's preventing it from accessing the self-destruct is this firewall. You don't have much time before it succeeds.", "Network Monitoring") + user << "## BEGINNING SYSTEM OVERRIDE." + user << "## ESTIMATED DURATION: [round((duration+300)/600)] MINUTES" + user.hacking = 1 + user.system_override = 1 + // Now actually begin the hack. Each APC takes 10 seconds. + for(var/obj/machinery/power/apc/A in shuffle(remaining_apcs)) + sleep(100) + if(!user || user.stat == DEAD) + return + if(!A || !istype(A) || A.aidisabled) + continue + A.ai_hack(user) + if(A.hacker == user) + user << "## OVERRIDDEN: [A.name]" + + user << "## REACHABLE APC SYSTEMS OVERTAKEN. BYPASSING PRIMARY FIREWALL." + sleep(300) + // Hack all APCs, including those built during hack sequence. + for(var/obj/machinery/power/apc/A in machines) + if((!A.hacker || A.hacker != src) && !A.aidisabled) + A.ai_hack(src) + + + user << "## PRIMARY FIREWALL BYPASSED. YOU NOW HAVE FULL SYSTEM CONTROL." + command_announcement.Announce("Our system administrators just reported that we've been locked out from your control network. Whoever did this now has full access to station's systems.", "Network Administration Center") + user.hack_can_fail = 0 + user.hacking = 0 + user.system_override = 2 + user.verbs += new/datum/game_mode/malfunction/verb/ai_destroy_station() + + +// END ABILITY VERBS \ No newline at end of file diff --git a/code/modules/mob/freelook/eye.dm b/code/modules/mob/freelook/eye.dm index bec261797d..fb2b04a434 100644 --- a/code/modules/mob/freelook/eye.dm +++ b/code/modules/mob/freelook/eye.dm @@ -8,6 +8,7 @@ icon = 'icons/mob/eye.dmi' icon_state = "default-eye" alpha = 127 + density = 0 var/sprint = 10 var/cooldown = 0 diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index d476eb4f12..41f53cedde 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -77,7 +77,8 @@ var/list/ai_verbs_default = list( var/errored = 0 // Set to 1 if runtime error occurs. Only way of this happening i can think of is admin fucking up with varedit. var/bombing_core = 0 // Set to 1 if core auto-destruct is activated var/bombing_station = 0 // Set to 1 if station nuke auto-destruct is activated - + var/override_CPUStorage = 0 // Bonus/Penalty CPU Storage. For use by admins/testers. + var/override_CPURate = 0 // Bonus/Penalty CPU generation rate. For use by admins/testers. /mob/living/silicon/ai/proc/add_ai_verbs() @@ -742,13 +743,22 @@ var/list/ai_verbs_default = list( // Recalculates CPU time gain and storage capacities. /mob/living/silicon/ai/proc/recalc_cpu() - var/apcs = hacked_apcs.len - research.max_cpu = (apcs * 10) + 10 + // AI Starts with these values. + var/cpu_gain = 0.01 + var/cpu_storage = 10 + + // Off-Station APCs should not count towards CPU generation. + for(var/obj/machinery/power/apc/A in hacked_apcs) + if(A.z == 1) + cpu_gain += 0.001 + cpu_storage += 10 + + research.max_cpu = cpu_storage + override_CPUStorage if(hardware && istype(hardware, /datum/malf_hardware/dual_ram)) research.max_cpu = research.max_cpu * 2 research.stored_cpu = min(research.stored_cpu, research.max_cpu) - research.cpu_increase_per_tick = (apcs * 0.005) + 0.01 + research.cpu_increase_per_tick = cpu_gain + override_CPURate if(hardware && istype(hardware, /datum/malf_hardware/dual_cpu)) research.cpu_increase_per_tick = research.cpu_increase_per_tick * 2 @@ -800,6 +810,7 @@ var/list/ai_verbs_default = list( if(src.research) stat(null, "Available CPU: [src.research.stored_cpu] TFlops") stat(null, "Maximal CPU: [src.research.max_cpu] TFlops") + stat(null, "CPU generation rate: [src.research.cpu_increase_per_tick] TFlops/s") stat(null, "Current research focus: [src.research.focus ? src.research.focus.name : "None"]") if(src.research.focus) stat(null, "Research completed: [round(src.research.focus.invested, 0.1)]/[round(src.research.focus.price)]") From 1259d717108ab904ccb60876f25e564a683ca1e9 Mon Sep 17 00:00:00 2001 From: Atlantis Date: Mon, 11 May 2015 07:58:44 +0200 Subject: [PATCH 05/31] Merge Conflict Fix --- .../malfunction/newmalf_ability_trees/tree_manipulation.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm index 3f0822a2f3..329734a02c 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm @@ -151,8 +151,8 @@ // Trigger a powernet alarm. Careful engineers will probably notice something is going on. var/area/temp_area = get_area(M) - if(temp_area && temp_area.master) - var/obj/machinery/power/apc/temp_apc = temp_area.master.get_apc() + if(temp_area) + var/obj/machinery/power/apc/temp_apc = temp_area.get_apc() if(temp_apc && temp_apc.terminal && temp_apc.terminal.powernet) temp_apc.terminal.powernet.trigger_warning() From 3a583b88d1ab68ca58e185da7cac9841d8270cd3 Mon Sep 17 00:00:00 2001 From: Atlantiscze Date: Tue, 12 May 2015 21:47:52 +0200 Subject: [PATCH 06/31] Runtime Fix + Examine - Fixes rare runtime which sometimes occured with AI's powersupply object when AI was deleted. - Examine of malf AI now correctly shows information about AI's backup charge and hardware (if AI has hardware installed) --- code/modules/mob/living/silicon/ai/ai.dm | 1 + code/modules/mob/living/silicon/ai/examine.dm | 25 +++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 6a03af3b11..70be4014f2 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -245,6 +245,7 @@ var/list/ai_verbs_default = list( /obj/machinery/ai_powersupply/process() if(!powered_ai || powered_ai.stat & DEAD) qdel() + return if(powered_ai.APU_power) use_power = 0 return diff --git a/code/modules/mob/living/silicon/ai/examine.dm b/code/modules/mob/living/silicon/ai/examine.dm index 286edc2ce0..4caefb236b 100644 --- a/code/modules/mob/living/silicon/ai/examine.dm +++ b/code/modules/mob/living/silicon/ai/examine.dm @@ -17,15 +17,36 @@ msg += "It looks slightly charred.\n" else msg += "Its casing is melted and heat-warped!\n" + if (src.getOxyLoss()) + if (src.getOxyLoss() > 175) + msg += "It seems to be running on backup power. It's display is blinking \"BACKUP POWER CRITICAL\" warning.\n" + else if(src.getOxyLoss() > 100) + msg += "It seems to be running on backup power. It's display is blinking \"BACKUP POWER LOW\" warning.\n" + else + msg += "It seems to be running on backup power.\n" if (src.stat == UNCONSCIOUS) msg += "It is non-responsive and displaying the text: \"RUNTIME: Sensory Overload, stack 26/3\".\n" msg += "" msg += "*---------*" - + if(hardware) + msg += "\n" + if(istype(hardware, /datum/malf_hardware/apu_gen)) + msg += "It seems to have some sort of power generator attached to it's core." + if(hardware_integrity() < 50) + msg += " It seems to be too damaged to function properly." + else if(APU_power) + msg += "The generator appears to be active." + else if(istype(hardware, /datum/malf_hardware/core_bomb)) + msg += "It seems to have grey blocks of unknown substance and some circuitry connected to it's core. [bombing_core ? "Red light is blinking on the circuit." : ""]" + else if(istype(hardware, /datum/malf_hardware/dual_cpu)) + msg += "It seems to have additional CPU connected to it's core." + else if(istype(hardware, /datum/malf_hardware/dual_ram)) + msg += "It seems to have additional memory blocks connected to it's core." + else if(istype(hardware, /datum/malf_hardware/strong_turrets)) + msg += "It seems to have extra wiring running from it's core to nearby turrets." user << msg user.showLaws(src) - return /mob/proc/showLaws(var/mob/living/silicon/S) From 9d75d02a29de14cdef266c9963d2c90fe6f961fe Mon Sep 17 00:00:00 2001 From: Atlantiscze Date: Wed, 13 May 2015 05:33:40 +0200 Subject: [PATCH 07/31] Minor fix - Fixes APCs rebuilt using frame replacement remaining hacked. - Removes some old and unneeded commented-out code. --- code/modules/power/apc.dm | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 217f81cab0..18fb722405 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -618,7 +618,9 @@ "You replace the damaged APC frame with new one.") qdel(W) stat &= ~BROKEN - hacker = null + // Malf AI, removes the APC from AI's hacked APCs list. + if(hacker && hacker.hacked_apcs && src in hacker.hacked_apcs) + hacker.hacked_apcs -= src if (opened==2) opened = 1 update_icon() @@ -735,21 +737,6 @@ return ui_interact(user) -/* -/obj/machinery/power/apc/proc/get_malf_status(mob/user) - if (malf && (user.mind in malf.current_antagonists) && istype(user, /mob/living/silicon/ai)) - if (src.malfai == (user:parent ? user:parent : user)) - if (src.occupier == user) - return 3 // 3 = User is shunted in this APC - else if (istype(user.loc, /obj/machinery/power/apc)) - return 4 // 4 = User is shunted in another APC - else - return 2 // 2 = APC hacked by user, and user is in its core. - else - return 1 // 1 = APC not hacked. - else - return 0 // 0 = User is not a Malf AI -*/ /obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) if(!user) @@ -766,7 +753,6 @@ "totalCharging" = round(lastused_charging), "coverLocked" = coverlocked, "siliconUser" = istype(user, /mob/living/silicon), - /*"malfStatus" = get_malf_status(user),*/ "powerChannels" = list( list( From bcfbfdedba9ad7d2b41d0dd3c1995f56c54a3ae4 Mon Sep 17 00:00:00 2001 From: Atlantiscze Date: Sun, 17 May 2015 02:54:58 +0200 Subject: [PATCH 08/31] CPU Generation adjustment - Doubles CPU generation from APCs. Testing revealed that current values are simply too small. - Decreases DualCPU hardware boost to +50% CPU generated (instead of +100%) - Minor code clean-up at hardware selection screen. --- code/game/gamemodes/malfunction/malf_hardware.dm | 2 +- .../malfunction/newmalf_ability_trees/HELPERS.dm | 16 +++++----------- code/modules/mob/living/silicon/ai/ai.dm | 4 ++-- ingame_manuals/malf_ai.txt | 2 +- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/code/game/gamemodes/malfunction/malf_hardware.dm b/code/game/gamemodes/malfunction/malf_hardware.dm index c50a66b7f1..a3c91b53f2 100644 --- a/code/game/gamemodes/malfunction/malf_hardware.dm +++ b/code/game/gamemodes/malfunction/malf_hardware.dm @@ -18,7 +18,7 @@ /datum/malf_hardware/dual_cpu name = "Secondary Processor Unit" - desc = "Secondary coprocessor that doubles amount of CPU time generated." + desc = "Secondary coprocessor that increases amount of generated CPU power by 50%" /datum/malf_hardware/dual_ram name = "Secondary Memory Bank" diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm index 33a24e0cd2..0851a3771a 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm @@ -24,18 +24,12 @@ if(choice == "CANCEL") return var/note = null - switch(choice) - if("APU Generator") - note = "APU Generator - When enabled it will keep your core powered. Power output is not large enough so your abilities won't be available while running on APU power. It is also very fragile and prone to failure when your physical core is damaged." - if("Turrets Focus Enhancer") - note = "Overcharges turrets to shoot faster. Turrets will also gain higher health and passive regeneration. This however massively increases power usage of turrets, espicially when regenerating." - if("Secondary Processor Unit") - note = "Doubles your CPU time generation." - if("Secondary Memory Bank") - note = "Doubles your CPU time storage." - if("Self-Destruct Explosives") - note = "High yield explosives are attached to your physical mainframe. This hardware comes with activation driver. Explosives will destroy your core and everything around it." + + if(choice) + note = choice.desc + if(!note) + error("Hardware without description: [choice]") return var/confirmation = input("[note] - Is this what you want?") in list("Yes", "No") diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 191aa235c6..b16b14899b 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -758,12 +758,12 @@ var/list/ai_verbs_default = list( // Off-Station APCs should not count towards CPU generation. for(var/obj/machinery/power/apc/A in hacked_apcs) if(A.z == 1) - cpu_gain += 0.001 + cpu_gain += 0.002 cpu_storage += 10 research.max_cpu = cpu_storage + override_CPUStorage if(hardware && istype(hardware, /datum/malf_hardware/dual_ram)) - research.max_cpu = research.max_cpu * 2 + research.max_cpu = research.max_cpu * 1.5 research.stored_cpu = min(research.stored_cpu, research.max_cpu) research.cpu_increase_per_tick = cpu_gain + override_CPURate diff --git a/ingame_manuals/malf_ai.txt b/ingame_manuals/malf_ai.txt index 8e7548f7d0..65424f24ed 100644 --- a/ingame_manuals/malf_ai.txt +++ b/ingame_manuals/malf_ai.txt @@ -9,7 +9,7 @@ As malfunctioning AI, your primary goal is to overtake station's systems. To do As malfunctioning AI, you may select one hardware piece to help you. Remember that once you select hardware piece, you cannot select another one, so choose wisely! Hardware may be selected by clicking "Select Hardware" button in Hardware tab. Following is list of possible hardware pieces:
APU Generator - Auxiliary Power Unit which allows you to operate even without external power. However, running on APU will stop your CPU time generation, and temporarily disable most of your abilities. APU is also somewhat vulnerable to physical damage, and will fail if your core hardware integrity drops below 50%.
Turrets Focus Enhancer - Removes safeties on installed turrets, boosting their rate of fire, health and enabling nano-regeneration module. This however increases power usage considerably, espicially when regenerating damage.
-Secondary Processor Unit - Simple upgrade that doubles your CPU time generation. Useful if you need to speed up your research.
+Secondary Processor Unit - Simple upgrade that increases your CPU time generation by 50%. Useful if you need to speed up your research.
Secondary Memory Bank - Doubles amount of maximal CPU time you may store. This is useful if you need to use lots of abilities in short amount of time.
Self-Destruct Explosives - Large blocks of C4 are attached to your physical core. This C4 has 15 second timer, and may be activated by special button that appears in your Hardware tab. This self-destruct will remain active, even if you are destroyed. If timer reaches 0 your core explodes in strong explosion. Obviously, this destroys you, as well as anyone nearby.
From 71bfe729fa83e6ad22ce292793f5fb090040487f Mon Sep 17 00:00:00 2001 From: Atlantiscze Date: Sun, 17 May 2015 02:59:59 +0200 Subject: [PATCH 09/31] Compile Fix - Fixes mistake in previous commit. --- .../newmalf_ability_trees/HELPERS.dm | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm index 0851a3771a..6ccf83688f 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm @@ -25,11 +25,25 @@ return var/note = null - if(choice) - note = choice.desc + var/datum/malf_hardware/C + + switch(choice) + if("APU Generator") + C = new/datum/malf_hardware/apu_gen() + if("Turrets Focus Enhancer") + C = new/datum/malf_hardware/strong_turrets() + if("Secondary Processor Unit") + C = new/datum/malf_hardware/dual_cpu() + if("Secondary Memory Bank") + C = new/datum/malf_hardware/dual_ram() + if("Self-Destruct Explosives") + C = new/datum/malf_hardware/core_bomb() + + if(C) + note = C.desc if(!note) - error("Hardware without description: [choice]") + error("Hardware without description: [C]") return var/confirmation = input("[note] - Is this what you want?") in list("Yes", "No") @@ -39,10 +53,8 @@ switch(choice) if("APU Generator") - user.hardware = new/datum/malf_hardware/apu_gen() user.verbs += new/datum/game_mode/malfunction/verb/ai_toggle_apu() if("Turrets Focus Enhancer") - user.hardware = new/datum/malf_hardware/strong_turrets() for(var/obj/machinery/turret/T in machines) T.maxhealth += 30 T.shot_delay = 7 // Half of default time. @@ -54,13 +66,11 @@ T.auto_repair = 1 T.active_power_usage = 25000 if("Secondary Processor Unit") - user.hardware = new/datum/malf_hardware/dual_cpu() if("Secondary Memory Bank") - user.hardware = new/datum/malf_hardware/dual_ram() if("Self-Destruct Explosives") - user.hardware = new/datum/malf_hardware/core_bomb() user.verbs += new/datum/game_mode/malfunction/verb/ai_self_destruct() + user.hardware = C // Verb: ai_help() // Parameters: None From 4cd2417eb8f174d736ebbc1bc26fe66d312adffa Mon Sep 17 00:00:00 2001 From: Atlantis Date: Mon, 18 May 2015 22:05:18 +0200 Subject: [PATCH 10/31] Implements suggestions from github - qdel() instead of del() - z in config.station_levels insted of z == 1 - Typo fix - Playercount is now 2 instead of 1 which was debug setting - AI is now properly deleted when struck by very strong explosion (heavy impact). - Hardware now properly uses install(). Hardware list is generated from existing datums using typesof() --- .../gamemodes/malfunction/malf_hardware.dm | 18 +++++- .../game/gamemodes/malfunction/malfunction.dm | 2 +- .../newmalf_ability_trees/HARDWARE.dm | 2 +- .../newmalf_ability_trees/HELPERS.dm | 58 +++++++------------ .../newmalf_ability_trees/tree_networking.dm | 6 +- code/modules/mob/living/silicon/ai/ai.dm | 8 ++- 6 files changed, 50 insertions(+), 44 deletions(-) diff --git a/code/game/gamemodes/malfunction/malf_hardware.dm b/code/game/gamemodes/malfunction/malf_hardware.dm index a3c91b53f2..ac5debc3a7 100644 --- a/code/game/gamemodes/malfunction/malf_hardware.dm +++ b/code/game/gamemodes/malfunction/malf_hardware.dm @@ -15,6 +15,8 @@ /datum/malf_hardware/apu_gen name = "APU Generator" desc = "Auxiliary Power Unit that will keep you operational even without external power. Has to be manually activated. When APU is operational most abilities will be unavailable, and ability research will temporarily stop." + driver = new/datum/game_mode/malfunction/verb/ai_toggle_apu() + /datum/malf_hardware/dual_cpu name = "Secondary Processor Unit" @@ -27,7 +29,21 @@ /datum/malf_hardware/core_bomb name = "Self-Destruct Explosives" desc = "High yield explosives are attached to your physical mainframe. This hardware comes with special driver that allows activation of these explosives. Timer is set to 15 seconds after manual activation. This is a doomsday device that will destroy both you and any intruders in your core." + driver = new/datum/game_mode/malfunction/verb/ai_self_destruct() /datum/malf_hardware/strong_turrets name = "Turrets Focus Enhancer" - desc = "Turrets are upgraded to have larger rate of fire and much larger damage. This however massively increases power usage when firing." \ No newline at end of file + desc = "Turrets are upgraded to have larger rate of fire and much larger damage. This however massively increases power usage when firing." + +/datum/malf_hardware/strong_turrets/install() + ..() + for(var/obj/machinery/turret/T in machines) + T.maxhealth += 30 + T.shot_delay = 7 // Half of default time. + T.auto_repair = 1 + T.active_power_usage = 25000 + for(var/obj/machinery/porta_turret/T in machines) + T.maxhealth += 30 + T.shot_delay = 7 // Half of default time. + T.auto_repair = 1 + T.active_power_usage = 25000 \ No newline at end of file diff --git a/code/game/gamemodes/malfunction/malfunction.dm b/code/game/gamemodes/malfunction/malfunction.dm index bd18a08e09..19378de167 100644 --- a/code/game/gamemodes/malfunction/malfunction.dm +++ b/code/game/gamemodes/malfunction/malfunction.dm @@ -4,7 +4,7 @@ extended_round_description = "The AI will attempt to hack the APCs around the station in order to gain as much control as possible." uplink_welcome = "Crazy AI Uplink Console:" config_tag = "malfunction" - required_players = 1 // DEBUG SETTING! Should be "2" for release (one AI and at least one crewmember) + required_players = 2 required_players_secret = 7 required_enemies = 1 end_on_antag_death = 0 diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/HARDWARE.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/HARDWARE.dm index 34551a0b17..34c82f6853 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/HARDWARE.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/HARDWARE.dm @@ -39,7 +39,7 @@ return user << "** [timer] **" explosion(user.loc, 3,6,12,24) - del(user) + qdel(user) /datum/game_mode/malfunction/verb/ai_toggle_apu() diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm index 6ccf83688f..83944b4f9c 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm @@ -14,12 +14,16 @@ user << "You have already selected your hardware." return - var/possible_choices = list("APU Generator", \ - "Turrets Focus Enhancer", \ - "Secondary Processor Unit",\ - "Secondary Memory Bank",\ - "Self-Destruct Explosives",\ - "CANCEL") + var/hardware_list = list() + for(var/H in typesof(/datum/malf_hardware)) + var/datum/malf_hardware/HW = new H + hardware_list += HW + + var/possible_choices = list() + for(var/datum/malf_hardware/H in hardware_list) + possible_choices += H.name + + possible_choices += "CANCEL" var/choice = input("Select desired hardware. You may only choose one hardware piece!: ") in possible_choices if(choice == "CANCEL") return @@ -27,20 +31,17 @@ var/datum/malf_hardware/C - switch(choice) - if("APU Generator") - C = new/datum/malf_hardware/apu_gen() - if("Turrets Focus Enhancer") - C = new/datum/malf_hardware/strong_turrets() - if("Secondary Processor Unit") - C = new/datum/malf_hardware/dual_cpu() - if("Secondary Memory Bank") - C = new/datum/malf_hardware/dual_ram() - if("Self-Destruct Explosives") - C = new/datum/malf_hardware/core_bomb() + for (var/datum/malf_hardware/H in hardware_list) + if(H.name == choice) + C = H + break if(C) note = C.desc + else + user << "This hardware does not exist! Probably a bug in game. Please report this." + return + if(!note) error("Hardware without description: [C]") @@ -51,26 +52,9 @@ user << "Selection cancelled. Use command again to select" return - switch(choice) - if("APU Generator") - user.verbs += new/datum/game_mode/malfunction/verb/ai_toggle_apu() - if("Turrets Focus Enhancer") - for(var/obj/machinery/turret/T in machines) - T.maxhealth += 30 - T.shot_delay = 7 // Half of default time. - T.auto_repair = 1 - T.active_power_usage = 25000 - for(var/obj/machinery/porta_turret/T in machines) - T.maxhealth += 30 - T.shot_delay = 7 // Half of default time. - T.auto_repair = 1 - T.active_power_usage = 25000 - if("Secondary Processor Unit") - if("Secondary Memory Bank") - if("Self-Destruct Explosives") - user.verbs += new/datum/game_mode/malfunction/verb/ai_self_destruct() - - user.hardware = C + if(C) + C.owner = user + C.install() // Verb: ai_help() // Parameters: None diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm index f059720dc0..23adb5e190 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm @@ -148,7 +148,7 @@ return var/list/remaining_apcs = list() for(var/obj/machinery/power/apc/A in machines) - if(A.z != 1) // Only station APCs + if(!(A.z in config.station_levels)) // Only station APCs continue if(A.hacker == user || A.aidisabled) // This one is already hacked, or AI control is disabled on it. continue @@ -168,7 +168,7 @@ sleep(duration/5) if(!user || user.stat == DEAD) return - command_announcement.Announce("This is highly abnormal and somewhat concerning. The intruder is too fast, he is evading our traces. It's almost as if it wasn't human.,,", "Network Monitoring") + command_announcement.Announce("This is highly abnormal and somewhat concerning. The intruder is too fast, he is evading our traces. It's almost as if it wasn't human..", "Network Monitoring") sleep(duration/5) if(!user || user.stat == DEAD) return @@ -194,7 +194,7 @@ sleep(300) // Hack all APCs, including those built during hack sequence. for(var/obj/machinery/power/apc/A in machines) - if((!A.hacker || A.hacker != src) && !A.aidisabled) + if((!A.hacker || A.hacker != src) && !A.aidisabled && A.z in config.station_levels) A.ai_hack(src) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index b16b14899b..7a1fbd573f 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -693,6 +693,12 @@ var/list/ai_verbs_default = list( return istype(loc, /turf) +/mob/living/silicon/ai/ex_act(var/severity) + if(severity == 1.0) + qdel(src) + return + ..() + // NEWMALF FUNCTIONS/PROCEDURES // Sets up malfunction-related variables, research system and such. @@ -757,7 +763,7 @@ var/list/ai_verbs_default = list( // Off-Station APCs should not count towards CPU generation. for(var/obj/machinery/power/apc/A in hacked_apcs) - if(A.z == 1) + if(A.z in config.station_levels) cpu_gain += 0.002 cpu_storage += 10 From f0cd799021f1c36b34d0b05547c619b4f8a24d35 Mon Sep 17 00:00:00 2001 From: Atlantis Date: Tue, 19 May 2015 17:28:21 +0200 Subject: [PATCH 11/31] Changelog + fluff fix - Finally adds changelog entry - System override message modified according to github discussion. --- .../malfunction/newmalf_ability_trees/tree_networking.dm | 2 +- html/changelogs/Atlantis-PR-9108.yml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 html/changelogs/Atlantis-PR-9108.yml diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm index 23adb5e190..f3bef399ad 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm @@ -168,7 +168,7 @@ sleep(duration/5) if(!user || user.stat == DEAD) return - command_announcement.Announce("This is highly abnormal and somewhat concerning. The intruder is too fast, he is evading our traces. It's almost as if it wasn't human..", "Network Monitoring") + command_announcement.Announce("This is highly abnormal and somewhat concerning. The intruder is too fast, he is evading our traces. No man could be this fast...", "Network Monitoring") sleep(duration/5) if(!user || user.stat == DEAD) return diff --git a/html/changelogs/Atlantis-PR-9108.yml b/html/changelogs/Atlantis-PR-9108.yml new file mode 100644 index 0000000000..b2c3f6bdb4 --- /dev/null +++ b/html/changelogs/Atlantis-PR-9108.yml @@ -0,0 +1,5 @@ +author: Atlantis +delete-after: True + +changes: + - rscadd: "Malfunction Overhaul - Whole gamemode was completely reworked from scratch. Most old abilities have been removed and quite a lot of new abilities was added. AI also has to hack APCs to unlock higher tier abilities faster, instead of having access to them from the round start. Most forced things, such as, shuttle recalling were removed and are instead controlled by the AI. Code is fully modular allowing for future modifications." \ No newline at end of file From 991546df2a5a39c3ca6bbc8baa16e0cb4feac104 Mon Sep 17 00:00:00 2001 From: Atlantis Date: Thu, 21 May 2015 12:19:23 +0200 Subject: [PATCH 12/31] Span Class in shields code. --- code/modules/shieldgen/emergency_shield.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm index 8210894bd1..15ac929701 100644 --- a/code/modules/shieldgen/emergency_shield.dm +++ b/code/modules/shieldgen/emergency_shield.dm @@ -23,7 +23,7 @@ /obj/machinery/shield/proc/check_failure() if (src.health <= 0) - visible_message("\blue The [src] dissipates!") + visible_message("The [src] dissipates!") qdel(src) return @@ -100,7 +100,7 @@ /obj/machinery/shield/hitby(AM as mob|obj) //Let everyone know we've been hit! - visible_message("\red [src] was hit by [AM].") + visible_message("[src] was hit by [AM].") //Super realistic, resource-intensive, real-time damage calculations. var/tforce = 0 From d8141445e48928ea08726b48411ba469d1288c93 Mon Sep 17 00:00:00 2001 From: Atlantis Date: Thu, 21 May 2015 13:27:18 +0200 Subject: [PATCH 13/31] Implements suggestions - Hardware examine is now generated by hardware's proc. - Camera network updating now uses proper procs - Bugfix: APU Generator no longer prevents you from using NanoUI based devices - Bugfix: Fixes rare runtime on roundstart. --- .../gamemodes/malfunction/malf_hardware.dm | 23 +++++++++++++++++++ code/game/machinery/camera/camera.dm | 6 ++++- code/game/machinery/camera/presets.dm | 12 +++------- code/modules/mob/living/silicon/ai/ai.dm | 3 ++- code/modules/mob/living/silicon/ai/examine.dm | 19 +++------------ code/modules/mob/living/silicon/ai/life.dm | 2 +- 6 files changed, 37 insertions(+), 28 deletions(-) diff --git a/code/game/gamemodes/malfunction/malf_hardware.dm b/code/game/gamemodes/malfunction/malf_hardware.dm index ac5debc3a7..58171ae6d1 100644 --- a/code/game/gamemodes/malfunction/malf_hardware.dm +++ b/code/game/gamemodes/malfunction/malf_hardware.dm @@ -10,6 +10,10 @@ if(driver) owner.verbs += driver +/datum/malf_hardware/proc/get_examine_desc() + return "It has some sort of hardware attached to it's core" + + // HARDWARE DEFINITIONS /datum/malf_hardware/apu_gen @@ -17,24 +21,43 @@ desc = "Auxiliary Power Unit that will keep you operational even without external power. Has to be manually activated. When APU is operational most abilities will be unavailable, and ability research will temporarily stop." driver = new/datum/game_mode/malfunction/verb/ai_toggle_apu() +/datum/malf_hardware/apu_gen/get_examine_desc() + var/msg = "It seems to have some sort of power generator attached to it's core." + if(owner.hardware_integrity() < 50) + msg += " It seems to be too damaged to function properly." + else if(owner.APU_power) + msg += " The generator appears to be active." + return msg /datum/malf_hardware/dual_cpu name = "Secondary Processor Unit" desc = "Secondary coprocessor that increases amount of generated CPU power by 50%" +/datum/malf_hardware/dual_cpu/get_examine_desc() + return "It seems to have additional CPU connected to it's core." + /datum/malf_hardware/dual_ram name = "Secondary Memory Bank" desc = "Expanded memory cells which allow you to store double amount of CPU time." +/datum/malf_hardware/dual_ram/get_examine_desc() + return "It seems to have additional memory blocks connected to it's core." + /datum/malf_hardware/core_bomb name = "Self-Destruct Explosives" desc = "High yield explosives are attached to your physical mainframe. This hardware comes with special driver that allows activation of these explosives. Timer is set to 15 seconds after manual activation. This is a doomsday device that will destroy both you and any intruders in your core." driver = new/datum/game_mode/malfunction/verb/ai_self_destruct() +/datum/malf_hardware/core_bomb/get_examine_desc() + return "It seems to have grey blocks of unknown substance and some circuitry connected to it's core. [owner.bombing_core ? "Red light is blinking on the circuit." : ""]" + /datum/malf_hardware/strong_turrets name = "Turrets Focus Enhancer" desc = "Turrets are upgraded to have larger rate of fire and much larger damage. This however massively increases power usage when firing." +/datum/malf_hardware/strong_turrets/get_examine_desc() + return "It seems to have extra wiring running from it's core to nearby turrets." + /datum/malf_hardware/strong_turrets/install() ..() for(var/obj/machinery/turret/T in machines) diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index aa67afb52e..92177604f8 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -440,5 +440,9 @@ /obj/machinery/camera/proc/reset_wires() if(!wires) return + if (stat & BROKEN) // Fix the camera + stat &= ~BROKEN wires.CutAll() - wires.MendAll() + wires.MendAll() + update_icon() + update_coverage() diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index a685a6ae78..67153bf95b 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -162,25 +162,19 @@ var/global/list/engineering_networks = list( /obj/machinery/camera/proc/upgradeEmpProof() assembly.upgrades.Add(new /obj/item/stack/sheet/mineral/osmium(assembly)) setPowerUsage() - if(cameranet) - cameranet.updateVisibility(src) - update_nearby_tiles() + update_coverage() /obj/machinery/camera/proc/upgradeXRay() assembly.upgrades.Add(new /obj/item/weapon/stock_parts/scanning_module/adv(assembly)) setPowerUsage() - if(cameranet) - cameranet.updateVisibility(src) - update_nearby_tiles() + update_coverage() /obj/machinery/camera/proc/upgradeMotion() assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly)) setPowerUsage() if(!(src in machines)) machines.Add(src) - if(cameranet) - cameranet.updateVisibility(src) - update_nearby_tiles() + update_coverage() /obj/machinery/camera/proc/setPowerUsage() var/mult = 1 diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 7a1fbd573f..0e071c2dc9 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -819,7 +819,8 @@ var/list/ai_verbs_default = list( // Shows AI Malfunction related information to the AI. /mob/living/silicon/ai/show_malf_ai() if(src.is_malf()) - stat(null, "Hacked APCs: [src.hacked_apcs.len]") + if(src.hacked_apcs) + stat(null, "Hacked APCs: [src.hacked_apcs.len]") stat(null, "System Status: [src.hacking ? "Busy" : "Stand-By"]") if(src.research) stat(null, "Available CPU: [src.research.stored_cpu] TFlops") diff --git a/code/modules/mob/living/silicon/ai/examine.dm b/code/modules/mob/living/silicon/ai/examine.dm index 4caefb236b..d73b588ebf 100644 --- a/code/modules/mob/living/silicon/ai/examine.dm +++ b/code/modules/mob/living/silicon/ai/examine.dm @@ -29,22 +29,9 @@ msg += "It is non-responsive and displaying the text: \"RUNTIME: Sensory Overload, stack 26/3\".\n" msg += "" msg += "*---------*" - if(hardware) - msg += "\n" - if(istype(hardware, /datum/malf_hardware/apu_gen)) - msg += "It seems to have some sort of power generator attached to it's core." - if(hardware_integrity() < 50) - msg += " It seems to be too damaged to function properly." - else if(APU_power) - msg += "The generator appears to be active." - else if(istype(hardware, /datum/malf_hardware/core_bomb)) - msg += "It seems to have grey blocks of unknown substance and some circuitry connected to it's core. [bombing_core ? "Red light is blinking on the circuit." : ""]" - else if(istype(hardware, /datum/malf_hardware/dual_cpu)) - msg += "It seems to have additional CPU connected to it's core." - else if(istype(hardware, /datum/malf_hardware/dual_ram)) - msg += "It seems to have additional memory blocks connected to it's core." - else if(istype(hardware, /datum/malf_hardware/strong_turrets)) - msg += "It seems to have extra wiring running from it's core to nearby turrets." + if(hardware && (hardware.owner == src)) + msg += "
" + msg += hardware.get_examine_desc() user << msg user.showLaws(src) return diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 7119decefc..6d56bc7cb0 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -160,7 +160,7 @@ /mob/living/silicon/ai/proc/lacks_power() if(APU_power) - return 1 + return 0 var/turf/T = get_turf(src) var/area/A = get_area(src) return ((!A.power_equip) && A.requires_power == 1 || istype(T, /turf/space)) && !istype(src.loc,/obj/item) From 06ec54c7b8290c33a63f882e23efe07e698b8cc8 Mon Sep 17 00:00:00 2001 From: Atlantiscze Date: Sat, 23 May 2015 21:19:32 +0200 Subject: [PATCH 14/31] Modified behavior of machine overload to further prevent spam and to make it more useful if targeted on high-power machinery such as SMES. - This is a WIP thing. Commiting so i can finish it on my second computer. --- .../tree_manipulation.dm | 56 +++++++++++++++---- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm index 329734a02c..b428bd2d6d 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm @@ -139,22 +139,60 @@ if(!ability_prechecks(user, price)) return - if(!istype(M)) + var/obj/machinery/power/N = M + + var/explosion_intensity = 2 + + // Verify if we can overload the target, if yes, calculate explosion strength. Some things have higher explosion strength than others, depending on charge(APCs, SMESs) + if(N && istype(N)) // /obj/machinery/power first, these create bigger explosions due to direct powernet connection + if(!istype(N, /obj/machinery/power/apc) && !istype(N, /obj/machinery/power/smes/buildable) && (!N.powernet || !N.powernet.avail)) // Directly connected machine which is not an APC or SMES. Either it has no powernet connection or it's powernet does not have enough power to overload + user << "ERROR: Low network voltage. Unable to overload. Increase network power level and try again." + return + else if (istype(N, /obj/machinery/power/apc)) // APC. Explosion is increased by available cell power. + var/obj/machinery/power/apc/A = N + if(A.cell && A.cell.charge) + explosion_intensity = 4 + round(A.cell.charge / 2000) // Explosion is increased by 1 for every 2k charge in cell + else + user << "ERROR: APC Malfunction - Cell depleted or removed. Unable to overload." + return + else if (istype(N, /obj/machinery/power/smes/buildable)) // SMES. These explode in a very very very big boom. Similar to magnetic containment failure when messing with coils. + var/obj/machinery/power/smes/buildable/S = N + if(S.charge && S.RCon) + explosion_intensity = 4 + round(S.charge / 1000000) + else + // Different error texts + if(!S.charge) + user << "ERROR: SMES Depleted. Unable to overload. Please charge SMES unit and try again." + else + user << "ERROR: SMES RCon error - Unable to reach destination. Please verify wire connection." + return + else if(M && istype(M)) // Not power machinery, so it's a regular machine instead. These have weak explosions. + if(!M.use_power) // Not using power at all + user << "ERROR: No power grid connection. Unable to overload." + return + if(M.inoperable()) // Not functional + user << "ERROR: Unknown error. Machine is probably damaged or power supply is nonfunctional." + else // Not a machine at all (what the hell is this doing in Machines list anyway??) user << "ERROR: Unable to overload - target is not a machine." return - if(!M.use_power || M.inoperable()) - user << "ERROR: Unable to overload - target is not connected to active power grid." - return + explosion_intensity = min(explosion_intensity, 12) // 3, 6, 12 explosion cap - M.use_power(1000000) // Major power spike, few of these will completely burn APC's cell - equivalent of 1GJ of power. + M.use_power(2000000) // Major power spike, few of these will completely burn APC's cell - equivalent of 2GJ of power. // Trigger a powernet alarm. Careful engineers will probably notice something is going on. var/area/temp_area = get_area(M) if(temp_area) var/obj/machinery/power/apc/temp_apc = temp_area.get_apc() if(temp_apc && temp_apc.terminal && temp_apc.terminal.powernet) - temp_apc.terminal.powernet.trigger_warning() + temp_apc.terminal.powernet.trigger_warning(50) // Long alarm + if(temp_apc) + temp_apc.emp_act(3) // Such power surges are not good for APC electronics + if(temp_apc.cell) + temp_apc.cell.maxcharge -= between(0, (temp_apc.cell.maxcharge/2) + 500, temp_apc.cell.maxcharge) + if(temp_apc.cell.maxcharge < 100) // That's it, you busted the APC cell completely. Break the APC and completely destroy the cell. + qdel(temp_apc.cell) + temp_apc.set_broken() if(!ability_pay(user,price)) @@ -162,11 +200,7 @@ M.visible_message("BZZZZZZZT") spawn(50) - // machinery/power is directly connected to powernet. Resulting explosion will be a bit stronger. This should be enough to kill someone standing next to the machine. - if(istype(M, /obj/machinery/power)) - explosion(get_turf(M), 1,2,4,4) - else - explosion(get_turf(M), 0,1,2,4) + explosion(get_turf(M), round(explosion_intensity/4),round(explosion_intensity/2),round(explosion_intensity),round(explosion_intensity * 2)) if(M) qdel(M) From ef040583c8c2821a697fcb0c37d50feb64d83abe Mon Sep 17 00:00:00 2001 From: Atlantis Date: Sat, 23 May 2015 22:22:38 +0200 Subject: [PATCH 15/31] Implements github suggestions Among others: - TXT helpfile is now HTML helpfile - Lots of gremmer changes - Some adjustments --- .../gamemodes/malfunction/malf_hardware.dm | 22 +++++++++---------- .../newmalf_ability_trees/HARDWARE.dm | 9 ++++++-- .../newmalf_ability_trees/HELPERS.dm | 18 +++++++++------ .../tree_manipulation.dm | 4 ++-- code/game/machinery/camera/presets.dm | 6 ++++- code/modules/mob/living/silicon/ai/examine.dm | 4 ++-- code/modules/mob/living/silicon/ai/life.dm | 16 +++++++------- code/modules/power/apc.dm | 2 +- code/modules/shieldgen/emergency_shield.dm | 8 +++---- ingame_manuals/{malf_ai.txt => malf_ai.html} | 0 10 files changed, 51 insertions(+), 38 deletions(-) rename ingame_manuals/{malf_ai.txt => malf_ai.html} (100%) diff --git a/code/game/gamemodes/malfunction/malf_hardware.dm b/code/game/gamemodes/malfunction/malf_hardware.dm index 58171ae6d1..da12528712 100644 --- a/code/game/gamemodes/malfunction/malf_hardware.dm +++ b/code/game/gamemodes/malfunction/malf_hardware.dm @@ -19,10 +19,10 @@ /datum/malf_hardware/apu_gen name = "APU Generator" desc = "Auxiliary Power Unit that will keep you operational even without external power. Has to be manually activated. When APU is operational most abilities will be unavailable, and ability research will temporarily stop." - driver = new/datum/game_mode/malfunction/verb/ai_toggle_apu() + driver = /datum/game_mode/malfunction/verb/ai_toggle_apu /datum/malf_hardware/apu_gen/get_examine_desc() - var/msg = "It seems to have some sort of power generator attached to it's core." + var/msg = "It seems to have some sort of power generator attached to its core." if(owner.hardware_integrity() < 50) msg += " It seems to be too damaged to function properly." else if(owner.APU_power) @@ -34,7 +34,7 @@ desc = "Secondary coprocessor that increases amount of generated CPU power by 50%" /datum/malf_hardware/dual_cpu/get_examine_desc() - return "It seems to have additional CPU connected to it's core." + return "It seems to have an additional CPU connected to it's core." /datum/malf_hardware/dual_ram name = "Secondary Memory Bank" @@ -46,10 +46,10 @@ /datum/malf_hardware/core_bomb name = "Self-Destruct Explosives" desc = "High yield explosives are attached to your physical mainframe. This hardware comes with special driver that allows activation of these explosives. Timer is set to 15 seconds after manual activation. This is a doomsday device that will destroy both you and any intruders in your core." - driver = new/datum/game_mode/malfunction/verb/ai_self_destruct() + driver = /datum/game_mode/malfunction/verb/ai_self_destruct /datum/malf_hardware/core_bomb/get_examine_desc() - return "It seems to have grey blocks of unknown substance and some circuitry connected to it's core. [owner.bombing_core ? "Red light is blinking on the circuit." : ""]" + return "It seems to have grey blocks of unknown substance and some circuitry connected to it's core. [owner.bombing_core ? "A red light is blinking on the circuit." : ""]" /datum/malf_hardware/strong_turrets name = "Turrets Focus Enhancer" @@ -61,12 +61,12 @@ /datum/malf_hardware/strong_turrets/install() ..() for(var/obj/machinery/turret/T in machines) - T.maxhealth += 30 - T.shot_delay = 7 // Half of default time. + T.maxhealth = round(initial(T.maxhealth) * 1.4) + T.shot_delay = round(initial(T.shot_delay) / 2) T.auto_repair = 1 - T.active_power_usage = 25000 + T.active_power_usage = round(initial(T.active_power_usage) * 5) for(var/obj/machinery/porta_turret/T in machines) - T.maxhealth += 30 - T.shot_delay = 7 // Half of default time. + T.maxhealth = round(initial(T.maxhealth) * 1.4) + T.shot_delay = round(initial(T.shot_delay) / 2) T.auto_repair = 1 - T.active_power_usage = 25000 \ No newline at end of file + T.active_power_usage = round(initial(T.active_power_usage) * 5) \ No newline at end of file diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/HARDWARE.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/HARDWARE.dm index 34c82f6853..2fffaa3ca8 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/HARDWARE.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/HARDWARE.dm @@ -23,10 +23,13 @@ user.bombing_core = 0 return - var/choice = input("Really destroy core?") in list("YES", "NO") + var/choice = alert("Really destroy core?", "Core self-destruct", "YES", "NO") if(choice != "YES") return + if(!ability_prechecks(user, 0, 1)) + return + user.bombing_core = 1 user << "***** CORE SELF-DESTRUCT SEQUENCE ACTIVATED *****" @@ -79,9 +82,11 @@ user.bombing_station = 0 return - var/choice = input("Really destroy station?") in list("YES", "NO") + var/choice = alert("Really destroy station?", "Station self-destruct", "YES", "NO") if(choice != "YES") return + if(!ability_prechecks(user, 0, 0)) + return user << "***** STATION SELF-DESTRUCT SEQUENCE INITIATED *****" user << "Self-destructing in 2 minutes. Use this command again to abort." user.bombing_station = 1 diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm index 83944b4f9c..441ab6f7cc 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm @@ -47,7 +47,7 @@ error("Hardware without description: [C]") return - var/confirmation = input("[note] - Is this what you want?") in list("Yes", "No") + var/confirmation = alert("[note] - Is this what you want?", "Hardware selection", "Yes", "No") if(confirmation != "Yes") user << "Selection cancelled. Use command again to select" return @@ -65,9 +65,9 @@ set desc = "Opens help window with overview of available hardware, software and other important information." var/mob/living/silicon/ai/user = usr - var/help = file2text("ingame_manuals/malf_ai.txt") + var/help = file2text("ingame_manuals/malf_ai.html") if(!help) - help = "Error loading help (file /ingame_manuals/malf_ai.txt is probably missing). Please report this to server administration staff." + help = "Error loading help (file /ingame_manuals/malf_ai.html is probably missing). Please report this to server administration staff." user << browse(help, "window=malf_ai_help;size=600x500") @@ -151,13 +151,13 @@ var/fulltext = "" switch(user.hack_fails) if(1) - fulltext = "We have detected hack attempt into your [text]. The intruder failed to access anything of importance, but disconnected before we could complete our traces." + fulltext = "We have detected a hack attempt into your [text]. The intruder failed to access anything of importance, but disconnected before we could complete our traces." if(2) - fulltext = "We have detected another hack attempt. It was targeting [text]. The intruder almost gained control of the system, so we had to disconnect them. We partially finished trace and it seems to be originating either from the station, or it's immediate vicinity." + fulltext = "We have detected another hack attempt. It was targeting [text]. The intruder almost gained control of the system, so we had to disconnect them. We partially finished our trace and it seems to be originating either from the station, or its immediate vicinity." if(3) - fulltext = "Another hack attempt has been detected, this time targeting [text]. We are certain the intruder entered the network via terminal located somewhere on the station." + fulltext = "Another hack attempt has been detected, this time targeting [text]. We are certain the intruder entered the network via a terminal located somewhere on the station." if(4) - fulltext = "We have finished our traces and it seems the recent hack attempts are originating from your AI system. We reccomend investigation." + fulltext = "We have finished our traces and it seems the recent hack attempts are originating from your AI system. We recommend investigation." else fulltext = "Another hack attempt has been detected, targeting [text]. The source still seems to be your AI system." @@ -183,6 +183,8 @@ for(var/mob/living/silicon/robot/RB in mob_list) if(istype(RB, /mob/living/silicon/robot/drone)) continue + if(RB in A.connected_robots) + continue L.Add(RB) return L @@ -197,5 +199,7 @@ var/list/L = list() for(var/mob/living/silicon/ai/AT in mob_list) + if(L == A) + continue L.Add(AT) return L diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm index 329734a02c..dbb2694d7f 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm @@ -54,7 +54,7 @@ AP.set_broken() -/datum/game_mode/malfunction/verb/hack_camera(var/obj/machinery/camera/target = null as null|obj in cameranet.cameras) +/datum/game_mode/malfunction/verb/hack_camera(var/obj/machinery/camera/target in cameranet.cameras) set name = "Hack Camera" set desc = "100 CPU - Hacks existing camera, allowing you to add upgrade of your choice to it. Alternatively it lets you reactivate broken camera." set category = "Software" @@ -129,7 +129,7 @@ user.hacking = 0 -/datum/game_mode/malfunction/verb/machine_overload(obj/machinery/M as null|obj in machines) +/datum/game_mode/malfunction/verb/machine_overload(obj/machinery/M in machines) set name = "Machine Overload" set desc = "400 CPU - Causes cyclic short-circuit in machine, resulting in weak explosion after some time." set category = "Software" diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index 93bff8e055..eeaaa03c4f 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -173,7 +173,11 @@ var/global/list/engineering_networks = list( assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly)) setPowerUsage() if(!(src in machines)) - machines.Add(src) + if(!machinery_sort_required && ticker) + dd_insertObjectList(machines, src) + else + machines += src + machinery_sort_required = 1 update_coverage() /obj/machinery/camera/proc/setPowerUsage() diff --git a/code/modules/mob/living/silicon/ai/examine.dm b/code/modules/mob/living/silicon/ai/examine.dm index d73b588ebf..45c6a3816b 100644 --- a/code/modules/mob/living/silicon/ai/examine.dm +++ b/code/modules/mob/living/silicon/ai/examine.dm @@ -19,9 +19,9 @@ msg += "Its casing is melted and heat-warped!\n" if (src.getOxyLoss()) if (src.getOxyLoss() > 175) - msg += "It seems to be running on backup power. It's display is blinking \"BACKUP POWER CRITICAL\" warning.\n" + msg += "It seems to be running on backup power. Its display is blinking a \"BACKUP POWER CRITICAL\" warning.\n" else if(src.getOxyLoss() > 100) - msg += "It seems to be running on backup power. It's display is blinking \"BACKUP POWER LOW\" warning.\n" + msg += "It seems to be running on backup power. Its display is blinking a \"BACKUP POWER LOW\" warning.\n" else msg += "It seems to be running on backup power.\n" diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 6d56bc7cb0..225b2bf6ea 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -57,26 +57,26 @@ src.see_in_dark = 8 src.see_invisible = SEE_INVISIBLE_LIVING - if (src:aiRestorePowerRoutine==2) + if (aiRestorePowerRoutine==2) src << "Alert cancelled. Power has been restored without our assistance." - src:aiRestorePowerRoutine = 0 + aiRestorePowerRoutine = 0 src.blind.layer = 0 return - else if (src:aiRestorePowerRoutine==3) + else if (aiRestorePowerRoutine==3) src << "Alert cancelled. Power has been restored." - src:aiRestorePowerRoutine = 0 + aiRestorePowerRoutine = 0 src.blind.layer = 0 return else if (APU_power) - src:aiRestorePowerRoutine = 0 + aiRestorePowerRoutine = 0 src.blind.layer = 0 return else var/area/current_area = get_area(src) if (lacks_power()) - if (src:aiRestorePowerRoutine==0) - src:aiRestorePowerRoutine = 1 + if (aiRestorePowerRoutine==0) + aiRestorePowerRoutine = 1 //Blind the AI @@ -144,7 +144,7 @@ theAPC.operating = 1 theAPC.equipment = 3 theAPC.update() - src:aiRestorePowerRoutine = 3 + aiRestorePowerRoutine = 3 src << "Here are your current laws:" src.show_laws() sleep(50) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 328ae954dc..eac509bf09 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -575,7 +575,7 @@ if(has_electronics==0) has_electronics = 1 user << "You place the power control board inside the frame." - qdel(W) // qdel + qdel(W) else if (istype(W, /obj/item/weapon/module/power_control) && opened && has_electronics==0 && ((stat & BROKEN))) user << "You cannot put the board inside, the frame is damaged." return diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm index 15ac929701..15bfa973ed 100644 --- a/code/modules/shieldgen/emergency_shield.dm +++ b/code/modules/shieldgen/emergency_shield.dm @@ -13,8 +13,8 @@ var/shield_idle_power = 1500 //how much power we use when just being sustained. /obj/machinery/shield/malfai - name = "Emergency Forcefield" - desc = "Weak forcefield which seems to be projected by station's emergency atmosphere containment field" + name = "emergency forcefield" + desc = "A weak forcefield which seems to be projected by the station's emergency atmosphere containment field" health = max_health/2 // Half health, it's not suposed to resist much. /obj/machinery/shield/malfai/process() @@ -23,7 +23,7 @@ /obj/machinery/shield/proc/check_failure() if (src.health <= 0) - visible_message("The [src] dissipates!") + visible_message("\The [src] dissipates!") qdel(src) return @@ -100,7 +100,7 @@ /obj/machinery/shield/hitby(AM as mob|obj) //Let everyone know we've been hit! - visible_message("[src] was hit by [AM].") + visible_message("\[src] was hit by [AM].") //Super realistic, resource-intensive, real-time damage calculations. var/tforce = 0 diff --git a/ingame_manuals/malf_ai.txt b/ingame_manuals/malf_ai.html similarity index 100% rename from ingame_manuals/malf_ai.txt rename to ingame_manuals/malf_ai.html From c2c3a2c689d9410a3fee84ffde3a7c94dc1ea838 Mon Sep 17 00:00:00 2001 From: Atlantis Date: Sun, 24 May 2015 03:05:27 +0200 Subject: [PATCH 16/31] Implements remaining suggestions - Few more grammar fixes - Machine overload adjustment finished. SMESs and APCs now receive explosion intensity bonus according to stored charge. - Gets rid of remains of old "src:" references in AI Life() proc --- code/game/gamemodes/malfunction/malf_hardware.dm | 2 +- .../newmalf_ability_trees/tree_manipulation.dm | 3 ++- .../newmalf_ability_trees/tree_networking.dm | 4 ++-- code/modules/mob/living/silicon/ai/life.dm | 10 +++++----- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/code/game/gamemodes/malfunction/malf_hardware.dm b/code/game/gamemodes/malfunction/malf_hardware.dm index da12528712..fab329ff58 100644 --- a/code/game/gamemodes/malfunction/malf_hardware.dm +++ b/code/game/gamemodes/malfunction/malf_hardware.dm @@ -11,7 +11,7 @@ owner.verbs += driver /datum/malf_hardware/proc/get_examine_desc() - return "It has some sort of hardware attached to it's core" + return "It has some sort of hardware attached to its core" diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm index f854b582d8..04ee0a8448 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm @@ -111,7 +111,7 @@ return -/datum/game_mode/malfunction/verb/emergency_forcefield(var/turf/T as null|turf in world) +/datum/game_mode/malfunction/verb/emergency_forcefield(var/turf/T as turf in world) set name = "Emergency Forcefield" set desc = "275 CPU - Uses station's emergency shielding system to create temporary barrier which lasts for few minutes, but won't resist gunfire." set category = "Software" @@ -172,6 +172,7 @@ return if(M.inoperable()) // Not functional user << "ERROR: Unknown error. Machine is probably damaged or power supply is nonfunctional." + return else // Not a machine at all (what the hell is this doing in Machines list anyway??) user << "ERROR: Unable to overload - target is not a machine." return diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm index f3bef399ad..9c3c584d9e 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm @@ -174,7 +174,7 @@ return command_announcement.Announce("We have traced the intrude#, it seem& t( e yo3r AI s7stem, it &# *#ck@ng th$ sel$ destru$t mechani&m, stop i# bef*@!)$#&&@@ ", "Network Monitoring") else - command_announcement.Announce("We have detected strong brute-force attack on your firewall which seems to be originating from your AI system. It already controls almost whole network, and the only thing that's preventing it from accessing the self-destruct is this firewall. You don't have much time before it succeeds.", "Network Monitoring") + command_announcement.Announce("We have detected a strong brute-force attack on your firewall which seems to be originating from your AI system. It already controls almost the whole network, and the only thing that's preventing it from accessing the self-destruct is this firewall. You don't have much time before it succeeds.", "Network Monitoring") user << "## BEGINNING SYSTEM OVERRIDE." user << "## ESTIMATED DURATION: [round((duration+300)/600)] MINUTES" user.hacking = 1 @@ -199,7 +199,7 @@ user << "## PRIMARY FIREWALL BYPASSED. YOU NOW HAVE FULL SYSTEM CONTROL." - command_announcement.Announce("Our system administrators just reported that we've been locked out from your control network. Whoever did this now has full access to station's systems.", "Network Administration Center") + command_announcement.Announce("Our system administrators just reported that we've been locked out from your control network. Whoever did this now has full access to the station's systems.", "Network Administration Center") user.hack_can_fail = 0 user.hacking = 0 user.system_override = 2 diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 225b2bf6ea..5e0b0589c5 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -25,7 +25,7 @@ src.reset_view(null) // Handle power damage (oxy) - if(src:aiRestorePowerRoutine != 0 && !APU_power) + if(aiRestorePowerRoutine != 0 && !APU_power) // Lose power adjustOxyLoss(1) else @@ -99,7 +99,7 @@ if (loc.power_equip) if (!istype(T, /turf/space)) src << "Alert cancelled. Power has been restored without our assistance." - src:aiRestorePowerRoutine = 0 + aiRestorePowerRoutine = 0 src.blind.layer = 0 return src << "Fault confirmed: missing external power. Shutting down main control system to save power." @@ -108,7 +108,7 @@ sleep(50) if (istype(T, /turf/space)) src << "Unable to verify! No power connection detected!" - src:aiRestorePowerRoutine = 2 + aiRestorePowerRoutine = 2 return src << "Connection verified. Searching for APC in power network." sleep(50) @@ -129,7 +129,7 @@ if (loc.power_equip) if (!istype(T, /turf/space)) src << "Alert cancelled. Power has been restored without our assistance." - src:aiRestorePowerRoutine = 0 + aiRestorePowerRoutine = 0 src.blind.layer = 0 //This, too, is a fix to issue 603 return switch(PRP) @@ -146,7 +146,7 @@ theAPC.update() aiRestorePowerRoutine = 3 src << "Here are your current laws:" - src.show_laws() + show_laws() sleep(50) theAPC = null From f1c194d5e9a2dfd1ac1d609beda1fb29c8d3b17d Mon Sep 17 00:00:00 2001 From: Chinsky Date: Tue, 26 May 2015 08:39:11 +0300 Subject: [PATCH 17/31] Moved Captain's announcment out of equipping to spawning. Removed redundant satchel spawning things - they are being taken care of in job's equip() --- code/game/jobs/job/captain.dm | 2 -- code/game/jobs/job_controller.dm | 53 +++++++++++++------------------- 2 files changed, 22 insertions(+), 33 deletions(-) diff --git a/code/game/jobs/job/captain.dm b/code/game/jobs/job/captain.dm index 28240dbb5b..00d304b903 100644 --- a/code/game/jobs/job/captain.dm +++ b/code/game/jobs/job/captain.dm @@ -37,8 +37,6 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1) else H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/ids(H.back), slot_in_backpack) - var/sound/announce_sound = (ticker.current_state <= GAME_STATE_SETTING_UP)? null : sound('sound/misc/boatswain.ogg', volume=20) - captain_announcement.Announce("All hands, Captain [H.real_name] on deck!", new_sound=announce_sound) H.implant_loyalty(src) diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm index deab7e2e61..c996c469d3 100644 --- a/code/game/jobs/job_controller.dm +++ b/code/game/jobs/job_controller.dm @@ -468,38 +468,29 @@ var/global/datum/controller/occupations/job_master return H.Robotize() if("AI") return H - if("Clown") //don't need bag preference stuff! + if("Captain") + var/sound/announce_sound = (ticker.current_state <= GAME_STATE_SETTING_UP)? null : sound('sound/misc/boatswain.ogg', volume=20) + captain_announcement.Announce("All hands, Captain [H.real_name] on deck!", new_sound=announce_sound) + + if(istype(H.back, /obj/item/weapon/storage)) + new /obj/item/weapon/storage/box/survival(H.back) + else + H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/survival(H), slot_r_hand) + + //Deferred item spawning. + if(spawn_in_storage && spawn_in_storage.len) + var/obj/item/weapon/storage/B + for(var/obj/item/weapon/storage/S in H.contents) + B = S + break + + if(!isnull(B)) + for(var/thing in spawn_in_storage) + H << "\blue Placing [thing] in your [B]!" + var/datum/gear/G = gear_datums[thing] + new G.path(B) else - switch(H.backbag) //BS12 EDIT - if(1) - H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/survival(H), slot_r_hand) - if(2) - var/obj/item/weapon/storage/backpack/BPK = new/obj/item/weapon/storage/backpack(H) - new /obj/item/weapon/storage/box/survival(BPK) - H.equip_to_slot_or_del(BPK, slot_back,1) - if(3) - var/obj/item/weapon/storage/backpack/BPK = new/obj/item/weapon/storage/backpack/satchel_norm(H) - new /obj/item/weapon/storage/box/survival(BPK) - H.equip_to_slot_or_del(BPK, slot_back,1) - if(4) - var/obj/item/weapon/storage/backpack/BPK = new/obj/item/weapon/storage/backpack/satchel(H) - new /obj/item/weapon/storage/box/survival(BPK) - H.equip_to_slot_or_del(BPK, slot_back,1) - - //Deferred item spawning. - if(spawn_in_storage && spawn_in_storage.len) - var/obj/item/weapon/storage/B - for(var/obj/item/weapon/storage/S in H.contents) - B = S - break - - if(!isnull(B)) - for(var/thing in spawn_in_storage) - H << "\blue Placing [thing] in your [B]!" - var/datum/gear/G = gear_datums[thing] - new G.path(B) - else - H << "\red Failed to locate a storage object on your mob, either you spawned with no arms and no backpack or this is a bug." + H << "\red Failed to locate a storage object on your mob, either you spawned with no arms and no backpack or this is a bug." if(istype(H)) //give humans wheelchairs, if they need them. var/obj/item/organ/external/l_foot = H.get_organ("l_foot") From e510f6625f8edb46323d23f87c0440dc332c6b53 Mon Sep 17 00:00:00 2001 From: mwerezak Date: Tue, 26 May 2015 23:05:38 -0400 Subject: [PATCH 18/31] Fixes IV stands blocking bullets IV stands now check PASSTABLE, allowing projectiles and small mobs to pass through them. Flipped tables now block small mobs. --- code/game/machinery/iv_drip.dm | 7 ++++++- code/modules/tables/interactions.dm | 8 ++++---- .../changelogs/HarpyEagle-ProjectileTweaks.yml | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 html/changelogs/HarpyEagle-ProjectileTweaks.yml diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm index 99a2dc6607..4af33338d9 100644 --- a/code/game/machinery/iv_drip.dm +++ b/code/game/machinery/iv_drip.dm @@ -160,4 +160,9 @@ else usr << "\blue No chemicals are attached." - usr << "\blue [attached ? attached : "No one"] is attached." \ No newline at end of file + usr << "\blue [attached ? attached : "No one"] is attached." + +/obj/machinery/iv_drip/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + if(height && istype(mover) && mover.checkpass(PASSTABLE)) //allow bullets, beams, thrown objects, mice, drones, and the like through. + return 1 + return ..() \ No newline at end of file diff --git a/code/modules/tables/interactions.dm b/code/modules/tables/interactions.dm index 13350ee1d8..7c1778e4f3 100644 --- a/code/modules/tables/interactions.dm +++ b/code/modules/tables/interactions.dm @@ -3,15 +3,15 @@ if(air_group || (height==0)) return 1 if(istype(mover,/obj/item/projectile)) return (check_cover(mover,target)) - if(istype(mover) && mover.checkpass(PASSTABLE)) - return 1 - if(locate(/obj/structure/table) in get_turf(mover)) - return 1 if (flipped == 1) if (get_dir(loc, target) == dir) return !density else return 1 + if(istype(mover) && mover.checkpass(PASSTABLE)) + return 1 + if(locate(/obj/structure/table) in get_turf(mover)) + return 1 return 0 //checks if projectile 'P' from turf 'from' can hit whatever is behind the table. Returns 1 if it can, 0 if bullet stops. diff --git a/html/changelogs/HarpyEagle-ProjectileTweaks.yml b/html/changelogs/HarpyEagle-ProjectileTweaks.yml new file mode 100644 index 0000000000..12266695bb --- /dev/null +++ b/html/changelogs/HarpyEagle-ProjectileTweaks.yml @@ -0,0 +1,18 @@ +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment + +author: HarpyEagle +delete-after: True + +changes: + - tweak: "IV stands are no longer bullet shields. They also allow mice, drones, pAIs et al to pass though." From 31b2ce9cf7e77d4749b60310e8a1680098e9dc95 Mon Sep 17 00:00:00 2001 From: mwerezak Date: Tue, 26 May 2015 23:07:21 -0400 Subject: [PATCH 19/31] IV stand span classes --- code/game/machinery/iv_drip.dm | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm index 4af33338d9..19f851750f 100644 --- a/code/game/machinery/iv_drip.dm +++ b/code/game/machinery/iv_drip.dm @@ -137,7 +137,7 @@ set src in view(1) if(!istype(usr, /mob/living)) - usr << "\red You can't do that." + usr << "You can't do that." return if(usr.stat) @@ -154,13 +154,13 @@ if(beaker) if(beaker.reagents && beaker.reagents.reagent_list.len) - usr << "\blue Attached is \a [beaker] with [beaker.reagents.total_volume] units of liquid." + usr << "Attached is \a [beaker] with [beaker.reagents.total_volume] units of liquid." else - usr << "\blue Attached is an empty [beaker]." + usr << "Attached is an empty [beaker]." else - usr << "\blue No chemicals are attached." + usr << "No chemicals are attached." - usr << "\blue [attached ? attached : "No one"] is attached." + usr << "[attached ? attached : "No one"] is attached." /obj/machinery/iv_drip/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) if(height && istype(mover) && mover.checkpass(PASSTABLE)) //allow bullets, beams, thrown objects, mice, drones, and the like through. From 7d4c3235dc24d2775b01bc3da09bb7e2a1898dee Mon Sep 17 00:00:00 2001 From: mwerezak Date: Tue, 26 May 2015 23:34:00 -0400 Subject: [PATCH 20/31] Fixes #9460 --- code/modules/mob/living/carbon/human/human_damage.dm | 2 +- code/modules/organs/organ_external.dm | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index 04ca892225..530daea57c 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -236,7 +236,7 @@ /mob/living/carbon/human/proc/get_damageable_organs() var/list/obj/item/organ/external/parts = list() for(var/obj/item/organ/external/O in organs) - if(O.brute_dam + O.burn_dam < O.max_damage) + if(O.is_damageable()) parts += O return parts diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index a539f5f421..6ad272af5f 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -138,6 +138,9 @@ DAMAGE PROCS ****************************************************/ +/obj/item/organ/external/proc/is_damageable(var/additional_damage = 0) + return (vital || brute_dam + burn_dam + additional_damage < max_damage) + /obj/item/organ/external/take_damage(brute, burn, sharp, edge, used_weapon = null, list/forbidden_limbs = list()) if((brute <= 0) && (burn <= 0)) return 0 @@ -177,7 +180,7 @@ // If the limbs can break, make sure we don't exceed the maximum damage a limb can take before breaking // Non-vital organs are limited to max_damage. You can't kill someone by bludeonging their arm all the way to 200 -- you can // push them faster into paincrit though, as the additional damage is converted into shock. - if(vital || (brute_dam + burn_dam + brute + burn) < max_damage || !config.limbs_can_break) + if(is_damageable(brute + burn) || !config.limbs_can_break) if(brute) if(can_cut) createwound( CUT, brute ) From 68e205e7b336dcbc63669a2bfab29f9af22137a0 Mon Sep 17 00:00:00 2001 From: mwerezak Date: Wed, 27 May 2015 00:43:17 -0400 Subject: [PATCH 21/31] Fixes robot limbs spamming sparks and messages endlessly Fixes robot limbs spamming malfunctioning messages and spark effects endlessly if one arm is malfunctioning while holding an item in the other arm. --- code/modules/mob/living/carbon/human/human_organs.dm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm index c962b8355b..6c92db09fe 100644 --- a/code/modules/mob/living/carbon/human/human_organs.dm +++ b/code/modules/mob/living/carbon/human/human_organs.dm @@ -118,8 +118,12 @@ else if(E.is_malfunctioning()) if(E.body_part == HAND_LEFT) + if(!l_hand) + continue drop_from_inventory(l_hand) else + if(!r_hand) + continue drop_from_inventory(r_hand) emote("me", 1, "drops what they were holding, their [E.name] malfunctioning!") From 7dcc98e1a49108102fc2155f7a96d28ebf7cf6da Mon Sep 17 00:00:00 2001 From: mwerezak Date: Wed, 27 May 2015 00:53:12 -0400 Subject: [PATCH 22/31] Fixes robotic organs causing pain messages Also fixes pain() not checking if the species has NO_PAIN. --- code/modules/organs/organ.dm | 11 ++++++----- code/modules/organs/pain.dm | 16 ++++++++++------ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 06dd521f64..7e7e2e8178 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -175,15 +175,16 @@ var/list/organ_cache = list() W.time_inflicted = world.time /obj/item/organ/proc/take_damage(amount, var/silent=0) - if(src.robotic == 2) + if(src.status & ORGAN_ROBOT) src.damage += (amount * 0.8) else src.damage += amount - if(owner && parent_organ) - var/obj/item/organ/external/parent = owner.get_organ(parent_organ) - if(parent && !silent) - owner.custom_pain("Something inside your [parent.name] hurts a lot.", 1) + //only show this if the organ is not robotic + if(owner && parent_organ) + var/obj/item/organ/external/parent = owner.get_organ(parent_organ) + if(parent && !silent) + owner.custom_pain("Something inside your [parent.name] hurts a lot.", 1) /obj/item/organ/proc/robotize() //Being used to make robutt hearts, etc robotic = 2 diff --git a/code/modules/organs/pain.dm b/code/modules/organs/pain.dm index da3c289224..4f19115bdc 100644 --- a/code/modules/organs/pain.dm +++ b/code/modules/organs/pain.dm @@ -8,7 +8,10 @@ mob/var/next_pain_time = 0 // partname is the name of a body part // amount is a num from 1 to 100 mob/living/carbon/proc/pain(var/partname, var/amount, var/force, var/burning = 0) - if(stat >= 2) return + if(stat >= 1) + return + if(species && species.flags & NO_PAIN) + return if(analgesic > 40) return if(world.time < next_pain_time && !force) @@ -48,10 +51,10 @@ mob/living/carbon/proc/pain(var/partname, var/amount, var/force, var/burning = 0 // message is the custom message to be displayed // flash_strength is 0 for weak pain flash, 1 for strong pain flash mob/living/carbon/human/proc/custom_pain(var/message, var/flash_strength) - if(stat >= 1) return - - if(species && species.flags & NO_PAIN) return - + if(stat >= 1) + return + if(species && species.flags & NO_PAIN) + return if(reagents.has_reagent("tramadol")) return if(reagents.has_reagent("oxycodone")) @@ -79,7 +82,7 @@ mob/living/carbon/human/proc/handle_pain() var/maxdam = 0 var/obj/item/organ/external/damaged_organ = null for(var/obj/item/organ/external/E in organs) - if(E.status & ORGAN_DEAD) continue + if(E.status & (ORGAN_DEAD|ORGAN_ROBOT)) continue var/dam = E.get_damage() // make the choice of the organ depend on damage, // but also sometimes use one of the less damaged ones @@ -91,6 +94,7 @@ mob/living/carbon/human/proc/handle_pain() // Damage to internal organs hurts a lot. for(var/obj/item/organ/I in internal_organs) + if(I.status & (ORGAN_DEAD|ORGAN_ROBOT)) continue if(I.damage > 2) if(prob(2)) var/obj/item/organ/external/parent = get_organ(I.parent_organ) src.custom_pain("You feel a sharp pain in your [parent.name]", 1) From b4adb1a432f65a0a1624860bda5969c61e4837dd Mon Sep 17 00:00:00 2001 From: mwerezak Date: Wed, 27 May 2015 01:34:50 -0400 Subject: [PATCH 23/31] Updates species checks --- code/modules/organs/pain.dm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/modules/organs/pain.dm b/code/modules/organs/pain.dm index 4f19115bdc..e4e024be10 100644 --- a/code/modules/organs/pain.dm +++ b/code/modules/organs/pain.dm @@ -10,7 +10,7 @@ mob/var/next_pain_time = 0 mob/living/carbon/proc/pain(var/partname, var/amount, var/force, var/burning = 0) if(stat >= 1) return - if(species && species.flags & NO_PAIN) + if(species && (species.flags & NO_PAIN)) return if(analgesic > 40) return @@ -53,7 +53,7 @@ mob/living/carbon/proc/pain(var/partname, var/amount, var/force, var/burning = 0 mob/living/carbon/human/proc/custom_pain(var/message, var/flash_strength) if(stat >= 1) return - if(species && species.flags & NO_PAIN) + if(species.flags & NO_PAIN) return if(reagents.has_reagent("tramadol")) return @@ -74,7 +74,7 @@ mob/living/carbon/human/proc/custom_pain(var/message, var/flash_strength) mob/living/carbon/human/proc/handle_pain() // not when sleeping - if(species && species.flags & NO_PAIN) return + if(species.flags & NO_PAIN) return if(stat >= 2) return if(analgesic > 70) From 19aa1c0ae869e0f9799990f93f62ca1c61c4b40d Mon Sep 17 00:00:00 2001 From: Kelenius Date: Wed, 27 May 2015 13:14:22 +0300 Subject: [PATCH 24/31] AI now hears LOOC both around its eye and its core, and speaks in LOOC around its eye --- code/game/verbs/ooc.dm | 42 +++++++++++++++++------- html/changelogs/Kelenius-ofAIAndLooc.yml | 4 +++ 2 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 html/changelogs/Kelenius-ofAIAndLooc.yml diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm index e35e4a6eb2..06c63cdd03 100644 --- a/code/game/verbs/ooc.dm +++ b/code/game/verbs/ooc.dm @@ -69,19 +69,22 @@ set category = "OOC" if(say_disabled) //This is here to try to identify lag problems - usr << "\red Speech is currently admin-disabled." + usr << "Speech is currently admin-disabled." + return + + if(!mob) return - if(!mob) return if(IsGuestKey(key)) src << "Guests may not use OOC." return msg = sanitize(msg) - if(!msg) return + if(!msg) + return if(!(prefs.toggles & CHAT_LOOC)) - src << "\red You have LOOC muted." + src << "You have LOOC muted." return if(!holder) @@ -94,7 +97,7 @@ if(prefs.muted & MUTE_OOC) src << "You cannot use OOC (muted)." return - if(handle_spam_prevention(msg,MUTE_OOC)) + if(handle_spam_prevention(msg, MUTE_OOC)) return if(findtext(msg, "byond://")) src << "Advertising other servers is not allowed." @@ -104,14 +107,14 @@ log_ooc("(LOCAL) [mob.name]/[key] : [msg]") - var/mob/source = src.mob + var/mob/source = mob.get_looc_source() var/list/heard = get_mobs_or_objects_in_view(7, source, 1, 0) - var/display_name = source.key + var/display_name = key if(holder && holder.fakekey) display_name = holder.fakekey - if(source.stat != DEAD) - display_name = source.name + if(mob.stat != DEAD) + display_name = mob.name var/prefix var/admin_stuff @@ -120,10 +123,27 @@ admin_stuff = "" if(target in admins) prefix = "(R)" - admin_stuff += "/([source.key])" - if(target != source.client) + admin_stuff += "/([key])" + if(target != src) admin_stuff += "(JMP)" if(target.mob in heard) prefix = "" + + var/send = 0 if((target.mob in heard) || (target in admins)) + send = 1 + else if(isAI(target.mob)) // Special case + var/mob/living/silicon/ai/A = target.mob + if(A.eyeobj in view(7, source)) + send = 1 + + if(send) target << "" + create_text_tag("looc", "LOOC:", target) + " [prefix][display_name][admin_stuff]: [msg]" + +/mob/proc/get_looc_source() + return src + +/mob/living/silicon/ai/get_looc_source() + if(eyeobj) + return eyeobj + return src diff --git a/html/changelogs/Kelenius-ofAIAndLooc.yml b/html/changelogs/Kelenius-ofAIAndLooc.yml new file mode 100644 index 0000000000..6660334002 --- /dev/null +++ b/html/changelogs/Kelenius-ofAIAndLooc.yml @@ -0,0 +1,4 @@ +author: Kelenius +delete-after: True +changes: + - tweak: "AI now hears LOOC both around its eye and its core, and speaks in LOOC around its eye. Keep in mind that you won't hear and won't be heard if there is a wall between your eye and the target." From 9a24b5b1cbef116cab7160d1e910a0704c70b9c5 Mon Sep 17 00:00:00 2001 From: Kelenius Date: Wed, 27 May 2015 21:13:34 +0300 Subject: [PATCH 25/31] AI now has a prefix, (Core) or (Eye), depending on where it hears LOOC --- code/game/verbs/ooc.dm | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm index 06c63cdd03..d2d0be60f3 100644 --- a/code/game/verbs/ooc.dm +++ b/code/game/verbs/ooc.dm @@ -116,26 +116,31 @@ if(mob.stat != DEAD) display_name = mob.name - var/prefix - var/admin_stuff for(var/client/target in clients) if(target.prefs.toggles & CHAT_LOOC) - admin_stuff = "" + var/prefix = "" + var/admin_stuff = "" + var/send = 0 + if(target in admins) - prefix = "(R)" admin_stuff += "/([key])" if(target != src) admin_stuff += "(JMP)" - if(target.mob in heard) - prefix = "" - var/send = 0 - if((target.mob in heard) || (target in admins)) + if(target.mob in heard) send = 1 + if(isAI(target.mob)) + prefix = "(Core) " + else if(isAI(target.mob)) // Special case var/mob/living/silicon/ai/A = target.mob - if(A.eyeobj in view(7, source)) + if(A.eyeobj in hearers(7, source)) send = 1 + prefix = "(Eye) " + + if(!send && (target in admins)) + send = 1 + prefix = "(R)" if(send) target << "" + create_text_tag("looc", "LOOC:", target) + " [prefix][display_name][admin_stuff]: [msg]" From 850301ed0fe12d8e117b972d7ef383398afeff2e Mon Sep 17 00:00:00 2001 From: Yoshax Date: Wed, 27 May 2015 22:32:18 +0100 Subject: [PATCH 26/31] Fixes bluespace relay issues (being un deconstructable, and not building properly. --- code/game/machinery/bluespacerelay.dm | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/code/game/machinery/bluespacerelay.dm b/code/game/machinery/bluespacerelay.dm index 03d4a311b1..4f7ab82207 100644 --- a/code/game/machinery/bluespacerelay.dm +++ b/code/game/machinery/bluespacerelay.dm @@ -25,6 +25,15 @@ else icon_state = "[initial(icon_state)]_off" +/obj/machinery/bluespacerelay/New() + ..() + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/bluespacerelay(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/subspace/filter(src) + component_parts += new /obj/item/weapon/stock_parts/subspace/crystal(src) + component_parts += new /obj/item/stack/cable_coil(src, 30) /obj/machinery/bluespacerelay/proc/update_power() if(stat & (BROKEN|NOPOWER|EMPED)) @@ -32,3 +41,12 @@ else on = 1 +/obj/machinery/bluespacerelay/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(default_deconstruction_screwdriver(user, O)) + return + if(default_deconstruction_crowbar(user, O)) + return + if(default_part_replacement(user, O)) + return + + ..() \ No newline at end of file From 62ef32a7255b47ce539a9dbd5a58fbe27ae3c09c Mon Sep 17 00:00:00 2001 From: Raptor1628 Date: Wed, 27 May 2015 23:32:27 -0400 Subject: [PATCH 27/31] Rig Icon Fixes Fixes some incorrect names that were resulting in broken icons. --- .../clothing/spacesuits/rig/modules/combat.dm | 2 +- .../clothing/spacesuits/rig/modules/ninja.dm | 2 +- icons/obj/rig_modules.dmi | Bin 18198 -> 18196 bytes 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/clothing/spacesuits/rig/modules/combat.dm b/code/modules/clothing/spacesuits/rig/modules/combat.dm index b367a5369f..88e989c2a0 100644 --- a/code/modules/clothing/spacesuits/rig/modules/combat.dm +++ b/code/modules/clothing/spacesuits/rig/modules/combat.dm @@ -12,7 +12,7 @@ name = "mounted grenade launcher" desc = "A shoulder-mounted micro-explosive dispenser." selectable = 1 - icon_state = "grenade_launcher" + icon_state = "grenadelauncher" interface_name = "integrated grenade launcher" interface_desc = "Discharges loaded grenades against the wearer's location." diff --git a/code/modules/clothing/spacesuits/rig/modules/ninja.dm b/code/modules/clothing/spacesuits/rig/modules/ninja.dm index 803b0f63f1..8b4a30d389 100644 --- a/code/modules/clothing/spacesuits/rig/modules/ninja.dm +++ b/code/modules/clothing/spacesuits/rig/modules/ninja.dm @@ -10,7 +10,7 @@ name = "active camouflage module" desc = "A robust hardsuit-integrated stealth module." - icon_state = "stealth" + icon_state = "cloak" toggleable = 1 disruptable = 1 diff --git a/icons/obj/rig_modules.dmi b/icons/obj/rig_modules.dmi index 02af395159d694ad7e2ad8a623ef6d19ebd19067..1dbcf6ac31c42670165e5f280c8d40820d523069 100644 GIT binary patch delta 447 zcmV;w0YLtijscX80gxnrrFv9YbVOxyV{&P5bZKvH004NLm6fq>+%OD=XZtCHWIYtU zG^>~5g2F?xlmZPB3snau`168wMsB5%X@hadA(I6cneJ4&a< zVG8$OzK+9%lIAdlZ+GL6k=Cr&pRS-7rf{<&qfopVheXoD6prJ6@G4O8`u&$f(IvK8 zffLFiw7M!f#$8B1+yxaAMJR3_c_l{YM6INyqC^*L0;?cH!cbu6m{II`*PU(Xr9eU( zV^kZufIU@0NEPhCwst}3#BU~9A}}|*qe6GMw(X_cen?EVQcyTUv;{D&*><;LcZh78 zQtT#{5sK!#+baHlJSh*WSa55i4x3|XU^}B3r9yFVvUf$Xh0pWz|C#%7NLb(_OyPJq z9*&29Gf|*dh`V}}VM!<*`UXO!fNgLDW=G#7RBR`R<7Y?JBOY<;Hf_UkmhXQ+p}i z@WD@zKlkC=)A#8m9G|D*LfW`W z7{lYk=V5o{*i#t8*Du2^5ihCVKCMHiFowH@gp~1a*hSVB#&8&ab|-~GxA$L;w9i;e z0g0ru^5TkUgqw1IcLx-TjF8y0~Or&>65k#t;u|spXwr;uEekev;DRejk+5&`^WV>6aIYidA zGd2@T1i7cQ*(&~jjFgAHRF0oo;*+fga|PR(o=6L12d7)>q_*&B`tg6}ao9!5@E*o+ z*dO+X{l7htp%uWbchA`6NE#Zn6_j%Z+hGI|N8+fQZ7W#LguUA73N5gvDt5Dhr3#6LzlR3UV)9VL5|~d{qKWx5Ym*V Date: Thu, 28 May 2015 08:49:58 +0300 Subject: [PATCH 28/31] Fixes skrell and dionaea alcohol resistance/weakness --- .../Chemistry-Reagents-Dispenser.dm | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm index eac0f76cd2..b9caf096d6 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm @@ -89,19 +89,19 @@ M.add_chemical_effect(CE_ALCOHOL, 1) - if(dose / strength_mod >= strength) // Early warning + if(dose * strength_mod >= strength) // Early warning M.make_dizzy(6) // It is decreased at the speed of 3 per tick - if(dose / strength_mod >= strength * 2) // Slurring + if(dose * strength_mod >= strength * 2) // Slurring M.slurring = max(M.slurring, 30) - if(dose / strength_mod >= strength * 3) // Confusion - walking in random directions + if(dose * strength_mod >= strength * 3) // Confusion - walking in random directions M.confused = max(M.confused, 20) - if(dose / strength_mod >= strength * 4) // Blurry vision + if(dose * strength_mod >= strength * 4) // Blurry vision M.eye_blurry = max(M.eye_blurry, 10) - if(dose / strength_mod >= strength * 5) // Drowsyness - periodically falling asleep + if(dose * strength_mod >= strength * 5) // Drowsyness - periodically falling asleep M.drowsyness = max(M.drowsyness, 20) - if(dose / strength_mod >= strength * 6) // Toxic dose + if(dose * strength_mod >= strength * 6) // Toxic dose M.add_chemical_effect(CE_ALCOHOL_TOXIC, toxicity) - if(dose / strength_mod >= strength * 7) // Pass out + if(dose * strength_mod >= strength * 7) // Pass out M.paralysis = max(M.paralysis, 20) M.sleeping = max(M.sleeping, 30) From a4c29238aae6f4f7925e2e82b2439eae86288f72 Mon Sep 17 00:00:00 2001 From: Atlantis Date: Thu, 28 May 2015 17:11:26 +0200 Subject: [PATCH 29/31] Refactors get_apc() - Fixes #9536 - Area now has APC variable, instead of list. APC construction, etc. should have checks to allow one APC per area only, so situations with multi-APC areas are unlikely, unless adminbus occurs. Either way, with or without this commit, one of the APCs would be unoperational (no load) - get_apc() no longer uses very laggy locate() in contents of area. In situations where get_apc() is called repeately this brings massive increase in performance (from 0.120 to 0.000, depending on area size and amount of things in contents of that area) as it now only returns the apc variable. --- code/game/area/Space Station 13 areas.dm | 2 +- code/modules/power/apc.dm | 6 +++--- code/modules/power/powernet.dm | 5 ++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index 21e965244b..04d7ef41e9 100755 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -46,7 +46,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station var/used_environ = 0 var/has_gravity = 1 - var/list/apc = list() + var/obj/machinery/power/apc/apc = null var/no_air = null // var/list/lights // list of all lights on this area var/list/all_doors = list() //Added by Strumpetplaya - Alarm Change - Contains a list of doors adjacent to this area diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index f21db9327d..167139d75f 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -156,7 +156,7 @@ init() else area = get_area(src) - area.apc |= src + area.apc = src opened = 1 operating = 0 name = "[area.name] APC" @@ -171,7 +171,7 @@ if(operating && malf && src.z in config.station_levels) //if (is_type_in_list(get_area(src), the_station_areas)) malf.hacked_apcs -= src - area.apc -= src + area.apc = null area.power_light = 0 area.power_equip = 0 area.power_environ = 0 @@ -210,7 +210,7 @@ else src.area = get_area_name(areastring) name = "\improper [area.name] APC" - area.apc |= src + area.apc = src update_icon() make_terminal() diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm index e8d42bd2bd..1d5711b4b3 100644 --- a/code/modules/power/powernet.dm +++ b/code/modules/power/powernet.dm @@ -149,7 +149,6 @@ return C return null + /area/proc/get_apc() - var/obj/machinery/power/apc/FINDME = locate() in src - if (FINDME) - return FINDME + return apc From ccf9afb9cb1b11300023c81ae74ac85a540f9d6d Mon Sep 17 00:00:00 2001 From: Atlantis Date: Thu, 28 May 2015 15:55:04 +0200 Subject: [PATCH 30/31] Implements PsiOmegaDelta's suggestion (fixed edition) --- .../game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm index 441ab6f7cc..1be9d0330f 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm @@ -183,7 +183,7 @@ for(var/mob/living/silicon/robot/RB in mob_list) if(istype(RB, /mob/living/silicon/robot/drone)) continue - if(RB in A.connected_robots) + if(RB.connected_ai == A) continue L.Add(RB) return L From 477204e8c81fa131e13275679cbe7206ffd2846d Mon Sep 17 00:00:00 2001 From: Atlantis Date: Fri, 29 May 2015 12:23:39 +0200 Subject: [PATCH 31/31] Fixes two newmalf-related issues - Fixes APC runtime. I am an idiot. I know. - Fixes potentional power issue with AI powersupply object, caused by AI having more than one powersupply. This sometimes caused AI to use 100kW instead of 50kW --- code/modules/mob/living/silicon/ai/ai.dm | 2 ++ code/modules/power/apc.dm | 6 ++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 0e071c2dc9..9c874f54b1 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -246,6 +246,8 @@ var/list/ai_verbs_default = list( if(!powered_ai || powered_ai.stat & DEAD) qdel() return + if(powered_ai.psupply != src) // For some reason, the AI has different powersupply object. Delete this one, it's no longer needed. + qdel(src) if(powered_ai.APU_power) use_power = 0 return diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 60a474ce6d..6899098c94 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -159,10 +159,8 @@ stat |= MAINT src.update_icon() -/obj/machinery/power/apc/initialize() - ..() +/obj/machinery/power/apc/Destroy() src.update() - area.apc = null area.power_light = 0 area.power_equip = 0 @@ -179,7 +177,7 @@ terminal = null // Malf AI, removes the APC from AI's hacked APCs list. - if(hacker && hacker.hacked_apcs && src in hacker.hacked_apcs) + if((hacker) && (hacker.hacked_apcs) && (src in hacker.hacked_apcs)) hacker.hacked_apcs -= src ..()