mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-25 06:00:16 +01:00
Merge remote-tracking branch 'remotes/tgstation/master' into The_Alien_Queen_Bends_Down_And_Plants_A_Kiss_On_WJ_With_Her_Long_Wet_Tongue-mouth
# Conflicts: # icons/mob/actions.dmi
This commit is contained in:
@@ -6,6 +6,7 @@ var/global/list/all_clockwork_objects = list() //All clockwork items, structures
|
||||
var/global/list/all_clockwork_mobs = list() //All clockwork SERVANTS (not creatures) in existence
|
||||
var/global/list/clockwork_component_cache = list("belligerent_eye" = 0, "vanguard_cogwheel" = 0, "guvax_capacitor" = 0, "replicant_alloy" = 0, "hierophant_ansible" = 0) //The pool of components that caches draw from
|
||||
var/global/ratvar_awakens = FALSE //If Ratvar has been summoned
|
||||
var/global/clockwork_gateway_activated = FALSE //if a gateway to the celestial derelict has ever been successfully activated
|
||||
|
||||
#define SCRIPTURE_PERIPHERAL "Peripheral" //Scripture tiers; peripherals should never be used
|
||||
#define SCRIPTURE_DRIVER "Driver"
|
||||
@@ -47,3 +48,11 @@ var/global/ratvar_awakens = FALSE //If Ratvar has been summoned
|
||||
#define GATEWAY_RATVAR_COMING 250 //when progress is at or above this, ratvar has entered and is coming through the gateway
|
||||
|
||||
#define GATEWAY_RATVAR_ARRIVAL 300 //when progress is at or above this, game over ratvar's here everybody go home
|
||||
|
||||
//Objective defines
|
||||
|
||||
#define CLOCKCULT_GATEWAY "gateway"
|
||||
|
||||
#define CLOCKCULT_ESCAPE "escape"
|
||||
|
||||
#define CLOCKCULT_SILICONS "silicons"
|
||||
|
||||
+30
-1
@@ -152,6 +152,7 @@
|
||||
|
||||
#define CLICK_CD_MELEE 8
|
||||
#define CLICK_CD_RANGE 4
|
||||
#define CLICK_CD_RAPID 2
|
||||
#define CLICK_CD_CLICK_ABILITY 6
|
||||
#define CLICK_CD_BREAKOUT 100
|
||||
#define CLICK_CD_HANDCUFFED 10
|
||||
@@ -498,4 +499,32 @@ var/global/list/ghost_others_options = list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE
|
||||
#define BUBBLEGUM_SCORE "Bubblegum Killed"
|
||||
#define DRAKE_SCORE "Drakes Killed"
|
||||
#define BOSS_SCORE "Bosses Killed"
|
||||
#define TENDRIL_CLEAR_SCORE "Tendrils Killed"
|
||||
#define TENDRIL_CLEAR_SCORE "Tendrils Killed"
|
||||
|
||||
|
||||
|
||||
// NTNet module-configuration values. Do not change these. If you need to add another use larger number (5..6..7 etc)
|
||||
#define NTNET_SOFTWAREDOWNLOAD 1 // Downloads of software from NTNet
|
||||
#define NTNET_PEERTOPEER 2 // P2P transfers of files between devices
|
||||
#define NTNET_COMMUNICATION 3 // Communication (messaging)
|
||||
#define NTNET_SYSTEMCONTROL 4 // Control of various systems, RCon, air alarm control, etc.
|
||||
|
||||
// NTNet transfer speeds, used when downloading/uploading a file/program.
|
||||
#define NTNETSPEED_LOWSIGNAL 0.5 // GQ/s transfer speed when the device is wirelessly connected and on Low signal
|
||||
#define NTNETSPEED_HIGHSIGNAL 1 // GQ/s transfer speed when the device is wirelessly connected and on High signal
|
||||
#define NTNETSPEED_ETHERNET 2 // GQ/s transfer speed when the device is using wired connection
|
||||
|
||||
|
||||
// Program bitflags
|
||||
#define PROGRAM_ALL 7
|
||||
#define PROGRAM_CONSOLE 1
|
||||
#define PROGRAM_LAPTOP 2
|
||||
#define PROGRAM_TABLET 4
|
||||
|
||||
#define PROGRAM_STATE_KILLED 0
|
||||
#define PROGRAM_STATE_BACKGROUND 1
|
||||
#define PROGRAM_STATE_ACTIVE 2
|
||||
|
||||
// Caps for NTNet logging. Less than 10 would make logging useless anyway, more than 500 may make the log browser too laggy. Defaults to 100 unless user changes it.
|
||||
#define MAX_NTNET_LOGS 300
|
||||
#define MIN_NTNET_LOGS 10
|
||||
@@ -1,7 +1,7 @@
|
||||
#define TICK_LIMIT_RUNNING 85
|
||||
#define TICK_LIMIT_TO_RUN 80
|
||||
#define TICK_LIMIT_MC 84
|
||||
#define TICK_LIMIT_MC_INIT 100
|
||||
#define TICK_LIMIT_RUNNING 80
|
||||
#define TICK_LIMIT_TO_RUN 78
|
||||
#define TICK_LIMIT_MC 70
|
||||
#define TICK_LIMIT_MC_INIT 98
|
||||
|
||||
#define TICK_CHECK ( world.tick_usage > CURRENT_TICKLIMIT ? stoplag() : 0 )
|
||||
#define CHECK_TICK if (world.tick_usage > CURRENT_TICKLIMIT) stoplag()
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
/proc/log_adminsay(text)
|
||||
if (config.log_adminchat)
|
||||
log_admin("ASAY: [text]")
|
||||
|
||||
|
||||
/proc/log_dsay(text)
|
||||
if (config.log_adminchat)
|
||||
log_admin("DSAY: [text]")
|
||||
@@ -76,3 +76,6 @@
|
||||
//reusing the PDA option because I really don't think news comments are worth a config option
|
||||
diary << "\[[time_stamp()]]COMMENT: [text]"
|
||||
|
||||
/proc/log_chat(text)
|
||||
if (config.log_pda)
|
||||
diary << "\[[time_stamp()]]CHAT: [text]"
|
||||
@@ -10,6 +10,7 @@ var/global/list/deliverybeacons = list() //list of all MULEbot delivery beacon
|
||||
var/global/list/deliverybeacontags = list() //list of all tags associated with delivery beacons.
|
||||
var/global/list/nuke_list = list()
|
||||
var/global/list/nuke_tiles = list() //list of all turfs that turn to animated red grids when a nuke is triggered
|
||||
var/global/list/alarmdisplay = list() //list of all machines or programs that can display station alerts
|
||||
|
||||
var/global/list/chemical_reactions_list //list of all /datum/chemical_reaction datums. Used during chemical reactions
|
||||
var/global/list/chemical_reagents_list //list of all /datum/reagent datums indexed by reagent id. Used by chemistry stuff
|
||||
|
||||
@@ -287,7 +287,10 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
for(var/obj/structure/clockwork/massive/celestial_gateway/G in all_clockwork_objects)
|
||||
var/area/gate_area = get_area(G)
|
||||
desc += "Ark Location: <b>[uppertext(gate_area.map_name)]</b><br>"
|
||||
desc += "Seconds until Ratvar's arrival: <b>[G.get_arrival_text(TRUE)]</b><br>"
|
||||
if(G.ratvar_portal)
|
||||
desc += "Seconds until Ratvar's arrival: <b>[G.get_arrival_text(TRUE)]</b><br>"
|
||||
else
|
||||
desc += "Seconds until Proselytization: <b>[G.get_arrival_text(TRUE)]</b><br>"
|
||||
if(unconverted_ai_exists)
|
||||
desc += "<b>An unconverted AI exists!</b><br>"
|
||||
if(scripture_states[SCRIPTURE_REVENANT])
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
/obj/screen/blob/Blobbernaut
|
||||
icon_state = "ui_blobbernaut"
|
||||
name = "Produce Blobbernaut (40)"
|
||||
desc = "Produces a strong, smart blobbernaut from a factory blob for 40 points.<br>The factory blob used will become fragile and unable to produce spores."
|
||||
desc = "Produces a strong, smart blobbernaut from a factory blob for 40 resources.<br>The factory blob used will become fragile and unable to produce spores."
|
||||
|
||||
/obj/screen/blob/Blobbernaut/Click()
|
||||
if(isovermind(usr))
|
||||
@@ -64,7 +64,7 @@
|
||||
/obj/screen/blob/ResourceBlob
|
||||
icon_state = "ui_resource"
|
||||
name = "Produce Resource Blob (40)"
|
||||
desc = "Produces a resource blob for 40 points.<br>Resource blobs will give you points every few seconds."
|
||||
desc = "Produces a resource blob for 40 resources.<br>Resource blobs will give you resources every few seconds."
|
||||
|
||||
/obj/screen/blob/ResourceBlob/Click()
|
||||
if(isovermind(usr))
|
||||
@@ -73,8 +73,8 @@
|
||||
|
||||
/obj/screen/blob/NodeBlob
|
||||
icon_state = "ui_node"
|
||||
name = "Produce Node Blob (60)"
|
||||
desc = "Produces a node blob for 60 points.<br>Node blobs will expand and activate nearby resource and factory blobs."
|
||||
name = "Produce Node Blob (50)"
|
||||
desc = "Produces a node blob for 50 resources.<br>Node blobs will expand and activate nearby resource and factory blobs."
|
||||
|
||||
/obj/screen/blob/NodeBlob/Click()
|
||||
if(isovermind(usr))
|
||||
@@ -84,7 +84,7 @@
|
||||
/obj/screen/blob/FactoryBlob
|
||||
icon_state = "ui_factory"
|
||||
name = "Produce Factory Blob (60)"
|
||||
desc = "Produces a factory blob for 60 points.<br>Factory blobs will produce spores every few seconds."
|
||||
desc = "Produces a factory blob for 60 resources.<br>Factory blobs will produce spores every few seconds."
|
||||
|
||||
/obj/screen/blob/FactoryBlob/Click()
|
||||
if(isovermind(usr))
|
||||
@@ -94,7 +94,7 @@
|
||||
/obj/screen/blob/ReadaptChemical
|
||||
icon_state = "ui_chemswap"
|
||||
name = "Readapt Chemical (40)"
|
||||
desc = "Randomly rerolls your chemical for 40 points."
|
||||
desc = "Randomly rerolls your chemical for 40 resources."
|
||||
|
||||
/obj/screen/blob/ReadaptChemical/MouseEntered(location,control,params)
|
||||
if(hud && hud.mymob && isovermind(hud.mymob))
|
||||
@@ -115,7 +115,7 @@
|
||||
/obj/screen/blob/RelocateCore
|
||||
icon_state = "ui_swap"
|
||||
name = "Relocate Core (80)"
|
||||
desc = "Swaps a node and your core for 80 points."
|
||||
desc = "Swaps a node and your core for 80 resources."
|
||||
|
||||
/obj/screen/blob/RelocateCore/Click()
|
||||
if(isovermind(usr))
|
||||
@@ -159,11 +159,11 @@
|
||||
static_inventory += using
|
||||
|
||||
using = new /obj/screen/blob/NodeBlob()
|
||||
using.screen_loc = ui_lhand
|
||||
using.screen_loc = ui_rhand
|
||||
static_inventory += using
|
||||
|
||||
using = new /obj/screen/blob/FactoryBlob()
|
||||
using.screen_loc = ui_rhand
|
||||
using.screen_loc = ui_lhand
|
||||
static_inventory += using
|
||||
|
||||
using = new /obj/screen/blob/ReadaptChemical()
|
||||
|
||||
+108
-110
@@ -208,7 +208,8 @@
|
||||
ticker.mode.update_wiz_icons_removed(src)
|
||||
ticker.mode.update_cult_icons_removed(src)
|
||||
ticker.mode.update_rev_icons_removed(src)
|
||||
gang_datum.remove_gang_hud(src)
|
||||
if(gang_datum)
|
||||
gang_datum.remove_gang_hud(src)
|
||||
|
||||
|
||||
//Link a new mobs mind to the creator of said mob. They will join any team they are currently on, and will only switch teams when their creator does.
|
||||
@@ -283,7 +284,7 @@
|
||||
)
|
||||
var/text = ""
|
||||
|
||||
if (istype(current, /mob/living/carbon/human) || istype(current, /mob/living/carbon/monkey))
|
||||
if(ishuman(current))
|
||||
/** REVOLUTION ***/
|
||||
text = "revolution"
|
||||
if (ticker.mode.config_tag=="revolution")
|
||||
@@ -362,100 +363,23 @@
|
||||
|
||||
sections["gang"] = text
|
||||
|
||||
|
||||
/** CULT ***/
|
||||
text = "cult"
|
||||
if (ticker.mode.config_tag=="cult")
|
||||
/** Abductors **/
|
||||
text = "Abductor"
|
||||
if(ticker.mode.config_tag == "abductor")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if (src in ticker.mode.cult)
|
||||
text += "loyal|<a href='?src=\ref[src];cult=clear'>employee</a>|<b>CULTIST</b>"
|
||||
text += "<br>Give <a href='?src=\ref[src];cult=tome'>tome</a>|<a href='?src=\ref[src];cult=amulet'>amulet</a>."
|
||||
/*
|
||||
if (objectives.len==0)
|
||||
text += "<br>Objectives are empty! Set to sacrifice and <a href='?src=\ref[src];cult=escape'>escape</a> or <a href='?src=\ref[src];cult=summon'>summon</a>."
|
||||
*/
|
||||
else if(isloyal(current))
|
||||
text += "<b>LOYAL</b>|employee|<a href='?src=\ref[src];cult=cultist'>cultist</a>"
|
||||
if(src in ticker.mode.abductors)
|
||||
text += "<b>Abductor</b>|<a href='?src=\ref[src];abductor=clear'>human</a>"
|
||||
text += "|<a href='?src=\ref[src];common=undress'>undress</a>|<a href='?src=\ref[src];abductor=equip'>equip</a>"
|
||||
else
|
||||
text += "loyal|<b>EMPLOYEE</b>|<a href='?src=\ref[src];cult=cultist'>cultist</a>"
|
||||
text += "<a href='?src=\ref[src];abductor=abductor'>Abductor</a>|<b>human</b>"
|
||||
|
||||
if(current && current.client && (ROLE_CULTIST in current.client.prefs.be_special))
|
||||
if(current && current.client && (ROLE_ABDUCTOR in current.client.prefs.be_special))
|
||||
text += "|Enabled in Prefs"
|
||||
else
|
||||
text += "|Disabled in Prefs"
|
||||
|
||||
sections["cult"] = text
|
||||
|
||||
/** CLOCKWORK CULT **/
|
||||
text = "clockwork cult"
|
||||
if(ticker.mode.config_tag == "clockwork cult")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if(src in ticker.mode.servants_of_ratvar)
|
||||
text += "loyal|<a href='?src=\ref[src];clockcult=clear'>employee</a>|<b>SERVANT</b>"
|
||||
text += "<br><a href='?src=\ref[src];clockcult=slab'>Give slab</a>"
|
||||
else if(isloyal(current))
|
||||
text += "<b>LOYAL</b>|employee|<a href='?src=\ref[src];clockcult=servant'>servant</a>"
|
||||
else
|
||||
text += "loyal|<b>EMPLOYEE</b>|<a href='?src=\ref[src];clockcult=servant'>servant</a>"
|
||||
|
||||
if(current && current.client && (ROLE_SERVANT_OF_RATVAR in current.client.prefs.be_special))
|
||||
text += "|Enabled in Prefs"
|
||||
else
|
||||
text += "|Disabled in Prefs"
|
||||
|
||||
sections["clockcult"] = text
|
||||
|
||||
/** WIZARD ***/
|
||||
text = "wizard"
|
||||
if (ticker.mode.config_tag=="wizard")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if ((src in ticker.mode.wizards) || (src in ticker.mode.apprentices))
|
||||
text += "<b>YES</b>|<a href='?src=\ref[src];wizard=clear'>no</a>"
|
||||
text += "<br><a href='?src=\ref[src];wizard=lair'>To lair</a>, <a href='?src=\ref[src];common=undress'>undress</a>, <a href='?src=\ref[src];wizard=dressup'>dress up</a>, <a href='?src=\ref[src];wizard=name'>let choose name</a>."
|
||||
if (objectives.len==0)
|
||||
text += "<br>Objectives are empty! <a href='?src=\ref[src];wizard=autoobjectives'>Randomize!</a>"
|
||||
else
|
||||
text += "<a href='?src=\ref[src];wizard=wizard'>yes</a>|<b>NO</b>"
|
||||
|
||||
if(current && current.client && (ROLE_WIZARD in current.client.prefs.be_special))
|
||||
text += "|Enabled in Prefs"
|
||||
else
|
||||
text += "|Disabled in Prefs"
|
||||
|
||||
sections["wizard"] = text
|
||||
|
||||
/** CHANGELING ***/
|
||||
text = "changeling"
|
||||
if (ticker.mode.config_tag=="changeling" || ticker.mode.config_tag=="traitorchan")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if ((src in ticker.mode.changelings) && special_role)
|
||||
text += "<b>YES</b>|<a href='?src=\ref[src];changeling=clear'>no</a>"
|
||||
if (objectives.len==0)
|
||||
text += "<br>Objectives are empty! <a href='?src=\ref[src];changeling=autoobjectives'>Randomize!</a>"
|
||||
if(changeling && changeling.stored_profiles.len && (current.real_name != changeling.first_prof.name) )
|
||||
text += "<br><a href='?src=\ref[src];changeling=initialdna'>Transform to initial appearance.</a>"
|
||||
else if(src in ticker.mode.changelings) //Station Aligned Changeling
|
||||
text += "<b>YES (but not an antag)</b>|<a href='?src=\ref[src];changeling=clear'>no</a>"
|
||||
if (objectives.len==0)
|
||||
text += "<br>Objectives are empty! <a href='?src=\ref[src];changeling=autoobjectives'>Randomize!</a>"
|
||||
if(changeling && changeling.stored_profiles.len && (current.real_name != changeling.first_prof.name) )
|
||||
text += "<br><a href='?src=\ref[src];changeling=initialdna'>Transform to initial appearance.</a>"
|
||||
else
|
||||
text += "<a href='?src=\ref[src];changeling=changeling'>yes</a>|<b>NO</b>"
|
||||
// var/datum/game_mode/changeling/changeling = ticker.mode
|
||||
// if (istype(changeling) && changeling.changelingdeath)
|
||||
// text += "<br>All the changelings are dead! Restart in [round((changeling.TIME_TO_GET_REVIVED-(world.time-changeling.changelingdeathtime))/10)] seconds."
|
||||
|
||||
if(current && current.client && (ROLE_CHANGELING in current.client.prefs.be_special))
|
||||
text += "|Enabled in Prefs"
|
||||
else
|
||||
text += "|Disabled in Prefs"
|
||||
|
||||
sections["changeling"] = text
|
||||
sections["abductor"] = text
|
||||
|
||||
/** NUCLEAR ***/
|
||||
text = "nuclear"
|
||||
@@ -482,6 +406,71 @@
|
||||
|
||||
sections["nuclear"] = text
|
||||
|
||||
/** WIZARD ***/
|
||||
text = "wizard"
|
||||
if (ticker.mode.config_tag=="wizard")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if ((src in ticker.mode.wizards) || (src in ticker.mode.apprentices))
|
||||
text += "<b>YES</b>|<a href='?src=\ref[src];wizard=clear'>no</a>"
|
||||
text += "<br><a href='?src=\ref[src];wizard=lair'>To lair</a>, <a href='?src=\ref[src];common=undress'>undress</a>, <a href='?src=\ref[src];wizard=dressup'>dress up</a>, <a href='?src=\ref[src];wizard=name'>let choose name</a>."
|
||||
if (objectives.len==0)
|
||||
text += "<br>Objectives are empty! <a href='?src=\ref[src];wizard=autoobjectives'>Randomize!</a>"
|
||||
else
|
||||
text += "<a href='?src=\ref[src];wizard=wizard'>yes</a>|<b>NO</b>"
|
||||
|
||||
if(current && current.client && (ROLE_WIZARD in current.client.prefs.be_special))
|
||||
text += "|Enabled in Prefs"
|
||||
else
|
||||
text += "|Disabled in Prefs"
|
||||
|
||||
sections["wizard"] = text
|
||||
|
||||
/** CULT ***/
|
||||
text = "cult"
|
||||
if (ticker.mode.config_tag=="cult")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if (src in ticker.mode.cult)
|
||||
text += "loyal|<a href='?src=\ref[src];cult=clear'>employee</a>|<b>CULTIST</b>"
|
||||
text += "<br>Give <a href='?src=\ref[src];cult=tome'>tome</a>|<a href='?src=\ref[src];cult=amulet'>amulet</a>."
|
||||
|
||||
else if(isloyal(current))
|
||||
text += "<b>LOYAL</b>|employee|<a href='?src=\ref[src];cult=cultist'>cultist</a>"
|
||||
else if(is_convertable_to_cult(src))
|
||||
text += "loyal|<b>EMPLOYEE</b>|<a href='?src=\ref[src];cult=cultist'>cultist</a>"
|
||||
else
|
||||
text += "loyal|<b>EMPLOYEE</b>|<i>cannot serve Nar-Sie</i>"
|
||||
|
||||
if(current && current.client && (ROLE_CULTIST in current.client.prefs.be_special))
|
||||
text += "|Enabled in Prefs"
|
||||
else
|
||||
text += "|Disabled in Prefs"
|
||||
|
||||
sections["cult"] = text
|
||||
|
||||
/** CLOCKWORK CULT **/
|
||||
text = "clockwork cult"
|
||||
if(ticker.mode.config_tag == "clockwork cult")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if(src in ticker.mode.servants_of_ratvar)
|
||||
text += "loyal|<a href='?src=\ref[src];clockcult=clear'>employee</a>|<b>SERVANT</b>"
|
||||
text += "<br><a href='?src=\ref[src];clockcult=slab'>Give slab</a>"
|
||||
else if(isloyal(current))
|
||||
text += "<b>LOYAL</b>|employee|<a href='?src=\ref[src];clockcult=servant'>servant</a>"
|
||||
else if(is_eligible_servant(current))
|
||||
text += "loyal|<b>EMPLOYEE</b>|<a href='?src=\ref[src];clockcult=servant'>servant</a>"
|
||||
else
|
||||
text += "loyal|<b>EMPLOYEE</b>|<i>cannot serve Ratvar</i>"
|
||||
|
||||
if(current && current.client && (ROLE_SERVANT_OF_RATVAR in current.client.prefs.be_special))
|
||||
text += "|Enabled in Prefs"
|
||||
else
|
||||
text += "|Disabled in Prefs"
|
||||
|
||||
sections["clockcult"] = text
|
||||
|
||||
/** TRAITOR ***/
|
||||
text = "traitor"
|
||||
if (ticker.mode.config_tag=="traitor" || ticker.mode.config_tag=="traitorchan")
|
||||
@@ -501,25 +490,6 @@
|
||||
|
||||
sections["traitor"] = text
|
||||
|
||||
/** Abductors **/
|
||||
|
||||
text = "Abductor"
|
||||
if(ticker.mode.config_tag == "abductor")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if(src in ticker.mode.abductors)
|
||||
text += "<b>Abductor</b>|<a href='?src=\ref[src];abductor=clear'>human</a>"
|
||||
text += "|<a href='?src=\ref[src];common=undress'>undress</a>|<a href='?src=\ref[src];abductor=equip'>equip</a>"
|
||||
else
|
||||
text += "<a href='?src=\ref[src];abductor=abductor'>Abductor</a>|<b>human</b>"
|
||||
|
||||
if(current && current.client && (ROLE_ABDUCTOR in current.client.prefs.be_special))
|
||||
text += "|Enabled in Prefs"
|
||||
else
|
||||
text += "|Disabled in Prefs"
|
||||
|
||||
sections["abductor"] = text
|
||||
|
||||
/** HAND OF GOD **/
|
||||
text = "hand of god"
|
||||
if(ticker.mode.config_tag == "handofgod")
|
||||
@@ -552,8 +522,36 @@
|
||||
|
||||
sections["follower"] = text
|
||||
|
||||
/** MONKEY ***/
|
||||
if (istype(current, /mob/living/carbon))
|
||||
if(ishuman(current) || ismonkey(current))
|
||||
|
||||
/** CHANGELING ***/
|
||||
text = "changeling"
|
||||
if (ticker.mode.config_tag=="changeling" || ticker.mode.config_tag=="traitorchan")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if ((src in ticker.mode.changelings) && special_role)
|
||||
text += "<b>YES</b>|<a href='?src=\ref[src];changeling=clear'>no</a>"
|
||||
if (objectives.len==0)
|
||||
text += "<br>Objectives are empty! <a href='?src=\ref[src];changeling=autoobjectives'>Randomize!</a>"
|
||||
if(changeling && changeling.stored_profiles.len && (current.real_name != changeling.first_prof.name) )
|
||||
text += "<br><a href='?src=\ref[src];changeling=initialdna'>Transform to initial appearance.</a>"
|
||||
else if(src in ticker.mode.changelings) //Station Aligned Changeling
|
||||
text += "<b>YES (but not an antag)</b>|<a href='?src=\ref[src];changeling=clear'>no</a>"
|
||||
if (objectives.len==0)
|
||||
text += "<br>Objectives are empty! <a href='?src=\ref[src];changeling=autoobjectives'>Randomize!</a>"
|
||||
if(changeling && changeling.stored_profiles.len && (current.real_name != changeling.first_prof.name) )
|
||||
text += "<br><a href='?src=\ref[src];changeling=initialdna'>Transform to initial appearance.</a>"
|
||||
else
|
||||
text += "<a href='?src=\ref[src];changeling=changeling'>yes</a>|<b>NO</b>"
|
||||
|
||||
if(current && current.client && (ROLE_CHANGELING in current.client.prefs.be_special))
|
||||
text += "|Enabled in Prefs"
|
||||
else
|
||||
text += "|Disabled in Prefs"
|
||||
|
||||
sections["changeling"] = text
|
||||
|
||||
/** MONKEY ***/
|
||||
text = "monkey"
|
||||
if (ticker.mode.config_tag=="monkey")
|
||||
text = uppertext(text)
|
||||
@@ -1227,7 +1225,7 @@
|
||||
log_admin("[key_name(usr)] has de-sintouch'ed [current].")
|
||||
if("devil")
|
||||
if(!ishuman(current) && !iscyborg(current))
|
||||
usr << "<span class='warning'>This only works on humans!</span>"
|
||||
usr << "<span class='warning'>This only works on humans and cyborgs!</span>"
|
||||
return
|
||||
ticker.mode.devils += src
|
||||
special_role = "devil"
|
||||
|
||||
@@ -139,27 +139,34 @@ var/list/teleportlocs = list()
|
||||
|
||||
/area/shuttle/mining
|
||||
name = "Mining Shuttle"
|
||||
blob_allowed = FALSE
|
||||
|
||||
/area/shuttle/labor
|
||||
name = "Labor Camp Shuttle"
|
||||
blob_allowed = FALSE
|
||||
|
||||
/area/shuttle/supply
|
||||
name = "Supply Shuttle"
|
||||
blob_allowed = FALSE
|
||||
|
||||
/area/shuttle/escape
|
||||
name = "Emergency Shuttle"
|
||||
|
||||
/area/shuttle/transport
|
||||
name = "Transport Shuttle"
|
||||
blob_allowed = FALSE
|
||||
|
||||
/area/shuttle/syndicate
|
||||
name = "Syndicate Infiltrator"
|
||||
blob_allowed = FALSE
|
||||
|
||||
/area/shuttle/assault_pod
|
||||
name = "Steel Rain"
|
||||
blob_allowed = FALSE
|
||||
|
||||
/area/shuttle/abandoned
|
||||
name = "Abandoned Ship"
|
||||
blob_allowed = FALSE
|
||||
|
||||
/area/start
|
||||
name = "start area"
|
||||
@@ -658,6 +665,7 @@ var/list/teleportlocs = list()
|
||||
luminosity = 1
|
||||
lighting_use_dynamic = DYNAMIC_LIGHTING_IFSTARLIGHT
|
||||
valid_territory = 0
|
||||
blob_allowed = FALSE
|
||||
|
||||
/area/solar/auxport
|
||||
name = "Fore Port Solar Array"
|
||||
|
||||
@@ -74,6 +74,11 @@
|
||||
D.cancelAlarm("Power", src, source)
|
||||
else
|
||||
D.triggerAlarm("Power", src, cameras, source)
|
||||
for(var/datum/computer_file/program/alarm_monitor/p in alarmdisplay)
|
||||
if(state == 1)
|
||||
p.cancelAlarm("Power", src, source)
|
||||
else
|
||||
p.triggerAlarm("Power", src, cameras, source)
|
||||
|
||||
/area/proc/atmosalert(danger_level, obj/source)
|
||||
if(danger_level != atmosalm)
|
||||
@@ -89,6 +94,8 @@
|
||||
a.triggerAlarm("Atmosphere", src, cameras, source)
|
||||
for(var/mob/living/simple_animal/drone/D in mob_list)
|
||||
D.triggerAlarm("Atmosphere", src, cameras, source)
|
||||
for(var/datum/computer_file/program/alarm_monitor/p in alarmdisplay)
|
||||
p.triggerAlarm("Atmosphere", src, cameras, source)
|
||||
|
||||
else if (src.atmosalm == 2)
|
||||
for(var/mob/living/silicon/aiPlayer in player_list)
|
||||
@@ -97,6 +104,8 @@
|
||||
a.cancelAlarm("Atmosphere", src, source)
|
||||
for(var/mob/living/simple_animal/drone/D in mob_list)
|
||||
D.cancelAlarm("Atmosphere", src, source)
|
||||
for(var/datum/computer_file/program/alarm_monitor/p in alarmdisplay)
|
||||
p.cancelAlarm("Atmosphere", src, source)
|
||||
|
||||
src.atmosalm = danger_level
|
||||
return 1
|
||||
@@ -128,6 +137,8 @@
|
||||
aiPlayer.triggerAlarm("Fire", src, cameras, source)
|
||||
for (var/mob/living/simple_animal/drone/D in mob_list)
|
||||
D.triggerAlarm("Fire", src, cameras, source)
|
||||
for(var/datum/computer_file/program/alarm_monitor/p in alarmdisplay)
|
||||
p.triggerAlarm("Fire", src, cameras, source)
|
||||
|
||||
/area/proc/firereset(obj/source)
|
||||
for(var/area/RA in related)
|
||||
@@ -150,6 +161,8 @@
|
||||
a.cancelAlarm("Fire", src, source)
|
||||
for (var/mob/living/simple_animal/drone/D in mob_list)
|
||||
D.cancelAlarm("Fire", src, source)
|
||||
for(var/datum/computer_file/program/alarm_monitor/p in alarmdisplay)
|
||||
p.cancelAlarm("Fire", src, source)
|
||||
|
||||
/area/proc/burglaralert(obj/trigger)
|
||||
if(always_unpowered == 1) //no burglar alarms in space/asteroid
|
||||
|
||||
@@ -25,11 +25,12 @@ var/list/blobs_legit = list() //used for win-score calculations, contains only b
|
||||
<span class='green'>Blobs</span>: Consume the station and spread as far as you can.\n\
|
||||
<span class='notice'>Crew</span>: Fight back the blobs and minimize station damage."
|
||||
|
||||
var/burst = 0
|
||||
var/message_sent = FALSE
|
||||
|
||||
var/cores_to_spawn = 1
|
||||
var/players_per_core = 25
|
||||
var/blob_point_rate = 3
|
||||
var/blob_base_starting_points = 80
|
||||
|
||||
var/blobwincount = 350
|
||||
|
||||
@@ -73,7 +74,8 @@ var/list/blobs_legit = list() //used for win-score calculations, contains only b
|
||||
/datum/game_mode/blob/post_setup()
|
||||
|
||||
for(var/datum/mind/blob in blob_overminds)
|
||||
var/mob/camera/blob/B = blob.current.become_overmind(1)
|
||||
var/mob/camera/blob/B = blob.current.become_overmind(TRUE, round(blob_base_starting_points/blob_overminds.len))
|
||||
B.mind.name = B.name
|
||||
var/turf/T = pick(blobstart)
|
||||
B.loc = T
|
||||
B.base_point_rate = blob_point_rate
|
||||
@@ -91,6 +93,7 @@ var/list/blobs_legit = list() //used for win-score calculations, contains only b
|
||||
sleep(message_delay)
|
||||
|
||||
send_intercept(1)
|
||||
message_sent = TRUE
|
||||
|
||||
sleep(24000) //40 minutes, plus burst_delay*3(minimum of 6 minutes, maximum of 8)
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
/datum/game_mode/blob/check_finished()
|
||||
if(blobwincount <= blobs_legit.len)//Blob took over
|
||||
return 1
|
||||
if(overminds.len)
|
||||
return 0
|
||||
for(var/datum/mind/blob in blob_overminds)
|
||||
if(isovermind(blob.current))
|
||||
var/mob/camera/blob/B = blob.current
|
||||
if(B.blob_core || !B.placed)
|
||||
return 0
|
||||
if(!blob_cores.len) //blob is dead
|
||||
if(config.continuous["blob"])
|
||||
message_sent = FALSE //disable the win count at this point
|
||||
continuous_sanity_checked = 1 //Nonstandard definition of "alive" gets past the check otherwise
|
||||
SSshuttle.clearHostileEnvironment(src)
|
||||
return ..()
|
||||
@@ -18,23 +22,38 @@
|
||||
if(blobwincount <= blobs_legit.len)
|
||||
feedback_set_details("round_end_result","win - blob took over")
|
||||
world << "<FONT size = 3><B>The blob has taken over the station!</B></FONT>"
|
||||
world << "<B>The entire station was eaten by the Blob</B>"
|
||||
world << "<B>The entire station was eaten by the Blob!</B>"
|
||||
log_game("Blob mode completed with a blob victory.")
|
||||
|
||||
else if(station_was_nuked)
|
||||
feedback_set_details("round_end_result","halfwin - nuke")
|
||||
world << "<FONT size = 3><B>Partial Win: The station has been destroyed!</B></FONT>"
|
||||
world << "<B>Directive 7-12 has been successfully carried out preventing the Blob from spreading.</B>"
|
||||
world << "<B>Directive 7-12 has been successfully carried out, preventing the Blob from spreading.</B>"
|
||||
log_game("Blob mode completed with a tie (station destroyed).")
|
||||
|
||||
else if(!blob_cores.len)
|
||||
feedback_set_details("round_end_result","loss - blob eliminated")
|
||||
world << "<FONT size = 3><B>The staff has won!</B></FONT>"
|
||||
world << "<B>The alien organism has been eradicated from the station</B>"
|
||||
world << "<B>The alien organism has been eradicated from the station!</B>"
|
||||
log_game("Blob mode completed with a crew victory.")
|
||||
..()
|
||||
return 1
|
||||
|
||||
/datum/game_mode/blob/printplayer(datum/mind/ply, fleecheck)
|
||||
if((ply in blob_overminds))
|
||||
var/text = "<br><b>[ply.key]</b> was <b>[ply.name]</b>"
|
||||
if(isovermind(ply.current))
|
||||
var/mob/camera/blob/B = ply.current
|
||||
text += "<b>(<font color=\"[B.blob_reagent_datum.color]\">[B.blob_reagent_datum.name]</font>)</b> and"
|
||||
if(B.blob_core)
|
||||
text += " <span class='greenannounce'>survived</span>"
|
||||
else
|
||||
text += " <span class='boldannounce'>was destroyed</span>"
|
||||
else
|
||||
text += " and <span class='boldannounce'>was destroyed</span>"
|
||||
return text
|
||||
return ..()
|
||||
|
||||
/datum/game_mode/proc/auto_declare_completion_blob()
|
||||
if(istype(ticker.mode,/datum/game_mode/blob) )
|
||||
var/datum/game_mode/blob/blob_mode = src
|
||||
|
||||
@@ -168,20 +168,23 @@
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/update_icons()
|
||||
..()
|
||||
if(overmind)
|
||||
color = overmind.blob_reagent_datum.complementary_color
|
||||
else
|
||||
color = initial(color)
|
||||
if(is_zombie)
|
||||
cut_overlays()
|
||||
overlays = human_overlays
|
||||
var/image/I = image('icons/mob/blob.dmi', icon_state = "blob_head")
|
||||
if(overmind)
|
||||
I.color = overmind.blob_reagent_datum.color
|
||||
I.color = overmind.blob_reagent_datum.complementary_color
|
||||
color = initial(color)//looks better.
|
||||
add_overlay(I)
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/weak
|
||||
name = "fragile blob spore"
|
||||
health = 20
|
||||
maxHealth = 20
|
||||
health = 15
|
||||
maxHealth = 15
|
||||
melee_damage_lower = 1
|
||||
melee_damage_upper = 2
|
||||
death_cloud_size = 0
|
||||
@@ -199,7 +202,6 @@
|
||||
health = 200
|
||||
maxHealth = 200
|
||||
damage_coeff = list(BRUTE = 0.5, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1)
|
||||
next_move_modifier = 1.5 //slow-ass attack speed, 3 times higher than how fast the blob can attack
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 20
|
||||
obj_damage = 20
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
maxhealth = 400
|
||||
explosion_block = 6
|
||||
point_return = -1
|
||||
atmosblock = 1
|
||||
health_regen = 0 //we regen in Life() instead of when pulsed
|
||||
var/core_regen = 2
|
||||
var/overmind_get_delay = 0 //we don't want to constantly try to find an overmind, this var tracks when we'll try to get an overmind again
|
||||
@@ -50,7 +49,8 @@
|
||||
return ..()
|
||||
|
||||
/obj/effect/blob/core/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
return
|
||||
exposed_temperature *= 0.5
|
||||
..()
|
||||
|
||||
/obj/effect/blob/core/ex_act(severity, target)
|
||||
var/damage = 50 - 10 * severity //remember, the core takes half brute damage, so this is 20/15/10 damage based on severity
|
||||
|
||||
@@ -7,13 +7,12 @@
|
||||
maxhealth = 200
|
||||
health_regen = 3
|
||||
point_return = 25
|
||||
atmosblock = 1
|
||||
|
||||
|
||||
/obj/effect/blob/node/New(loc, var/h = 100)
|
||||
/obj/effect/blob/node/New(loc)
|
||||
blob_nodes += src
|
||||
START_PROCESSING(SSobj, src)
|
||||
..(loc, h)
|
||||
..(loc)
|
||||
|
||||
/obj/effect/blob/node/scannerreport()
|
||||
return "Gradually expands and sustains nearby blob spores and blobbernauts."
|
||||
@@ -28,14 +27,15 @@
|
||||
var/image/C = new('icons/mob/blob.dmi', "blob_node_overlay")
|
||||
src.add_overlay(C)
|
||||
|
||||
/obj/effect/blob/node/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
return
|
||||
|
||||
/obj/effect/blob/node/Destroy()
|
||||
blob_nodes -= src
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/effect/blob/node/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
exposed_temperature *= 0.75
|
||||
..()
|
||||
|
||||
/obj/effect/blob/node/Life()
|
||||
Pulse_Area(overmind, 10, 3, 2)
|
||||
color = null
|
||||
|
||||
@@ -1,21 +1,37 @@
|
||||
/obj/effect/blob/shield
|
||||
name = "strong blob"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blob_idle"
|
||||
icon_state = "blob_shield"
|
||||
desc = "A solid wall of slightly twitching tendrils."
|
||||
health = 150
|
||||
maxhealth = 150
|
||||
brute_resist = 0.1
|
||||
brute_resist = 0.25
|
||||
explosion_block = 3
|
||||
point_return = 4
|
||||
atmosblock = 1
|
||||
|
||||
|
||||
/obj/effect/blob/shield/scannerreport()
|
||||
return "Will prevent the spread of atmospheric changes."
|
||||
if(atmosblock)
|
||||
return "Will prevent the spread of atmospheric changes."
|
||||
return "N/A"
|
||||
|
||||
/obj/effect/blob/shield/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
return
|
||||
|
||||
/obj/effect/blob/shield/core
|
||||
point_return = 0
|
||||
|
||||
/obj/effect/blob/shield/update_icon()
|
||||
..()
|
||||
if(health <= 75)
|
||||
icon_state = "blob_shield_damaged"
|
||||
name = "weakened strong blob"
|
||||
desc = "A wall of twitching tendrils."
|
||||
atmosblock = 0
|
||||
else
|
||||
icon_state = initial(icon_state)
|
||||
name = initial(name)
|
||||
desc = initial(desc)
|
||||
atmosblock = 1
|
||||
air_update_turf(1)
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
/mob/camera/blob
|
||||
name = "Blob Overmind"
|
||||
real_name = "Blob Overmind"
|
||||
desc = "The overmind. It controls the blob."
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "marker"
|
||||
|
||||
mouse_opacity = 1
|
||||
move_on_shuttle = 1
|
||||
see_in_dark = 8
|
||||
see_invisible = SEE_INVISIBLE_MINIMUM
|
||||
invisibility = INVISIBILITY_OBSERVER
|
||||
layer = FLY_LAYER
|
||||
|
||||
pass_flags = PASSBLOB
|
||||
faction = list("blob")
|
||||
@@ -26,7 +29,8 @@
|
||||
var/manualplace_min_time = 600 //in deciseconds //a minute, to get bearings
|
||||
var/autoplace_max_time = 3600 //six minutes, as long as should be needed
|
||||
|
||||
/mob/camera/blob/New(loc, pre_placed = 0, mode_made = 0)
|
||||
/mob/camera/blob/New(loc, pre_placed = 0, mode_made = 0, starting_points = 60)
|
||||
blob_points = starting_points
|
||||
if(pre_placed) //we already have a core!
|
||||
manualplace_min_time = 0
|
||||
autoplace_max_time = 0
|
||||
@@ -42,10 +46,9 @@
|
||||
name = new_name
|
||||
real_name = new_name
|
||||
last_attack = world.time
|
||||
var/list/possible_reagents = list()
|
||||
for(var/type in (subtypesof(/datum/reagent/blob)))
|
||||
possible_reagents.Add(new type)
|
||||
blob_reagent_datum = pick(possible_reagents)
|
||||
var/datum/reagent/blob/BC = pick((subtypesof(/datum/reagent/blob)))
|
||||
blob_reagent_datum = new BC
|
||||
color = blob_reagent_datum.complementary_color
|
||||
if(blob_core)
|
||||
blob_core.update_icon()
|
||||
|
||||
@@ -70,18 +73,19 @@
|
||||
/mob/camera/blob/Destroy()
|
||||
for(var/BL in blobs)
|
||||
var/obj/effect/blob/B = BL
|
||||
if(B.overmind == src)
|
||||
if(B && B.overmind == src)
|
||||
B.overmind = null
|
||||
B.update_icon() //reset anything that was ours
|
||||
for(var/BLO in blob_mobs)
|
||||
var/mob/living/simple_animal/hostile/blob/BM = BLO
|
||||
BM.overmind = null
|
||||
BM.update_icons()
|
||||
if(BM)
|
||||
BM.overmind = null
|
||||
BM.update_icons()
|
||||
overminds -= src
|
||||
if(ghostimage)
|
||||
ghost_darkness_images -= ghostimage
|
||||
qdel(ghostimage)
|
||||
ghostimage = null;
|
||||
ghostimage = null
|
||||
updateallghostimages()
|
||||
return ..()
|
||||
|
||||
@@ -91,6 +95,12 @@
|
||||
src << "<span class='notice'>You are the overmind!</span>"
|
||||
blob_help()
|
||||
update_health_hud()
|
||||
add_points(0)
|
||||
|
||||
/mob/camera/blob/examine(mob/user)
|
||||
..()
|
||||
if(blob_reagent_datum)
|
||||
user << "Its chemical is <font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</font>."
|
||||
|
||||
/mob/camera/blob/update_health_hud()
|
||||
if(blob_core)
|
||||
@@ -100,9 +110,8 @@
|
||||
B.hud_used.blobpwrdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#82ed00'>[round(blob_core.health)]</font></div>"
|
||||
|
||||
/mob/camera/blob/proc/add_points(points)
|
||||
if(points != 0)
|
||||
blob_points = Clamp(blob_points + points, 0, max_blob_points)
|
||||
hud_used.blobpwrdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#82ed00'>[round(src.blob_points)]</font></div>"
|
||||
blob_points = Clamp(blob_points + points, 0, max_blob_points)
|
||||
hud_used.blobpwrdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#82ed00'>[round(blob_points)]</font></div>"
|
||||
|
||||
/mob/camera/blob/say(message)
|
||||
if (!message)
|
||||
@@ -150,6 +159,11 @@
|
||||
if(blob_core)
|
||||
stat(null, "Core Health: [blob_core.health]")
|
||||
stat(null, "Power Stored: [blob_points]/[max_blob_points]")
|
||||
if(ticker && istype(ticker.mode, /datum/game_mode/blob))
|
||||
var/datum/game_mode/blob/B = ticker.mode
|
||||
stat(null, "Blobs to Win: [blobs_legit.len]/[B.blobwincount]")
|
||||
else
|
||||
stat(null, "Total Blobs: [blobs.len]")
|
||||
if(free_chem_rerolls)
|
||||
stat(null, "You have [free_chem_rerolls] Free Chemical Reroll\s Remaining")
|
||||
if(!placed)
|
||||
@@ -170,6 +184,3 @@
|
||||
return 0
|
||||
loc = NewLoc
|
||||
return 1
|
||||
|
||||
/mob/camera/blob/proc/can_attack()
|
||||
return (world.time > (last_attack + CLICK_CD_RANGE))
|
||||
|
||||
@@ -67,12 +67,13 @@
|
||||
set desc = "Move your camera to a selected node."
|
||||
if(blob_nodes.len)
|
||||
var/list/nodes = list()
|
||||
for(var/i = 1; i <= blob_nodes.len; i++)
|
||||
nodes["Blob Node #[i]"] = blob_nodes[i]
|
||||
for(var/i in 1 to blob_nodes.len)
|
||||
var/obj/effect/blob/node/B = blob_nodes[i]
|
||||
nodes["Blob Node #[i] ([B.overmind ? "B.overmind.blob_reagent_datum.name":"No Chemical"]"] = B
|
||||
var/node_name = input(src, "Choose a node to jump to.", "Node Jump") in nodes
|
||||
var/obj/effect/blob/node/chosen_node = nodes[node_name]
|
||||
if(chosen_node)
|
||||
src.loc = chosen_node.loc
|
||||
loc = chosen_node.loc
|
||||
|
||||
/mob/camera/blob/proc/createSpecial(price, blobType, nearEquals, needsNode, turf/T)
|
||||
if(!T)
|
||||
@@ -110,12 +111,12 @@
|
||||
|
||||
/mob/camera/blob/verb/create_shield_power()
|
||||
set category = "Blob"
|
||||
set name = "Create Shield Blob (10)"
|
||||
set name = "Create Shield Blob (15)"
|
||||
set desc = "Create a shield blob, which will block fire and is hard to kill."
|
||||
create_shield()
|
||||
|
||||
/mob/camera/blob/proc/create_shield(turf/T)
|
||||
createSpecial(10, /obj/effect/blob/shield, 0, 0, T)
|
||||
createSpecial(15, /obj/effect/blob/shield, 0, 0, T)
|
||||
|
||||
/mob/camera/blob/verb/create_resource()
|
||||
set category = "Blob"
|
||||
@@ -125,9 +126,9 @@
|
||||
|
||||
/mob/camera/blob/verb/create_node()
|
||||
set category = "Blob"
|
||||
set name = "Create Node Blob (60)"
|
||||
set name = "Create Node Blob (50)"
|
||||
set desc = "Create a node, which will power nearby factory and resource blobs."
|
||||
createSpecial(60, /obj/effect/blob/node, 5, 0)
|
||||
createSpecial(50, /obj/effect/blob/node, 5, 0)
|
||||
|
||||
/mob/camera/blob/verb/create_factory()
|
||||
set category = "Blob"
|
||||
@@ -166,9 +167,8 @@
|
||||
blobber.adjustHealth(blobber.maxHealth * 0.5)
|
||||
blob_mobs += blobber
|
||||
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [blob_reagent_datum.name] blobbernaut?", ROLE_BLOB, null, ROLE_BLOB, 50) //players must answer rapidly
|
||||
var/client/C = null
|
||||
if(candidates.len) //if we got at least one candidate, they're a blobbernaut now.
|
||||
C = pick(candidates)
|
||||
var/client/C = pick(candidates)
|
||||
blobber.notransform = 0
|
||||
blobber.key = C.key
|
||||
blobber << 'sound/effects/blobattack.ogg'
|
||||
@@ -190,11 +190,21 @@
|
||||
if(!B)
|
||||
src << "<span class='warning'>You must be on a blob node!</span>"
|
||||
return
|
||||
if(!blob_core)
|
||||
src << "<span class='userdanger'>You have no core and are about to die! May you rest in peace.</span>"
|
||||
return
|
||||
var/area/A = get_area(T)
|
||||
if(istype(T, /turf/open/space) || A && !A.blob_allowed)
|
||||
src << "<span class='warning'>You cannot relocate your core here!</span>"
|
||||
return
|
||||
if(!can_buy(80))
|
||||
return
|
||||
var/turf/old_turf = blob_core.loc
|
||||
blob_core.loc = T
|
||||
B.loc = old_turf
|
||||
var/turf/old_turf = get_turf(blob_core)
|
||||
var/olddir = blob_core.dir
|
||||
blob_core.forceMove(T)
|
||||
blob_core.setDir(B.dir)
|
||||
B.forceMove(old_turf)
|
||||
B.setDir(olddir)
|
||||
|
||||
/mob/camera/blob/verb/revert()
|
||||
set category = "Blob"
|
||||
@@ -221,21 +231,22 @@
|
||||
|
||||
/mob/camera/blob/verb/expand_blob_power()
|
||||
set category = "Blob"
|
||||
set name = "Expand/Attack Blob (5)"
|
||||
set name = "Expand/Attack Blob (4)"
|
||||
set desc = "Attempts to create a new blob in this tile. If the tile isn't clear, instead attacks it, damaging mobs and objects."
|
||||
var/turf/T = get_turf(src)
|
||||
expand_blob(T)
|
||||
|
||||
/mob/camera/blob/proc/expand_blob(turf/T)
|
||||
if(!can_attack())
|
||||
if(world.time > last_attack)
|
||||
return
|
||||
var/obj/effect/blob/OB = locate() in circlerange(T, 1)
|
||||
if(!OB)
|
||||
var/list/possibleblobs = list()
|
||||
for(var/obj/effect/blob/AB in range(T, 1))
|
||||
possibleblobs += AB
|
||||
if(!possibleblobs.len)
|
||||
src << "<span class='warning'>There is no blob adjacent to the target tile!</span>"
|
||||
return
|
||||
if(can_buy(5))
|
||||
if(can_buy(4))
|
||||
var/attacksuccess = FALSE
|
||||
last_attack = world.time
|
||||
for(var/mob/living/L in T)
|
||||
if("blob" in L.faction) //no friendly/dead fire
|
||||
continue
|
||||
@@ -250,10 +261,31 @@
|
||||
B.blob_attack_animation(T, src)
|
||||
else
|
||||
src << "<span class='warning'>There is a blob there!</span>"
|
||||
add_points(5) //otherwise, refund all of the cost
|
||||
return
|
||||
add_points(4) //otherwise, refund all of the cost
|
||||
else
|
||||
OB.expand(T, src)
|
||||
var/list/cardinalblobs = list()
|
||||
var/list/diagonalblobs = list()
|
||||
for(var/I in possibleblobs)
|
||||
var/obj/effect/blob/IB = I
|
||||
if(get_dir(IB, T) in cardinal)
|
||||
cardinalblobs += IB
|
||||
else
|
||||
diagonalblobs += IB
|
||||
var/obj/effect/blob/OB
|
||||
if(cardinalblobs.len)
|
||||
OB = pick(cardinalblobs)
|
||||
OB.expand(T, src)
|
||||
else
|
||||
OB = pick(diagonalblobs)
|
||||
if(attacksuccess)
|
||||
OB.blob_attack_animation(T, src)
|
||||
playsound(OB, 'sound/effects/splat.ogg', 50, 1)
|
||||
else
|
||||
add_points(4) //if we're attacking diagonally and didn't hit anything, refund
|
||||
if(attacksuccess)
|
||||
last_attack = world.time + CLICK_CD_MELEE
|
||||
else
|
||||
last_attack = world.time + CLICK_CD_RAPID
|
||||
|
||||
/mob/camera/blob/verb/rally_spores_power()
|
||||
set category = "Blob"
|
||||
@@ -298,6 +330,7 @@
|
||||
/mob/camera/blob/proc/set_chemical()
|
||||
var/datum/reagent/blob/BC = pick((subtypesof(/datum/reagent/blob) - blob_reagent_datum.type))
|
||||
blob_reagent_datum = new BC
|
||||
color = blob_reagent_datum.complementary_color
|
||||
for(var/BL in blobs)
|
||||
var/obj/effect/blob/B = BL
|
||||
B.update_icon()
|
||||
@@ -308,6 +341,8 @@
|
||||
BM << "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.shortdesc ? "[blob_reagent_datum.shortdesc]" : "[blob_reagent_datum.description]"]"
|
||||
src << "Your reagent is now: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!"
|
||||
src << "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.description]"
|
||||
if(blob_reagent_datum.effectdesc)
|
||||
src << "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.effectdesc]"
|
||||
|
||||
/mob/camera/blob/verb/blob_help()
|
||||
set category = "Blob"
|
||||
@@ -316,6 +351,8 @@
|
||||
src << "<b>As the overmind, you can control the blob!</b>"
|
||||
src << "Your blob reagent is: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!"
|
||||
src << "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.description]"
|
||||
if(blob_reagent_datum.effectdesc)
|
||||
src << "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.effectdesc]"
|
||||
src << "<b>You can expand, which will attack people, damage objects, or place a Normal Blob if the tile is clear.</b>"
|
||||
src << "<i>Normal Blobs</i> will expand your reach and can be upgraded into special blobs that perform certain functions."
|
||||
src << "<b>You can upgrade normal blobs into the following types of blob:</b>"
|
||||
@@ -329,4 +366,4 @@
|
||||
src << "Attempting to talk will send a message to all other overminds, allowing you to coordinate with them."
|
||||
if(!placed && autoplace_max_time <= world.time)
|
||||
src << "<span class='big'><font color=\"#EE4000\">You will automatically place your blob core in [round((autoplace_max_time - world.time)/600, 0.5)] minutes.</font></span>"
|
||||
src << "<span class='big'><font color=\"#EE4000\">You [manualplace_min_time ? "will be able to":"can"] manually place your blob core by pressing the button in the bottom right corner of the screen.</font></span>"
|
||||
src << "<span class='big'><font color=\"#EE4000\">You [manualplace_min_time ? "will be able to":"can"] manually place your blob core by pressing the Place Blob Core button in the bottom right corner of the screen.</font></span>"
|
||||
|
||||
@@ -7,12 +7,13 @@
|
||||
density = 0 //this being 0 causes two bugs, being able to attack blob tiles behind other blobs and being unable to move on blob tiles in no gravity, but turning it to 1 causes the blob mobs to be unable to path through blobs, which is probably worse.
|
||||
opacity = 0
|
||||
anchored = 1
|
||||
explosion_block = 1
|
||||
layer = BELOW_MOB_LAYER
|
||||
var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed.
|
||||
var/health = 30
|
||||
var/maxhealth = 30
|
||||
var/health_regen = 2 //how much health this blob regens when pulsed
|
||||
var/pulse_timestamp = 0 //we got pulsed/healed when?
|
||||
var/pulse_timestamp = 0 //we got pulsed when?
|
||||
var/heal_timestamp = 0 //we got healed when?
|
||||
var/brute_resist = 0.5 //multiplies brute damage by this
|
||||
var/fire_resist = 1 //multiplies burn damage by this
|
||||
var/atmosblock = 0 //if the blob blocks atmos and heat spread
|
||||
@@ -24,8 +25,8 @@
|
||||
if(Ablob.blob_allowed) //Is this area allowed for winning as blob?
|
||||
blobs_legit += src
|
||||
blobs += src //Keep track of the blob in the normal list either way
|
||||
src.setDir(pick(1, 2, 4, 8))
|
||||
src.update_icon()
|
||||
setDir(pick(cardinal))
|
||||
update_icon()
|
||||
..(loc)
|
||||
ConsumeTile()
|
||||
if(atmosblock)
|
||||
@@ -84,46 +85,56 @@
|
||||
if(overmind)
|
||||
overmind.blob_reagent_datum.death_reaction(src, cause)
|
||||
qdel(src) //we dead now
|
||||
return
|
||||
return
|
||||
|
||||
/obj/effect/blob/update_icon() //Updates color based on overmind color if we have an overmind.
|
||||
if(overmind)
|
||||
color = overmind.blob_reagent_datum.color
|
||||
else
|
||||
color = null
|
||||
return
|
||||
|
||||
|
||||
/obj/effect/blob/process()
|
||||
Life()
|
||||
return
|
||||
|
||||
/obj/effect/blob/proc/Life()
|
||||
return
|
||||
|
||||
/obj/effect/blob/proc/Pulse_Area(pulsing_overmind = overmind, claim_range = 10, pulse_range = 3, expand_range = 2)
|
||||
src.Be_Pulsed()
|
||||
if(claim_range)
|
||||
for(var/obj/effect/blob/B in urange(claim_range, src, 1))
|
||||
if(!B.overmind && !istype(B, /obj/effect/blob/core) && prob(30))
|
||||
B.overmind = pulsing_overmind //reclaim unclaimed, non-core blobs.
|
||||
B.update_icon()
|
||||
if(pulse_range)
|
||||
for(var/obj/effect/blob/B in orange(pulse_range, src))
|
||||
var/expanded = FALSE
|
||||
if(prob(70) && expand())
|
||||
expanded = TRUE
|
||||
var/list/blobs_to_affect = list()
|
||||
for(var/obj/effect/blob/B in urange(claim_range, src, 1))
|
||||
blobs_to_affect += B
|
||||
shuffle(blobs_to_affect)
|
||||
for(var/L in blobs_to_affect)
|
||||
var/obj/effect/blob/B = L
|
||||
if(!B.overmind && !istype(B, /obj/effect/blob/core) && prob(30))
|
||||
B.overmind = pulsing_overmind //reclaim unclaimed, non-core blobs.
|
||||
B.update_icon()
|
||||
var/distance = get_dist(get_turf(src), get_turf(B))
|
||||
var/expand_probablity = max(20 - distance * 8, 1)
|
||||
if(B.Adjacent(src))
|
||||
expand_probablity = 20
|
||||
if(distance <= expand_range)
|
||||
var/can_expand = TRUE
|
||||
if(blobs_to_affect.len >= 120 && B.heal_timestamp > world.time)
|
||||
can_expand = FALSE
|
||||
if(can_expand && B.pulse_timestamp <= world.time && prob(expand_probablity))
|
||||
var/obj/effect/blob/newB = B.expand(null, null, !expanded) //expansion falls off with range but is faster near the blob causing the expansion
|
||||
if(newB)
|
||||
if(expanded)
|
||||
qdel(newB)
|
||||
expanded = TRUE
|
||||
if(distance <= pulse_range)
|
||||
B.Be_Pulsed()
|
||||
if(expand_range)
|
||||
if(prob(85))
|
||||
src.expand()
|
||||
for(var/obj/effect/blob/B in orange(expand_range, src))
|
||||
if(prob(max(13 - get_dist(get_turf(src), get_turf(B)) * 4, 1))) //expand falls off with range but is faster near the blob causing the expansion
|
||||
B.expand()
|
||||
return
|
||||
|
||||
/obj/effect/blob/proc/Be_Pulsed()
|
||||
if(pulse_timestamp <= world.time)
|
||||
ConsumeTile()
|
||||
health = min(maxhealth, health+health_regen)
|
||||
if(heal_timestamp <= world.time)
|
||||
health = min(maxhealth, health+health_regen)
|
||||
heal_timestamp = world.time + 20
|
||||
update_icon()
|
||||
pulse_timestamp = world.time + 10
|
||||
return 1 //we did it, we were pulsed!
|
||||
@@ -137,6 +148,7 @@
|
||||
|
||||
/obj/effect/blob/proc/blob_attack_animation(atom/A = null, controller) //visually attacks an atom
|
||||
var/obj/effect/overlay/temp/blob/O = PoolOrNew(/obj/effect/overlay/temp/blob, src.loc)
|
||||
O.setDir(dir)
|
||||
if(controller)
|
||||
var/mob/camera/blob/BO = controller
|
||||
O.color = BO.blob_reagent_datum.color
|
||||
@@ -311,6 +323,7 @@
|
||||
B.overmind = controller
|
||||
B.creation_action()
|
||||
B.update_icon()
|
||||
B.setDir(dir)
|
||||
qdel(src)
|
||||
return B
|
||||
|
||||
@@ -342,13 +355,13 @@
|
||||
brute_resist = 0.25
|
||||
|
||||
/obj/effect/blob/normal/scannerreport()
|
||||
if(health <= 10)
|
||||
if(health <= 15)
|
||||
return "Currently weak to brute damage."
|
||||
return "N/A"
|
||||
|
||||
/obj/effect/blob/normal/update_icon()
|
||||
..()
|
||||
if(health <= 10)
|
||||
if(health <= 15)
|
||||
icon_state = "blob_damaged"
|
||||
name = "fragile blob"
|
||||
desc = "A thin lattice of slightly twitching tendrils."
|
||||
|
||||
@@ -16,7 +16,6 @@ Components, which are fallen fragments of Ratvar's body as he rusts in Reebe, ar
|
||||
Game-wise, clockwork slabs will generate components over time, with more powerful components being slower.
|
||||
|
||||
This file's folder contains:
|
||||
__clock_defines.dm: Defined variables
|
||||
clock_cult.dm: Core gamemode files.
|
||||
clock_items.dm: Items (excluding the proselytizer).
|
||||
clock_machines.dm: Machinery like the mending motor.
|
||||
@@ -27,6 +26,7 @@ This file's folder contains:
|
||||
clock_structures.dm: Structures and effects
|
||||
clock_unsorted.dm: Anything else with no place to be
|
||||
|
||||
clockcult defines are in __DEFINES/clockcult.dm
|
||||
*/
|
||||
|
||||
///////////
|
||||
@@ -183,7 +183,7 @@ This file's folder contains:
|
||||
var/list/servants_of_ratvar = list() //The Enlightened servants of Ratvar
|
||||
var/required_escapees = 0 //How many servants need to escape, if applicable
|
||||
var/required_silicon_converts = 0 //How many robotic lifeforms need to be converted, if applicable
|
||||
var/clockwork_objective = "gateway" //The objective that the servants must fulfill
|
||||
var/clockwork_objective = CLOCKCULT_GATEWAY //The objective that the servants must fulfill
|
||||
var/clockwork_explanation = "Construct a Gateway to the Celestial Derelict and free Ratvar." //The description of the current objective
|
||||
|
||||
/datum/game_mode/clockwork_cult
|
||||
@@ -237,20 +237,20 @@ This file's folder contains:
|
||||
return 1
|
||||
|
||||
/datum/game_mode/clockwork_cult/proc/forge_clock_objectives() //Determine what objective that Ratvar's servants will fulfill
|
||||
var/list/possible_objectives = list("escape", "gateway")
|
||||
var/list/possible_objectives = list(CLOCKCULT_ESCAPE, CLOCKCULT_GATEWAY)
|
||||
var/silicons_possible = FALSE
|
||||
for(var/mob/living/silicon/S in living_mob_list)
|
||||
silicons_possible = TRUE
|
||||
if(silicons_possible)
|
||||
possible_objectives += "silicons"
|
||||
possible_objectives += CLOCKCULT_SILICONS
|
||||
clockwork_objective = pick(possible_objectives)
|
||||
switch(clockwork_objective)
|
||||
if("escape")
|
||||
if(CLOCKCULT_ESCAPE)
|
||||
required_escapees = round(max(1, roundstart_player_count / 3)) //33% of the player count must be cultists
|
||||
clockwork_explanation = "Ensure that [required_escapees] servants of Ratvar escape from [station_name()]."
|
||||
if("gateway")
|
||||
if(CLOCKCULT_GATEWAY)
|
||||
clockwork_explanation = "Construct a Gateway to the Celestial Derelict and free Ratvar."
|
||||
if("silicons")
|
||||
if(CLOCKCULT_SILICONS)
|
||||
clockwork_explanation = "Ensure that all active silicon-based lifeforms on [station_name()] are servants of Ratvar by the end of the shift."
|
||||
return 1
|
||||
|
||||
@@ -291,16 +291,16 @@ This file's folder contains:
|
||||
|
||||
/datum/game_mode/clockwork_cult/proc/check_clockwork_victory()
|
||||
switch(clockwork_objective)
|
||||
if("escape")
|
||||
if(CLOCKCULT_ESCAPE)
|
||||
var/surviving_servants = 0
|
||||
for(var/datum/mind/M in servants_of_ratvar)
|
||||
if(M.current && M.current.stat != DEAD && (M.current.onCentcom() || M.current.onSyndieBase()))
|
||||
surviving_servants++
|
||||
clockwork_explanation = "Ensure that [required_escapees] servant(s) of Ratvar escape from [station_name()]. <i><b>[surviving_servants]</b> managed to escape!</i>"
|
||||
clockwork_explanation = "Ensure that [required_escapees] servant(s) of Ratvar escape from [station_name()].<br><i><b>[surviving_servants]</b> managed to escape!</i>"
|
||||
if(surviving_servants >= required_escapees)
|
||||
return TRUE
|
||||
return FALSE
|
||||
if("silicons")
|
||||
if(CLOCKCULT_SILICONS)
|
||||
var/total_silicons = 0
|
||||
var/valid_silicons = 0
|
||||
var/successful = TRUE
|
||||
@@ -314,10 +314,10 @@ This file's folder contains:
|
||||
valid_silicons++
|
||||
if(valid_silicons < total_silicons)
|
||||
successful = FALSE
|
||||
clockwork_explanation = "Ensure that all active silicon-based lifeforms on [station_name()] are servants of Ratvar by the end of the shift. \
|
||||
clockwork_explanation = "Ensure that all active silicon-based lifeforms on [station_name()] are servants of Ratvar by the end of the shift.<br>\
|
||||
<i><b>[valid_silicons]/[total_silicons]</b> silicons were killed or converted!</i>"
|
||||
return successful
|
||||
if("gateway")
|
||||
if(CLOCKCULT_GATEWAY)
|
||||
return ratvar_awakens
|
||||
return FALSE //This shouldn't ever be reached, but just in case it is
|
||||
|
||||
@@ -334,7 +334,7 @@ This file's folder contains:
|
||||
feedback_set_details("round_end_result", "win - servants completed their objective ([clockwork_objective])")
|
||||
else
|
||||
var/half_victory = FALSE
|
||||
if(clockwork_objective == "gateway")
|
||||
if(clockwork_objective == CLOCKCULT_GATEWAY)
|
||||
var/obj/structure/clockwork/massive/celestial_gateway/G = locate() in all_clockwork_objects
|
||||
if(G)
|
||||
half_victory = TRUE
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
poi_list -= src
|
||||
return ..()
|
||||
|
||||
/obj/structure/clockwork/massive/singularity_pull(S, current_size)
|
||||
return
|
||||
|
||||
/obj/structure/clockwork/massive/celestial_gateway //The gateway to Reebe, from which Ratvar emerges
|
||||
name = "Gateway to the Celestial Derelict"
|
||||
desc = "A massive, thrumming rip in spacetime."
|
||||
@@ -29,6 +32,7 @@
|
||||
var/first_sound_played = FALSE
|
||||
var/second_sound_played = FALSE
|
||||
var/third_sound_played = FALSE
|
||||
var/ratvar_portal = TRUE //if the gateway actually summons ratvar or just produces a hugeass conversion burst
|
||||
var/obj/effect/clockwork/overlay/gateway_glow/glow
|
||||
var/obj/effect/countdown/clockworkgate/countdown
|
||||
|
||||
@@ -120,23 +124,48 @@
|
||||
sleep(125)
|
||||
make_glow()
|
||||
animate(glow, transform = matrix() * 3, alpha = 0, time = 5)
|
||||
sleep(5)
|
||||
new/obj/structure/clockwork/massive/ratvar(get_turf(src))
|
||||
qdel(src)
|
||||
var/turf/startpoint = get_turf(src)
|
||||
sleep(3)
|
||||
QDEL_IN(src, 3)
|
||||
clockwork_gateway_activated = TRUE
|
||||
if(ratvar_portal)
|
||||
new/obj/structure/clockwork/massive/ratvar(startpoint)
|
||||
else
|
||||
world << "<span class='ratvar'>\"[text2ratvar("Behold")]!\"</span>\n<span class='inathneq_large'>\"[text2ratvar("See Engine's mercy")]!\"</span>\n\
|
||||
<span class='sevtug_large'>\"[text2ratvar("Observe Engine's design skills")]!\"</span>\n<span class='nezbere_large'>\"[text2ratvar("Behold Engine's light")]!!\"</span>\n\
|
||||
<span class='nzcrentr_large'>\"[text2ratvar("Gaze upon Engine's power")]!\"</span>"
|
||||
world << 'sound/magic/clockwork/invoke_general.ogg'
|
||||
var/x0 = startpoint.x
|
||||
var/y0 = startpoint.y
|
||||
for(var/I in spiral_range_turfs(255, startpoint))
|
||||
var/turf/T = I
|
||||
if(!T)
|
||||
continue
|
||||
var/dist = cheap_hypotenuse(T.x, T.y, x0, y0)
|
||||
if(dist < 60)
|
||||
dist = TRUE
|
||||
else
|
||||
dist = FALSE
|
||||
T.ratvar_act(dist)
|
||||
CHECK_TICK
|
||||
for(var/I in all_clockwork_mobs)
|
||||
var/mob/M = I
|
||||
if(M.stat == CONSCIOUS)
|
||||
clockwork_say(M, text2ratvar(pick("Purge all untruths and honor Engine!", "All glory to Engine's light!", "Engine's power is unmatched!")))
|
||||
|
||||
/obj/structure/clockwork/massive/celestial_gateway/examine(mob/user)
|
||||
icon_state = "spatial_gateway" //cheat wildly by pretending to have an icon
|
||||
..()
|
||||
icon_state = initial(icon_state)
|
||||
if(is_servant_of_ratvar(user) || isobserver(user))
|
||||
user << "<span class='big'><b>Seconds until Ratvar's arrival:</b> [get_arrival_text(TRUE)]</span>"
|
||||
user << "<span class='big'><b>Seconds until [ratvar_portal ? "Ratvar's arrival":"Proselytization"]:</b> [get_arrival_text(TRUE)]</span>"
|
||||
switch(progress_in_seconds)
|
||||
if(-INFINITY to GATEWAY_REEBE_FOUND)
|
||||
user << "<span class='heavy_brass'>It's still opening.</span>"
|
||||
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
|
||||
user << "<span class='heavy_brass'>It's reached the Celestial Derelict and is drawing power from it.</span>"
|
||||
if(GATEWAY_RATVAR_COMING to INFINITY)
|
||||
user << "<span class='heavy_brass'>Ratvar is coming through the gateway!</span>"
|
||||
user << "<span class='heavy_brass'>[ratvar_portal ? "Ratvar is coming through the gateway":"The gateway is glowing with massed power"]!</span>"
|
||||
else
|
||||
switch(progress_in_seconds)
|
||||
if(-INFINITY to GATEWAY_REEBE_FOUND)
|
||||
@@ -144,7 +173,7 @@
|
||||
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
|
||||
user << "<span class='warning'>It seems to be leading somewhere.</span>"
|
||||
if(GATEWAY_RATVAR_COMING to INFINITY)
|
||||
user << "<span class='warning'><b>Something is coming through!</b></span>"
|
||||
user << "<span class='boldwarning'>[ratvar_portal ? "Something is coming through":"It's glowing brightly"]!</span>"
|
||||
|
||||
/obj/effect/clockwork/overlay/gateway_glow //the actual appearance of the Gateway to the Celestial Derelict; an object so the edges of the gate can be clicked through.
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
@@ -170,11 +199,11 @@
|
||||
|
||||
/obj/structure/clockwork/massive/ratvar/New()
|
||||
..()
|
||||
ratvar_awakens = TRUE
|
||||
ratvar_awakens++
|
||||
for(var/obj/item/clockwork/ratvarian_spear/R in all_clockwork_objects)
|
||||
R.update_force()
|
||||
START_PROCESSING(SSobj, src)
|
||||
world << "<span class='heavy_brass'><font size=6>\"[text2ratvar("ONCE AGAIN MY LIGHT SHALL SHINE ACROSS THIS PATHETIC REALM")]!!\"</font></span>"
|
||||
world << "<span class='ratvar'>\"[text2ratvar("ONCE AGAIN MY LIGHT SHALL SHINE ACROSS THIS PATHETIC REALM")]!!\"</span>"
|
||||
world << 'sound/effects/ratvar_reveal.ogg'
|
||||
var/image/alert_overlay = image('icons/effects/clockwork_effects.dmi', "ratvar_alert")
|
||||
var/area/A = get_area(src)
|
||||
@@ -183,7 +212,7 @@
|
||||
|
||||
|
||||
/obj/structure/clockwork/massive/ratvar/Destroy()
|
||||
ratvar_awakens = FALSE
|
||||
ratvar_awakens--
|
||||
for(var/obj/item/clockwork/ratvarian_spear/R in all_clockwork_objects)
|
||||
R.update_force()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
@@ -201,8 +230,9 @@
|
||||
|
||||
|
||||
/obj/structure/clockwork/massive/ratvar/Bump(atom/A)
|
||||
forceMove(get_turf(A))
|
||||
A.ratvar_act()
|
||||
var/turf/T = get_turf(A)
|
||||
forceMove(T)
|
||||
T.ratvar_act()
|
||||
|
||||
|
||||
/obj/structure/clockwork/massive/ratvar/Process_Spacemove()
|
||||
@@ -212,8 +242,12 @@
|
||||
/obj/structure/clockwork/massive/ratvar/process()
|
||||
if(clashing) //I'm a bit occupied right now, thanks
|
||||
return
|
||||
for(var/atom/A in range(proselytize_range, src))
|
||||
A.ratvar_act()
|
||||
for(var/I in circlerangeturfs(src, proselytize_range))
|
||||
var/turf/T = I
|
||||
T.ratvar_act()
|
||||
for(var/I in circleviewturfs(src, round(proselytize_range * 0.5)))
|
||||
var/turf/T = I
|
||||
T.ratvar_act(1)
|
||||
var/dir_to_step_in = pick(cardinal)
|
||||
if(!prey)
|
||||
for(var/obj/singularity/narsie/N in poi_list)
|
||||
|
||||
@@ -1226,7 +1226,7 @@ Judgement: 10 servants, 100 CV, and any existing AIs are converted or destroyed
|
||||
|
||||
|
||||
/datum/clockwork_scripture/ark_of_the_clockwork_justiciar //Ark of the Clockwork Justiciar: Creates a Gateway to the Celestial Derelict.
|
||||
descname = "Summon Ratvar"
|
||||
descname = "Win Condition"
|
||||
name = "Ark of the Clockwork Justiciar"
|
||||
desc = "Pulls from the power of all of Ratvar's servants and generals to construct a massive machine used to tear apart a rift in spacetime to the Celestial Derelict. This gateway will \
|
||||
call forth Ratvar from his exile after some time."
|
||||
@@ -1236,55 +1236,83 @@ Judgement: 10 servants, 100 CV, and any existing AIs are converted or destroyed
|
||||
channel_time = 150
|
||||
required_components = list("belligerent_eye" = 10, "vanguard_cogwheel" = 10, "guvax_capacitor" = 10, "replicant_alloy" = 10, "hierophant_ansible" = 10)
|
||||
consumed_components = list("belligerent_eye" = 10, "vanguard_cogwheel" = 10, "guvax_capacitor" = 10, "replicant_alloy" = 10, "hierophant_ansible" = 10)
|
||||
invokers_required = 4
|
||||
invokers_required = 5
|
||||
multiple_invokers_used = TRUE
|
||||
usage_tip = "The gateway is completely vulnerable to attack during its five-minute duration. In addition to preventing shuttle departure, it will periodically give indication of its general \
|
||||
position to everyone on the station as well as being loud enough to be heard throughout the entire sector. Defend it with your life!"
|
||||
usage_tip = "The gateway is completely vulnerable to attack during its five-minute duration. It will periodically give indication of its general position to everyone on the station \
|
||||
as well as being loud enough to be heard throughout the entire sector. Defend it with your life!"
|
||||
tier = SCRIPTURE_JUDGEMENT
|
||||
|
||||
/datum/clockwork_scripture/ark_of_the_clockwork_justiciar/New()
|
||||
if(ticker && ticker.mode && ticker.mode.clockwork_objective != CLOCKCULT_GATEWAY)
|
||||
invocations = list("ARMORER! FRIGHT! AMPERAGE! VANGUARD! I CALL UPON YOU!!", \
|
||||
"THIS STATION WILL BE A BEACON OF HOPE IN THE DARKNESS OF SPACE!!", \
|
||||
"HELP US MAKE THIS SHOW ENGINE'S GLORY!!")
|
||||
..()
|
||||
|
||||
/datum/clockwork_scripture/ark_of_the_clockwork_justiciar/check_special_requirements()
|
||||
if(!slab.no_cost && ratvar_awakens)
|
||||
invoker << "<span class='big_brass'>\"I am already here, idiot.\"</span>"
|
||||
return 0
|
||||
for(var/obj/structure/clockwork/massive/celestial_gateway/G in all_clockwork_objects)
|
||||
var/area/gate_area = get_area(G)
|
||||
invoker << "<span class='userdanger'>There is already a gateway at [gate_area.map_name]!</span>"
|
||||
return 0
|
||||
var/area/A = get_area(invoker)
|
||||
if(!slab.no_cost && (invoker.z != ZLEVEL_STATION || istype(A, /area/shuttle)))
|
||||
invoker << "<span class='warning'>You must be on the station to activate the Ark!</span>"
|
||||
return 0
|
||||
if(!slab.no_cost && ticker.mode.clockwork_objective != "gateway")
|
||||
invoker << "<span class='warning'>As painful as it is, Ratvar's will is not to be freed!</span>"
|
||||
return 0
|
||||
if(!slab.no_cost)
|
||||
if(ratvar_awakens)
|
||||
invoker << "<span class='big_brass'>\"I am already here, idiot.\"</span>"
|
||||
return 0
|
||||
for(var/obj/structure/clockwork/massive/celestial_gateway/G in all_clockwork_objects)
|
||||
var/area/gate_area = get_area(G)
|
||||
invoker << "<span class='userdanger'>There is already a gateway at [gate_area.map_name]!</span>"
|
||||
return 0
|
||||
var/area/A = get_area(invoker)
|
||||
var/turf/T = get_turf(invoker)
|
||||
if(!T || T.z != ZLEVEL_STATION || istype(A, /area/shuttle))
|
||||
invoker << "<span class='warning'>You must be on the station to activate the Ark!</span>"
|
||||
return 0
|
||||
if(clockwork_gateway_activated)
|
||||
if(ticker && ticker.mode && ticker.mode.clockwork_objective != CLOCKCULT_GATEWAY)
|
||||
invoker << "<span class='nezbere'>\"Look upon his works. Is it not glorious?\"</span>"
|
||||
else
|
||||
invoker << "<span class='warning'>Ratvar's recent banishment renders him too weak to be wrung forth from Reebe!</span>"
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/datum/clockwork_scripture/ark_of_the_clockwork_justiciar/scripture_effects()
|
||||
var/turf/T = get_turf(invoker)
|
||||
new/obj/effect/clockwork/general_marker/inathneq(T)
|
||||
T.visible_message("<span class='inathneq'>\"[text2ratvar("Engine, come forth and show your servants your mercy!")]\"</span>")
|
||||
if(ticker && ticker.mode && ticker.mode.clockwork_objective == CLOCKCULT_GATEWAY)
|
||||
T.visible_message("<span class='inathneq'>\"[text2ratvar("Engine, come forth and show your servants your mercy!")]\"</span>")
|
||||
else
|
||||
T.visible_message("<span class='inathneq'>\"[text2ratvar("We will show all the mercy of Engine!")]\"</span>")
|
||||
playsound(T, 'sound/magic/clockwork/invoke_general.ogg', 30, 0)
|
||||
sleep(10)
|
||||
if(!check_special_requirements())
|
||||
return 0
|
||||
new/obj/effect/clockwork/general_marker/sevtug(T)
|
||||
T.visible_message("<span class='sevtug'>\"[text2ratvar("Engine, come forth and show this station your decorating skills!")]\"</span>")
|
||||
if(ticker && ticker.mode && ticker.mode.clockwork_objective == CLOCKCULT_GATEWAY)
|
||||
T.visible_message("<span class='sevtug'>\"[text2ratvar("Engine, come forth and show this station your decorating skills!")]\"</span>")
|
||||
else
|
||||
T.visible_message("<span class='sevtug'>\"[text2ratvar("We will show all Engine's decorating skills.")]\"</span>")
|
||||
playsound(T, 'sound/magic/clockwork/invoke_general.ogg', 45, 0)
|
||||
sleep(10)
|
||||
if(!check_special_requirements())
|
||||
return 0
|
||||
new/obj/effect/clockwork/general_marker/nezbere(T)
|
||||
T.visible_message("<span class='nezbere'>\"[text2ratvar("Engine, come forth and shine your light across this realm!")]\"</span>")
|
||||
if(ticker && ticker.mode && ticker.mode.clockwork_objective == CLOCKCULT_GATEWAY)
|
||||
T.visible_message("<span class='nezbere'>\"[text2ratvar("Engine, come forth and shine your light across this realm!!")]\"</span>")
|
||||
else
|
||||
T.visible_message("<span class='nezbere'>\"[text2ratvar("We will show all Engine's light!!")]\"</span>")
|
||||
playsound(T, 'sound/magic/clockwork/invoke_general.ogg', 60, 0)
|
||||
sleep(10)
|
||||
if(!check_special_requirements())
|
||||
return 0
|
||||
new/obj/effect/clockwork/general_marker/nzcrentr(T)
|
||||
T.visible_message("<span class='nzcrentr'>\"[text2ratvar("Engine, come forth.")]\"</span>")
|
||||
if(ticker && ticker.mode && ticker.mode.clockwork_objective == CLOCKCULT_GATEWAY)
|
||||
T.visible_message("<span class='nzcrentr'>\"[text2ratvar("Engine, come forth.")]\"</span>")
|
||||
else
|
||||
T.visible_message("<span class='nezbere'>\"[text2ratvar("We will show all Engine's power!")]\"</span>")
|
||||
playsound(T, 'sound/magic/clockwork/invoke_general.ogg', 75, 0)
|
||||
sleep(10)
|
||||
if(check_special_requirements())
|
||||
new/obj/structure/clockwork/massive/celestial_gateway(T)
|
||||
var/obj/structure/clockwork/massive/celestial_gateway/CG = new/obj/structure/clockwork/massive/celestial_gateway(T)
|
||||
if(ticker && ticker.mode && ticker.mode.clockwork_objective != CLOCKCULT_GATEWAY)
|
||||
CG.ratvar_portal = FALSE
|
||||
hierophant_message("<span class='big_brass'>This newly constructed gateway will not free Ratvar, \
|
||||
and will instead simply proselytize and convert everything and everyone on the station.</span>", TRUE)
|
||||
playsound(T, 'sound/magic/clockwork/invoke_general.ogg', 100, 0)
|
||||
var/list/open_turfs = list()
|
||||
for(var/turf/open/OT in orange(1, T))
|
||||
|
||||
@@ -653,6 +653,9 @@
|
||||
/obj/effect/clockwork/overlay/ex_act()
|
||||
return FALSE
|
||||
|
||||
/obj/effect/clockwork/overlay/singularity_pull(S, current_size)
|
||||
return
|
||||
|
||||
/obj/effect/clockwork/overlay/Destroy()
|
||||
if(linked)
|
||||
linked = null
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
if(!candidates.len)
|
||||
return NOT_ENOUGH_PLAYERS
|
||||
|
||||
var/mob/dead/selected = popleft(candidates)
|
||||
var/mob/dead/selected = pick_n_take(candidates)
|
||||
|
||||
var/datum/mind/player_mind = new /datum/mind(selected.key)
|
||||
player_mind.active = 1
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
var/list/drained_mobs = list() //Cannot harvest the same mob twice
|
||||
var/perfectsouls = 0 //How many perfect, regen-cap increasing souls the revenant has. //TODO, add objective for getting a perfect soul(s?)
|
||||
var/image/ghostimage = null //Visible to ghost with darkness off
|
||||
var/generated_objectives_and_spells = FALSE
|
||||
|
||||
/mob/living/simple_animal/revenant/New()
|
||||
..()
|
||||
@@ -68,28 +69,31 @@
|
||||
ghost_darkness_images |= ghostimage
|
||||
updateallghostimages()
|
||||
|
||||
spawn(5)
|
||||
if(src.mind)
|
||||
src.mind.remove_all_antag()
|
||||
src.mind.wipe_memory()
|
||||
src << 'sound/effects/ghost.ogg'
|
||||
src << "<br>"
|
||||
src << "<span class='deadsay'><font size=3><b>You are a revenant.</b></font></span>"
|
||||
src << "<b>Your formerly mundane spirit has been infused with alien energies and empowered into a revenant.</b>"
|
||||
src << "<b>You are not dead, not alive, but somewhere in between. You are capable of limited interaction with both worlds.</b>"
|
||||
src << "<b>You are invincible and invisible to everyone but other ghosts. Most abilities will reveal you, rendering you vulnerable.</b>"
|
||||
src << "<b>To function, you are to drain the life essence from humans. This essence is a resource, as well as your health, and will power all of your abilities.</b>"
|
||||
src << "<b><i>You do not remember anything of your past lives, nor will you remember anything about this one after your death.</i></b>"
|
||||
src << "<b>Be sure to read the wiki page at https://tgstation13.org/wiki/Revenant to learn more.</b>"
|
||||
var/datum/objective/revenant/objective = new
|
||||
objective.owner = src.mind
|
||||
src.mind.objectives += objective
|
||||
src << "<b>Objective #1</b>: [objective.explanation_text]"
|
||||
var/datum/objective/revenantFluff/objective2 = new
|
||||
objective2.owner = src.mind
|
||||
src.mind.objectives += objective2
|
||||
src << "<b>Objective #2</b>: [objective2.explanation_text]"
|
||||
ticker.mode.traitors |= src.mind //Necessary for announcing
|
||||
/mob/living/simple_animal/revenant/Login()
|
||||
..()
|
||||
src << "<span class='deadsay'><font size=3><b>You are a revenant.</b></font></span>"
|
||||
src << "<b>Your formerly mundane spirit has been infused with alien energies and empowered into a revenant.</b>"
|
||||
src << "<b>You are not dead, not alive, but somewhere in between. You are capable of limited interaction with both worlds.</b>"
|
||||
src << "<b>You are invincible and invisible to everyone but other ghosts. Most abilities will reveal you, rendering you vulnerable.</b>"
|
||||
src << "<b>To function, you are to drain the life essence from humans. This essence is a resource, as well as your health, and will power all of your abilities.</b>"
|
||||
src << "<b><i>You do not remember anything of your past lives, nor will you remember anything about this one after your death.</i></b>"
|
||||
src << "<b>Be sure to read the wiki page at https://tgstation13.org/wiki/Revenant to learn more.</b>"
|
||||
if(!generated_objectives_and_spells)
|
||||
generated_objectives_and_spells = TRUE
|
||||
mind.remove_all_antag()
|
||||
mind.wipe_memory()
|
||||
src << 'sound/effects/ghost.ogg'
|
||||
var/datum/objective/revenant/objective = new
|
||||
objective.owner = mind
|
||||
mind.objectives += objective
|
||||
src << "<b>Objective #1</b>: [objective.explanation_text]"
|
||||
var/datum/objective/revenantFluff/objective2 = new
|
||||
objective2.owner = mind
|
||||
mind.objectives += objective2
|
||||
src << "<b>Objective #2</b>: [objective2.explanation_text]"
|
||||
mind.assigned_role = "revenant"
|
||||
mind.special_role = "Revenant"
|
||||
ticker.mode.traitors |= mind //Necessary for announcing
|
||||
AddSpell(new /obj/effect/proc_holder/spell/targeted/night_vision/revenant(null))
|
||||
AddSpell(new /obj/effect/proc_holder/spell/targeted/revenant_transmit(null))
|
||||
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/defile(null))
|
||||
@@ -97,7 +101,6 @@
|
||||
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/blight(null))
|
||||
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/malfunction(null))
|
||||
|
||||
|
||||
//Life, Stat, Hud Updates, and Say
|
||||
/mob/living/simple_animal/revenant/Life()
|
||||
if(revealed && essence <= 0)
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
|
||||
|
||||
/datum/round_event/ghost_role/revenant
|
||||
var/force_spawn
|
||||
var/ignore_mobcheck = FALSE
|
||||
role_name = "revenant"
|
||||
|
||||
/datum/round_event/ghost_role/revenant/New(my_force_spawn = FALSE)
|
||||
/datum/round_event/ghost_role/revenant/New(my_processing = TRUE, new_ignore_mobcheck = FALSE)
|
||||
..()
|
||||
force_spawn = my_force_spawn
|
||||
ignore_mobcheck = new_ignore_mobcheck
|
||||
|
||||
/datum/round_event/ghost_role/revenant/spawn_role()
|
||||
if(!force_spawn)
|
||||
if(!ignore_mobcheck)
|
||||
var/deadMobs = 0
|
||||
for(var/mob/M in dead_mob_list)
|
||||
deadMobs++
|
||||
@@ -26,14 +26,12 @@
|
||||
message_admins("Event attempted to spawn a revenant, but there were only [deadMobs]/[REVENANT_SPAWN_THRESHOLD] dead mobs.")
|
||||
return WAITING_FOR_SOMETHING
|
||||
|
||||
var/list/mob/dead/observer/candidates = get_candidates("revenant", null, ROLE_REVENANT)
|
||||
var/list/candidates = get_candidates("revenant", null, ROLE_REVENANT)
|
||||
if(!candidates.len)
|
||||
return NOT_ENOUGH_PLAYERS
|
||||
|
||||
var/mob/dead/observer/selected = popleft(candidates)
|
||||
var/mob/dead/observer/selected = pick_n_take(candidates)
|
||||
|
||||
var/datum/mind/player_mind = new /datum/mind(selected.key)
|
||||
player_mind.active = 1
|
||||
var/list/spawn_locs = list()
|
||||
|
||||
for(var/obj/effect/landmark/L in landmarks_list)
|
||||
@@ -48,16 +46,13 @@
|
||||
if("carpspawn")
|
||||
spawn_locs += L.loc
|
||||
if(!spawn_locs) //If we can't find either, just spawn the revenant at the player's location
|
||||
spawn_locs += get_turf(player_mind.current)
|
||||
spawn_locs += get_turf(selected)
|
||||
if(!spawn_locs) //If we can't find THAT, then just give up and cry
|
||||
return MAP_ERROR
|
||||
|
||||
var/mob/living/simple_animal/revenant/revvie = new /mob/living/simple_animal/revenant/(pick(spawn_locs))
|
||||
player_mind.transfer_to(revvie)
|
||||
player_mind.assigned_role = "revenant"
|
||||
player_mind.special_role = "Revenant"
|
||||
ticker.mode.traitors |= player_mind
|
||||
message_admins("[player_mind.key] has been made into a revenant by an event.")
|
||||
log_game("[player_mind.key] was spawned as a revenant by an event.")
|
||||
revvie.key = selected.key
|
||||
message_admins("[revvie.key] has been made into a revenant by an event.")
|
||||
log_game("[revvie.key] was spawned as a revenant by an event.")
|
||||
spawned_mobs += revvie
|
||||
return SUCCESSFUL_SPAWN
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
if(!candidates.len)
|
||||
return NOT_ENOUGH_PLAYERS
|
||||
|
||||
var/mob/dead/selected = popleft(candidates)
|
||||
var/mob/dead/selected = pick_n_take(candidates)
|
||||
|
||||
var/datum/mind/player_mind = new /datum/mind(selected.key)
|
||||
player_mind.active = 1
|
||||
|
||||
@@ -89,14 +89,6 @@
|
||||
open_machine()
|
||||
..(severity)
|
||||
|
||||
/obj/machinery/sleeper/blob_act(obj/effect/blob/B)
|
||||
if(prob(75))
|
||||
var/turf/T = get_turf(src)
|
||||
for(var/atom/movable/A in src)
|
||||
A.forceMove(T)
|
||||
A.blob_act(B)
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/sleeper/MouseDrop_T(mob/target, mob/user)
|
||||
if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser())
|
||||
return
|
||||
|
||||
@@ -106,6 +106,10 @@
|
||||
..()
|
||||
|
||||
|
||||
/obj/machinery/camera/blob_act(obj/effect/blob/B)
|
||||
if(B && B.loc == loc)
|
||||
take_damage(health, BRUTE, 0)
|
||||
|
||||
/obj/machinery/camera/ex_act(severity, target)
|
||||
if(src.invuln)
|
||||
return
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
take_damage(rand(10,30), BRUTE, 0)
|
||||
|
||||
/obj/machinery/computer/ratvar_act()
|
||||
if(!clockwork && prob(20))
|
||||
if(!clockwork)
|
||||
clockwork = TRUE
|
||||
icon_screen = "ratvar[rand(1, 4)]"
|
||||
icon_keyboard = "ratvar_key[rand(1, 6)]"
|
||||
|
||||
@@ -156,12 +156,11 @@ var/list/airlock_overlays = list()
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/door/airlock/ratvar_act() //Airlocks become pinion airlocks that only allow servants
|
||||
if(prob(20))
|
||||
if(glass)
|
||||
new/obj/machinery/door/airlock/clockwork/brass(get_turf(src))
|
||||
else
|
||||
new/obj/machinery/door/airlock/clockwork(get_turf(src))
|
||||
qdel(src)
|
||||
if(glass)
|
||||
new/obj/machinery/door/airlock/clockwork/brass(get_turf(src))
|
||||
else
|
||||
new/obj/machinery/door/airlock/clockwork(get_turf(src))
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/door/airlock/Destroy()
|
||||
qdel(wires)
|
||||
|
||||
@@ -352,13 +352,6 @@
|
||||
/obj/machinery/door/airlock/cult/narsie_act()
|
||||
return
|
||||
|
||||
/obj/machinery/door/airlock/cult/ratvar_act()
|
||||
..()
|
||||
if(src)
|
||||
var/previouscolor = color
|
||||
color = "#FAE48C"
|
||||
animate(src, color = previouscolor, time = 8)
|
||||
|
||||
/obj/machinery/door/airlock/cult/friendly
|
||||
friendly = TRUE
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ obj/machinery/door/proc/try_to_crowbar(obj/item/I, mob/user)
|
||||
|
||||
|
||||
/obj/machinery/door/blob_act(obj/effect/blob/B)
|
||||
if(prob(40))
|
||||
if(prob(60))
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/door/emp_act(severity)
|
||||
|
||||
@@ -196,9 +196,8 @@
|
||||
color = "#7D1919"
|
||||
|
||||
/obj/machinery/door/window/ratvar_act()
|
||||
if(prob(20))
|
||||
new/obj/machinery/door/window/clockwork(src.loc, dir)
|
||||
qdel(src)
|
||||
new/obj/machinery/door/window/clockwork(src.loc, dir)
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/door/window/bullet_act(obj/item/projectile/P)
|
||||
. = ..()
|
||||
|
||||
@@ -189,9 +189,7 @@ Class Procs:
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/blob_act(obj/effect/blob/B)
|
||||
if(!density)
|
||||
qdel(src)
|
||||
if(prob(75))
|
||||
if(density && prob(75))
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/proc/auto_use_power()
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
idle_power_usage = 4
|
||||
active_power_usage = 250
|
||||
var/obj/item/weapon/charging = null
|
||||
var/list/allowed_devices = list(/obj/item/weapon/gun/energy,/obj/item/weapon/melee/baton,/obj/item/ammo_box/magazine/recharge,/obj/item/weapon/computer_hardware/battery_module,/obj/item/laptop,/obj/item/modular_computer/)
|
||||
var/recharge_coeff = 1
|
||||
|
||||
/obj/machinery/recharger/New()
|
||||
@@ -36,7 +37,11 @@
|
||||
playsound(loc, 'sound/items/Ratchet.ogg', 75, 1)
|
||||
return
|
||||
|
||||
if(istype(G, /obj/item/weapon/gun/energy) || istype(G, /obj/item/weapon/melee/baton) || istype(G, /obj/item/ammo_box/magazine/recharge))
|
||||
var/allowed = 0
|
||||
for (var/allowed_type in allowed_devices)
|
||||
if (istype(G, allowed_type)) allowed = 1
|
||||
|
||||
if(allowed)
|
||||
if(anchored)
|
||||
if(charging || panel_open)
|
||||
return 1
|
||||
@@ -127,6 +132,34 @@
|
||||
use_power(200 * recharge_coeff)
|
||||
using_power = 1
|
||||
|
||||
if(istype(charging, /obj/item/laptop))
|
||||
var/obj/item/laptop/L = charging
|
||||
if(L.stored_computer.cpu.battery_module)
|
||||
var/obj/item/weapon/computer_hardware/battery_module/B = L.stored_computer.cpu.battery_module
|
||||
if(B.battery)
|
||||
if(B.battery.charge < B.battery.maxcharge)
|
||||
B.battery.give(B.battery.chargerate * recharge_coeff)
|
||||
use_power(200 * recharge_coeff)
|
||||
using_power = 1
|
||||
|
||||
if(istype(charging, /obj/item/weapon/computer_hardware/battery_module))
|
||||
var/obj/item/weapon/computer_hardware/battery_module/B = charging
|
||||
if(B.battery)
|
||||
if(B.battery.charge < B.battery.maxcharge)
|
||||
B.battery.give(B.battery.chargerate * recharge_coeff)
|
||||
use_power(200 * recharge_coeff)
|
||||
using_power = 1
|
||||
|
||||
if(istype(charging, /obj/item/modular_computer))
|
||||
var/obj/item/modular_computer/C = charging
|
||||
if(C.battery_module)
|
||||
var/obj/item/weapon/computer_hardware/battery_module/B = C.battery_module
|
||||
if(B.battery)
|
||||
if(B.battery.charge < B.battery.maxcharge)
|
||||
B.battery.give(B.battery.chargerate * recharge_coeff)
|
||||
use_power(200 * recharge_coeff)
|
||||
using_power = 1
|
||||
|
||||
update_icon(using_power)
|
||||
|
||||
/obj/machinery/recharger/power_change()
|
||||
|
||||
@@ -118,7 +118,8 @@ var/global/image/fire_overlay = image("icon" = 'icons/effects/fire.dmi', "icon_s
|
||||
return ..()
|
||||
|
||||
/obj/item/blob_act(obj/effect/blob/B)
|
||||
qdel(src)
|
||||
if(B && B.loc == loc)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/ex_act(severity, target)
|
||||
if(severity == 1 || target == src)
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
/obj/item/modkit
|
||||
name = "modification kit"
|
||||
desc = "A one-use kit, which enables kinetic accelerators to retain their \
|
||||
charge when away from a bioelectric source, renders them immune to \
|
||||
interference with other accelerators, as well as allowing less \
|
||||
dextrous races to use the tool."
|
||||
icon = 'icons/obj/objects.dmi'
|
||||
icon_state = "modkit"
|
||||
origin_tech = "programming=2;materials=2;magnets=4"
|
||||
var/uses = 1
|
||||
|
||||
/obj/item/modkit/afterattack(obj/item/weapon/gun/energy/kinetic_accelerator/C, mob/user)
|
||||
..()
|
||||
if(!uses)
|
||||
qdel(src)
|
||||
return
|
||||
if(!istype(C))
|
||||
user << "<span class='warning'>This kit can only modify kinetic \
|
||||
accelerators!</span>"
|
||||
return ..()
|
||||
// RIP the 'improved improved improved improved kinetic accelerator
|
||||
if(C.holds_charge && C.unique_frequency)
|
||||
user << "<span class='warning'>This kinetic accelerator already has \
|
||||
these upgrades.</span>"
|
||||
return ..()
|
||||
|
||||
user <<"<span class='notice'>You modify the [C], adjusting the trigger \
|
||||
guard and internal capacitor.</span>"
|
||||
C.name = "improved [C.name]"
|
||||
C.holds_charge = TRUE
|
||||
C.unique_frequency = TRUE
|
||||
C.trigger_guard = TRIGGER_GUARD_ALLOW_ALL
|
||||
uses--
|
||||
if(!uses)
|
||||
qdel(src)
|
||||
@@ -30,6 +30,7 @@ var/global/list/datum/stack_recipe/metal_recipes = list ( \
|
||||
new/datum/stack_recipe("wall girders", /obj/structure/girder, 2, time = 40, one_per_turf = 1, on_floor = 1), \
|
||||
null, \
|
||||
new/datum/stack_recipe("computer frame", /obj/structure/frame/computer, 5, time = 25, one_per_turf = 1, on_floor = 1), \
|
||||
new/datum/stack_recipe("modular console", /obj/machinery/modular_computer/console/buildable/, 10, time = 25, one_per_turf = 1, on_floor = 1), \
|
||||
new/datum/stack_recipe("machine frame", /obj/structure/frame/machine, 5, time = 25, one_per_turf = 1, on_floor = 1), \
|
||||
new/datum/stack_recipe("airlock assembly", /obj/structure/door_assembly, 4, time = 50, one_per_turf = 1, on_floor = 1), \
|
||||
new/datum/stack_recipe("firelock frame", /obj/structure/firelock_frame, 3, time = 50, one_per_turf = 1, on_floor = 1), \
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
add_mutations_static = list(CLOWNMUT)
|
||||
|
||||
/obj/item/weapon/dnainjector/anticlumsy
|
||||
name = "\improper DNA injector (Anti-Clumy)"
|
||||
name = "\improper DNA injector (Anti-Clumsy)"
|
||||
desc = "Apply this for Security Clown."
|
||||
remove_mutations_static = list(CLOWNMUT)
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
bang(get_turf(M), M)
|
||||
|
||||
for(var/obj/effect/blob/B in get_hear(8,flashbang_turf)) //Blob damage here
|
||||
var/damage = round(40/(get_dist(B,get_turf(src))+1))
|
||||
var/distance = get_dist(B, get_turf(src))
|
||||
var/damage = round(100/(distance*distance)+1)
|
||||
B.take_damage(damage, BURN)
|
||||
qdel(src)
|
||||
|
||||
|
||||
@@ -108,8 +108,8 @@
|
||||
name = "plant bag"
|
||||
icon = 'icons/obj/hydroponics/equipment.dmi'
|
||||
icon_state = "plantbag"
|
||||
storage_slots = 50; //the number of plant pieces it can carry.
|
||||
max_combined_w_class = 200 //Doesn't matter what this is, so long as it's more or equal to storage_slots * plants.w_class
|
||||
storage_slots = 100; //the number of plant pieces it can carry.
|
||||
max_combined_w_class = 100 //Doesn't matter what this is, so long as it's more or equal to storage_slots * plants.w_class
|
||||
max_w_class = 3
|
||||
w_class = 1
|
||||
can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/grown,/obj/item/seeds,/obj/item/weapon/grown)
|
||||
|
||||
@@ -183,7 +183,10 @@
|
||||
/obj/item/weapon/reagent_containers/pill,
|
||||
/obj/item/weapon/storage/pill_bottle,
|
||||
/obj/item/weapon/ore,
|
||||
/obj/item/weapon/reagent_containers/food/drinks
|
||||
/obj/item/weapon/reagent_containers/food/drinks,
|
||||
/obj/item/organ/hivelord_core,
|
||||
/obj/item/device/wormhole_jaunter
|
||||
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
|
||||
/obj/item/weapon/storage/box/survival_mining/New()
|
||||
..()
|
||||
new /obj/item/clothing/mask/breath(src)
|
||||
new /obj/item/clothing/mask/gas/explorer(src)
|
||||
new /obj/item/weapon/tank/internals/emergency_oxygen/engi(src)
|
||||
new /obj/item/weapon/crowbar/red(src)
|
||||
new /obj/item/weapon/reagent_containers/hypospray/medipen(src)
|
||||
|
||||
@@ -92,12 +92,12 @@
|
||||
user << "<span class='notice'>It feels [descriptive].</span>"
|
||||
|
||||
/obj/item/weapon/tank/blob_act(obj/effect/blob/B)
|
||||
if(prob(50))
|
||||
var/turf/location = src.loc
|
||||
if (!( istype(location, /turf) ))
|
||||
if(B && B.loc == loc)
|
||||
var/turf/location = get_turf(src)
|
||||
if(!location)
|
||||
qdel(src)
|
||||
|
||||
if(src.air_contents)
|
||||
if(air_contents)
|
||||
location.assume_air(air_contents)
|
||||
|
||||
qdel(src)
|
||||
|
||||
@@ -218,7 +218,6 @@
|
||||
w_class = 2
|
||||
|
||||
|
||||
|
||||
materials = list(MAT_METAL=70, MAT_GLASS=30)
|
||||
origin_tech = "engineering=1;plasmatech=1"
|
||||
var/welding = 0 //Whether or not the welding tool is off(0), on(1) or currently welding(2)
|
||||
@@ -332,8 +331,6 @@
|
||||
if(welding)
|
||||
SetLuminosity(0)
|
||||
user.AddLuminosity(light_intensity)
|
||||
else
|
||||
user.AddLuminosity(-light_intensity)
|
||||
update_icon()
|
||||
|
||||
|
||||
@@ -396,9 +393,8 @@ obj/item/weapon/weldingtool/proc/switched_on(mob/user)
|
||||
//Switches the welder off
|
||||
obj/item/weapon/weldingtool/proc/switched_off(mob/user)
|
||||
welding = 0
|
||||
var/mob/Player = loc
|
||||
if(ismob(loc)) //If player is holding the welder
|
||||
Player.AddLuminosity(-light_intensity)
|
||||
if(user == loc) //If player is holding the welder
|
||||
user.AddLuminosity(-light_intensity)
|
||||
SetLuminosity(0)
|
||||
else
|
||||
SetLuminosity(0)
|
||||
|
||||
@@ -83,13 +83,6 @@
|
||||
user.put_in_inactive_hand(O)
|
||||
return
|
||||
|
||||
/obj/item/weapon/twohanded/mob_can_equip(mob/M, mob/equipper, slot, disable_warning = 0)
|
||||
//Cannot equip wielded items.
|
||||
if(wielded)
|
||||
M << "<span class='warning'>Unwield the [name] first!</span>"
|
||||
return 0
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/twohanded/dropped(mob/user)
|
||||
..()
|
||||
//handles unwielding a twohanded weapon when dropped as well as clearing up the offhand
|
||||
@@ -109,6 +102,11 @@
|
||||
else //Trying to wield it
|
||||
wield(user)
|
||||
|
||||
/obj/item/weapon/twohanded/equip_to_best_slot(mob/M)
|
||||
if(..())
|
||||
unwield(M)
|
||||
return
|
||||
|
||||
///////////OFFHAND///////////////
|
||||
/obj/item/weapon/twohanded/offhand
|
||||
name = "offhand"
|
||||
@@ -131,7 +129,7 @@
|
||||
return
|
||||
|
||||
/obj/item/weapon/twohanded/required/mob_can_equip(mob/M, mob/equipper, slot, disable_warning = 0)
|
||||
if(wielded)
|
||||
if(wielded && !slot_flags)
|
||||
M << "<span class='warning'>\The [src] is too cumbersome to carry with anything but your hands!</span>"
|
||||
return 0
|
||||
return ..()
|
||||
@@ -143,9 +141,17 @@
|
||||
if(H != null)
|
||||
user << "<span class='notice'>\The [src] is too cumbersome to carry in one hand!</span>"
|
||||
return
|
||||
wield(user)
|
||||
if(src.loc != user)
|
||||
wield(user)
|
||||
..()
|
||||
|
||||
/obj/item/weapon/twohanded/required/equipped(mob/user, slot)
|
||||
..()
|
||||
if(slot == slot_l_hand || slot == slot_r_hand)
|
||||
wield(user)
|
||||
else
|
||||
unwield(user)
|
||||
|
||||
|
||||
/obj/item/weapon/twohanded/
|
||||
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
cameranet.updateVisibility(src)
|
||||
|
||||
/obj/structure/blob_act(obj/effect/blob/B)
|
||||
if(!density)
|
||||
qdel(src)
|
||||
if(prob(50))
|
||||
if(density && prob(50))
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/Destroy()
|
||||
|
||||
@@ -208,10 +208,6 @@
|
||||
visible_message("<span class='danger'>[user] destroys \the [src].</span>")
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/closet/blob_act(obj/effect/blob/B)
|
||||
if(prob(75))
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/closet/attackby(obj/item/weapon/W, mob/user, params)
|
||||
if(user in src)
|
||||
return
|
||||
|
||||
@@ -49,10 +49,7 @@
|
||||
showpiece = null
|
||||
|
||||
/obj/structure/displaycase/blob_act(obj/effect/blob/B)
|
||||
if (prob(75))
|
||||
new /obj/item/weapon/shard( src.loc )
|
||||
dump()
|
||||
qdel(src)
|
||||
take_damage(30)
|
||||
|
||||
/obj/structure/displaycase/hitby(atom/movable/AM)
|
||||
..()
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
|
||||
/obj/structure/fireaxecabinet/blob_act(obj/effect/blob/B)
|
||||
if(fireaxe)
|
||||
fireaxe.loc = src.loc
|
||||
fireaxe.forceMove(loc)
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/fireaxecabinet/attack_hand(mob/user)
|
||||
|
||||
@@ -25,15 +25,15 @@
|
||||
take_damage(rand(5,10), BRUTE, 0)
|
||||
|
||||
/obj/structure/grille/ratvar_act()
|
||||
if(prob(20))
|
||||
if(destroyed)
|
||||
new /obj/structure/grille/ratvar/broken(src.loc)
|
||||
else
|
||||
new /obj/structure/grille/ratvar(src.loc)
|
||||
qdel(src)
|
||||
if(destroyed)
|
||||
new /obj/structure/grille/ratvar/broken(src.loc)
|
||||
else
|
||||
new /obj/structure/grille/ratvar(src.loc)
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/grille/blob_act(obj/effect/blob/B)
|
||||
qdel(src)
|
||||
if(!destroyed)
|
||||
Break()
|
||||
|
||||
/obj/structure/grille/Bumped(atom/user)
|
||||
if(ismob(user))
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
/obj/structure/sign/blob_act(obj/effect/blob/B)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
/obj/structure/sign/attackby(obj/item/O, mob/user, params)
|
||||
if(istype(O, /obj/item/weapon/wrench))
|
||||
|
||||
@@ -79,9 +79,8 @@
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/table_frame/ratvar_act()
|
||||
if(prob(20))
|
||||
new /obj/structure/table_frame/brass(src.loc)
|
||||
qdel(src)
|
||||
new /obj/structure/table_frame/brass(src.loc)
|
||||
qdel(src)
|
||||
|
||||
/*
|
||||
* Wooden Frames
|
||||
|
||||
@@ -54,16 +54,14 @@
|
||||
take_damage(rand(40,80), BRUTE, 0)
|
||||
|
||||
/obj/structure/table/blob_act(obj/effect/blob/B)
|
||||
if(prob(75))
|
||||
qdel(src)
|
||||
take_damage(rand(75,150), BRUTE, 0)
|
||||
|
||||
/obj/structure/table/narsie_act()
|
||||
if(prob(20))
|
||||
new /obj/structure/table/wood(src.loc)
|
||||
|
||||
/obj/structure/table/ratvar_act()
|
||||
if(prob(20))
|
||||
new /obj/structure/table/reinforced/brass(src.loc)
|
||||
new /obj/structure/table/reinforced/brass(src.loc)
|
||||
|
||||
/obj/structure/table/mech_melee_attack(obj/mecha/M)
|
||||
playsound(src.loc, 'sound/weapons/punch4.ogg', 50, 1)
|
||||
@@ -463,10 +461,7 @@
|
||||
take_damage(rand(5,25), BRUTE, 0)
|
||||
|
||||
/obj/structure/rack/blob_act(obj/effect/blob/B)
|
||||
if(prob(75))
|
||||
qdel(src)
|
||||
else
|
||||
rack_destroy()
|
||||
rack_destroy()
|
||||
|
||||
|
||||
/obj/structure/rack/mech_melee_attack(obj/mecha/M)
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
take_damage(rand(25,75), BRUTE, 0)
|
||||
|
||||
/obj/structure/window/blob_act(obj/effect/blob/B)
|
||||
shatter()
|
||||
take_damage(rand(75,150), BRUTE, 0)
|
||||
|
||||
/obj/structure/window/narsie_act()
|
||||
color = NARSIE_WINDOW_COLOUR
|
||||
@@ -74,12 +74,11 @@
|
||||
shard.color = NARSIE_WINDOW_COLOUR
|
||||
|
||||
/obj/structure/window/ratvar_act()
|
||||
if(prob(20))
|
||||
if(!fulltile)
|
||||
new/obj/structure/window/reinforced/clockwork(get_turf(src), dir)
|
||||
else
|
||||
new/obj/structure/window/reinforced/clockwork/fulltile(get_turf(src))
|
||||
qdel(src)
|
||||
if(!fulltile)
|
||||
new/obj/structure/window/reinforced/clockwork(get_turf(src), dir)
|
||||
else
|
||||
new/obj/structure/window/reinforced/clockwork/fulltile(get_turf(src))
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/window/singularity_pull(S, current_size)
|
||||
if(current_size >= STAGE_FIVE)
|
||||
|
||||
@@ -170,9 +170,14 @@ var/list/icons_to_ignore_at_floor_init = list("damaged1","damaged2","damaged3","
|
||||
if(prob(20))
|
||||
ChangeTurf(/turf/open/floor/engine/cult)
|
||||
|
||||
/turf/open/floor/ratvar_act()
|
||||
if(prob(20))
|
||||
/turf/open/floor/ratvar_act(force)
|
||||
var/converted = (prob(40) || force)
|
||||
if(converted)
|
||||
ChangeTurf(/turf/open/floor/clockwork)
|
||||
for(var/I in src)
|
||||
var/atom/A = I
|
||||
if(ismob(A) || converted)
|
||||
A.ratvar_act()
|
||||
|
||||
/turf/open/floor/initialize()
|
||||
..()
|
||||
|
||||
@@ -187,7 +187,8 @@
|
||||
return ..()
|
||||
|
||||
/turf/open/floor/clockwork/ratvar_act()
|
||||
return 0
|
||||
for(var/mob/M in src)
|
||||
M.ratvar_act()
|
||||
|
||||
/turf/open/floor/clockwork/narsie_act()
|
||||
..()
|
||||
|
||||
@@ -256,9 +256,14 @@
|
||||
if(prob(20))
|
||||
ChangeTurf(/turf/closed/wall/mineral/cult)
|
||||
|
||||
/turf/closed/wall/ratvar_act()
|
||||
if(prob(20))
|
||||
/turf/closed/wall/ratvar_act(force)
|
||||
var/converted = (prob(40) || force)
|
||||
if(converted)
|
||||
ChangeTurf(/turf/closed/wall/clockwork)
|
||||
for(var/I in src)
|
||||
var/atom/A = I
|
||||
if(ismob(A) || converted)
|
||||
A.ratvar_act()
|
||||
|
||||
/turf/closed/wall/storage_contents_dump_act(obj/item/weapon/storage/src_object, mob/user)
|
||||
return 0
|
||||
|
||||
@@ -94,7 +94,8 @@
|
||||
return ..()
|
||||
|
||||
/turf/closed/wall/clockwork/ratvar_act()
|
||||
return 0
|
||||
for(var/mob/M in src)
|
||||
M.ratvar_act()
|
||||
|
||||
/turf/closed/wall/clockwork/narsie_act()
|
||||
..()
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
throw_atom(AM)
|
||||
|
||||
/turf/open/space/transit/proc/throw_atom(atom/movable/AM)
|
||||
if(!AM)
|
||||
if(!AM || istype(AM, /obj/docking_port))
|
||||
return
|
||||
var/max = world.maxx-TRANSITIONEDGE
|
||||
var/min = 1+TRANSITIONEDGE
|
||||
|
||||
@@ -284,6 +284,9 @@
|
||||
if(A.level >= affecting_level)
|
||||
A.ex_act(severity, target)
|
||||
|
||||
/turf/ratvar_act()
|
||||
for(var/mob/M in src)
|
||||
M.ratvar_act()
|
||||
|
||||
/turf/proc/add_blueprints(atom/movable/AM)
|
||||
var/image/I = new
|
||||
|
||||
@@ -161,6 +161,9 @@ var/list/admin_ranks = list() //list of all admin_rank datums
|
||||
C.holder = null
|
||||
admins.Cut()
|
||||
load_admin_ranks()
|
||||
//Clear profile access
|
||||
for(var/A in world.GetConfig("admin"))
|
||||
world.SetConfig("APP/admin", A, null)
|
||||
|
||||
var/list/rank_names = list()
|
||||
for(var/datum/admin_rank/R in admin_ranks)
|
||||
@@ -189,6 +192,8 @@ var/list/admin_ranks = list() //list of all admin_rank datums
|
||||
var/datum/admins/D = new(rank_names[rank], ckey) //create the admin datum and store it for later use
|
||||
if(!D)
|
||||
continue //will occur if an invalid rank is provided
|
||||
if(D.rank.rights & R_DEBUG) //grant profile access
|
||||
world.SetConfig("APP/admin", ckey, "role=admin")
|
||||
D.associate(directory[ckey]) //find the client for a ckey if they are connected and associate them with the new admin datum
|
||||
else
|
||||
establish_db_connection()
|
||||
@@ -214,6 +219,8 @@ var/list/admin_ranks = list() //list of all admin_rank datums
|
||||
var/datum/admins/D = new(rank_names[rank], ckey) //create the admin datum and store it for later use
|
||||
if(!D)
|
||||
continue //will occur if an invalid rank is provided
|
||||
if(D.rank.rights & R_DEBUG) //grant profile access
|
||||
world.SetConfig("APP/admin", ckey, "role=admin")
|
||||
D.associate(directory[ckey]) //find the client for a ckey if they are connected and associate them with the new admin datum
|
||||
|
||||
#ifdef TESTING
|
||||
@@ -369,4 +376,4 @@ var/list/admin_ranks = list() //list of all admin_rank datums
|
||||
var/sql_admin_rank = sanitizeSQL(newrank)
|
||||
|
||||
var/DBQuery/query_update = dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastadminrank = '[sql_admin_rank]' WHERE ckey = '[sql_ckey]'")
|
||||
query_update.Execute()
|
||||
query_update.Execute()
|
||||
|
||||
@@ -92,10 +92,10 @@
|
||||
message_admins("[key_name_admin(usr)] tried to create a death squad. Unfortunately, there were not enough candidates available.")
|
||||
log_admin("[key_name(usr)] failed to create a death squad.")
|
||||
if("blob")
|
||||
var/strength = input("Set Blob Strength (1=Weak, 2=Strong, 3=Full)","Set Strength",1) as num
|
||||
message_admins("[key_name(usr)] spawned a blob with strength [strength].")
|
||||
log_admin("[key_name(usr)] spawned a blob with strength [strength].")
|
||||
new/datum/round_event/blob(strength)
|
||||
var/strength = input("Set Blob Resource Gain Rate","Set Resource Rate",1) as num
|
||||
message_admins("[key_name(usr)] spawned a blob with base resource gain [strength].")
|
||||
log_admin("[key_name(usr)] spawned a blob with base resource gain [strength].")
|
||||
new/datum/round_event/ghost_role/blob(TRUE, strength)
|
||||
if("gangs")
|
||||
if(src.makeGangsters())
|
||||
message_admins("[key_name(usr)] created gangs.")
|
||||
|
||||
@@ -261,7 +261,7 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
|
||||
log_admin("[key_name(src)] has blobized [M.key].")
|
||||
var/mob/living/carbon/human/H = M
|
||||
spawn(0)
|
||||
var/mob/camera/blob/B = H.become_overmind()
|
||||
var/mob/camera/blob/B = H.become_overmind(FALSE)
|
||||
B.place_blob_core(B.base_point_rate, -1) //place them wherever they are
|
||||
|
||||
else
|
||||
|
||||
@@ -134,7 +134,7 @@
|
||||
|
||||
var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", "wizard", null)
|
||||
|
||||
var/mob/dead/observer/selected = popleft(candidates)
|
||||
var/mob/dead/observer/selected = pick_n_take(candidates)
|
||||
|
||||
var/mob/living/carbon/human/new_character = makeBody(selected)
|
||||
new_character.mind.make_Wizard()
|
||||
@@ -552,5 +552,5 @@
|
||||
return 1
|
||||
|
||||
/datum/admins/proc/makeRevenant()
|
||||
new /datum/round_event/ghost_role/revenant
|
||||
new /datum/round_event/ghost_role/revenant(TRUE, TRUE)
|
||||
return 1
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
if(!cost)
|
||||
return 0
|
||||
|
||||
var/potDiff = max(S.potency - discoveredPlants[S.type], 0)
|
||||
|
||||
return round(..() * potDiff)
|
||||
var/potDiff = (S.potency - discoveredPlants[S.type])
|
||||
if(potDiff < 1)
|
||||
return round(S.rarity * S.potency / 10)
|
||||
else
|
||||
return round(..() * potDiff)
|
||||
|
||||
@@ -179,6 +179,36 @@ You can set verify to TRUE if you want send() to sleep until the client has the
|
||||
"tgui.js" = 'tgui/assets/tgui.js'
|
||||
)
|
||||
|
||||
/datum/asset/simple/headers
|
||||
assets = list(
|
||||
"alarm_green.gif" = 'icons/program_icons/alarm_green.gif',
|
||||
"alarm_red.gif" = 'icons/program_icons/alarm_red.gif',
|
||||
"batt_5.gif" = 'icons/program_icons/batt_5.gif',
|
||||
"batt_20.gif" = 'icons/program_icons/batt_20.gif',
|
||||
"batt_40.gif" = 'icons/program_icons/batt_40.gif',
|
||||
"batt_60.gif" = 'icons/program_icons/batt_60.gif',
|
||||
"batt_80.gif" = 'icons/program_icons/batt_80.gif',
|
||||
"batt_100.gif" = 'icons/program_icons/batt_100.gif',
|
||||
"charging.gif" = 'icons/program_icons/charging.gif',
|
||||
"downloader_finished.gif" = 'icons/program_icons/downloader_finished.gif',
|
||||
"downloader_running.gif" = 'icons/program_icons/downloader_running.gif',
|
||||
"ntnrc_idle.gif" = 'icons/program_icons/ntnrc_idle.gif',
|
||||
"ntnrc_new.gif" = 'icons/program_icons/ntnrc_new.gif',
|
||||
"power_norm.gif" = 'icons/program_icons/power_norm.gif',
|
||||
"power_warn.gif" = 'icons/program_icons/power_warn.gif',
|
||||
"sig_high.gif" = 'icons/program_icons/sig_high.gif',
|
||||
"sig_low.gif" = 'icons/program_icons/sig_low.gif',
|
||||
"sig_lan.gif" = 'icons/program_icons/sig_lan.gif',
|
||||
"sig_none.gif" = 'icons/program_icons/sig_none.gif',
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/datum/asset/simple/pda
|
||||
assets = list(
|
||||
"pda_atmos.png" = 'icons/pda_icons/pda_atmos.png',
|
||||
|
||||
@@ -77,7 +77,12 @@
|
||||
chameleon_blacklist += target.type
|
||||
var/list/temp_list = typesof(chameleon_type)
|
||||
for(var/V in temp_list - (chameleon_blacklist))
|
||||
chameleon_list += V
|
||||
if(istype(V, /obj/item))
|
||||
var/obj/item/I = V
|
||||
if(initial(I.flags) & ABSTRACT)
|
||||
continue
|
||||
else
|
||||
chameleon_list += I
|
||||
|
||||
/datum/action/item_action/chameleon/change/proc/select_look(mob/user)
|
||||
var/list/item_names = list()
|
||||
@@ -86,7 +91,7 @@
|
||||
var/obj/item/I = U
|
||||
item_names += initial(I.name)
|
||||
var/picked_name
|
||||
picked_name = input("Select [chameleon_name] to change it to", "Chameleon [chameleon_name]", picked_name) in item_names
|
||||
picked_name = input("Select [chameleon_name] to change into", "Chameleon [chameleon_name]", picked_name) in item_names
|
||||
if(!picked_name)
|
||||
return
|
||||
for(var/V in chameleon_list)
|
||||
|
||||
@@ -494,6 +494,32 @@
|
||||
armor = list(melee = 45, bullet = 25, laser = 30, energy = 10, bomb = 25, bio = 100, rad = 50)
|
||||
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/security/hos
|
||||
|
||||
//Clown
|
||||
/obj/item/clothing/head/helmet/space/hardsuit/clown
|
||||
name = "cosmohonk hardsuit helmet"
|
||||
desc = "A special helmet designed for work in a hazardous, low-humor environment. Has radiation shielding."
|
||||
icon_state = "hardsuit0-clown"
|
||||
item_state = "hardsuit0-clown"
|
||||
armor = list(melee = 30, bullet = 5, laser = 10, energy = 5, bomb = 10, bio = 100, rad = 75)
|
||||
item_color = "clown"
|
||||
|
||||
/obj/item/clothing/suit/space/hardsuit/clown
|
||||
name = "cosmohonk hardsuit"
|
||||
desc = "A special suit that protects against hazardous, low humor environments. Has radiation shielding. Only a true clown can wear it."
|
||||
icon_state = "hardsuit-clown"
|
||||
item_state = "clown_hardsuit"
|
||||
armor = list(melee = 30, bullet = 5, laser = 10, energy = 5, bomb = 10, bio = 100, rad = 75)
|
||||
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/clown
|
||||
pockets = /obj/item/weapon/storage/internal/pocket
|
||||
|
||||
/obj/item/clothing/suit/space/hardsuit/clown/mob_can_equip(mob/M, slot)
|
||||
if(!..() || !ishuman(M))
|
||||
return FALSE
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(H.mind.assigned_role == "Clown")
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
|
||||
/////////////SHIELDED//////////////////////////////////
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
else
|
||||
temp = new
|
||||
|
||||
var/agent_mind = popleft(candidates)
|
||||
var/scientist_mind = popleft(candidates)
|
||||
var/agent_mind = pick_n_take(candidates)
|
||||
var/scientist_mind = pick_n_take(candidates)
|
||||
|
||||
var/mob/living/carbon/human/agent = makeBody(agent_mind)
|
||||
var/mob/living/carbon/human/scientist = makeBody(scientist_mind)
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
|
||||
while(spawncount > 0 && vents.len && candidates.len)
|
||||
var/obj/vent = pick_n_take(vents)
|
||||
var/client/C = popleft(candidates)
|
||||
var/client/C = pick_n_take(candidates)
|
||||
|
||||
var/mob/living/carbon/alien/larva/new_xeno = new(vent.loc)
|
||||
new_xeno.key = C.key
|
||||
|
||||
+19
-12
@@ -1,6 +1,6 @@
|
||||
/datum/round_event_control/blob
|
||||
name = "Blob"
|
||||
typepath = /datum/round_event/blob
|
||||
typepath = /datum/round_event/ghost_role/blob
|
||||
weight = 5
|
||||
max_occurrences = 1
|
||||
|
||||
@@ -9,22 +9,29 @@
|
||||
|
||||
gamemode_blacklist = list("blob") //Just in case a blob survives that long
|
||||
|
||||
/datum/round_event/blob
|
||||
/datum/round_event/ghost_role/blob
|
||||
announceWhen = 12
|
||||
endWhen = 120
|
||||
var/new_rate = 2
|
||||
|
||||
/datum/round_event/blob/New(var/strength)
|
||||
/datum/round_event/ghost_role/blob/New(my_processing = TRUE, set_point_rate)
|
||||
..()
|
||||
if(strength)
|
||||
new_rate = strength
|
||||
if(set_point_rate)
|
||||
new_rate = set_point_rate
|
||||
|
||||
/datum/round_event/blob/announce()
|
||||
/datum/round_event/ghost_role/blob/announce()
|
||||
priority_announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak5.ogg')
|
||||
|
||||
|
||||
/datum/round_event/blob/start()
|
||||
var/turf/T = pick(blobstart)
|
||||
if(!T)
|
||||
return kill()
|
||||
new/obj/effect/blob/core(T, null, new_rate)
|
||||
/datum/round_event/ghost_role/blob/spawn_role()
|
||||
if(!blobstart.len)
|
||||
return MAP_ERROR
|
||||
var/list/candidates = get_candidates("blob", null, ROLE_BLOB)
|
||||
if(!candidates.len)
|
||||
return NOT_ENOUGH_PLAYERS
|
||||
var/mob/dead/observer/new_blob = pick(candidates)
|
||||
var/obj/effect/blob/core/BC = new/obj/effect/blob/core(pick(blobstart), new_blob.client, new_rate)
|
||||
BC.overmind.blob_points = min(20 + player_list.len, BC.overmind.max_blob_points)
|
||||
spawned_mobs += BC.overmind
|
||||
message_admins("[BC.overmind.key] has been made into a blob overmind by an event.")
|
||||
log_game("[BC.overmind.key] was spawned as a blob overmind by an event.")
|
||||
return SUCCESSFUL_SPAWN
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
if(!candidates.len)
|
||||
return NOT_ENOUGH_PLAYERS
|
||||
|
||||
var/mob/dead/selected_candidate = popleft(candidates)
|
||||
var/mob/dead/selected_candidate = pick_n_take(candidates)
|
||||
var/key = selected_candidate.key
|
||||
|
||||
var/datum/mind/Mind = create_devil_mind(key)
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
if(!candidates.len)
|
||||
return NOT_ENOUGH_PLAYERS
|
||||
|
||||
var/mob/dead/selected = popleft(candidates)
|
||||
var/mob/dead/selected = pick_n_take(candidates)
|
||||
|
||||
var/list/spawn_locs = list()
|
||||
for(var/obj/effect/landmark/L in landmarks_list)
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
/var/const/access_mineral_storeroom = 64
|
||||
/var/const/access_minisat = 65
|
||||
/var/const/access_weapons = 66 //Weapon authorization for secbots
|
||||
/var/const/access_network = 67
|
||||
|
||||
//BEGIN CENTCOM ACCESS
|
||||
/*Should leave plenty of room if we need to add more access levels.
|
||||
@@ -237,7 +238,7 @@
|
||||
access_hydroponics, access_library, access_lawyer, access_virology, access_cmo, access_qm, access_surgery,
|
||||
access_theatre, access_research, access_mining, access_mailsorting, access_weapons,
|
||||
access_heads_vault, access_mining_station, access_xenobiology, access_ce, access_hop, access_hos, access_RC_announce,
|
||||
access_keycard_auth, access_tcomsat, access_gateway, access_mineral_storeroom, access_minisat)
|
||||
access_keycard_auth, access_tcomsat, access_gateway, access_mineral_storeroom, access_minisat, access_network)
|
||||
|
||||
/proc/get_all_centcom_access()
|
||||
return list(access_cent_general, access_cent_thunder, access_cent_specops, access_cent_medical, access_cent_living, access_cent_storage, access_cent_teleporter, access_cent_captain)
|
||||
@@ -267,7 +268,7 @@
|
||||
if(3) //medbay
|
||||
return list(access_medical, access_genetics, access_morgue, access_chemistry, access_virology, access_surgery, access_cmo)
|
||||
if(4) //research
|
||||
return list(access_research, access_tox, access_tox_storage, access_genetics, access_robotics, access_xenobiology, access_minisat, access_rd)
|
||||
return list(access_research, access_tox, access_tox_storage, access_genetics, access_robotics, access_xenobiology, access_minisat, access_rd, access_network)
|
||||
if(5) //engineering and maintenance
|
||||
return list(access_construction, access_maint_tunnels, access_engine, access_engine_equip, access_external_airlocks, access_tech_storage, access_atmospherics, access_tcomsat, access_minisat, access_ce)
|
||||
if(6) //supply
|
||||
@@ -424,6 +425,8 @@
|
||||
return "AI Satellite"
|
||||
if(access_weapons)
|
||||
return "Weapon Permit"
|
||||
if(access_network)
|
||||
return "Network Access"
|
||||
|
||||
/proc/get_centcom_access_desc(A)
|
||||
switch(A)
|
||||
|
||||
@@ -14,6 +14,7 @@ Assistant
|
||||
minimal_access = list() //See /datum/job/assistant/get_access()
|
||||
outfit = /datum/outfit/job/assistant
|
||||
|
||||
|
||||
/datum/job/assistant/get_access()
|
||||
if((config.jobs_have_maint_access & ASSISTANTS_HAVE_MAINT_ACCESS) || !config.jobs_have_minimal_access) //Config has assistant maint access set
|
||||
. = ..()
|
||||
|
||||
@@ -99,7 +99,7 @@ Head of Personnel
|
||||
shoes = /obj/item/clothing/shoes/sneakers/brown
|
||||
head = /obj/item/clothing/head/hopcap
|
||||
backpack_contents = list(/obj/item/weapon/storage/box/ids=1,\
|
||||
/obj/item/weapon/melee/classic_baton/telescopic=1)
|
||||
/obj/item/weapon/melee/classic_baton/telescopic=1, /obj/item/modular_computer/tablet/preset/advanced = 1)
|
||||
|
||||
/datum/outfit/job/hop/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
|
||||
..()
|
||||
|
||||
@@ -52,6 +52,7 @@ Cargo Technician
|
||||
belt = /obj/item/device/pda/cargo
|
||||
ears = /obj/item/device/radio/headset/headset_cargo
|
||||
uniform = /obj/item/clothing/under/rank/cargotech
|
||||
backpack_contents = list(/obj/item/modular_computer/tablet/preset/cheap=1)
|
||||
|
||||
|
||||
/*
|
||||
@@ -86,7 +87,7 @@ Shaft Miner
|
||||
backpack_contents = list(
|
||||
/obj/item/weapon/storage/bag/ore=1,\
|
||||
/obj/item/weapon/kitchen/knife/combat/survival=1,\
|
||||
/obj/item/weapon/mining_voucher=1)
|
||||
/obj/item/weapon/mining_voucher=1,/obj/item/modular_computer/tablet/preset/cheap=1)
|
||||
|
||||
backpack = /obj/item/weapon/storage/backpack/explorer
|
||||
satchel = /obj/item/weapon/storage/backpack/satchel/explorer
|
||||
@@ -121,7 +122,7 @@ Bartender
|
||||
ears = /obj/item/device/radio/headset/headset_srv
|
||||
uniform = /obj/item/clothing/under/rank/bartender
|
||||
suit = /obj/item/clothing/suit/armor/vest
|
||||
backpack_contents = list(/obj/item/weapon/storage/box/beanbag=1)
|
||||
backpack_contents = list(/obj/item/weapon/storage/box/beanbag=1,/obj/item/modular_computer/tablet/preset/cheap=1)
|
||||
shoes = /obj/item/clothing/shoes/laceup
|
||||
|
||||
/*
|
||||
@@ -152,6 +153,7 @@ Cook
|
||||
uniform = /obj/item/clothing/under/rank/chef
|
||||
suit = /obj/item/clothing/suit/toggle/chef
|
||||
head = /obj/item/clothing/head/chefhat
|
||||
backpack_contents = list(/obj/item/modular_computer/tablet/preset/cheap=1)
|
||||
|
||||
/datum/outfit/job/cook/pre_equip(mob/living/carbon/human/H)
|
||||
..()
|
||||
@@ -200,6 +202,7 @@ Botanist
|
||||
|
||||
backpack = /obj/item/weapon/storage/backpack/botany
|
||||
satchel = /obj/item/weapon/storage/backpack/satchel/hyd
|
||||
backpack_contents = list(/obj/item/modular_computer/tablet/preset/cheap=1)
|
||||
|
||||
|
||||
/*
|
||||
@@ -228,3 +231,4 @@ Janitor
|
||||
belt = /obj/item/device/pda/janitor
|
||||
ears = /obj/item/device/radio/headset/headset_srv
|
||||
uniform = /obj/item/clothing/under/rank/janitor
|
||||
backpack_contents = list(/obj/item/modular_computer/tablet/preset/cheap=1)
|
||||
|
||||
@@ -20,12 +20,12 @@ Research Director
|
||||
access_tox_storage, access_teleporter, access_sec_doors,
|
||||
access_research, access_robotics, access_xenobiology, access_ai_upload,
|
||||
access_RC_announce, access_keycard_auth, access_gateway, access_mineral_storeroom,
|
||||
access_tech_storage, access_minisat, access_maint_tunnels)
|
||||
access_tech_storage, access_minisat, access_maint_tunnels, access_network)
|
||||
minimal_access = list(access_rd, access_heads, access_tox, access_genetics, access_morgue,
|
||||
access_tox_storage, access_teleporter, access_sec_doors,
|
||||
access_research, access_robotics, access_xenobiology, access_ai_upload,
|
||||
access_RC_announce, access_keycard_auth, access_gateway, access_mineral_storeroom,
|
||||
access_tech_storage, access_minisat, access_maint_tunnels)
|
||||
access_tech_storage, access_minisat, access_maint_tunnels, access_network)
|
||||
|
||||
/datum/outfit/job/rd
|
||||
name = "Research Director"
|
||||
@@ -38,7 +38,7 @@ Research Director
|
||||
suit = /obj/item/clothing/suit/toggle/labcoat
|
||||
l_hand = /obj/item/weapon/clipboard
|
||||
l_pocket = /obj/item/device/laser_pointer
|
||||
backpack_contents = list(/obj/item/weapon/melee/classic_baton/telescopic=1)
|
||||
backpack_contents = list(/obj/item/weapon/melee/classic_baton/telescopic=1,/obj/item/modular_computer/tablet/preset/advanced=1)
|
||||
|
||||
backpack = /obj/item/weapon/storage/backpack/science
|
||||
satchel = /obj/item/weapon/storage/backpack/satchel/tox
|
||||
|
||||
@@ -158,7 +158,7 @@ Detective
|
||||
backpack_contents = list(/obj/item/weapon/storage/box/evidence=1,\
|
||||
/obj/item/device/detective_scanner=1,\
|
||||
/obj/item/weapon/melee/classic_baton=1,\
|
||||
/obj/item/weapon/reagent_containers/food/drinks/flask/det)
|
||||
/obj/item/weapon/reagent_containers/food/drinks/flask/det=1)
|
||||
mask = /obj/item/clothing/mask/cigarette
|
||||
|
||||
/datum/outfit/job/detective/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
|
||||
|
||||
@@ -459,4 +459,111 @@
|
||||
|
||||
C.preserved()
|
||||
user << "<span class='notice'>You inject the [M] with the stabilizer. It will no longer go inert.</span>"
|
||||
qdel(src)
|
||||
qdel(src)
|
||||
|
||||
/*********************Mining Hammer****************/
|
||||
/obj/item/weapon/twohanded/required/mining_hammer
|
||||
icon = 'icons/obj/mining.dmi'
|
||||
icon_state = "mining_hammer1"
|
||||
item_state = "mining_hammer1"
|
||||
name = "proto-kinetic crusher"
|
||||
desc = "An early design of the proto-kinetic accelerator, it is little more than an combination of various mining tools cobbled together, forming a high-tech club.\
|
||||
While it is an effective mining tool, it did little to aid any but the most skilled and/or suicidal miners against local fauna. \
|
||||
\n<span class='info'>Mark a mob with the destabilizing force, then hit them in melee to activate it for extra damage. Extra damage if backstabbed in this fashion. \
|
||||
This weapon is only particularly effective against large creatures.</span>"
|
||||
force = 20 //As much as a bone spear, but this is significantly more annoying to carry around due to requiring the use of both hands at all times
|
||||
w_class = 4
|
||||
slot_flags = SLOT_BACK
|
||||
force_unwielded = 20 //It's never not wielded so these are the same
|
||||
force_wielded = 20
|
||||
throwforce = 5
|
||||
throw_speed = 4
|
||||
luminosity = 4
|
||||
armour_penetration = 10
|
||||
materials = list(MAT_METAL=1150, MAT_GLASS=2075)
|
||||
hitsound = 'sound/weapons/bladeslice.ogg'
|
||||
attack_verb = list("smashes", "crushes", "cleaves", "chops", "pulps")
|
||||
sharpness = IS_SHARP
|
||||
var/charged = 1
|
||||
var/charge_time = 16
|
||||
var/atom/mark = null
|
||||
var/marked_image = null
|
||||
|
||||
/obj/item/projectile/destabilizer
|
||||
name = "destabilizing force"
|
||||
icon_state = "pulse1"
|
||||
damage = 0 //We're just here to mark people. This is still a melee weapon.
|
||||
damage_type = BRUTE
|
||||
flag = "bomb"
|
||||
range = 6
|
||||
var/obj/item/weapon/twohanded/required/mining_hammer/hammer_synced = null
|
||||
|
||||
/obj/item/projectile/destabilizer/on_hit(atom/target, blocked = 0, hit_zone)
|
||||
if(hammer_synced)
|
||||
if(hammer_synced.mark == target)
|
||||
return ..()
|
||||
if(isliving(target))
|
||||
if(hammer_synced.mark && hammer_synced.marked_image)
|
||||
hammer_synced.mark.underlays -= hammer_synced.marked_image
|
||||
hammer_synced.marked_image = null
|
||||
var/mob/living/L = target
|
||||
if(L.mob_size >= MOB_SIZE_LARGE)
|
||||
hammer_synced.mark = L
|
||||
var/image/I = image('icons/effects/effects.dmi', loc = L, icon_state = "shield2",pixel_y = (-L.pixel_y),pixel_x = (-L.pixel_x))
|
||||
L.underlays += I
|
||||
hammer_synced.marked_image = I
|
||||
var/target_turf = get_turf(target)
|
||||
if(istype(target_turf, /turf/closed/mineral))
|
||||
var/turf/closed/mineral/M = target_turf
|
||||
PoolOrNew(/obj/effect/overlay/temp/kinetic_blast, M)
|
||||
M.gets_drilled(firer)
|
||||
..()
|
||||
|
||||
/obj/item/weapon/twohanded/required/mining_hammer/afterattack(atom/target, mob/user, proximity_flag)
|
||||
if(!proximity_flag && charged)//Mark a target, or mine a tile.
|
||||
var/turf/proj_turf = get_turf(src)
|
||||
if(!istype(proj_turf, /turf))
|
||||
return
|
||||
var/datum/gas_mixture/environment = proj_turf.return_air()
|
||||
var/pressure = environment.return_pressure()
|
||||
if(pressure > 50)
|
||||
playsound(user, 'sound/weapons/empty.ogg', 100, 1)
|
||||
return
|
||||
var/obj/item/projectile/destabilizer/D = new /obj/item/projectile/destabilizer(user.loc)
|
||||
D.preparePixelProjectile(target,get_turf(target), user)
|
||||
D.hammer_synced = src
|
||||
playsound(user, 'sound/weapons/plasma_cutter.ogg', 100, 1)
|
||||
D.fire()
|
||||
charged = 0
|
||||
icon_state = "mining_hammer1_uncharged"
|
||||
addtimer(src, "Recharge", charge_time)
|
||||
return
|
||||
if(proximity_flag && target == mark && isliving(target))
|
||||
var/mob/living/L = target
|
||||
PoolOrNew(/obj/effect/overlay/temp/kinetic_blast, get_turf(L))
|
||||
mark = 0
|
||||
if(L.mob_size >= MOB_SIZE_LARGE)
|
||||
L.underlays -= marked_image
|
||||
qdel(marked_image)
|
||||
marked_image = null
|
||||
var/backstab = check_target_facings(user, L)
|
||||
var/def_check = L.getarmor(type = "bomb")
|
||||
if(backstab == FACING_INIT_FACING_TARGET_TARGET_FACING_PERPENDICULAR || backstab == FACING_SAME_DIR)
|
||||
L.apply_damage(80, BRUTE, blocked = def_check)
|
||||
playsound(user, 'sound/weapons/Kenetic_accel.ogg', 100, 1) //Seriously who spelled it wrong
|
||||
else
|
||||
L.apply_damage(50, BRUTE, blocked = def_check)
|
||||
|
||||
/obj/item/weapon/twohanded/required/mining_hammer/proc/Recharge()
|
||||
if(!charged)
|
||||
charged = 1
|
||||
icon_state = "mining_hammer1"
|
||||
playsound(src.loc, 'sound/weapons/kenetic_reload.ogg', 60, 1)
|
||||
|
||||
/obj/item/weapon/twohanded/required/mining_hammer/pickup(mob/user)
|
||||
..()
|
||||
user.AddLuminosity(luminosity)
|
||||
|
||||
/obj/item/weapon/twohanded/required/mining_hammer/dropped(mob/user)
|
||||
..()
|
||||
user.AddLuminosity(-luminosity)
|
||||
@@ -27,6 +27,7 @@
|
||||
new /datum/data/mining_equipment("Brute First-Aid Kit", /obj/item/weapon/storage/firstaid/brute, 600),
|
||||
new /datum/data/mining_equipment("Tracking Implant Kit",/obj/item/weapon/storage/box/minertracker, 600),
|
||||
new /datum/data/mining_equipment("Jaunter", /obj/item/device/wormhole_jaunter, 750),
|
||||
new /datum/data/mining_equipment("Kinetic Crusher", /obj/item/weapon/twohanded/required/mining_hammer, 750),
|
||||
new /datum/data/mining_equipment("Kinetic Accelerator", /obj/item/weapon/gun/energy/kinetic_accelerator, 750),
|
||||
new /datum/data/mining_equipment("Resonator", /obj/item/weapon/resonator, 800),
|
||||
new /datum/data/mining_equipment("Medivac Balloon", /obj/item/weapon/extraction_pack/medivac, 800),
|
||||
@@ -38,7 +39,6 @@
|
||||
new /datum/data/mining_equipment("Mining Hardsuit", /obj/item/clothing/suit/space/hardsuit/mining, 2000),
|
||||
new /datum/data/mining_equipment("Diamond Pickaxe", /obj/item/weapon/pickaxe/diamond, 2000),
|
||||
new /datum/data/mining_equipment("Super Resonator", /obj/item/weapon/resonator/upgraded, 2500),
|
||||
new /datum/data/mining_equipment("Super Accelerator", /obj/item/weapon/gun/energy/kinetic_accelerator/super, 3000),
|
||||
new /datum/data/mining_equipment("Point Transfer Card", /obj/item/weapon/card/mining_point_card, 500),
|
||||
new /datum/data/mining_equipment("Mining Drone", /mob/living/simple_animal/hostile/mining_drone, 800),
|
||||
new /datum/data/mining_equipment("Drone Melee Upgrade", /obj/item/device/mine_bot_ugprade, 400),
|
||||
@@ -159,7 +159,7 @@
|
||||
return ..()
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/proc/RedeemVoucher(obj/item/weapon/mining_voucher/voucher, mob/redeemer)
|
||||
var/items = list("Survival Capsule and Explorer's Webbing", "Resonator and Advanced Scanner", "Mining Drone", "Medivac Kit", "Extraction Kit")
|
||||
var/items = list("Survival Capsule and Explorer's Webbing", "Resonator and Advanced Scanner", "Mining Drone", "Medivac Kit", "Extraction Kit", "Crusher Kit")
|
||||
|
||||
var/selection = input(redeemer, "Pick your equipment", "Mining Voucher Redemption") as null|anything in items
|
||||
if(!selection || !Adjacent(redeemer) || qdeleted(voucher) || voucher.loc != redeemer)
|
||||
@@ -182,6 +182,10 @@
|
||||
new /obj/item/stack/sheet/metal/five(loc)
|
||||
new /obj/item/weapon/extraction_pack(loc)
|
||||
new /obj/item/fulton_core(loc)
|
||||
if("Crusher Kit")
|
||||
new /obj/item/weapon/twohanded/required/mining_hammer(loc)
|
||||
new /obj/item/weapon/storage/belt/mining/alt(loc)
|
||||
new /obj/item/weapon/extinguisher/mini(loc)
|
||||
|
||||
feedback_add_details("mining_voucher_redeemed", selection)
|
||||
qdel(voucher)
|
||||
|
||||
@@ -270,7 +270,7 @@
|
||||
/turf/open/floor/plating/asteroid/airless/cave/volcanic
|
||||
mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/goldgrub = 10, /mob/living/simple_animal/hostile/asteroid/goliath/beast = 50, /mob/living/simple_animal/hostile/asteroid/basilisk/watcher = 40, /mob/living/simple_animal/hostile/asteroid/hivelord/legion = 30,
|
||||
/mob/living/simple_animal/hostile/spawner/lavaland = 2, /mob/living/simple_animal/hostile/spawner/lavaland/goliath = 3, /mob/living/simple_animal/hostile/spawner/lavaland/legion = 3, \
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon = 2, /mob/living/simple_animal/hostile/megafauna/bubblegum = 2, /mob/living/simple_animal/hostile/megafauna/colossus = 2)
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon = 2, /mob/living/simple_animal/hostile/megafauna/bubblegum = 2)
|
||||
|
||||
turf_type = /turf/open/floor/plating/asteroid/basalt/lava_land_surface
|
||||
initial_gas_mix = "o2=14;n2=23;TEMP=300"
|
||||
|
||||
@@ -297,11 +297,14 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
stat(null, "Station Time: [worldtime2text()]")
|
||||
if(ticker)
|
||||
if(ticker.mode)
|
||||
for(var/datum/gang/G in ticker.mode.gangs)
|
||||
if(G.is_dominating)
|
||||
stat(null, "[G.name] Gang Takeover: [max(G.domination_time_remaining(), 0)]")
|
||||
if(ticker && ticker.mode)
|
||||
for(var/datum/gang/G in ticker.mode.gangs)
|
||||
if(G.is_dominating)
|
||||
stat(null, "[G.name] Gang Takeover: [max(G.domination_time_remaining(), 0)]")
|
||||
if(istype(ticker.mode, /datum/game_mode/blob))
|
||||
var/datum/game_mode/blob/B = ticker.mode
|
||||
if(B.message_sent)
|
||||
stat(null, "Blobs to Blob Win: [blobs_legit.len]/[B.blobwincount]")
|
||||
|
||||
/mob/dead/observer/verb/reenter_corpse()
|
||||
set category = "Ghost"
|
||||
|
||||
@@ -203,12 +203,19 @@
|
||||
for(var/obj/item/I in BP.embedded_objects)
|
||||
msg += "<B>[t_He] [t_has] \a \icon[I] [I] embedded in [t_his] [BP.name]!</B>\n"
|
||||
|
||||
//stores how many left limbs are missing
|
||||
var/l_limbs_missing = 0
|
||||
for(var/t in missing)
|
||||
if(t=="head")
|
||||
msg += "<span class='deadsay'><B>[capitalize(t_his)] [parse_zone(t)] is missing!</B><span class='warning'>\n"
|
||||
continue
|
||||
if(t == "l_arm" || t == "l_leg")
|
||||
l_limbs_missing++
|
||||
msg += "<B>[capitalize(t_his)] [parse_zone(t)] is missing!</B>\n"
|
||||
|
||||
if(l_limbs_missing >= 2)
|
||||
msg += "[t_He] looks all right now.\n"
|
||||
|
||||
if(temp)
|
||||
if(temp < 30)
|
||||
msg += "[t_He] [t_has] minor bruising.\n"
|
||||
@@ -345,7 +352,6 @@
|
||||
msg += "<a href='?src=\ref[src];hud=s;add_crime=1'>\[Add crime\]</a> "
|
||||
msg += "<a href='?src=\ref[src];hud=s;view_comment=1'>\[View comment log\]</a> "
|
||||
msg += "<a href='?src=\ref[src];hud=s;add_comment=1'>\[Add comment\]</a>\n"
|
||||
|
||||
msg += "*---------*</span>"
|
||||
|
||||
user << msg
|
||||
|
||||
@@ -953,12 +953,16 @@ Sorry Giacom. Please don't be mad :(
|
||||
|
||||
/mob/living/Stat()
|
||||
..()
|
||||
|
||||
if(statpanel("Status"))
|
||||
if(ticker)
|
||||
if(ticker.mode)
|
||||
for(var/datum/gang/G in ticker.mode.gangs)
|
||||
if(G.is_dominating)
|
||||
stat(null, "[G.name] Gang Takeover: [max(G.domination_time_remaining(), 0)]")
|
||||
if(ticker && ticker.mode)
|
||||
for(var/datum/gang/G in ticker.mode.gangs)
|
||||
if(G.is_dominating)
|
||||
stat(null, "[G.name] Gang Takeover: [max(G.domination_time_remaining(), 0)]")
|
||||
if(istype(ticker.mode, /datum/game_mode/blob))
|
||||
var/datum/game_mode/blob/B = ticker.mode
|
||||
if(B.message_sent)
|
||||
stat(null, "Blobs to Blob Win: [blobs_legit.len]/[B.blobwincount]")
|
||||
|
||||
/mob/living/cancel_camera()
|
||||
..()
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
healable = 0
|
||||
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
maxbodytemp = INFINITY
|
||||
minbodytemp = 0
|
||||
has_unlimited_silicon_privilege = 1
|
||||
sentience_type = SENTIENCE_ARTIFICIAL
|
||||
|
||||
@@ -313,6 +313,7 @@ Difficulty: Very Hard
|
||||
return
|
||||
current = src
|
||||
ReadMemory()
|
||||
. = ..()
|
||||
|
||||
/obj/machinery/smartfridge/black_box/process()
|
||||
..()
|
||||
|
||||
@@ -51,6 +51,7 @@ Difficulty: Medium
|
||||
aggro_vision_range = 18
|
||||
idle_vision_range = 13
|
||||
appearance_flags = 0
|
||||
mouse_opacity = 1
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/legion/New()
|
||||
..()
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
var/elimination = 0
|
||||
anchored = TRUE
|
||||
layer = LARGE_MOB_LAYER //Looks weird with them slipping under mineral walls and cameras and shit otherwise
|
||||
mouse_opacity = 2 // Easier to click on in melee, they're giant targets anyway
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/death(gibbed)
|
||||
if(health > 0)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
var/icon_aggro = null // for swapping to when we get aggressive
|
||||
see_in_dark = 8
|
||||
see_invisible = SEE_INVISIBLE_MINIMUM
|
||||
mob_size = MOB_SIZE_LARGE
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/Aggro()
|
||||
..()
|
||||
@@ -452,7 +453,6 @@
|
||||
aggro_vision_range = 9
|
||||
idle_vision_range = 5
|
||||
anchored = 1 //Stays anchored until death as to be unpullable
|
||||
mob_size = MOB_SIZE_LARGE
|
||||
var/pre_attack = 0
|
||||
var/pre_attack_icon = "Goliath_preattack"
|
||||
loot = list(/obj/item/stack/sheet/animalhide/goliath_hide)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
mob_list -= src
|
||||
dead_mob_list -= src
|
||||
living_mob_list -= src
|
||||
all_clockwork_mobs -= src
|
||||
if(observers && observers.len)
|
||||
for(var/M in observers)
|
||||
var/mob/dead/observe = M
|
||||
@@ -593,7 +594,7 @@ var/next_mob_id = 0
|
||||
|
||||
if(statpanel("Status"))
|
||||
stat(null, "Map: [MAP_NAME]")
|
||||
if (nextmap && istype(nextmap))
|
||||
if(nextmap && istype(nextmap))
|
||||
stat(null, "Next Map: [nextmap.friendlyname]")
|
||||
stat(null, "Server Time: [time2text(world.realtime, "YYYY-MM-DD hh:mm")]")
|
||||
if(SSshuttle.emergency)
|
||||
|
||||
@@ -12,7 +12,26 @@
|
||||
return 1
|
||||
return (!mover.density || !density || lying)
|
||||
|
||||
//there are bugs with byond's default versions of these verbs
|
||||
/client/verb/moveup()
|
||||
set name = ".moveup"
|
||||
set instant = 1
|
||||
Move(get_step(mob, NORTH), NORTH)
|
||||
|
||||
/client/verb/movedown()
|
||||
set name = ".movedown"
|
||||
set instant = 1
|
||||
Move(get_step(mob, SOUTH), SOUTH)
|
||||
|
||||
/client/verb/moveright()
|
||||
set name = ".moveright"
|
||||
set instant = 1
|
||||
Move(get_step(mob, EAST), EAST)
|
||||
|
||||
/client/verb/moveleft()
|
||||
set name = ".moveleft"
|
||||
set instant = 1
|
||||
Move(get_step(mob, WEST), WEST)
|
||||
|
||||
/client/Northeast()
|
||||
swap_hand()
|
||||
@@ -90,14 +109,15 @@
|
||||
|
||||
|
||||
/client/Move(n, direct)
|
||||
if(world.time < move_delay)
|
||||
return 0
|
||||
move_delay = world.time+world.tick_lag //this is here because Move() can now be called mutiple times per tick
|
||||
if(!mob || !mob.loc)
|
||||
return 0
|
||||
if(mob.notransform)
|
||||
return 0 //This is sota the goto stop mobs from moving var
|
||||
if(mob.control_object)
|
||||
return Move_object(direct)
|
||||
if(world.time < move_delay)
|
||||
return 0
|
||||
if(!isliving(mob))
|
||||
return mob.Move(n,direct)
|
||||
if(mob.stat == DEAD)
|
||||
|
||||
@@ -422,8 +422,8 @@
|
||||
. = new_slime
|
||||
qdel(src)
|
||||
|
||||
/mob/proc/become_overmind(mode_made = 0)
|
||||
var/mob/camera/blob/B = new /mob/camera/blob(loc, 0, mode_made)
|
||||
/mob/proc/become_overmind(mode_made, starting_points = 60)
|
||||
var/mob/camera/blob/B = new /mob/camera/blob(loc, 0, mode_made, starting_points)
|
||||
if(mind)
|
||||
mind.transfer_to(B)
|
||||
else
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
var/global/static/ntnrc_uid = 0
|
||||
|
||||
/datum/ntnet_conversation/
|
||||
var/id = null
|
||||
var/title = "Untitled Conversation"
|
||||
var/datum/computer_file/program/chatclient/operator // "Administrator" of this channel. Creator starts as channel's operator,
|
||||
var/list/messages = list()
|
||||
var/list/clients = list()
|
||||
var/password
|
||||
|
||||
/datum/ntnet_conversation/New()
|
||||
id = ntnrc_uid
|
||||
ntnrc_uid++
|
||||
if(ntnet_global)
|
||||
ntnet_global.chat_channels.Add(src)
|
||||
..()
|
||||
|
||||
/datum/ntnet_conversation/proc/add_message(message, username)
|
||||
message = "[worldtime2text()] [username]: [message]"
|
||||
messages.Add(message)
|
||||
trim_message_list()
|
||||
|
||||
/datum/ntnet_conversation/proc/add_status_message(message)
|
||||
messages.Add("[worldtime2text()] -!- [message]")
|
||||
trim_message_list()
|
||||
|
||||
/datum/ntnet_conversation/proc/trim_message_list()
|
||||
if(messages.len <= 50)
|
||||
return
|
||||
messages = messages.Copy(messages.len-50 ,0)
|
||||
|
||||
/datum/ntnet_conversation/proc/add_client(datum/computer_file/program/chatclient/C)
|
||||
if(!istype(C))
|
||||
return
|
||||
clients.Add(C)
|
||||
add_status_message("[C.username] has joined the channel.")
|
||||
// No operator, so we assume the channel was empty. Assign this user as operator.
|
||||
if(!operator)
|
||||
changeop(C)
|
||||
|
||||
/datum/ntnet_conversation/proc/remove_client(datum/computer_file/program/chatclient/C)
|
||||
if(!istype(C) || !(C in clients))
|
||||
return
|
||||
clients.Remove(C)
|
||||
add_status_message("[C.username] has left the channel.")
|
||||
|
||||
// Channel operator left, pick new operator
|
||||
if(C == operator)
|
||||
operator = null
|
||||
if(clients.len)
|
||||
var/datum/computer_file/program/chatclient/newop = pick(clients)
|
||||
changeop(newop)
|
||||
|
||||
|
||||
/datum/ntnet_conversation/proc/changeop(datum/computer_file/program/chatclient/newop)
|
||||
if(istype(newop))
|
||||
operator = newop
|
||||
add_status_message("Channel operator status transferred to [newop.username].")
|
||||
|
||||
/datum/ntnet_conversation/proc/change_title(newtitle, datum/computer_file/program/chatclient/client)
|
||||
if(operator != client)
|
||||
return 0 // Not Authorised
|
||||
|
||||
add_status_message("[client.username] has changed channel title from [title] to [newtitle]")
|
||||
title = newtitle
|
||||
@@ -0,0 +1,145 @@
|
||||
var/global/datum/ntnet/ntnet_global = new()
|
||||
|
||||
|
||||
// This is the NTNet datum. There can be only one NTNet datum in game at once. Modular computers read data from this.
|
||||
/datum/ntnet/
|
||||
var/list/relays = list()
|
||||
var/list/logs = list()
|
||||
var/list/available_station_software = list()
|
||||
var/list/available_antag_software = list()
|
||||
var/list/chat_channels = list()
|
||||
var/list/fileservers = list()
|
||||
// Amount of logs the system tries to keep in memory. Keep below 999 to prevent byond from acting weirdly.
|
||||
// High values make displaying logs much laggier.
|
||||
var/setting_maxlogcount = 100
|
||||
|
||||
// These only affect wireless. LAN (consoles) are unaffected since it would be possible to create scenario where someone turns off NTNet, and is unable to turn it back on since it refuses connections
|
||||
var/setting_softwaredownload = 1
|
||||
var/setting_peertopeer = 1
|
||||
var/setting_communication = 1
|
||||
var/setting_systemcontrol = 1
|
||||
var/setting_disabled = 0 // Setting to 1 will disable all wireless, independently on relays status.
|
||||
|
||||
var/intrusion_detection_enabled = 1 // Whether the IDS warning system is enabled
|
||||
var/intrusion_detection_alarm = 0 // Set when there is an IDS warning due to malicious (antag) software.
|
||||
|
||||
|
||||
// If new NTNet datum is spawned, it replaces the old one.
|
||||
/datum/ntnet/New()
|
||||
if(ntnet_global && (ntnet_global != src))
|
||||
ntnet_global = src // There can be only one.
|
||||
for(var/obj/machinery/ntnet_relay/R in machines)
|
||||
relays.Add(R)
|
||||
R.NTNet = src
|
||||
build_software_lists()
|
||||
add_log("NTNet logging system activated.")
|
||||
|
||||
// Simplified logging: Adds a log. log_string is mandatory parameter, source is optional.
|
||||
/datum/ntnet/proc/add_log(log_string, obj/item/weapon/computer_hardware/network_card/source = null)
|
||||
var/log_text = "[worldtime2text()] - "
|
||||
if(source)
|
||||
log_text += "[source.get_network_tag()] - "
|
||||
else
|
||||
log_text += "*SYSTEM* - "
|
||||
log_text += log_string
|
||||
logs.Add(log_text)
|
||||
|
||||
|
||||
// We have too many logs, remove the oldest entries until we get into the limit
|
||||
if(logs.len > setting_maxlogcount)
|
||||
logs = logs.Copy(logs.len-setting_maxlogcount,0)
|
||||
|
||||
|
||||
// Checks whether NTNet operates. If parameter is passed checks whether specific function is enabled.
|
||||
/datum/ntnet/proc/check_function(specific_action = 0)
|
||||
if(!relays || !relays.len) // No relays found. NTNet is down
|
||||
return 0
|
||||
|
||||
var/operating = 0
|
||||
|
||||
// Check all relays. If we have at least one working relay, network is up.
|
||||
for(var/M in relays)
|
||||
var/obj/machinery/ntnet_relay/R = M
|
||||
if(R.is_operational())
|
||||
operating = 1
|
||||
break
|
||||
|
||||
if(setting_disabled)
|
||||
return 0
|
||||
|
||||
switch(specific_action)
|
||||
if(NTNET_SOFTWAREDOWNLOAD)
|
||||
return (operating && setting_softwaredownload)
|
||||
if(NTNET_PEERTOPEER)
|
||||
return (operating && setting_peertopeer)
|
||||
if(NTNET_COMMUNICATION)
|
||||
return (operating && setting_communication)
|
||||
if(NTNET_SYSTEMCONTROL)
|
||||
return (operating && setting_systemcontrol)
|
||||
return operating
|
||||
|
||||
// Builds lists that contain downloadable software.
|
||||
/datum/ntnet/proc/build_software_lists()
|
||||
available_station_software = list()
|
||||
available_antag_software = list()
|
||||
for(var/F in typesof(/datum/computer_file/program))
|
||||
var/datum/computer_file/program/prog = new F
|
||||
// Invalid type (shouldn't be possible but just in case), invalid filetype (not executable program) or invalid filename (unset program)
|
||||
if(!prog || prog.filename == "UnknownProgram" || prog.filetype != "PRG")
|
||||
continue
|
||||
// Check whether the program should be available for station/antag download, if yes, add it to lists.
|
||||
if(prog.available_on_ntnet)
|
||||
available_station_software.Add(prog)
|
||||
if(prog.available_on_syndinet)
|
||||
available_antag_software.Add(prog)
|
||||
|
||||
// Attempts to find a downloadable file according to filename var
|
||||
/datum/ntnet/proc/find_ntnet_file_by_name(filename)
|
||||
for(var/N in available_station_software)
|
||||
var/datum/computer_file/program/P = N
|
||||
if(filename == P.filename)
|
||||
return P
|
||||
for(var/N in available_antag_software)
|
||||
var/datum/computer_file/program/P = N
|
||||
if(filename == P.filename)
|
||||
return P
|
||||
|
||||
// Resets the IDS alarm
|
||||
/datum/ntnet/proc/resetIDS()
|
||||
intrusion_detection_alarm = 0
|
||||
|
||||
/datum/ntnet/proc/toggleIDS()
|
||||
resetIDS()
|
||||
intrusion_detection_enabled = !intrusion_detection_enabled
|
||||
|
||||
// Removes all logs
|
||||
/datum/ntnet/proc/purge_logs()
|
||||
logs = list()
|
||||
add_log("-!- LOGS DELETED BY SYSTEM OPERATOR -!-")
|
||||
|
||||
// Updates maximal amount of stored logs. Use this instead of setting the number, it performs required checks.
|
||||
/datum/ntnet/proc/update_max_log_count(lognumber)
|
||||
if(!lognumber)
|
||||
return 0
|
||||
// Trim the value if necessary
|
||||
lognumber = max(MIN_NTNET_LOGS, min(lognumber, MAX_NTNET_LOGS))
|
||||
setting_maxlogcount = lognumber
|
||||
add_log("Configuration Updated. Now keeping [setting_maxlogcount] logs in system memory.")
|
||||
|
||||
/datum/ntnet/proc/toggle_function(function)
|
||||
if(!function)
|
||||
return
|
||||
function = text2num(function)
|
||||
switch(function)
|
||||
if(NTNET_SOFTWAREDOWNLOAD)
|
||||
setting_softwaredownload = !setting_softwaredownload
|
||||
add_log("Configuration Updated. Wireless network firewall now [setting_softwaredownload ? "allows" : "disallows"] connection to software repositories.")
|
||||
if(NTNET_PEERTOPEER)
|
||||
setting_peertopeer = !setting_peertopeer
|
||||
add_log("Configuration Updated. Wireless network firewall now [setting_peertopeer ? "allows" : "disallows"] peer to peer network traffic.")
|
||||
if(NTNET_COMMUNICATION)
|
||||
setting_communication = !setting_communication
|
||||
add_log("Configuration Updated. Wireless network firewall now [setting_communication ? "allows" : "disallows"] instant messaging and similar communication services.")
|
||||
if(NTNET_SYSTEMCONTROL)
|
||||
setting_systemcontrol = !setting_systemcontrol
|
||||
add_log("Configuration Updated. Wireless network firewall now [setting_systemcontrol ? "allows" : "disallows"] remote control of station's systems.")
|
||||
@@ -0,0 +1,127 @@
|
||||
// Relays don't handle any actual communication. Global NTNet datum does that, relays only tell the datum if it should or shouldn't work.
|
||||
/obj/machinery/ntnet_relay
|
||||
name = "NTNet Quantum Relay"
|
||||
desc = "A very complex router and transmitter capable of connecting electronic devices together. Looks fragile."
|
||||
use_power = 2
|
||||
active_power_usage = 20000 //20kW, apropriate for machine that keeps massive cross-Zlevel wireless network operational.
|
||||
idle_power_usage = 100
|
||||
icon = 'icons/obj/machines/telecomms.dmi'
|
||||
icon_state = "bus"
|
||||
anchored = 1
|
||||
density = 1
|
||||
var/datum/ntnet/NTNet = null // This is mostly for backwards reference and to allow varedit modifications from ingame.
|
||||
var/enabled = 1 // Set to 0 if the relay was turned off
|
||||
var/dos_failure = 0 // Set to 1 if the relay failed due to (D)DoS attack
|
||||
var/list/dos_sources = list() // Backwards reference for qdel() stuff
|
||||
|
||||
// Denial of Service attack variables
|
||||
var/dos_overload = 0 // Amount of DoS "packets" in this relay's buffer
|
||||
var/dos_capacity = 500 // Amount of DoS "packets" in buffer required to crash the relay
|
||||
var/dos_dissipate = 1 // Amount of DoS "packets" dissipated over time.
|
||||
|
||||
|
||||
// TODO: Implement more logic here. For now it's only a placeholder.
|
||||
/obj/machinery/ntnet_relay/is_operational()
|
||||
if(stat & (BROKEN | NOPOWER | EMPED))
|
||||
return 0
|
||||
if(dos_failure)
|
||||
return 0
|
||||
if(!enabled)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/obj/machinery/ntnet_relay/update_icon()
|
||||
if(is_operational())
|
||||
icon_state = "bus"
|
||||
else
|
||||
icon_state = "bus_off"
|
||||
|
||||
/obj/machinery/ntnet_relay/process()
|
||||
if(is_operational())
|
||||
use_power = 2
|
||||
else
|
||||
use_power = 1
|
||||
|
||||
if(dos_overload)
|
||||
dos_overload = max(0, dos_overload - dos_dissipate)
|
||||
|
||||
// If DoS traffic exceeded capacity, crash.
|
||||
if((dos_overload > dos_capacity) && !dos_failure)
|
||||
dos_failure = 1
|
||||
update_icon()
|
||||
ntnet_global.add_log("Quantum relay switched from normal operation mode to overload recovery mode.")
|
||||
// If the DoS buffer reaches 0 again, restart.
|
||||
if((dos_overload == 0) && dos_failure)
|
||||
dos_failure = 0
|
||||
update_icon()
|
||||
ntnet_global.add_log("Quantum relay switched from overload recovery mode to normal operation mode.")
|
||||
..()
|
||||
|
||||
/obj/machinery/ntnet_relay/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = default_state)
|
||||
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "ntnet_relay", "NTNet Quantum Relay", 500, 300, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
|
||||
/obj/machinery/ntnet_relay/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["enabled"] = enabled
|
||||
data["dos_capacity"] = dos_capacity
|
||||
data["dos_overload"] = dos_overload
|
||||
data["dos_crashed"] = dos_failure
|
||||
return data
|
||||
|
||||
|
||||
/obj/machinery/ntnet_relay/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
switch(action)
|
||||
if("restart")
|
||||
dos_overload = 0
|
||||
dos_failure = 0
|
||||
update_icon()
|
||||
ntnet_global.add_log("Quantum relay manually restarted from overload recovery mode to normal operation mode.")
|
||||
if("toggle")
|
||||
enabled = !enabled
|
||||
ntnet_global.add_log("Quantum relay manually [enabled ? "enabled" : "disabled"].")
|
||||
update_icon()
|
||||
|
||||
|
||||
/obj/machinery/ntnet_relay/attack_hand(mob/living/user)
|
||||
ui_interact(user)
|
||||
|
||||
/obj/machinery/ntnet_relay/New()
|
||||
uid = gl_uid
|
||||
gl_uid++
|
||||
component_parts = list()
|
||||
var/obj/item/weapon/circuitboard/machine/B = new /obj/item/weapon/circuitboard/machine/ntnet_relay(null)
|
||||
B.apply_default_parts(src)
|
||||
|
||||
if(ntnet_global)
|
||||
ntnet_global.relays.Add(src)
|
||||
NTNet = ntnet_global
|
||||
ntnet_global.add_log("New quantum relay activated. Current amount of linked relays: [NTNet.relays.len]")
|
||||
..()
|
||||
|
||||
/obj/machinery/ntnet_relay/Destroy()
|
||||
if(ntnet_global)
|
||||
ntnet_global.relays.Remove(src)
|
||||
ntnet_global.add_log("Quantum relay connection severed. Current amount of linked relays: [NTNet.relays.len]")
|
||||
NTNet = null
|
||||
|
||||
for(var/datum/computer_file/program/ntnet_dos/D in dos_sources)
|
||||
D.target = null
|
||||
D.error = "Connection to quantum relay severed"
|
||||
|
||||
..()
|
||||
|
||||
/obj/item/weapon/circuitboard/machine/ntnet_relay
|
||||
name = "circuit board (NTNet Relay)"
|
||||
build_path = /obj/machinery/ntnet_relay
|
||||
origin_tech = "programming=3;bluespace=3;magnets=2"
|
||||
req_components = list(
|
||||
/obj/item/stack/cable_coil = 2,
|
||||
/obj/item/weapon/stock_parts/subspace/filter = 1)
|
||||
@@ -0,0 +1,704 @@
|
||||
// This is the base type that does all the hardware stuff.
|
||||
// Other types expand it - tablets use a direct subtypes, and
|
||||
// consoles and laptops use "procssor" item that is held inside machinery piece
|
||||
/obj/item/modular_computer
|
||||
name = "Modular Microcomputer"
|
||||
desc = "A small portable microcomputer."
|
||||
|
||||
var/enabled = 0 // Whether the computer is turned on.
|
||||
var/screen_on = 1 // Whether the computer is active/opened/it's screen is on.
|
||||
var/datum/computer_file/program/active_program = null // A currently active program running on the computer.
|
||||
var/hardware_flag = 0 // A flag that describes this device type
|
||||
var/last_power_usage = 0
|
||||
var/last_battery_percent = 0 // Used for deciding if battery percentage has chandged
|
||||
var/last_world_time = "00:00"
|
||||
var/list/last_header_icons
|
||||
var/computer_emagged = 0 // Whether the computer is emagged.
|
||||
|
||||
var/base_active_power_usage = 50 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too.
|
||||
var/base_idle_power_usage = 5 // Power usage when the computer is idle and screen is off (currently only applies to laptops)
|
||||
|
||||
// Modular computers can run on various devices. Each DEVICE (Laptop, Console, Tablet,..)
|
||||
// must have it's own DMI file. Icon states must be called exactly the same in all files, but may look differently
|
||||
// If you create a program which is limited to Laptops and Consoles you don't have to add it's icon_state overlay for Tablets too, for example.
|
||||
|
||||
icon = 'icons/obj/computer.dmi'
|
||||
icon_state = "laptop-open"
|
||||
var/icon_state_unpowered = null // Icon state when the computer is turned off
|
||||
var/icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen.
|
||||
var/max_hardware_size = 0 // Maximal hardware size. Currently, tablets have 1, laptops 2 and consoles 3. Limits what hardware types can be installed.
|
||||
var/steel_sheet_cost = 5 // Amount of steel sheets refunded when disassembling an empty frame of this computer.
|
||||
|
||||
// Damage of the chassis. If the chassis takes too much damage it will break apart.
|
||||
var/damage = 0 // Current damage level
|
||||
var/broken_damage = 50 // Damage level at which the computer ceases to operate
|
||||
var/max_damage = 100 // Damage level at which the computer breaks apart.
|
||||
|
||||
// Important hardware (must be installed for computer to work)
|
||||
var/obj/item/weapon/computer_hardware/processor_unit/processor_unit // CPU. Without it the computer won't run. Better CPUs can run more programs at once.
|
||||
var/obj/item/weapon/computer_hardware/network_card/network_card // Network Card component of this computer. Allows connection to NTNet
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/hard_drive // Hard Drive component of this computer. Stores programs and files.
|
||||
var/obj/item/weapon/computer_hardware/battery_module/battery_module // An internal power source for this computer. Can be recharged.
|
||||
// Optional hardware (improves functionality, but is not critical for computer to work)
|
||||
var/obj/item/weapon/computer_hardware/card_slot/card_slot // ID Card slot component of this computer. Mostly for HoP modification console that needs ID slot for modification.
|
||||
var/obj/item/weapon/computer_hardware/card_slot/card_slot2 // second slot. popular demand requested the hop need to insert 2 ids.
|
||||
var/obj/item/weapon/computer_hardware/nano_printer/nano_printer // Nano Printer component of this computer, for your everyday paperwork needs.
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/portable/portable_drive // Portable data storage
|
||||
|
||||
var/list/idle_threads = list() // Idle programs on background. They still receive process calls but can't be interacted with.
|
||||
var/obj/physical = null
|
||||
|
||||
|
||||
|
||||
// Eject ID card from computer, if it has ID slot with card inside.
|
||||
/obj/item/modular_computer/verb/eject_id()
|
||||
set name = "Eject ID"
|
||||
set category = "Object"
|
||||
set src in view(1)
|
||||
|
||||
if(usr.incapacitated() || !istype(usr, /mob/living))
|
||||
usr << "<span class='warning'>You can't do that.</span>"
|
||||
return
|
||||
|
||||
if(!Adjacent(usr))
|
||||
usr << "<span class='warning'>You can't reach it.</span>"
|
||||
return
|
||||
|
||||
proc_eject_id(usr)
|
||||
|
||||
// Eject ID card from computer, if it has ID slot with card inside.
|
||||
/obj/item/modular_computer/verb/eject_usb()
|
||||
set name = "Eject Portable Device"
|
||||
set category = "Object"
|
||||
set src in view(1)
|
||||
|
||||
if(usr.incapacitated() || !istype(usr, /mob/living))
|
||||
usr << "<span class='warning'>You can't do that.</span>"
|
||||
return
|
||||
|
||||
if(!Adjacent(usr))
|
||||
usr << "<span class='warning'>You can't reach it.</span>"
|
||||
return
|
||||
|
||||
proc_eject_usb(usr)
|
||||
|
||||
/obj/item/modular_computer/proc/proc_eject_id(mob/user, slot)
|
||||
if(!user)
|
||||
user = usr
|
||||
|
||||
if(!card_slot)
|
||||
user << "\The [src] does not have an ID card slot"
|
||||
return
|
||||
|
||||
if(!card_slot.stored_card && !card_slot.stored_card2)
|
||||
user << "There is no card in \the [src]"
|
||||
return
|
||||
|
||||
if(active_program)
|
||||
active_program.event_idremoved(0, slot)
|
||||
|
||||
for(var/I in idle_threads)
|
||||
var/datum/computer_file/program/P = I
|
||||
P.event_idremoved(1, slot)
|
||||
if(card_slot.stored_card && (!slot || slot == 1))
|
||||
card_slot.stored_card.forceMove(get_turf(src))
|
||||
card_slot.stored_card = null
|
||||
if(card_slot.stored_card2 && (!slot || slot == 2))
|
||||
card_slot.stored_card2.forceMove(get_turf(src))
|
||||
card_slot.stored_card2 = null
|
||||
user << "You remove the card[card_slot.stored_card && card_slot.stored_card2 && !slot ? "s" : ""] from \the [src]"
|
||||
|
||||
/obj/item/modular_computer/proc/proc_eject_usb(mob/user)
|
||||
if(!user)
|
||||
user = usr
|
||||
|
||||
if(!portable_drive)
|
||||
user << "There is no portable device connected to \the [src]."
|
||||
return
|
||||
|
||||
uninstall_component(user, portable_drive)
|
||||
|
||||
/obj/item/modular_computer/attack_ghost(mob/dead/observer/user)
|
||||
if(enabled)
|
||||
ui_interact(user)
|
||||
else if(check_rights_for(user.client, R_ADMIN))
|
||||
var/response = alert(user, "This computer is turned off. Would you like to turn it on?", "Admin Override", "Yes", "No")
|
||||
if(response == "Yes")
|
||||
turn_on(user)
|
||||
|
||||
/obj/item/modular_computer/emag_act(mob/user)
|
||||
if(computer_emagged)
|
||||
user << "\The [src] was already emagged."
|
||||
return 0
|
||||
else
|
||||
computer_emagged = 1
|
||||
user << "You emag \the [src]. It's screen briefly shows a \"OVERRIDE ACCEPTED: New software downloads available.\" message."
|
||||
return 1
|
||||
|
||||
/obj/item/modular_computer/examine(mob/user)
|
||||
..()
|
||||
if(damage > broken_damage)
|
||||
user << "<span class='danger'>It is heavily damaged!</span>"
|
||||
else if(damage)
|
||||
user << "It is damaged."
|
||||
|
||||
/obj/item/modular_computer/New()
|
||||
START_PROCESSING(SSmachine, src)
|
||||
update_icon()
|
||||
if(!physical)
|
||||
physical = src
|
||||
..()
|
||||
|
||||
/obj/item/modular_computer/Destroy()
|
||||
kill_program(1)
|
||||
STOP_PROCESSING(SSmachine, src)
|
||||
for(var/H in get_all_components())
|
||||
var/obj/item/weapon/computer_hardware/CH = H
|
||||
uninstall_component(null, CH)
|
||||
qdel(CH)
|
||||
return ..()
|
||||
|
||||
/obj/item/modular_computer/update_icon()
|
||||
icon_state = icon_state_unpowered
|
||||
cut_overlays()
|
||||
if(!enabled)
|
||||
return
|
||||
if(active_program)
|
||||
add_overlay(active_program.program_icon_state ? active_program.program_icon_state : icon_state_menu)
|
||||
else
|
||||
add_overlay(icon_state_menu)
|
||||
|
||||
// Used by child types if they have other power source than battery
|
||||
/obj/item/modular_computer/proc/check_power_override()
|
||||
return 0
|
||||
|
||||
// Operates NanoUI
|
||||
/obj/item/modular_computer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = default_state)
|
||||
|
||||
|
||||
if(!screen_on || !enabled)
|
||||
if(ui)
|
||||
ui.close()
|
||||
return 0
|
||||
if((!battery_module || !battery_module.battery.charge) && !check_power_override())
|
||||
if(ui)
|
||||
ui.close()
|
||||
return 0
|
||||
|
||||
// If we have an active program switch to it now.
|
||||
if(active_program)
|
||||
if(ui) // This is the main laptop screen. Since we are switching to program's UI close it for now.
|
||||
ui.close()
|
||||
active_program.ui_interact(user)
|
||||
return
|
||||
|
||||
// We are still here, that means there is no program loaded. Load the BIOS/ROM/OS/whatever you want to call it.
|
||||
// This screen simply lists available programs and user may select them.
|
||||
if(!hard_drive || !hard_drive.stored_files || !hard_drive.stored_files.len)
|
||||
physical.visible_message("\The [src] beeps three times, it's screen displaying \"DISK ERROR\" warning.")
|
||||
return // No HDD, No HDD files list or no stored files. Something is very broken.
|
||||
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if (!ui)
|
||||
|
||||
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers)
|
||||
assets.send(user)
|
||||
|
||||
ui = new(user, src, ui_key, "computer_main", "NTOS Main menu", 400, 500, master_ui, state)
|
||||
ui.open()
|
||||
ui.set_autoupdate(state = 1)
|
||||
|
||||
|
||||
/obj/item/modular_computer/ui_data(mob/user)
|
||||
|
||||
var/list/data = get_header_data()
|
||||
data["programs"] = list()
|
||||
for(var/datum/computer_file/program/P in hard_drive.stored_files)
|
||||
var/running = 0
|
||||
if(P in idle_threads)
|
||||
running = 1
|
||||
|
||||
data["programs"] += list(list("name" = P.filename, "desc" = P.filedesc, "running" = running))
|
||||
|
||||
return data
|
||||
|
||||
|
||||
// On-click handling. Turns on the computer if it's off and opens the GUI.
|
||||
/obj/item/modular_computer/attack_self(mob/user)
|
||||
if(enabled)
|
||||
ui_interact(user)
|
||||
else
|
||||
turn_on(user)
|
||||
|
||||
/obj/item/modular_computer/proc/break_apart()
|
||||
physical.visible_message("\The [src] breaks apart!")
|
||||
var/turf/newloc = get_turf(src)
|
||||
new /obj/item/stack/sheet/metal(newloc, round(steel_sheet_cost/2))
|
||||
for(var/C in get_all_components())
|
||||
var/obj/item/weapon/computer_hardware/H = C
|
||||
uninstall_component(null, H)
|
||||
H.forceMove(newloc)
|
||||
if(prob(25))
|
||||
H.take_damage(rand(10,30))
|
||||
relay_qdel()
|
||||
qdel()
|
||||
|
||||
/obj/item/modular_computer/proc/turn_on(mob/user)
|
||||
var/issynth = issilicon(user) // Robots and AIs get different activation messages.
|
||||
if(damage > broken_damage)
|
||||
if(issynth)
|
||||
user << "You send an activation signal to \the [src], but it responds with an error code. It must be damaged."
|
||||
else
|
||||
user << "You press the power button, but the computer fails to boot up, displaying variety of errors before shutting down again."
|
||||
return
|
||||
if(processor_unit && ((battery_module && battery_module.battery.charge && battery_module.check_functionality()) || check_power_override())) // Battery-run and charged or non-battery but powered by APC.
|
||||
if(issynth)
|
||||
user << "You send an activation signal to \the [src], turning it on"
|
||||
else
|
||||
user << "You press the power button and start up \the [src]"
|
||||
enabled = 1
|
||||
update_icon()
|
||||
ui_interact(user)
|
||||
else // Unpowered
|
||||
if(issynth)
|
||||
user << "You send an activation signal to \the [src] but it does not respond"
|
||||
else
|
||||
user << "You press the power button but \the [src] does not respond"
|
||||
|
||||
// Process currently calls handle_power(), may be expanded in future if more things are added.
|
||||
/obj/item/modular_computer/process()
|
||||
if(!enabled) // The computer is turned off
|
||||
last_power_usage = 0
|
||||
return 0
|
||||
|
||||
if(damage > broken_damage)
|
||||
shutdown_computer()
|
||||
return 0
|
||||
|
||||
if(active_program && active_program.requires_ntnet && !get_ntnet_status(active_program.requires_ntnet_feature)) // Active program requires NTNet to run but we've just lost connection. Crash.
|
||||
active_program.event_networkfailure(0)
|
||||
|
||||
for(var/I in idle_threads)
|
||||
var/datum/computer_file/program/P = I
|
||||
if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature))
|
||||
P.event_networkfailure(1)
|
||||
|
||||
if(active_program)
|
||||
if(active_program.program_state != PROGRAM_STATE_KILLED)
|
||||
active_program.process_tick()
|
||||
active_program.ntnet_status = get_ntnet_status()
|
||||
active_program.computer_emagged = computer_emagged
|
||||
else
|
||||
active_program = null
|
||||
|
||||
for(var/I in idle_threads)
|
||||
var/datum/computer_file/program/P = I
|
||||
if(P.program_state != PROGRAM_STATE_KILLED)
|
||||
P.process_tick()
|
||||
P.ntnet_status = get_ntnet_status()
|
||||
P.computer_emagged = computer_emagged
|
||||
else
|
||||
idle_threads.Remove(P)
|
||||
|
||||
handle_power() // Handles all computer power interaction
|
||||
//check_update_ui_need()
|
||||
|
||||
// Function used by NanoUI's to obtain data for header. All relevant entries begin with "PC_"
|
||||
/obj/item/modular_computer/proc/get_header_data()
|
||||
var/list/data = list()
|
||||
|
||||
if(battery_module)
|
||||
switch(battery_module.battery.percent())
|
||||
if(80 to 200) // 100 should be maximal but just in case..
|
||||
data["PC_batteryicon"] = "batt_100.gif"
|
||||
if(60 to 80)
|
||||
data["PC_batteryicon"] = "batt_80.gif"
|
||||
if(40 to 60)
|
||||
data["PC_batteryicon"] = "batt_60.gif"
|
||||
if(20 to 40)
|
||||
data["PC_batteryicon"] = "batt_40.gif"
|
||||
if(5 to 20)
|
||||
data["PC_batteryicon"] = "batt_20.gif"
|
||||
else
|
||||
data["PC_batteryicon"] = "batt_5.gif"
|
||||
data["PC_batterypercent"] = "[round(battery_module.battery.percent())] %"
|
||||
data["PC_showbatteryicon"] = 1
|
||||
else
|
||||
data["PC_batteryicon"] = "batt_5.gif"
|
||||
data["PC_batterypercent"] = "N/C"
|
||||
data["PC_showbatteryicon"] = battery_module ? 1 : 0
|
||||
|
||||
switch(get_ntnet_status())
|
||||
if(0)
|
||||
data["PC_ntneticon"] = "sig_none.gif"
|
||||
if(1)
|
||||
data["PC_ntneticon"] = "sig_low.gif"
|
||||
if(2)
|
||||
data["PC_ntneticon"] = "sig_high.gif"
|
||||
if(3)
|
||||
data["PC_ntneticon"] = "sig_lan.gif"
|
||||
|
||||
if(idle_threads.len)
|
||||
var/list/program_headers = list()
|
||||
for(var/I in idle_threads)
|
||||
var/datum/computer_file/program/P = I
|
||||
if(!P.ui_header)
|
||||
continue
|
||||
program_headers.Add(list(list(
|
||||
"icon" = P.ui_header
|
||||
)))
|
||||
|
||||
data["PC_programheaders"] = program_headers
|
||||
|
||||
data["PC_stationtime"] = worldtime2text()
|
||||
data["PC_hasheader"] = 1
|
||||
data["PC_showexitprogram"] = active_program ? 1 : 0 // Hides "Exit Program" button on mainscreen
|
||||
return data
|
||||
|
||||
// Relays kill program request to currently active program. Use this to quit current program.
|
||||
/obj/item/modular_computer/proc/kill_program(forced = 0)
|
||||
if(active_program)
|
||||
active_program.kill_program(forced)
|
||||
active_program = null
|
||||
var/mob/user = usr
|
||||
if(user && istype(user))
|
||||
ui_interact(user) // Re-open the UI on this computer. It should show the main screen now.
|
||||
update_icon()
|
||||
|
||||
// Returns 0 for No Signal, 1 for Low Signal and 2 for Good Signal. 3 is for wired connection (always-on)
|
||||
/obj/item/modular_computer/proc/get_ntnet_status(specific_action = 0)
|
||||
if(network_card)
|
||||
return network_card.get_signal(specific_action)
|
||||
else
|
||||
return 0
|
||||
|
||||
/obj/item/modular_computer/proc/add_log(text)
|
||||
if(!get_ntnet_status())
|
||||
return 0
|
||||
return ntnet_global.add_log(text, network_card)
|
||||
|
||||
/obj/item/modular_computer/proc/shutdown_computer(loud = 1)
|
||||
kill_program(1)
|
||||
for(var/datum/computer_file/program/P in idle_threads)
|
||||
P.kill_program(1)
|
||||
idle_threads.Remove(P)
|
||||
if(loud)
|
||||
physical.visible_message("\The [src] shuts down.")
|
||||
enabled = 0
|
||||
update_icon()
|
||||
return
|
||||
|
||||
// Handles user's GUI input
|
||||
/obj/item/modular_computer/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
switch(action)
|
||||
if("PC_exit")
|
||||
kill_program()
|
||||
return 1
|
||||
if("PC_shutdown")
|
||||
shutdown_computer()
|
||||
return 1
|
||||
if("PC_minimize")
|
||||
var/mob/user = usr
|
||||
if(!active_program || !processor_unit)
|
||||
return
|
||||
|
||||
idle_threads.Add(active_program)
|
||||
active_program.program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs
|
||||
|
||||
active_program = null
|
||||
update_icon()
|
||||
if(user && istype(user))
|
||||
ui_interact(user) // Re-open the UI on this computer. It should show the main screen now.
|
||||
|
||||
if("PC_killprogram")
|
||||
var/prog = params["name"]
|
||||
var/datum/computer_file/program/P = null
|
||||
var/mob/user = usr
|
||||
if(hard_drive)
|
||||
P = hard_drive.find_file_by_name(prog)
|
||||
|
||||
if(!istype(P) || P.program_state == PROGRAM_STATE_KILLED)
|
||||
return
|
||||
|
||||
P.kill_program(1)
|
||||
user << "<span class='notice'>Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed.</span>"
|
||||
|
||||
if("PC_runprogram")
|
||||
var/prog = params["name"]
|
||||
var/datum/computer_file/program/P = null
|
||||
var/mob/user = usr
|
||||
if(hard_drive)
|
||||
P = hard_drive.find_file_by_name(prog)
|
||||
|
||||
if(!P || !istype(P)) // Program not found or it's not executable program.
|
||||
user << "<span class='danger'>\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning.</span>"
|
||||
return
|
||||
|
||||
P.computer = src
|
||||
|
||||
if(!P.is_supported_by_hardware(hardware_flag, 1, user))
|
||||
return
|
||||
|
||||
// The program is already running. Resume it.
|
||||
if(P in idle_threads)
|
||||
P.program_state = PROGRAM_STATE_ACTIVE
|
||||
active_program = P
|
||||
idle_threads.Remove(P)
|
||||
update_icon()
|
||||
return
|
||||
|
||||
if(idle_threads.len >= processor_unit.max_idle_programs+1)
|
||||
user << "<span class='notice'>\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error</span>"
|
||||
return
|
||||
|
||||
if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) // The program requires NTNet connection, but we are not connected to NTNet.
|
||||
user << "<span class='danger'>\The [src]'s screen shows \"NETWORK ERROR - Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning.</span>"
|
||||
return
|
||||
P.computer_emagged = computer_emagged
|
||||
if(P.run_program(user))
|
||||
active_program = P
|
||||
update_icon()
|
||||
return 1
|
||||
else
|
||||
return
|
||||
|
||||
// Used in following function to reduce copypaste
|
||||
/obj/item/modular_computer/proc/power_failure(malfunction = 0)
|
||||
if(enabled) // Shut down the computer
|
||||
physical.visible_message("<span class='danger'>\The [src]'s screen flickers \"BATTERY [malfunction ? "MALFUNCTION" : "CRITICAL"]\" warning as it shuts down unexpectedly.</span>")
|
||||
if(active_program)
|
||||
active_program.event_powerfailure(0)
|
||||
for(var/I in idle_threads)
|
||||
var/datum/computer_file/program/PRG = I
|
||||
PRG.event_powerfailure(1)
|
||||
shutdown_computer(0)
|
||||
|
||||
// Handles power-related things, such as battery interaction, recharging, shutdown when it's discharged
|
||||
/obj/item/modular_computer/proc/handle_power()
|
||||
if(!battery_module || battery_module.battery.charge <= 0) // Battery-run but battery is depleted.
|
||||
power_failure()
|
||||
return 0
|
||||
|
||||
var/power_usage = screen_on ? base_active_power_usage : base_idle_power_usage
|
||||
|
||||
for(var/obj/item/weapon/computer_hardware/H in get_all_components())
|
||||
if(H.enabled)
|
||||
power_usage += H.power_usage
|
||||
|
||||
if(battery_module)
|
||||
if(!battery_module.check_functionality())
|
||||
power_failure(1)
|
||||
return
|
||||
if(!battery_module.battery.use(power_usage * CELLRATE))
|
||||
battery_module.battery.charge = 0
|
||||
power_failure()
|
||||
return 0
|
||||
|
||||
last_power_usage = power_usage
|
||||
|
||||
/obj/item/modular_computer/attackby(obj/item/weapon/W as obj, mob/user as mob)
|
||||
if(istype(W, /obj/item/weapon/card/id)) // ID Card, try to insert it.
|
||||
var/obj/item/weapon/card/id/I = W
|
||||
if(!card_slot)
|
||||
user << "You try to insert \the [I] into \the [src], but it does not have an ID card slot installed."
|
||||
return
|
||||
|
||||
if(card_slot.stored_card && card_slot.stored_card2)
|
||||
user << "You try to insert \the [I] into \the [src], but it's ID card slots are occupied."
|
||||
return
|
||||
if(!user.drop_item(I))
|
||||
return
|
||||
if(!card_slot.stored_card)
|
||||
card_slot.stored_card = I
|
||||
else
|
||||
card_slot.stored_card2 = I
|
||||
I.forceMove(src)
|
||||
// update_uis()
|
||||
user << "You insert \the [I] into \the [src]."
|
||||
return
|
||||
if(istype(W, /obj/item/weapon/paper))
|
||||
if(!nano_printer)
|
||||
return
|
||||
nano_printer.attackby(W, user)
|
||||
if(istype(W, /obj/item/weapon/computer_hardware))
|
||||
var/obj/item/weapon/computer_hardware/C = W
|
||||
if(C.hardware_size <= max_hardware_size)
|
||||
C.try_install_component(user, src)
|
||||
else
|
||||
user << "This component is too large for \the [src]."
|
||||
if(istype(W, /obj/item/weapon/wrench))
|
||||
var/list/components = get_all_components()
|
||||
if(components.len)
|
||||
user << "Remove all components from \the [src] before disassembling it."
|
||||
return
|
||||
new /obj/item/stack/sheet/metal( get_turf(src.loc), steel_sheet_cost )
|
||||
physical.visible_message("\The [src] has been disassembled by [user].")
|
||||
relay_qdel()
|
||||
qdel(src)
|
||||
return
|
||||
if(istype(W, /obj/item/weapon/weldingtool))
|
||||
var/obj/item/weapon/weldingtool/WT = W
|
||||
if(!WT.isOn())
|
||||
user << "\The [W] is off."
|
||||
return
|
||||
|
||||
if(!damage)
|
||||
user << "\The [src] does not require repairs."
|
||||
return
|
||||
|
||||
user << "You begin repairing damage to \the [src]..."
|
||||
if(WT.remove_fuel(round(damage/75)) && do_after(usr, damage/10))
|
||||
damage = 0
|
||||
user << "You repair \the [src]."
|
||||
return
|
||||
|
||||
if(istype(W, /obj/item/weapon/screwdriver))
|
||||
var/list/all_components = get_all_components()
|
||||
if(!all_components.len)
|
||||
user << "This device doesn't have any components installed."
|
||||
return
|
||||
var/list/component_names = list()
|
||||
for(var/obj/item/weapon/computer_hardware/H in all_components)
|
||||
component_names.Add(H.name)
|
||||
|
||||
var/choice = input(usr, "Which component do you want to uninstall?", "Computer maintenance", null) as null|anything in component_names
|
||||
|
||||
if(!choice)
|
||||
return
|
||||
|
||||
if(!Adjacent(usr))
|
||||
return
|
||||
|
||||
var/obj/item/weapon/computer_hardware/H = find_hardware_by_name(choice)
|
||||
|
||||
if(!H)
|
||||
return
|
||||
|
||||
uninstall_component(user, H)
|
||||
|
||||
return
|
||||
|
||||
..()
|
||||
|
||||
// Used by processor to relay qdel() to machinery type.
|
||||
/obj/item/modular_computer/proc/relay_qdel()
|
||||
return
|
||||
|
||||
// Uninstalls component. Found and Critical vars may be passed by parent types, if they have additional hardware.
|
||||
/obj/item/modular_computer/proc/uninstall_component(mob/living/user, obj/item/weapon/computer_hardware/H, found = 0, critical = 0)
|
||||
if(portable_drive == H)
|
||||
portable_drive = null
|
||||
found = 1
|
||||
if(hard_drive == H)
|
||||
hard_drive = null
|
||||
found = 1
|
||||
critical = 1
|
||||
if(network_card == H)
|
||||
network_card = null
|
||||
found = 1
|
||||
if(nano_printer == H)
|
||||
nano_printer = null
|
||||
found = 1
|
||||
if(card_slot == H)
|
||||
card_slot = null
|
||||
found = 1
|
||||
if(battery_module == H)
|
||||
battery_module = null
|
||||
found = 1
|
||||
if(processor_unit == H)
|
||||
processor_unit = null
|
||||
found = 1
|
||||
critical = 1
|
||||
if(found)
|
||||
if(user)
|
||||
user << "You remove \the [H] from \the [src]."
|
||||
H.forceMove(get_turf(src))
|
||||
H.holder2 = null
|
||||
if(critical && enabled)
|
||||
if(user)
|
||||
user << "<span class='danger'>\The [src]'s screen freezes for few seconds and then displays an \"HARDWARE ERROR: Critical component disconnected. Please verify component connection and reboot the device. If the problem persists contact technical support for assistance.\" warning.</span>"
|
||||
shutdown_computer()
|
||||
update_icon()
|
||||
|
||||
|
||||
// Checks all hardware pieces to determine if name matches, if yes, returns the hardware piece, otherwise returns null
|
||||
/obj/item/modular_computer/proc/find_hardware_by_name(name)
|
||||
if(portable_drive && (portable_drive.name == name))
|
||||
return portable_drive
|
||||
if(hard_drive && (hard_drive.name == name))
|
||||
return hard_drive
|
||||
if(network_card && (network_card.name == name))
|
||||
return network_card
|
||||
if(nano_printer && (nano_printer.name == name))
|
||||
return nano_printer
|
||||
if(card_slot && (card_slot.name == name))
|
||||
return card_slot
|
||||
if(battery_module && (battery_module.name == name))
|
||||
return battery_module
|
||||
if(processor_unit && (processor_unit.name == name))
|
||||
return processor_unit
|
||||
return null
|
||||
|
||||
// Returns list of all components
|
||||
/obj/item/modular_computer/proc/get_all_components()
|
||||
var/list/all_components = list()
|
||||
if(hard_drive)
|
||||
all_components.Add(hard_drive)
|
||||
if(network_card)
|
||||
all_components.Add(network_card)
|
||||
if(portable_drive)
|
||||
all_components.Add(portable_drive)
|
||||
if(nano_printer)
|
||||
all_components.Add(nano_printer)
|
||||
if(card_slot)
|
||||
all_components.Add(card_slot)
|
||||
if(battery_module)
|
||||
all_components.Add(battery_module)
|
||||
if(processor_unit)
|
||||
all_components.Add(processor_unit)
|
||||
return all_components
|
||||
|
||||
/obj/item/modular_computer/proc/take_damage(amount, component_probability, damage_casing = 1, randomize = 1)
|
||||
if(randomize)
|
||||
// 75%-125%, rand() works with integers, apparently.
|
||||
amount *= (rand(75, 125) / 100.0)
|
||||
amount = round(amount)
|
||||
if(damage_casing)
|
||||
damage += amount
|
||||
damage = max(0,min(max_damage,damage))
|
||||
|
||||
if(component_probability)
|
||||
for(var/I in get_all_components())
|
||||
var/obj/item/weapon/computer_hardware/H = I
|
||||
if(prob(component_probability))
|
||||
H.take_damage(round(amount / 2))
|
||||
|
||||
if(damage >= max_damage)
|
||||
break_apart()
|
||||
|
||||
// Stronger explosions cause serious damage to internal components
|
||||
// Minor explosions are mostly mitigitated by casing.
|
||||
/obj/item/modular_computer/ex_act(severity)
|
||||
take_damage(rand(100,200) / severity, 30 / severity)
|
||||
|
||||
// EMPs are similar to explosions, but don't cause physical damage to the casing. Instead they screw up the components
|
||||
/obj/item/modular_computer/emp_act(severity)
|
||||
take_damage(rand(100,200) / severity, 50 / severity, 0)
|
||||
|
||||
// "Stun" weapons can cause minor damage to components (short-circuits?)
|
||||
// "Burn" damage is equally strong against internal components and exterior casing
|
||||
// "Brute" damage mostly damages the casing.
|
||||
/obj/item/modular_computer/bullet_act(obj/item/projectile/Proj)
|
||||
switch(Proj.damage_type)
|
||||
if(BRUTE)
|
||||
take_damage(Proj.damage, Proj.damage / 2)
|
||||
if(BURN)
|
||||
take_damage(Proj.damage, Proj.damage / 1.5)
|
||||
|
||||
/obj/item/modular_computer/ui_host()
|
||||
if(physical)
|
||||
return physical
|
||||
else
|
||||
return src
|
||||
@@ -0,0 +1,119 @@
|
||||
// Held by /obj/machinery/modular_computer to reduce amount of copy-pasted code.
|
||||
/obj/item/modular_computer/processor
|
||||
name = "processing unit"
|
||||
desc = "You shouldn't see this. If you do, report it."
|
||||
icon = null
|
||||
icon_state = null
|
||||
icon_state_unpowered = null
|
||||
icon_state_menu = null
|
||||
hardware_flag = 0
|
||||
|
||||
var/obj/machinery/modular_computer/machinery_computer = null
|
||||
|
||||
/obj/item/modular_computer/processor/Destroy()
|
||||
. = ..()
|
||||
if(machinery_computer && (machinery_computer.cpu == src))
|
||||
machinery_computer.cpu = null
|
||||
machinery_computer = null
|
||||
|
||||
// Due to how processes work, we'd receive two process calls - one from machinery type and one from our own type.
|
||||
// Since we want this to be in-sync with machinery (as it's hidden type for machinery-based computers) we'll ignore
|
||||
// non-relayed process calls.
|
||||
/obj/item/modular_computer/processor/process(relayed = 0)
|
||||
if(relayed)
|
||||
..()
|
||||
else
|
||||
return
|
||||
|
||||
/obj/item/modular_computer/processor/examine(var/mob/user)
|
||||
if(damage > broken_damage)
|
||||
user << "<span class='danger'>It is heavily damaged!</span>"
|
||||
else if(damage)
|
||||
user << "It is damaged."
|
||||
|
||||
// Power interaction is handled by our machinery part, due to machinery having APC connection.
|
||||
/obj/item/modular_computer/processor/handle_power()
|
||||
if(machinery_computer)
|
||||
machinery_computer.handle_power()
|
||||
|
||||
/obj/item/modular_computer/processor/New(comp)
|
||||
if(!comp || !istype(comp, /obj/machinery/modular_computer))
|
||||
CRASH("Inapropriate type passed to obj/item/modular_computer/processor/New()! Aborting.")
|
||||
return
|
||||
// Obtain reference to machinery computer
|
||||
machinery_computer = comp
|
||||
machinery_computer.cpu = src
|
||||
hardware_flag = machinery_computer.hardware_flag
|
||||
max_hardware_size = machinery_computer.max_hardware_size
|
||||
steel_sheet_cost = machinery_computer.steel_sheet_cost
|
||||
max_damage = machinery_computer._max_damage
|
||||
broken_damage = machinery_computer._break_damage
|
||||
|
||||
/obj/item/modular_computer/processor/relay_qdel()
|
||||
qdel(machinery_computer)
|
||||
|
||||
/obj/item/modular_computer/processor/find_hardware_by_name(N)
|
||||
var/obj/item/weapon/computer_hardware/H = machinery_computer.find_hardware_by_name(N)
|
||||
if(H)
|
||||
return H
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/modular_computer/processor/update_icon()
|
||||
if(machinery_computer)
|
||||
return machinery_computer.update_icon()
|
||||
|
||||
/obj/item/modular_computer/processor/get_header_data()
|
||||
var/list/L = ..()
|
||||
if(machinery_computer.tesla_link && machinery_computer.tesla_link.enabled && machinery_computer.powered())
|
||||
L["PC_apclinkicon"] = "charging.gif"
|
||||
return L
|
||||
|
||||
// Checks whether the machinery computer doesn't take power from APC network
|
||||
/obj/item/modular_computer/processor/check_power_override()
|
||||
if(!machinery_computer)
|
||||
return 0
|
||||
if(!machinery_computer.tesla_link || !machinery_computer.tesla_link.enabled)
|
||||
return 0
|
||||
return machinery_computer.powered()
|
||||
|
||||
// This thing is not meant to be used on it's own, get topic data from our machinery owner.
|
||||
//obj/item/modular_computer/processor/canUseTopic(user, state)
|
||||
// if(!machinery_computer)
|
||||
// return 0
|
||||
|
||||
// return machinery_computer.canUseTopic(user, state)
|
||||
|
||||
/obj/item/modular_computer/processor/shutdown_computer()
|
||||
if(!machinery_computer)
|
||||
return
|
||||
..()
|
||||
machinery_computer.update_icon()
|
||||
machinery_computer.use_power = 0
|
||||
return
|
||||
|
||||
/obj/item/modular_computer/processor/uninstall_component(mob/living/user, obj/item/weapon/computer_hardware/H, found = 0, critical = 0)
|
||||
if(machinery_computer.tesla_link == H)
|
||||
machinery_computer.tesla_link = null
|
||||
var/obj/item/weapon/computer_hardware/tesla_link/L = H
|
||||
L.holder = null
|
||||
found = 1
|
||||
..(user, H, found, critical)
|
||||
|
||||
/obj/item/modular_computer/processor/get_all_components()
|
||||
var/list/all_components = ..()
|
||||
if(machinery_computer && machinery_computer.tesla_link)
|
||||
all_components.Add(machinery_computer.tesla_link)
|
||||
return all_components
|
||||
|
||||
// Perform adjacency checks on our machinery counterpart, rather than on ourselves.
|
||||
/obj/item/modular_computer/processor/Adjacent(atom/neighbor)
|
||||
if(!machinery_computer)
|
||||
return 0
|
||||
return machinery_computer.Adjacent(neighbor)
|
||||
|
||||
/obj/item/modular_computer/processor/turn_on(mob/user)
|
||||
// If we have a tesla link on our machinery counterpart, enable it automatically. Lets computer without a battery work.
|
||||
if(machinery_computer && machinery_computer.tesla_link)
|
||||
machinery_computer.tesla_link.enabled = 1
|
||||
..()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user