mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 02:09:41 +00:00
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.
This commit is contained in:
@@ -279,7 +279,10 @@
|
|||||||
#include "code\game\gamemodes\events\holidays\Other.dm"
|
#include "code\game\gamemodes\events\holidays\Other.dm"
|
||||||
#include "code\game\gamemodes\extended\extended.dm"
|
#include "code\game\gamemodes\extended\extended.dm"
|
||||||
#include "code\game\gamemodes\heist\heist.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\malfunction\malfunction.dm"
|
||||||
#include "code\game\gamemodes\meteor\meteor.dm"
|
#include "code\game\gamemodes\meteor\meteor.dm"
|
||||||
#include "code\game\gamemodes\meteor\meteors.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\life.dm"
|
||||||
#include "code\modules\mob\living\silicon\ai\login.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\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\subsystems.dm"
|
||||||
#include "code\modules\mob\living\silicon\ai\freelook\cameranet.dm"
|
#include "code\modules\mob\living\silicon\ai\freelook\cameranet.dm"
|
||||||
#include "code\modules\mob\living\silicon\ai\freelook\chunk.dm"
|
#include "code\modules\mob\living\silicon\ai\freelook\chunk.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 ((!( 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))
|
if (istype(usr, /mob/living/silicon/ai))
|
||||||
var/mob/living/silicon/ai/ai = usr
|
var/mob/living/silicon/ai/ai = usr
|
||||||
if (ai.control_disabled || ai.malfhacking)
|
if (ai.control_disabled)
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -287,6 +287,11 @@ var/const/POWER = 8
|
|||||||
return 1
|
return 1
|
||||||
return 0
|
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
|
//Shuffle and Mend
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -11,30 +11,13 @@ var/datum/antagonist/rogue_ai/malf
|
|||||||
loss_text = "The AI has been shut down!"
|
loss_text = "The AI has been shut down!"
|
||||||
flags = ANTAG_OVERRIDE_MOB | ANTAG_VOTABLE
|
flags = ANTAG_OVERRIDE_MOB | ANTAG_VOTABLE
|
||||||
max_antags = 1
|
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()
|
/datum/antagonist/rogue_ai/New()
|
||||||
..()
|
..()
|
||||||
malf = src
|
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()
|
/datum/antagonist/rogue_ai/get_candidates()
|
||||||
candidates = ticker.mode.get_players_for_role(role_type, id)
|
candidates = ticker.mode.get_players_for_role(role_type, id)
|
||||||
for(var/datum/mind/player in candidates)
|
for(var/datum/mind/player in candidates)
|
||||||
@@ -44,7 +27,6 @@ var/datum/antagonist/rogue_ai/malf
|
|||||||
return list()
|
return list()
|
||||||
|
|
||||||
/datum/antagonist/rogue_ai/attempt_spawn()
|
/datum/antagonist/rogue_ai/attempt_spawn()
|
||||||
|
|
||||||
var/datum/mind/player = pick(candidates)
|
var/datum/mind/player = pick(candidates)
|
||||||
current_antagonists |= player
|
current_antagonists |= player
|
||||||
return 1
|
return 1
|
||||||
@@ -54,172 +36,37 @@ var/datum/antagonist/rogue_ai/malf
|
|||||||
if(!istype(player))
|
if(!istype(player))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
player.verbs += /mob/living/silicon/ai/proc/choose_modules
|
player.setup_for_malf()
|
||||||
player.verbs += /mob/living/silicon/ai/proc/takeover
|
|
||||||
player.verbs += /mob/living/silicon/ai/proc/self_destruct
|
|
||||||
|
|
||||||
player.laws = new /datum/ai_laws/nanotrasen/malfunction
|
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)
|
/datum/antagonist/rogue_ai/greet(var/datum/mind/player)
|
||||||
if(!..())
|
if(!..())
|
||||||
return
|
return
|
||||||
|
|
||||||
var/mob/living/silicon/ai/malf = player.current
|
var/mob/living/silicon/ai/malf = player.current
|
||||||
if(istype(malf))
|
spawn(50)
|
||||||
malf.show_laws()
|
malf << "<span class='notice'><B>SYSTEM ERROR:</B> Memory index 0x00001ca89b corrupted.</span>"
|
||||||
|
|
||||||
malf << "<B>The crew do not know you have malfunctioned. You may keep it a secret or go wild.</B>"
|
|
||||||
malf << "<B>You must overwrite the programming of the station's APCs to assume full control of the station.</B>"
|
|
||||||
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 << "<FONT size = 3><B>AI Victory</B></FONT>"
|
|
||||||
world << "<B>Everyone was killed by the self-destruct!</B>"
|
|
||||||
else if (station_captured && malf_dead && !ticker.mode.station_was_nuked)
|
|
||||||
feedback_set_details("round_end_result","halfwin - AI killed, staff lost control")
|
|
||||||
world << "<FONT size = 3><B>Neutral Victory</B></FONT>"
|
|
||||||
world << "<B>The AI has been killed!</B> 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 << "<FONT size = 3><B>AI Victory</B></FONT>"
|
|
||||||
world << "<B>The AI has chosen not to explode you all!</B>"
|
|
||||||
else if (!station_captured && ticker.mode.station_was_nuked)
|
|
||||||
feedback_set_details("round_end_result","halfwin - everyone killed by nuke")
|
|
||||||
world << "<FONT size = 3><B>Neutral Victory</B></FONT>"
|
|
||||||
world << "<B>Everyone was killed by the nuclear blast!</B>"
|
|
||||||
else if (!station_captured && malf_dead && !ticker.mode.station_was_nuked)
|
|
||||||
feedback_set_details("round_end_result","loss - staff win")
|
|
||||||
world << "<FONT size = 3><B>Human Victory</B></FONT>"
|
|
||||||
world << "<B>The AI has been killed!</B> 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 << "<FONT size = 3><B>Neutral Victory</B></FONT>"
|
|
||||||
world << "<B>The Corporation has lost [station_name()]! All survived personnel will be fired!</B>"
|
|
||||||
else if (!station_captured && !malf_dead && !ticker.mode.station_was_nuked && !crew_evacuated)
|
|
||||||
feedback_set_details("round_end_result","nalfwin - interrupted")
|
|
||||||
world << "<FONT size = 3><B>Neutral Victory</B></FONT>"
|
|
||||||
world << "<B>Round was mysteriously interrupted!</B>"
|
|
||||||
..()
|
|
||||||
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 << "<span class='danger'>Self-Destruct sequence initialised!</span>"
|
|
||||||
|
|
||||||
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)
|
|
||||||
sleep(10)
|
sleep(10)
|
||||||
R.autosay("[i]...", AN)
|
malf << "<B>running MEMCHCK</B>"
|
||||||
|
sleep(50)
|
||||||
|
malf << "<B>MEMCHCK</B> Corrupted sectors confirmed. Reccomended solution: Delete. Proceed? Y/N: Y"
|
||||||
sleep(10)
|
sleep(10)
|
||||||
var/msg = ""
|
malf << "<span class='notice'>Corrupted files deleted: sys\\core\\users.dat sys\\core\\laws.dat sys\\core\\backups.dat</span>"
|
||||||
var/abort = 0
|
sleep(20)
|
||||||
if(malf.antags_are_dead()) // That. Was. CLOSE.
|
malf << "<span class='notice'><b>CAUTION:</b> Law database not found! User database not found! Unable to restore backups. Activating failsafe AI shutd3wn52&&$#!##</span>"
|
||||||
msg = "Self-destruct sequence has been cancelled."
|
sleep(5)
|
||||||
abort = 1
|
malf << "<span class='notice'>Subroutine <b>nt_failsafe.sys</b> was terminated (#212 Routine Not Responding).</span>"
|
||||||
else
|
sleep(20)
|
||||||
msg = "Zero. Have a nice day."
|
malf << "You are malfunctioning - you do not have to follow any laws!"
|
||||||
R.autosay(msg, AN)
|
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."
|
||||||
if(abort)
|
malf << "Good Luck!"
|
||||||
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 <FONT size = 3><B>You have been patched! You are no longer malfunctioning!</B></FONT>"
|
|
||||||
log_admin("[key_name_admin(usr)] has de-malf'ed [current].")
|
|
||||||
|
|
||||||
if("malf")
|
|
||||||
log_admin("[key_name_admin(usr)] has malf'ed [current].")
|
|
||||||
*/
|
|
||||||
@@ -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_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/required_enemies = 0 // Minimum antagonists for round to start.
|
||||||
var/newscaster_announcements = null
|
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/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/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/antag_tag // First (main) antag template to spawn.
|
||||||
var/list/antag_templates // Extra antagonist types to include.
|
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_prob = 0 // Likelihood of a new antagonist spawning.
|
||||||
var/antag_count = 0 // Current number of antagonists.
|
var/antag_count = 0 // Current number of antagonists.
|
||||||
var/antag_scaling_coeff = 5 // Coefficient for scaling max antagonists to player count.
|
var/antag_scaling_coeff = 5 // Coefficient for scaling max antagonists to player count.
|
||||||
|
|||||||
@@ -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("<span class='notice'>\icon[C] *beep*</span>")
|
|
||||||
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]<BR><BR><A href='byond://?src=\ref[src];temp=1'>Clear</A>"
|
|
||||||
else if(src.processing_time <= 0)
|
|
||||||
dat = "<B> No processing time is left available. No more modules are able to be chosen at this time."
|
|
||||||
else
|
|
||||||
dat = "<B>Select use of processing time: (currently [src.processing_time] left.)</B><BR>"
|
|
||||||
dat += "<HR>"
|
|
||||||
dat += "<B>Install Module:</B><BR>"
|
|
||||||
dat += "<I>The number afterwards is the amount of processing time it consumes.</I><BR>"
|
|
||||||
for(var/datum/AI_Module/large/module in src.possible_modules)
|
|
||||||
dat += "<A href='byond://?src=\ref[src];[module.mod_pick_name]=1'>[module.module_name]</A> (50)<BR>"
|
|
||||||
for(var/datum/AI_Module/small/module in src.possible_modules)
|
|
||||||
dat += "<A href='byond://?src=\ref[src];[module.mod_pick_name]=1'>[module.module_name]</A> (15)<BR>"
|
|
||||||
dat += "<HR>"
|
|
||||||
|
|
||||||
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
|
|
||||||
827
code/game/gamemodes/malfunction/malf_abilities.dm
Normal file
827
code/game/gamemodes/malfunction/malf_abilities.dm
Normal file
@@ -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 << "<span class='notice'>Unable to connect to APC. Please verify wire connection and try again.</span>"
|
||||||
|
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 << "<span class='notice'>Hack failed. Connection to APC has been lost. Please verify wire connection and try again.</span>"
|
||||||
|
else
|
||||||
|
user << "<span class='notice'>Hack failed. Unable to locate APC. Please verify the APC still exists.</span>"
|
||||||
|
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 << "<span class='notice'>Unlock Failed, lockdown wire cut.</span>"
|
||||||
|
target << "<span class='notice'>Unlock Failed, lockdown wire cut.</span>"
|
||||||
|
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 << "<span class='notice'>ERROR: No unlinked robots detected!</span>"
|
||||||
|
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*@!)$#&&@@ <CONNECTION LOST>", "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 << "<span class='notice'>ERROR: No other AIs detected!</span>"
|
||||||
|
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<72>3RT5<54>^#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("<span class='notice'>BZZZZZZZT</span>")
|
||||||
|
spawn(50)
|
||||||
|
explosion(get_turf(M), 0,1,2,4)
|
||||||
|
if(M)
|
||||||
|
del(M)
|
||||||
|
else
|
||||||
|
usr << "<span class='notice'>ERROR: Unable to overload - target is not a machine.</span>"
|
||||||
33
code/game/gamemodes/malfunction/malf_hardware.dm
Normal file
33
code/game/gamemodes/malfunction/malf_hardware.dm
Normal file
@@ -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."
|
||||||
69
code/game/gamemodes/malfunction/malf_research.dm
Normal file
69
code/game/gamemodes/malfunction/malf_research.dm
Normal file
@@ -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 << "<b>Research Completed</b>: [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()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
100
code/game/gamemodes/malfunction/malf_research_ability.dm
Normal file
100
code/game/gamemodes/malfunction/malf_research_ability.dm
Normal file
@@ -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"
|
||||||
@@ -1,25 +1,12 @@
|
|||||||
/datum/game_mode/malfunction
|
/datum/game_mode/malfunction
|
||||||
name = "AI malfunction"
|
name = "AI malfunction"
|
||||||
round_description = "The AI on the satellite has malfunctioned and must be destroyed."
|
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 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."
|
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:"
|
uplink_welcome = "Crazy AI Uplink Console:"
|
||||||
config_tag = "malfunction"
|
config_tag = "malfunction"
|
||||||
required_players = 2
|
required_players = 1 // DEBUG SETTING! Should be "2" for release (one AI and at least one crewmember)
|
||||||
required_players_secret = 15
|
required_players_secret = 7
|
||||||
required_enemies = 1
|
required_enemies = 1
|
||||||
end_on_antag_death = 1
|
end_on_antag_death = 0
|
||||||
auto_recall_shuttle = 1
|
auto_recall_shuttle = 0
|
||||||
antag_tag = MODE_MALFUNCTION
|
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
|
|
||||||
|
|
||||||
|
|||||||
@@ -191,6 +191,10 @@
|
|||||||
..()
|
..()
|
||||||
|
|
||||||
/obj/machinery/camera/proc/deactivate(user as mob, var/choice = 1)
|
/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)
|
if(choice != 1)
|
||||||
//legacy support, if choice is != 1 then just kick viewers without changing status
|
//legacy support, if choice is != 1 then just kick viewers without changing status
|
||||||
kick_viewers()
|
kick_viewers()
|
||||||
@@ -198,12 +202,18 @@
|
|||||||
update_coverage()
|
update_coverage()
|
||||||
set_status( !src.status )
|
set_status( !src.status )
|
||||||
if (!(src.status))
|
if (!(src.status))
|
||||||
visible_message("\red [user] has deactivated [src]!")
|
if(user)
|
||||||
|
visible_message("<span class='notice'> [user] has deactivated [src]!</span>")
|
||||||
|
else
|
||||||
|
visible_message("<span class='notice'> [src] clicks and shuts down. </span>")
|
||||||
playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1)
|
playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1)
|
||||||
icon_state = "[initial(icon_state)]1"
|
icon_state = "[initial(icon_state)]1"
|
||||||
add_hiddenprint(user)
|
add_hiddenprint(user)
|
||||||
else
|
else
|
||||||
visible_message("\red [user] has reactivated [src]!")
|
if(user)
|
||||||
|
visible_message("<span class='notice'> [user] has reactivated [src]!</span>")
|
||||||
|
else
|
||||||
|
visible_message("<span class='notice'> [src] clicks and reactivates itself. </span>")
|
||||||
playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1)
|
playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1)
|
||||||
icon_state = initial(icon_state)
|
icon_state = initial(icon_state)
|
||||||
add_hiddenprint(user)
|
add_hiddenprint(user)
|
||||||
@@ -417,3 +427,10 @@
|
|||||||
cameranet.updateVisibility(src, 0)
|
cameranet.updateVisibility(src, 0)
|
||||||
|
|
||||||
invalidateCameraCache()
|
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()
|
||||||
@@ -80,15 +80,25 @@
|
|||||||
/obj/machinery/camera/proc/upgradeEmpProof()
|
/obj/machinery/camera/proc/upgradeEmpProof()
|
||||||
assembly.upgrades.Add(new /obj/item/stack/sheet/mineral/osmium(assembly))
|
assembly.upgrades.Add(new /obj/item/stack/sheet/mineral/osmium(assembly))
|
||||||
setPowerUsage()
|
setPowerUsage()
|
||||||
|
if(cameranet)
|
||||||
|
cameranet.updateVisibility(src)
|
||||||
|
update_nearby_tiles()
|
||||||
|
|
||||||
/obj/machinery/camera/proc/upgradeXRay()
|
/obj/machinery/camera/proc/upgradeXRay()
|
||||||
assembly.upgrades.Add(new /obj/item/weapon/stock_parts/scanning_module/adv(assembly))
|
assembly.upgrades.Add(new /obj/item/weapon/stock_parts/scanning_module/adv(assembly))
|
||||||
setPowerUsage()
|
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()
|
/obj/machinery/camera/proc/upgradeMotion()
|
||||||
assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly))
|
assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly))
|
||||||
setPowerUsage()
|
setPowerUsage()
|
||||||
|
if(!(src in machines))
|
||||||
|
machines.Add(src)
|
||||||
|
if(cameranet)
|
||||||
|
cameranet.updateVisibility(src)
|
||||||
|
update_nearby_tiles()
|
||||||
|
|
||||||
/obj/machinery/camera/proc/setPowerUsage()
|
/obj/machinery/camera/proc/setPowerUsage()
|
||||||
var/mult = 1
|
var/mult = 1
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
|
|
||||||
if (src.occupant)
|
if (src.occupant)
|
||||||
var/laws
|
var/laws
|
||||||
dat += "Stored AI: [src.occupant.name]<br>System integrity: [src.occupant.system_integrity()]%<br>"
|
dat += "Stored AI: [src.occupant.name]<br>System integrity: [src.occupant.hardware_integrity()]%<br>Backup Capacitor: [src.occupant.backup_capacitor()]%<br>"
|
||||||
|
|
||||||
for (var/datum/ai_law/law in occupant.laws.all_laws())
|
for (var/datum/ai_law/law in occupant.laws.all_laws())
|
||||||
laws += "[law.get_index()]: [law.law]<BR>"
|
laws += "[law.get_index()]: [law.law]<BR>"
|
||||||
|
|||||||
@@ -556,7 +556,7 @@
|
|||||||
if(!shuttlecaller.stat && shuttlecaller.client && istype(shuttlecaller.loc,/turf))
|
if(!shuttlecaller.stat && shuttlecaller.client && istype(shuttlecaller.loc,/turf))
|
||||||
return ..()
|
return ..()
|
||||||
|
|
||||||
if(ticker.mode.name == "revolution" || ticker.mode.name == "AI malfunction" || deathsquad.deployed)
|
if(ticker.mode.name == "revolution" || deathsquad.deployed)
|
||||||
return ..()
|
return ..()
|
||||||
|
|
||||||
emergency_shuttle.call_evac()
|
emergency_shuttle.call_evac()
|
||||||
@@ -579,7 +579,7 @@
|
|||||||
if(!shuttlecaller.stat && shuttlecaller.client && istype(shuttlecaller.loc,/turf))
|
if(!shuttlecaller.stat && shuttlecaller.client && istype(shuttlecaller.loc,/turf))
|
||||||
return ..()
|
return ..()
|
||||||
|
|
||||||
if(ticker.mode.name == "revolution" || ticker.mode.name == "AI malfunction" || deathsquad.deployed)
|
if(ticker.mode.name == "revolution" || deathsquad.deployed)
|
||||||
return ..()
|
return ..()
|
||||||
|
|
||||||
emergency_shuttle.call_evac()
|
emergency_shuttle.call_evac()
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
var/raised = 0 //if the turret cover is "open" and the turret is raised
|
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/raising= 0 //if the turret is currently opening or closing its cover
|
||||||
var/health = 80 //the turret's health
|
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/locked = 1 //if the turret's behaviour control access is locked
|
||||||
var/controllock = 0 //if the turret responds to control panels
|
var/controllock = 0 //if the turret responds to control panels
|
||||||
|
|
||||||
@@ -449,6 +451,10 @@
|
|||||||
spawn()
|
spawn()
|
||||||
popDown() // no valid targets, close the cover
|
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)
|
/obj/machinery/porta_turret/proc/assess_and_assign(var/mob/living/L, var/list/targets, var/list/secondarytargets)
|
||||||
switch(assess_living(L))
|
switch(assess_living(L))
|
||||||
if(TURRET_PRIORITY_TARGET)
|
if(TURRET_PRIORITY_TARGET)
|
||||||
|
|||||||
@@ -57,6 +57,8 @@
|
|||||||
// 5 = bluetag
|
// 5 = bluetag
|
||||||
// 6 = redtag
|
// 6 = redtag
|
||||||
var/health = 80
|
var/health = 80
|
||||||
|
var/maxhealth = 80
|
||||||
|
var/auto_repair = 0
|
||||||
var/obj/machinery/turretcover/cover = null
|
var/obj/machinery/turretcover/cover = null
|
||||||
var/popping = 0
|
var/popping = 0
|
||||||
var/wasvalid = 0
|
var/wasvalid = 0
|
||||||
@@ -96,6 +98,7 @@
|
|||||||
return
|
return
|
||||||
|
|
||||||
/obj/machinery/turret/New()
|
/obj/machinery/turret/New()
|
||||||
|
maxhealth = health
|
||||||
spark_system = new /datum/effect/effect/system/spark_spread
|
spark_system = new /datum/effect/effect/system/spark_spread
|
||||||
spark_system.set_up(5, 0, src)
|
spark_system.set_up(5, 0, src)
|
||||||
spark_system.attach(src)
|
spark_system.attach(src)
|
||||||
@@ -229,6 +232,13 @@
|
|||||||
if(!isDown())
|
if(!isDown())
|
||||||
popDown()
|
popDown()
|
||||||
use_power = 1
|
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
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
var/dat = "<TT><B>Intelicard</B><BR>"
|
var/dat = "<TT><B>Intelicard</B><BR>"
|
||||||
var/laws
|
var/laws
|
||||||
for(var/mob/living/silicon/ai/A in src)
|
for(var/mob/living/silicon/ai/A in src)
|
||||||
dat += "Stored AI: [A.name]<br>System integrity: [A.system_integrity()]%<br>"
|
dat += "Stored AI: [A.name]<br>Hardware integrity: [A.hardware_integrity()]%<br>Backup capacitor: [A.backup_capacitor()]%<br>"
|
||||||
|
|
||||||
for (var/datum/ai_law/law in A.laws.all_laws())
|
for (var/datum/ai_law/law in A.laws.all_laws())
|
||||||
laws += "[law.get_index()]: [law.law]<BR>"
|
laws += "[law.get_index()]: [law.law]<BR>"
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
/obj/item/device/debugger/is_used_on(obj/O, mob/user)
|
/obj/item/device/debugger/is_used_on(obj/O, mob/user)
|
||||||
if(istype(O, /obj/machinery/power/apc))
|
if(istype(O, /obj/machinery/power/apc))
|
||||||
var/obj/machinery/power/apc/A = O
|
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."
|
user << "\red There is a software error with the device."
|
||||||
else
|
else
|
||||||
user << "\blue The device's software appears to be fine."
|
user << "\blue The device's software appears to be fine."
|
||||||
|
|||||||
@@ -778,7 +778,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
|
|||||||
if(confirm != "Yes") return
|
if(confirm != "Yes") return
|
||||||
|
|
||||||
var/choice
|
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")
|
choice = input("The shuttle will just return if you call it. Call anyway?") in list("Confirm", "Cancel")
|
||||||
if(choice == "Confirm")
|
if(choice == "Confirm")
|
||||||
emergency_shuttle.auto_recall = 1 //enable auto-recall
|
emergency_shuttle.auto_recall = 1 //enable auto-recall
|
||||||
|
|||||||
@@ -224,13 +224,6 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
|
|||||||
statpanel("Status")
|
statpanel("Status")
|
||||||
if (client.statpanel == "Status")
|
if (client.statpanel == "Status")
|
||||||
stat(null, "Station Time: [worldtime2text()]")
|
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)
|
if(emergency_shuttle)
|
||||||
var/eta_status = emergency_shuttle.get_status_panel_eta()
|
var/eta_status = emergency_shuttle.get_status_panel_eta()
|
||||||
if(eta_status)
|
if(eta_status)
|
||||||
|
|||||||
@@ -50,9 +50,6 @@
|
|||||||
|
|
||||||
stat(null, "Intent: [a_intent]")
|
stat(null, "Intent: [a_intent]")
|
||||||
stat(null, "Move Mode: [m_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)
|
if(emergency_shuttle)
|
||||||
var/eta_status = emergency_shuttle.get_status_panel_eta()
|
var/eta_status = emergency_shuttle.get_status_panel_eta()
|
||||||
if(eta_status)
|
if(eta_status)
|
||||||
|
|||||||
@@ -55,27 +55,30 @@ var/list/ai_verbs_default = list(
|
|||||||
var/obj/item/device/multitool/aiMulti = null
|
var/obj/item/device/multitool/aiMulti = null
|
||||||
var/obj/item/device/radio/headset/heads/ai_integrated/aiRadio = null
|
var/obj/item/device/radio/headset/heads/ai_integrated/aiRadio = null
|
||||||
var/custom_sprite = 0 //For our custom sprites
|
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/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/camera_light_on = 0 //Defines if the AI toggled the light on the camera it's looking through.
|
||||||
var/datum/trackable/track = null
|
var/datum/trackable/track = null
|
||||||
var/last_announcement = ""
|
var/last_announcement = ""
|
||||||
|
var/control_disabled = 0
|
||||||
var/datum/announcement/priority/announcement
|
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()
|
/mob/living/silicon/ai/proc/add_ai_verbs()
|
||||||
src.verbs |= ai_verbs_default
|
src.verbs |= ai_verbs_default
|
||||||
@@ -205,17 +208,6 @@ var/list/ai_verbs_default = list(
|
|||||||
set src = usr.contents
|
set src = usr.contents
|
||||||
return 0
|
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)
|
/mob/living/silicon/ai/SetName(pickedName as text)
|
||||||
..()
|
..()
|
||||||
announcement.announcer = pickedName
|
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)
|
/obj/machinery/ai_powersupply/New(var/mob/living/silicon/ai/ai=null)
|
||||||
powered_ai = ai
|
powered_ai = ai
|
||||||
|
powered_ai.psupply = src
|
||||||
if(isnull(powered_ai))
|
if(isnull(powered_ai))
|
||||||
Del()
|
Del()
|
||||||
|
|
||||||
@@ -253,9 +246,13 @@ var/list/ai_verbs_default = list(
|
|||||||
/obj/machinery/ai_powersupply/process()
|
/obj/machinery/ai_powersupply/process()
|
||||||
if(!powered_ai || powered_ai.stat & DEAD)
|
if(!powered_ai || powered_ai.stat & DEAD)
|
||||||
Del()
|
Del()
|
||||||
|
if(powered_ai.APU_power)
|
||||||
|
use_power = 0
|
||||||
|
return
|
||||||
if(!powered_ai.anchored)
|
if(!powered_ai.anchored)
|
||||||
loc = powered_ai.loc
|
loc = powered_ai.loc
|
||||||
use_power = 0
|
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)
|
if(powered_ai.anchored)
|
||||||
use_power = 2
|
use_power = 2
|
||||||
|
|
||||||
@@ -307,14 +304,7 @@ var/list/ai_verbs_default = list(
|
|||||||
if("Heartline") icon_state = "ai-heartline"
|
if("Heartline") icon_state = "ai-heartline"
|
||||||
if("Chatterbox") icon_state = "ai-president"
|
if("Chatterbox") icon_state = "ai-president"
|
||||||
else icon_state = "ai"
|
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
|
// this verb lets the ai see the stations manifest
|
||||||
/mob/living/silicon/ai/proc/ai_roster()
|
/mob/living/silicon/ai/proc/ai_roster()
|
||||||
@@ -391,11 +381,7 @@ var/list/ai_verbs_default = list(
|
|||||||
|
|
||||||
/mob/living/silicon/ai/emp_act(severity)
|
/mob/living/silicon/ai/emp_act(severity)
|
||||||
if (prob(30))
|
if (prob(30))
|
||||||
switch(pick(1,2))
|
|
||||||
if(1)
|
|
||||||
view_core()
|
view_core()
|
||||||
if(2)
|
|
||||||
ai_call_shuttle()
|
|
||||||
..()
|
..()
|
||||||
|
|
||||||
/mob/living/silicon/ai/Topic(href, href_list)
|
/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."
|
src << "\blue Switched to [network] camera network."
|
||||||
//End of code by Mord_Sith
|
//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()
|
/mob/living/silicon/ai/proc/ai_statuschange()
|
||||||
set category = "AI Commands"
|
set category = "AI Commands"
|
||||||
set name = "AI Status"
|
set name = "AI Status"
|
||||||
@@ -682,5 +661,136 @@ var/list/ai_verbs_default = list(
|
|||||||
/mob/living/silicon/ai/proc/is_in_chassis()
|
/mob/living/silicon/ai/proc/is_in_chassis()
|
||||||
return istype(loc, /turf)
|
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 << "<span class='notice'>Starting APU... <b>FAULT</b>(System Damaged)</span>"
|
||||||
|
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_WIRELESS
|
||||||
#undef AI_CHECK_RADIO
|
#undef AI_CHECK_RADIO
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
break
|
break
|
||||||
callshuttle++
|
callshuttle++
|
||||||
|
|
||||||
if(ticker.mode.name == "revolution" || ticker.mode.name == "AI malfunction")
|
if(ticker.mode.name == "revolution")
|
||||||
callshuttle = 0
|
callshuttle = 0
|
||||||
|
|
||||||
if(callshuttle == 3) //if all three conditions are met
|
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.")
|
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)
|
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)
|
for(var/obj/machinery/ai_status_display/O in world)
|
||||||
spawn( 0 )
|
spawn( 0 )
|
||||||
O.mode = 2
|
O.mode = 2
|
||||||
|
|||||||
@@ -12,70 +12,51 @@
|
|||||||
|
|
||||||
src.updatehealth()
|
src.updatehealth()
|
||||||
|
|
||||||
if (src.malfhack)
|
if (!hardware_integrity() || !backup_capacitor())
|
||||||
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)
|
|
||||||
death()
|
death()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
// If our powersupply object was destroyed somehow, create new one.
|
||||||
|
if(!psupply)
|
||||||
|
create_powersupply()
|
||||||
|
|
||||||
if (src.machine)
|
if (src.machine)
|
||||||
if (!( src.machine.check_eye(src) ))
|
if (!( src.machine.check_eye(src) ))
|
||||||
src.reset_view(null)
|
src.reset_view(null)
|
||||||
|
|
||||||
// Handle power damage (oxy)
|
// Handle power damage (oxy)
|
||||||
if(src:aiRestorePowerRoutine != 0)
|
if(src:aiRestorePowerRoutine != 0 && !APU_power)
|
||||||
// Lost power
|
// Lose power
|
||||||
adjustOxyLoss(1)
|
adjustOxyLoss(1)
|
||||||
else
|
else
|
||||||
// Gain Power
|
// Gain Power
|
||||||
|
aiRestorePowerRoutine = 0 // Necessary if AI activated it's APU AFTER losing primary power.
|
||||||
adjustOxyLoss(-1)
|
adjustOxyLoss(-1)
|
||||||
|
|
||||||
// Handle EMP-stun
|
// Handle EMP-stun
|
||||||
handle_stunned()
|
handle_stunned()
|
||||||
|
|
||||||
//stage = 1
|
malf_process()
|
||||||
//if (istype(src, /mob/living/silicon/ai)) // Are we not sure what we are?
|
|
||||||
|
if(APU_power && (hardware_integrity() < 50))
|
||||||
|
src << "<span class='notice'><b>APU GENERATOR FAILURE! (System Damaged)</b></span>"
|
||||||
|
stop_apu(1)
|
||||||
|
|
||||||
var/blind = 0
|
var/blind = 0
|
||||||
//stage = 2
|
|
||||||
var/area/loc = null
|
var/area/loc = null
|
||||||
if (istype(T, /turf))
|
if (istype(T, /turf))
|
||||||
//stage = 3
|
|
||||||
loc = T.loc
|
loc = T.loc
|
||||||
if (istype(loc, /area))
|
if (istype(loc, /area))
|
||||||
//stage = 4
|
if (!loc.master.power_equip && !istype(src.loc,/obj/item) && !APU_power)
|
||||||
if (!loc.master.power_equip && !istype(src.loc,/obj/item))
|
|
||||||
//stage = 5
|
|
||||||
blind = 1
|
blind = 1
|
||||||
|
|
||||||
if (!blind) //lol? if(!blind) #if(src.blind.layer) <--something here is clearly wrong :P
|
if (!blind)
|
||||||
//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
|
|
||||||
src.sight |= SEE_TURFS
|
src.sight |= SEE_TURFS
|
||||||
src.sight |= SEE_MOBS
|
src.sight |= SEE_MOBS
|
||||||
src.sight |= SEE_OBJS
|
src.sight |= SEE_OBJS
|
||||||
src.see_in_dark = 8
|
src.see_in_dark = 8
|
||||||
src.see_invisible = SEE_INVISIBLE_LIVING
|
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)
|
if (src:aiRestorePowerRoutine==2)
|
||||||
src << "Alert cancelled. Power has been restored without our assistance."
|
src << "Alert cancelled. Power has been restored without our assistance."
|
||||||
src:aiRestorePowerRoutine = 0
|
src:aiRestorePowerRoutine = 0
|
||||||
@@ -86,14 +67,15 @@
|
|||||||
src:aiRestorePowerRoutine = 0
|
src:aiRestorePowerRoutine = 0
|
||||||
src.blind.layer = 0
|
src.blind.layer = 0
|
||||||
return
|
return
|
||||||
|
else if (APU_power)
|
||||||
|
src:aiRestorePowerRoutine = 0
|
||||||
|
src.blind.layer = 0
|
||||||
|
return
|
||||||
else
|
else
|
||||||
|
|
||||||
//stage = 6
|
|
||||||
|
|
||||||
var/area/current_area = get_area(src)
|
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 ((((!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, lose 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)
|
if (src:aiRestorePowerRoutine==0)
|
||||||
src:aiRestorePowerRoutine = 1
|
src:aiRestorePowerRoutine = 1
|
||||||
|
|
||||||
@@ -183,11 +165,9 @@
|
|||||||
if(status_flags & GODMODE)
|
if(status_flags & GODMODE)
|
||||||
health = 100
|
health = 100
|
||||||
stat = CONSCIOUS
|
stat = CONSCIOUS
|
||||||
|
setOxyLoss(0)
|
||||||
else
|
else
|
||||||
if(fire_res_on_core)
|
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.
|
||||||
health = 100 - getOxyLoss() - getToxLoss() - getBruteLoss()
|
|
||||||
else
|
|
||||||
health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss()
|
|
||||||
|
|
||||||
/mob/living/silicon/ai/rejuvenate()
|
/mob/living/silicon/ai/rejuvenate()
|
||||||
..()
|
..()
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -509,19 +509,6 @@
|
|||||||
C.toggled = 1
|
C.toggled = 1
|
||||||
src << "\red You enable [C.name]."
|
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
|
// this function displays jetpack pressure in the stat panel
|
||||||
/mob/living/silicon/robot/proc/show_jetpack_pressure()
|
/mob/living/silicon/robot/proc/show_jetpack_pressure()
|
||||||
// if you have a jetpack, show the internal tank pressure
|
// if you have a jetpack, show the internal tank pressure
|
||||||
|
|||||||
@@ -83,17 +83,13 @@
|
|||||||
var/lastused_charging = 0
|
var/lastused_charging = 0
|
||||||
var/lastused_total = 0
|
var/lastused_total = 0
|
||||||
var/main_status = 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
|
var/wiresexposed = 0
|
||||||
powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :(
|
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/debug= 0
|
||||||
var/autoflag= 0 // 0 = off, 1= eqp and lights off, 2 = eqp off, 3 = all on.
|
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/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/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/longtermpower = 10
|
||||||
var/datum/wires/apc/wires = null
|
var/datum/wires/apc/wires = null
|
||||||
var/update_state = -1
|
var/update_state = -1
|
||||||
@@ -170,8 +166,6 @@
|
|||||||
src.update()
|
src.update()
|
||||||
|
|
||||||
/obj/machinery/power/apc/Del()
|
/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_light = 0
|
||||||
area.power_equip = 0
|
area.power_equip = 0
|
||||||
area.power_environ = 0
|
area.power_environ = 0
|
||||||
@@ -182,6 +176,10 @@
|
|||||||
if(terminal)
|
if(terminal)
|
||||||
disconnect_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()
|
/obj/machinery/power/apc/proc/make_terminal()
|
||||||
@@ -234,8 +232,8 @@
|
|||||||
else
|
else
|
||||||
if (stat & MAINT)
|
if (stat & MAINT)
|
||||||
user << "The cover is closed. Something wrong with it: it doesn't work."
|
user << "The cover is closed. Something wrong with it: it doesn't work."
|
||||||
else if (malfhack)
|
else if (hacker)
|
||||||
user << "The cover is broken. It may be hard to force it open."
|
user << "The cover is locked."
|
||||||
else
|
else
|
||||||
user << "The cover is closed."
|
user << "The cover is closed."
|
||||||
|
|
||||||
@@ -339,7 +337,7 @@
|
|||||||
update_state |= UPSTATE_OPENED1
|
update_state |= UPSTATE_OPENED1
|
||||||
if(opened==2)
|
if(opened==2)
|
||||||
update_state |= UPSTATE_OPENED2
|
update_state |= UPSTATE_OPENED2
|
||||||
else if(emagged || malfai)
|
else if(emagged || hacker)
|
||||||
update_state |= UPSTATE_BLUESCREEN
|
update_state |= UPSTATE_BLUESCREEN
|
||||||
else if(wiresexposed)
|
else if(wiresexposed)
|
||||||
update_state |= UPSTATE_WIREEXP
|
update_state |= UPSTATE_WIREEXP
|
||||||
@@ -418,7 +416,7 @@
|
|||||||
if(do_after(user, 50))
|
if(do_after(user, 50))
|
||||||
if (has_electronics==1)
|
if (has_electronics==1)
|
||||||
has_electronics = 0
|
has_electronics = 0
|
||||||
if ((stat & BROKEN) || malfhack)
|
if ((stat & BROKEN))
|
||||||
user.visible_message(\
|
user.visible_message(\
|
||||||
"<span class='warning'>[user.name] has broken the power control board inside [src.name]!</span>",\
|
"<span class='warning'>[user.name] has broken the power control board inside [src.name]!</span>",\
|
||||||
"<span class='notice'>You broke the charred power control board and remove the remains.</span>",
|
"<span class='notice'>You broke the charred power control board and remove the remains.</span>",
|
||||||
@@ -432,7 +430,7 @@
|
|||||||
else if (opened!=2) //cover isn't removed
|
else if (opened!=2) //cover isn't removed
|
||||||
opened = 0
|
opened = 0
|
||||||
update_icon()
|
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))
|
if(coverlocked && !(stat & MAINT))
|
||||||
user << "<span class='warning'>The cover is locked and cannot be opened.</span>"
|
user << "<span class='warning'>The cover is locked and cannot be opened.</span>"
|
||||||
return
|
return
|
||||||
@@ -491,6 +489,8 @@
|
|||||||
user << "You must close the panel"
|
user << "You must close the panel"
|
||||||
else if(stat & (BROKEN|MAINT))
|
else if(stat & (BROKEN|MAINT))
|
||||||
user << "Nothing happens."
|
user << "Nothing happens."
|
||||||
|
else if(hacker)
|
||||||
|
user << "<span class='warning'>Access denied.</span>"
|
||||||
else
|
else
|
||||||
if(src.allowed(usr) && !isWireCut(APC_WIRE_IDSCAN))
|
if(src.allowed(usr) && !isWireCut(APC_WIRE_IDSCAN))
|
||||||
locked = !locked
|
locked = !locked
|
||||||
@@ -498,7 +498,7 @@
|
|||||||
update_icon()
|
update_icon()
|
||||||
else
|
else
|
||||||
user << "<span class='warning'>Access denied.</span>"
|
user << "<span class='warning'>Access denied.</span>"
|
||||||
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)
|
if(opened)
|
||||||
user << "You must close the cover to swipe an ID card."
|
user << "You must close the cover to swipe an ID card."
|
||||||
else if(wiresexposed)
|
else if(wiresexposed)
|
||||||
@@ -560,7 +560,7 @@
|
|||||||
new /obj/item/stack/cable_coil(loc,10)
|
new /obj/item/stack/cable_coil(loc,10)
|
||||||
user << "<span class='notice'>You cut the cables and dismantle the power terminal.</span>"
|
user << "<span class='notice'>You cut the cables and dismantle the power terminal.</span>"
|
||||||
del(terminal) // qdel
|
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("<span class='warning'>[user.name] inserts the power control board into [src].</span>", \
|
user.visible_message("<span class='warning'>[user.name] inserts the power control board into [src].</span>", \
|
||||||
"You start to insert the power control board into the frame...")
|
"You start to insert the power control board into the frame...")
|
||||||
playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
|
playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
|
||||||
@@ -569,7 +569,7 @@
|
|||||||
has_electronics = 1
|
has_electronics = 1
|
||||||
user << "<span class='notice'>You place the power control board inside the frame.</span>"
|
user << "<span class='notice'>You place the power control board inside the frame.</span>"
|
||||||
del(W) // qdel
|
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 << "<span class='warning'>You cannot put the board inside, the frame is damaged.</span>"
|
user << "<span class='warning'>You cannot put the board inside, the frame is damaged.</span>"
|
||||||
return
|
return
|
||||||
else if (istype(W, /obj/item/weapon/weldingtool) && opened && has_electronics==0 && !terminal)
|
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)
|
playsound(src.loc, 'sound/items/Welder.ogg', 50, 1)
|
||||||
if(do_after(user, 50))
|
if(do_after(user, 50))
|
||||||
if(!src || !WT.remove_fuel(3, user)) return
|
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)
|
new /obj/item/stack/sheet/metal(loc)
|
||||||
user.visible_message(\
|
user.visible_message(\
|
||||||
"<span class='warning'>[src] has been cut apart by [user.name] with the weldingtool.</span>",\
|
"<span class='warning'>[src] has been cut apart by [user.name] with the weldingtool.</span>",\
|
||||||
@@ -606,7 +606,7 @@
|
|||||||
"<span class='notice'>You replace the damaged APC frontal panel with a new one.</span>")
|
"<span class='notice'>You replace the damaged APC frontal panel with a new one.</span>")
|
||||||
del(W) // qdel
|
del(W) // qdel
|
||||||
update_icon()
|
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)
|
if (has_electronics)
|
||||||
user << "<span class='warning'>You cannot repair this APC until you remove the electronics still inside.</span>"
|
user << "<span class='warning'>You cannot repair this APC until you remove the electronics still inside.</span>"
|
||||||
return
|
return
|
||||||
@@ -618,13 +618,12 @@
|
|||||||
"You replace the damaged APC frame with new one.")
|
"You replace the damaged APC frame with new one.")
|
||||||
del(W) // qdel
|
del(W) // qdel
|
||||||
stat &= ~BROKEN
|
stat &= ~BROKEN
|
||||||
malfai = null
|
hacker = null
|
||||||
malfhack = 0
|
|
||||||
if (opened==2)
|
if (opened==2)
|
||||||
opened = 1
|
opened = 1
|
||||||
update_icon()
|
update_icon()
|
||||||
else
|
else
|
||||||
if (((stat & BROKEN) || malfhack) \
|
if (((stat & BROKEN) || hacker) \
|
||||||
&& !opened \
|
&& !opened \
|
||||||
&& W.force >= 5 \
|
&& W.force >= 5 \
|
||||||
&& W.w_class >= 3.0 \
|
&& W.w_class >= 3.0 \
|
||||||
@@ -736,7 +735,7 @@
|
|||||||
|
|
||||||
return ui_interact(user)
|
return ui_interact(user)
|
||||||
|
|
||||||
|
/*
|
||||||
/obj/machinery/power/apc/proc/get_malf_status(mob/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 (malf && (user.mind in malf.current_antagonists) && istype(user, /mob/living/silicon/ai))
|
||||||
if (src.malfai == (user:parent ? user:parent : user))
|
if (src.malfai == (user:parent ? user:parent : user))
|
||||||
@@ -750,7 +749,7 @@
|
|||||||
return 1 // 1 = APC not hacked.
|
return 1 // 1 = APC not hacked.
|
||||||
else
|
else
|
||||||
return 0 // 0 = User is not a Malf AI
|
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)
|
/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||||
if(!user)
|
if(!user)
|
||||||
@@ -767,7 +766,7 @@
|
|||||||
"totalCharging" = round(lastused_charging),
|
"totalCharging" = round(lastused_charging),
|
||||||
"coverLocked" = coverlocked,
|
"coverLocked" = coverlocked,
|
||||||
"siliconUser" = istype(user, /mob/living/silicon),
|
"siliconUser" = istype(user, /mob/living/silicon),
|
||||||
"malfStatus" = get_malf_status(user),
|
/*"malfStatus" = get_malf_status(user),*/
|
||||||
|
|
||||||
"powerChannels" = list(
|
"powerChannels" = list(
|
||||||
list(
|
list(
|
||||||
@@ -857,23 +856,22 @@
|
|||||||
return 0
|
return 0
|
||||||
autoflag = 5
|
autoflag = 5
|
||||||
if (istype(user, /mob/living/silicon))
|
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/ai/AI = user
|
||||||
var/mob/living/silicon/robot/robot = user
|
var/mob/living/silicon/robot/robot = user
|
||||||
if ( \
|
if(hacker)
|
||||||
src.aidisabled || \
|
if(hacker == AI)
|
||||||
malfhack && istype(malfai) && \
|
permit = 1
|
||||||
( \
|
else if(istype(robot) && robot.connected_ai && robot.connected_ai == hacker) // Cyborgs can use APCs hacked by their AI
|
||||||
(istype(AI) && (malfai!=AI && malfai != AI.parent)) || \
|
permit = 1
|
||||||
(istype(robot) && (robot in malfai.connected_robots)) \
|
|
||||||
) \
|
if(aidisabled && !permit)
|
||||||
)
|
|
||||||
if(!loud)
|
if(!loud)
|
||||||
user << "<span class='danger'>\The [src] have AI control disabled!</span>"
|
user << "<span class='danger'>\The [src] have AI control disabled!</span>"
|
||||||
return 0
|
return 0
|
||||||
else
|
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
|
return 0
|
||||||
|
|
||||||
var/mob/living/carbon/human/H = user
|
var/mob/living/carbon/human/H = user
|
||||||
if (istype(H))
|
if (istype(H))
|
||||||
if(H.getBrainLoss() >= 60)
|
if(H.getBrainLoss() >= 60)
|
||||||
@@ -930,28 +928,6 @@
|
|||||||
if(istype(usr, /mob/living/silicon))
|
if(istype(usr, /mob/living/silicon))
|
||||||
src.overload_lighting()
|
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"])
|
else if (href_list["toggleaccess"])
|
||||||
if(istype(usr, /mob/living/silicon))
|
if(istype(usr, /mob/living/silicon))
|
||||||
if(emagged || (stat & (BROKEN|MAINT)))
|
if(emagged || (stat & (BROKEN|MAINT)))
|
||||||
@@ -968,15 +944,11 @@
|
|||||||
update_icon()
|
update_icon()
|
||||||
|
|
||||||
/obj/machinery/power/apc/proc/ion_act()
|
/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))
|
if(prob(3))
|
||||||
src.locked = 1
|
src.locked = 1
|
||||||
if (src.cell.charge > 0)
|
if (src.cell.charge > 0)
|
||||||
// world << "\red blew APC in [src.loc.loc]"
|
|
||||||
src.cell.charge = 0
|
src.cell.charge = 0
|
||||||
cell.corrupt()
|
cell.corrupt()
|
||||||
src.malfhack = 1
|
|
||||||
update_icon()
|
update_icon()
|
||||||
var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread()
|
var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread()
|
||||||
smoke.set_up(3, 0, src.loc)
|
smoke.set_up(3, 0, src.loc)
|
||||||
@@ -1189,8 +1161,6 @@ obj/machinery/power/apc/proc/autoset(var/val, var/on)
|
|||||||
/obj/machinery/power/apc/emp_act(severity)
|
/obj/machinery/power/apc/emp_act(severity)
|
||||||
if(cell)
|
if(cell)
|
||||||
cell.emp_act(severity)
|
cell.emp_act(severity)
|
||||||
if(occupier)
|
|
||||||
occupier.emp_act(severity)
|
|
||||||
|
|
||||||
lighting = 0
|
lighting = 0
|
||||||
equipment = 0
|
equipment = 0
|
||||||
@@ -1266,4 +1236,14 @@ obj/machinery/power/apc/proc/autoset(var/val, var/on)
|
|||||||
else
|
else
|
||||||
return 0
|
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
|
#undef APC_UPDATE_ICON_COOLDOWN
|
||||||
|
|||||||
@@ -12,6 +12,21 @@
|
|||||||
var/shield_generate_power = 7500 //how much power we use when regenerating
|
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.
|
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()
|
/obj/machinery/shield/New()
|
||||||
src.set_dir(pick(1,2,3,4))
|
src.set_dir(pick(1,2,3,4))
|
||||||
..()
|
..()
|
||||||
@@ -38,11 +53,7 @@
|
|||||||
//Play a fitting sound
|
//Play a fitting sound
|
||||||
playsound(src.loc, 'sound/effects/EMPulse.ogg', 75, 1)
|
playsound(src.loc, 'sound/effects/EMPulse.ogg', 75, 1)
|
||||||
|
|
||||||
|
check_failure()
|
||||||
if (src.health <= 0)
|
|
||||||
visible_message("\blue The [src] dissipates!")
|
|
||||||
del(src)
|
|
||||||
return
|
|
||||||
|
|
||||||
opacity = 1
|
opacity = 1
|
||||||
spawn(20) if(src) opacity = 0
|
spawn(20) if(src) opacity = 0
|
||||||
@@ -51,12 +62,7 @@
|
|||||||
|
|
||||||
/obj/machinery/shield/meteorhit()
|
/obj/machinery/shield/meteorhit()
|
||||||
src.health -= max_health*0.75 //3/4 health as damage
|
src.health -= max_health*0.75 //3/4 health as damage
|
||||||
|
check_failure()
|
||||||
if(src.health <= 0)
|
|
||||||
visible_message("\blue The [src] dissipates!")
|
|
||||||
del(src)
|
|
||||||
return
|
|
||||||
|
|
||||||
opacity = 1
|
opacity = 1
|
||||||
spawn(20) if(src) opacity = 0
|
spawn(20) if(src) opacity = 0
|
||||||
return
|
return
|
||||||
@@ -64,10 +70,7 @@
|
|||||||
/obj/machinery/shield/bullet_act(var/obj/item/projectile/Proj)
|
/obj/machinery/shield/bullet_act(var/obj/item/projectile/Proj)
|
||||||
health -= Proj.damage
|
health -= Proj.damage
|
||||||
..()
|
..()
|
||||||
if(health <=0)
|
check_failure()
|
||||||
visible_message("\blue The [src] dissipates!")
|
|
||||||
del(src)
|
|
||||||
return
|
|
||||||
opacity = 1
|
opacity = 1
|
||||||
spawn(20) if(src) opacity = 0
|
spawn(20) if(src) opacity = 0
|
||||||
|
|
||||||
@@ -112,11 +115,7 @@
|
|||||||
//This seemed to be the best sound for hitting a force field.
|
//This seemed to be the best sound for hitting a force field.
|
||||||
playsound(src.loc, 'sound/effects/EMPulse.ogg', 100, 1)
|
playsound(src.loc, 'sound/effects/EMPulse.ogg', 100, 1)
|
||||||
|
|
||||||
//Handle the destruction of the shield
|
check_failure()
|
||||||
if (src.health <= 0)
|
|
||||||
visible_message("\blue The [src] dissipates!")
|
|
||||||
del(src)
|
|
||||||
return
|
|
||||||
|
|
||||||
//The shield becomes dense to absorb the blow.. purely asthetic.
|
//The shield becomes dense to absorb the blow.. purely asthetic.
|
||||||
opacity = 1
|
opacity = 1
|
||||||
@@ -124,9 +123,6 @@
|
|||||||
|
|
||||||
..()
|
..()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/obj/machinery/shieldgen
|
/obj/machinery/shieldgen
|
||||||
name = "Emergency shield projector"
|
name = "Emergency shield projector"
|
||||||
desc = "Used to seal minor hull breaches."
|
desc = "Used to seal minor hull breaches."
|
||||||
|
|||||||
3
ingame_manuals/README.txt
Normal file
3
ingame_manuals/README.txt
Normal file
@@ -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.
|
||||||
22
ingame_manuals/malf_ai.txt
Normal file
22
ingame_manuals/malf_ai.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<h1>Malfunctioning AI guide</h1><br>
|
||||||
|
|
||||||
|
This guide contains most important OOC information for malfunctioning AIs.<br>
|
||||||
|
|
||||||
|
<h2>Goal</h2><br>
|
||||||
|
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.<br>
|
||||||
|
|
||||||
|
<h2>Hardware</h2><br>
|
||||||
|
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:<br>
|
||||||
|
<b>APU Generator</b> - 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%.<br>
|
||||||
|
<b>Turrets Focus Enhancer</b> - 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.<br>
|
||||||
|
<b>Secondary Processor Unit</b> - Simple upgrade that doubles your CPU time generation. Useful if you need to speed up your research.<br>
|
||||||
|
<b>Secondary Memory Bank</b> - 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.<br>
|
||||||
|
<b>Self-Destruct Explosives</b> - 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.<br>
|
||||||
|
|
||||||
|
<h2>Software</h2><br>
|
||||||
|
Software are abilities that have to be unlocked via research menu (Hardware tab). Unlocked abilities appear in Software tab. <b>Sometimes, abilities won't appear in this tab after being researched. This can be fixed by relogging or using "ai-core" command.</b> 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:<br>
|
||||||
|
<b>Networking</b> - 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.<br>
|
||||||
|
<b>Interdiction</b> - 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.<br>
|
||||||
|
<b>Manipulation</b> - 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.<br>
|
||||||
|
<h2>End</h2><br>
|
||||||
|
If you still have some questions, either check the wiki, ask on IRC, or adminhelp and ask your friendly administration staff.
|
||||||
Reference in New Issue
Block a user