Merge remote-tracking branch 'upstream/master' into drones-no-camms

This commit is contained in:
joep van der velden
2020-06-28 22:48:20 +02:00
162 changed files with 68557 additions and 143888 deletions
+1 -1
View File
@@ -50,7 +50,7 @@
sources_assoc[source] = AS
// Currently only non-0 durations can be altered (normal alarms VS EMP blasts)
if(AS.duration)
duration = SecondsToTicks(duration)
duration = duration SECONDS
AS.duration = duration
AS.severity = severity
@@ -80,7 +80,7 @@
/obj/machinery/computer/mob_battle_terminal/proc/eject_card(override = 0)
if(!override)
if(ready && SSmob_hunt.battle_turn != team)
audible_message("You can't recall on your rival's turn!", null, 2)
atom_say("You can't recall on your rival's turn!")
return
card.mob_data = mob_info
mob_info = null
@@ -202,7 +202,7 @@
start_battle()
else if(option == 2)
ready = 0
audible_message("[team] Player cancels their battle challenge.", null, 5)
atom_say("[team] Player cancels their battle challenge.")
updateUsrDialog()
@@ -246,7 +246,7 @@
var/message = "[mob_info.mob_name] attacks!"
if(mob_info.nickname)
message = "[mob_info.nickname] attacks!"
audible_message(message, null, 5)
atom_say(message)
SSmob_hunt.launch_attack(team, mob_info.get_raw_damage(), mob_info.get_attack_type())
/obj/machinery/computer/mob_battle_terminal/proc/start_battle()
@@ -255,7 +255,7 @@
if(!card) //don't do anything if there isn't a card inserted
return
ready = 1
audible_message("[team] Player is ready for battle! Waiting for rival...", null, 5)
atom_say("[team] Player is ready for battle! Waiting for rival...")
SSmob_hunt.start_check()
/obj/machinery/computer/mob_battle_terminal/proc/receive_attack(raw_damage, datum/mob_type/attack_type)
@@ -268,7 +268,7 @@
SSmob_hunt.end_turn()
/obj/machinery/computer/mob_battle_terminal/proc/surrender()
audible_message("[team] Player surrenders the battle!", null, 5)
atom_say("[team] Player surrenders the battle!")
SSmob_hunt.end_battle(team, 1)
//////////////////////////////
+3 -3
View File
@@ -55,7 +55,7 @@
var/datum/data/pda/app/mob_hunter_game/client = P.current_app
var/total_catch_mod = client.catch_mod + catch_mod //negative values decrease the chance of the mob running, positive values makes it more likely to flee
if(!client.connected) //must be connected to attempt captures
P.audible_message("[bicon(P)] No server connection. Capture aborted.", null, 4)
P.atom_say("No server connection. Capture aborted.")
return
if(mob_info.is_trap) //traps work even if you ran into them before, which is why this is before the clients_encountered check
@@ -79,7 +79,7 @@
return
else //deal with the new hunter by either running away or getting caught
clients_encountered += client
var/message = "[bicon(P)] "
var/message = null
var/effective_run_chance = mob_info.run_chance + total_catch_mod
if((effective_run_chance > 0) && prob(effective_run_chance))
message += "Capture failed! [name] escaped [P.owner ? "from [P.owner]" : "from this hunter"]!"
@@ -91,7 +91,7 @@
else
message += "Capture error! Try again."
clients_encountered -= client //if the capture registration failed somehow, let them have another chance with this mob
P.audible_message(message, null, 4)
P.atom_say(message)
/obj/effect/nanomob/proc/despawn()
if(SSmob_hunt)
+1 -1
View File
@@ -139,7 +139,7 @@
for(var/areapath in typesof(A))
possible_areas[areapath] -= 2
//removes "bad areas" which shouldn't be on-station but are subtypes of station areas. probably should the unused ones and consider repathing the rest
var/list/bad_areas = list(subtypesof(/area/construction), /area/solar/derelict_starboard, /area/solar/derelict_aft, /area/solar/constructionsite)
var/list/bad_areas = list(subtypesof(/area/construction), /area/solar/derelict_starboard, /area/solar/derelict_aft)
for(var/A in bad_areas)
possible_areas -= A
//weight check, remove negative or zero weight areas from the list, then return the list.
+1 -1
View File
@@ -55,7 +55,7 @@
health_scan = M.health
if(health_scan <= alarm_health)
pulse()
audible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*")
audible_message("[bicon(src)] *beep* *beep*")
toggle_scan()
return
return
+1 -1
View File
@@ -129,7 +129,7 @@
return FALSE
cooldown = 2
pulse(FALSE)
audible_message("[bicon(src)] *beep* *beep*", null, 3)
audible_message("[bicon(src)] *beep* *beep*", hearing_distance = 3)
if(first)
qdel(first)
addtimer(CALLBACK(src, .proc/process_cooldown), 10)
@@ -318,6 +318,36 @@
if(isliving(user))
user.visible_message("<span class='warning'>[user] invades [M]'s personal space, thrusting [src] into [M.p_their()] face insistently.</span>","<span class='warning'>You invade [M]'s personal space, thrusting [src] into [M.p_their()] face insistently. You are the law.</span>")
//////////////
//OBJECTION!//
//////////////
/obj/item/clothing/accessory/lawyers_badge
name = "attorney's badge"
desc = "Fills you with the conviction of JUSTICE. Lawyers tend to want to show it to everyone they meet."
icon_state = "lawyerbadge"
item_state = "lawyerbadge"
item_color = "lawyerbadge"
var/cached_bubble_icon = null
/obj/item/clothing/accessory/attack_self(mob/user)
if(prob(1))
user.say("The testimony contradicts the evidence!")
user.visible_message("<span class='notice'>[user] shows [user.p_their()] attorney's badge.</span>", "<span class='notice'>You show your attorney's badge.</span>")
/obj/item/clothing/accessory/lawyers_badge/on_attached(obj/item/clothing/under/S, mob/user)
..()
if(has_suit && ismob(has_suit.loc))
var/mob/M = has_suit.loc
cached_bubble_icon = M.bubble_icon
M.bubble_icon = "lawyer"
/obj/item/clothing/accessory/lawyers_badge/on_removed(mob/user)
if(has_suit && ismob(has_suit.loc))
var/mob/M = has_suit.loc
M.bubble_icon = cached_bubble_icon
..()
///////////
//SCARVES//
///////////
@@ -18,7 +18,6 @@
amount_per_transfer_from_this = 5
possible_transfer_amounts = list(5)
volume = 5
can_be_placed_into = null
flags = NOBLUDGEON
container_type = OPENCONTAINER
has_lid = FALSE
@@ -17,7 +17,7 @@
/datum/event/communications_blackout/start()
// This only affects the cores, relays should be unaffected imo
for(var/obj/machinery/tcomms/core/T in GLOB.tcomms_machines)
T.disable_machine()
T.start_ion()
// Bring it back sometime between 3-5 minutes. This uses deciseconds, so 1800 and 3000 respecticely.
// Note that because this is a strict enable not a toggle, the crew or AI can re-enable the machine themselves
addtimer(CALLBACK(T, /obj/machinery/tcomms.proc/enable_machine), rand(1800, 3000))
// The AI cannot disable this, it must be waited for
addtimer(CALLBACK(T, /obj/machinery/tcomms.proc/end_ion), rand(1800, 3000))
+1 -1
View File
@@ -493,7 +493,7 @@
/proc/generate_static_ion_law()
var/list/players = list()
for(var/mob/living/carbon/human/player in GLOB.player_list)
if( !player.mind || player.mind.assigned_role == player.mind.special_role || player.client.inactivity > MinutesToTicks(10))
if( !player.mind || player.mind.assigned_role == player.mind.special_role || player.client.inactivity > 10 MINUTES)
continue
players += player.real_name
var/random_player = "The Captain"
+1
View File
@@ -184,3 +184,4 @@
D.consume(src)
else
return ..()
+2 -2
View File
@@ -207,7 +207,7 @@
var/obj/item/barcodescanner/scanner = W
scanner.computer = src
to_chat(user, "[scanner]'s associated machine has been set to [src].")
audible_message("[src] lets out a low, short blip.", 2)
audible_message("[src] lets out a low, short blip.", hearing_distance = 2)
return 1
else
return ..()
@@ -413,7 +413,7 @@
return
if(bibledelay)
audible_message("<b>[src]</b>'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"")
visible_message("<b>[src]</b>'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"")
else
bibledelay = 1
spawn(60)
@@ -2,6 +2,7 @@
name = "alien"
voice_name = "alien"
speak_emote = list("hisses")
bubble_icon = "alien"
icon = 'icons/mob/alien.dmi'
gender = NEUTER
dna = null
@@ -6,6 +6,7 @@
icon_state = "alienq_s"
status_flags = CANPARALYSE
mob_size = MOB_SIZE_LARGE
bubble_icon = "alienroyal"
large = 1
ventcrawler = 0
@@ -260,8 +260,6 @@
add_attack_logs(user, src, "Grabbed passively", ATKLOG_ALL)
var/obj/item/grab/G = new /obj/item/grab(user, src)
if(buckled)
to_chat(user, "<span class='notice'>You cannot grab [src]; [p_they()] [p_are()] buckled in!</span>")
if(!G) //the grab will delete itself in New if src is anchored
return 0
user.put_in_active_hand(G)
+10 -13
View File
@@ -269,12 +269,12 @@ proc/get_radio_key_from_channel(var/channel)
M.hear_say(message_pieces, verb, italics, src, speech_sound, sound_vol, sound_frequency)
if(M.client)
speech_bubble_recipients.Add(M.client)
spawn(0)
if(loc && !isturf(loc))
var/atom/A = loc //Non-turf, let it handle the speech bubble
A.speech_bubble("hR[speech_bubble_test]", A, speech_bubble_recipients)
else //Turf, leave speech bubbles to the mob
speech_bubble("h[speech_bubble_test]", src, speech_bubble_recipients)
if(loc && !isturf(loc))
var/atom/A = loc //Non-turf, let it handle the speech bubble
A.speech_bubble("[A.bubble_icon][speech_bubble_test]", A, speech_bubble_recipients)
else //Turf, leave speech bubbles to the mob
speech_bubble("[bubble_icon][speech_bubble_test]", src, speech_bubble_recipients)
for(var/obj/O in listening_obj)
spawn(0)
@@ -462,10 +462,7 @@ proc/get_radio_key_from_channel(var/channel)
if(M.client)
speech_bubble_recipients.Add(M.client)
spawn(0)
var/image/I = image('icons/mob/talk.dmi', src, "h[speech_bubble_test]", MOB_LAYER + 1)
I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
flick_overlay(I, speech_bubble_recipients, 30)
speech_bubble("[bubble_icon][speech_bubble_test]", src, speech_bubble_recipients)
if(watching.len)
var/rendered = "<span class='game say'><span class='name'>[name]</span> [not_heard].</span>"
@@ -474,7 +471,7 @@ proc/get_radio_key_from_channel(var/channel)
return 1
/mob/living/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list())
var/image/I = image('icons/mob/talk.dmi', bubble_loc, bubble_state, MOB_LAYER + 1)
/mob/living/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list())
var/image/I = image('icons/mob/talk.dmi', bubble_loc, bubble_state, FLY_LAYER)
I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
flick_overlay(I, bubble_recipients, 30)
INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, bubble_recipients, 30)
@@ -6,6 +6,7 @@
icon_state = "repairbot"
maxHealth = 35
health = 35
bubble_icon = "machine"
universal_speak = 0
universal_understand = 1
gender = NEUTER
@@ -64,7 +64,7 @@
..()
can_hold = typecacheof(can_hold)
/obj/item/gripper/verb/drop_item()
/obj/item/gripper/verb/drop_item_gripped()
set name = "Drop Gripped Item"
set desc = "Release an item from your magnetic gripper."
set category = "Drone"
+113 -68
View File
@@ -9,6 +9,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
icon_state = "robot"
maxHealth = 100
health = 100
bubble_icon = "robot"
universal_understand = 1
deathgasp_on_death = TRUE
@@ -16,8 +17,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
var/custom_name = ""
var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best
//Hud stuff
//Hud stuff
var/obj/screen/inv1 = null
var/obj/screen/inv2 = null
var/obj/screen/inv3 = null
@@ -27,7 +27,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not
var/obj/screen/robot_modules_background
//3 Modules can be activated at any one time.
//3 Modules can be activated at any one time.
var/obj/item/robot_module/module = null
var/module_active = null
var/module_state_1 = null
@@ -57,6 +57,9 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
var/is_emaggable = TRUE
var/eye_protection = 0
var/ear_protection = 0
var/damage_protection = 0
var/emp_protection = FALSE
var/xeno_disarm_chance = 85
var/list/force_modules = list()
var/allow_rename = TRUE
@@ -87,7 +90,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
var/tracking_entities = 0 //The number of known entities currently accessing the internal camera
var/braintype = "Cyborg"
var/base_icon = ""
var/crisis = 0
var/modules_break = TRUE
var/lamp_max = 10 //Maximum brightness of a borg lamp. Set as a var for easy adjusting.
@@ -98,6 +100,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD)
var/default_cell_type = /obj/item/stock_parts/cell/high
var/magpulse = 0
var/ionpulse = 0 // Jetpack-like effect.
var/ionpulse_on = 0 // Jetpack-like effect.
@@ -145,7 +148,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
mmi.icon_state = "boris"
if(!cell) // Make sure a new cell gets created *before* executing initialize_components(). The cell component needs an existing cell for it to get set up properly
cell = new /obj/item/stock_parts/cell/high(src)
cell = new default_cell_type(src)
initialize_components()
//if(!unfinished)
@@ -289,12 +292,9 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
/mob/living/silicon/robot/proc/pick_module()
if(module)
return
var/list/modules = list("Standard", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security")
var/list/modules = list("Generalist", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security")
if(islist(force_modules) && force_modules.len)
modules = force_modules.Copy()
if(GLOB.security_level == (SEC_LEVEL_GAMMA || SEC_LEVEL_EPSILON) || crisis)
to_chat(src, "<span class='warning'>Crisis mode active. The combat module is now available.</span>")
modules += "Combat"
if(mmi != null && mmi.alien)
modules = list("Hunter")
modtype = input("Please, select a module!", "Robot", null, null) as null|anything in modules
@@ -307,9 +307,9 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
return
switch(modtype)
if("Standard")
if("Generalist")
module = new /obj/item/robot_module/standard(src)
module.channels = list("Service" = 1)
module.channels = list("Engineering" = 1, "Medical" = 1, "Security" = 1, "Service" = 1)
module_sprites["Basic"] = "robot_old"
module_sprites["Android"] = "droid"
module_sprites["Default"] = "Standard"
@@ -338,6 +338,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
module_sprites["Standard"] = "Standard-Mine"
module_sprites["Noble-DIG"] = "Noble-DIG"
module_sprites["Cricket"] = "Cricket-MINE"
module_sprites["Lavaland"] = "lavaland"
if("Medical")
module = new /obj/item/robot_module/medical(src)
@@ -354,6 +355,17 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
status_flags &= ~CANPUSH
if("Security")
if(!weapons_unlock)
var/count_secborgs = 0
for(var/mob/living/silicon/robot/R in GLOB.alive_mob_list)
if(R && R.stat != DEAD && R.module && istype(R.module, /obj/item/robot_module/security))
count_secborgs++
var/max_secborgs = 2
if(GLOB.security_level == SEC_LEVEL_GREEN)
max_secborgs = 1
if(count_secborgs >= max_secborgs)
to_chat(src, "<span class='warning'>There are too many Security cyborgs active. Please choose another module.</span>")
return
module = new /obj/item/robot_module/security(src)
module.channels = list("Security" = 1)
module_sprites["Basic"] = "secborg"
@@ -388,10 +400,16 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
module_sprites["Noble-CLN"] = "Noble-CLN"
module_sprites["Cricket"] = "Cricket-JANI"
if("Combat")
module = new /obj/item/robot_module/combat(src)
if("Destroyer") // Rolling Borg
module = new /obj/item/robot_module/destroyer(src)
module.channels = list("Security" = 1)
icon_state = "droidcombat"
status_flags &= ~CANPUSH
if("Combat") // Gamma ERT
module = new /obj/item/robot_module/combat(src)
icon_state = "ertgamma"
status_flags &= ~CANPUSH
if("Hunter")
module = new /obj/item/robot_module/alien/hunter(src)
@@ -443,6 +461,8 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
ionpulse = FALSE
magpulse = FALSE
add_language("Robot Talk", 1)
if("lava" in weather_immunities) // Remove the lava-immunity effect given by a printable upgrade
weather_immunities -= "lava"
status_flags |= CANPUSH
@@ -917,7 +937,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
return 0
/mob/living/silicon/robot/update_icons()
overlays.Cut()
if(stat != DEAD && !(paralysis || stunned || IsWeakened() || low_power_mode)) //Not dead, not stunned.
if(custom_panel in custom_eye_names)
@@ -926,36 +945,24 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
overlays += "eyes-[icon_state]"
else
overlays -= "eyes"
if(opened)
var/panelprefix = "ov"
if(custom_sprite) //Custom borgs also have custom panels, heh
panelprefix = "[ckey]"
if(custom_panel in custom_panel_names) //For default borgs with different panels
panelprefix = custom_panel
if(wiresexposed)
overlays += "[panelprefix]-openpanel +w"
else if(cell)
overlays += "[panelprefix]-openpanel +c"
else
overlays += "[panelprefix]-openpanel -c"
var/combat = list("Combat")
if(modtype in combat)
if(base_icon == "")
base_icon = icon_state
if(module_active && istype(module_active,/obj/item/borg/combat/mobility))
icon_state = "[base_icon]-roll"
else
icon_state = base_icon
if(module)
for(var/obj/item/borg/combat/shield/S in module.modules)
if(activated(S))
overlays += "[base_icon]-shield"
borg_icons()
update_fire()
/mob/living/silicon/robot/proc/borg_icons() // Exists so that robot/destroyer can override it
return
/mob/living/silicon/robot/proc/installed_modules()
if(weapon_lock)
to_chat(src, "<span class='warning'>Weapon lock active, unable to use modules! Count:[weaponlock_time]</span>")
@@ -1310,56 +1317,52 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
..()
update_module_icon()
/mob/living/silicon/robot/emp_act(severity)
if(emp_protection)
return
..()
switch(severity)
if(1)
disable_component("comms", 160)
if(2)
disable_component("comms", 60)
/mob/living/silicon/robot/deathsquad
base_icon = "nano_bloodhound"
icon_state = "nano_bloodhound"
designation = "SpecOps"
lawupdate = 0
scrambledcodes = 1
has_camera = FALSE
req_one_access = list(ACCESS_CENT_SPECOPS)
ionpulse = 1
magpulse = 1
pdahide = 1
eye_protection = 2 // Immunity to flashes and the visual part of flashbangs
ear_protection = 1 // Immunity to the audio part of flashbangs
damage_protection = 10 // Reduce all incoming damage by this number
xeno_disarm_chance = 20
allow_rename = FALSE
modtype = "Commando"
faction = list("nanotrasen")
is_emaggable = FALSE
/mob/living/silicon/robot/deathsquad/New(loc)
..()
cell = new /obj/item/stock_parts/cell/hyper(src)
default_cell_type = /obj/item/stock_parts/cell/bluespace
/mob/living/silicon/robot/deathsquad/init()
laws = new /datum/ai_laws/deathsquad
module = new /obj/item/robot_module/deathsquad(src)
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
radio = new /obj/item/radio/borg/deathsquad(src)
radio.recalculateChannels()
playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0)
/mob/living/silicon/robot/combat
base_icon = "droidcombat"
icon_state = "droidcombat"
modtype = "Combat"
designation = "Combat"
/mob/living/silicon/robot/deathsquad/bullet_act(var/obj/item/projectile/P)
if(istype(P) && P.is_reflectable && P.starting)
visible_message("<span class='danger'>The [P.name] gets reflected by [src]!</span>", "<span class='userdanger'>The [P.name] gets reflected by [src]!</span>")
P.reflect_back(src)
return -1
return ..(P)
/mob/living/silicon/robot/combat/init()
..()
module = new /obj/item/robot_module/combat(src)
module.channels = list("Security" = 1)
//languages
module.add_languages(src)
//subsystems
module.add_subsystems_and_actions(src)
status_flags &= ~CANPUSH
radio.config(module.channels)
notify_ai(2)
/mob/living/silicon/robot/ert
designation = "ERT"
@@ -1367,11 +1370,12 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
scrambledcodes = 1
req_one_access = list(ACCESS_CENT_SPECOPS)
ionpulse = 1
force_modules = list("Engineering", "Medical", "Security")
static_radio_channels = 1
allow_rename = FALSE
weapons_unlock = TRUE
default_cell_type = /obj/item/stock_parts/cell/super
var/eprefix = "Amber"
/mob/living/silicon/robot/ert/init()
@@ -1380,11 +1384,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
radio.recalculateChannels()
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
/mob/living/silicon/robot/ert/New(loc, cyborg_unlock)
/mob/living/silicon/robot/ert/New(loc)
..(loc)
cell = new /obj/item/stock_parts/cell/hyper(src)
var/rnum = rand(1,1000)
var/borgname = "ERT [rnum]"
var/borgname = "[eprefix] ERT [rnum]"
name = borgname
custom_name = borgname
real_name = name
@@ -1393,22 +1396,64 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
mind.original = src
mind.assigned_role = SPECIAL_ROLE_ERT
mind.special_role = SPECIAL_ROLE_ERT
if(cyborg_unlock)
crisis = 1
if(!(mind in SSticker.minds))
SSticker.minds += mind
SSticker.mode.ert += mind
/mob/living/silicon/robot/ert/gamma
crisis = 1
/mob/living/silicon/robot/emp_act(severity)
/mob/living/silicon/robot/ert/red
eprefix = "Red"
default_cell_type = /obj/item/stock_parts/cell/hyper
/mob/living/silicon/robot/ert/gamma
default_cell_type = /obj/item/stock_parts/cell/bluespace
force_modules = list("Combat", "Engineering", "Medical")
damage_protection = 5 // Reduce all incoming damage by this number
eprefix = "Gamma"
magpulse = 1
xeno_disarm_chance = 40
/mob/living/silicon/robot/destroyer
// admin-only borg, the seraph / special ops officer of borgs
base_icon = "droidcombat"
icon_state = "droidcombat"
modtype = "Destroyer"
designation = "Destroyer"
lawupdate = 0
scrambledcodes = 1
has_camera = FALSE
req_one_access = list(ACCESS_CENT_SPECOPS)
ionpulse = 1
magpulse = 1
pdahide = 1
eye_protection = 2 // Immunity to flashes and the visual part of flashbangs
ear_protection = 1 // Immunity to the audio part of flashbangs
emp_protection = TRUE // Immunity to EMP, due to heavy shielding
damage_protection = 20 // Reduce all incoming damage by this number. Very high in the case of /destroyer borgs, since it is an admin-only borg.
xeno_disarm_chance = 10
default_cell_type = /obj/item/stock_parts/cell/bluespace
/mob/living/silicon/robot/destroyer/init()
..()
switch(severity)
if(1)
disable_component("comms", 160)
if(2)
disable_component("comms", 60)
module = new /obj/item/robot_module/destroyer(src)
module.add_languages(src)
module.add_subsystems_and_actions(src)
status_flags &= ~CANPUSH
if(radio)
qdel(radio)
radio = new /obj/item/radio/borg/ert/specops(src)
radio.recalculateChannels()
/mob/living/silicon/robot/destroyer/borg_icons()
if(base_icon == "")
base_icon = icon_state
if(module_active && istype(module_active,/obj/item/borg/destroyer/mobility))
icon_state = "[base_icon]-roll"
else
icon_state = base_icon
overlays += "[base_icon]-shield"
/mob/living/silicon/robot/extinguish_light()
update_headlamp(1, 150)
@@ -76,29 +76,6 @@
if(!LAZYLEN(components))
return
//Combat shielding absorbs a percentage of damage directly into the cell.
var/obj/item/borg/combat/shield/shield
if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield))
shield = module_state_1
else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield))
shield = module_state_2
else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield))
shield = module_state_3
if(shield)
//Shields absorb a certain percentage of damage based on their power setting.
var/absorb_brute = brute * shield.shield_level
var/absorb_burn = burn * shield.shield_level
var/cost = (absorb_brute+absorb_burn) * 100
cell.charge -= cost
if(cell.charge <= 0)
cell.charge = 0
to_chat(src, "<span class='warning'>Your shield has overloaded!</span>")
else
brute -= absorb_brute
burn -= absorb_burn
to_chat(src, "<span class='warning'>Your shield absorbs some of the impact!</span>")
var/datum/robot_component/armour/A = get_armour()
if(A)
A.take_damage(brute, burn, sharp, updating_health)
@@ -127,32 +104,15 @@
updatehealth("heal overall damage")
/mob/living/silicon/robot/take_overall_damage(brute = 0, burn = 0, updating_health = TRUE, used_weapon = null, sharp = 0)
if(status_flags & GODMODE) return //godmode
if(status_flags & GODMODE)
return
if(damage_protection)
brute = clamp(brute - damage_protection, 0, brute)
burn = clamp(burn - damage_protection, 0, burn)
var/list/datum/robot_component/parts = get_damageable_components()
//Combat shielding absorbs a percentage of damage directly into the cell.
var/obj/item/borg/combat/shield/shield
if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield))
shield = module_state_1
else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield))
shield = module_state_2
else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield))
shield = module_state_3
if(shield)
//Shields absorb a certain percentage of damage based on their power setting.
var/absorb_brute = brute * shield.shield_level
var/absorb_burn = burn * shield.shield_level
var/cost = (absorb_brute+absorb_burn) * 100
cell.charge -= cost
if(cell.charge <= 0)
cell.charge = 0
to_chat(src, "<span class='warning'>Your shield has overloaded!</span>")
else
brute -= absorb_brute
burn -= absorb_burn
to_chat(src, "<span class='warning'>Your shield absorbs some of the impact!</span>")
var/datum/robot_component/armour/A = get_armour()
if(A)
A.take_damage(brute, burn, sharp)
@@ -2,7 +2,7 @@
if(M.a_intent == INTENT_DISARM)
if(!lying)
M.do_attack_animation(src, ATTACK_EFFECT_DISARM)
if(prob(85))
if(prob(xeno_disarm_chance))
Stun(7)
step(src, get_dir(M,src))
spawn(5)
@@ -73,24 +73,7 @@
/obj/item/borg
var/powerneeded // Percentage of power remaining required to run item
/obj/item/borg/combat/shield
name = "personal shielding"
desc = "A powerful experimental module that turns aside or absorbs incoming attacks at the cost of charge."
icon = 'icons/obj/decals.dmi'
icon_state = "shock"
powerneeded = 25
var/shield_level = 0.5 //Percentage of damage absorbed by the shield.
/obj/item/borg/combat/shield/verb/set_shield_level()
set name = "Set shield level"
set category = "Object"
set src in range(0)
var/N = input("How much damage should the shield absorb?") in list("5","10","25","50","75","100")
if(N)
shield_level = text2num(N)/100
/obj/item/borg/combat/mobility
/obj/item/borg/destroyer/mobility
name = "mobility module"
desc = "By retracting limbs and tucking in its head, a combat android can roll at high speeds."
icon = 'icons/obj/decals.dmi'
@@ -118,20 +118,48 @@
return
/obj/item/robot_module/standard
name = "standard robot module"
// if station is fine, assist with constructing station goal room, cleaning, and repairing cables chewed by rats
// if medical crisis, assist by providing basic healthcare, retrieving corpses, and monitoring crew lifesigns
// if eng crisis, assist by helping repair hull breaches
// if sec crisis, assist by opening doors for sec and providing a backup stunbaton on patrols
name = "generalist robot module"
module_type = "Standard"
subsystems = list(/mob/living/silicon/proc/subsystem_power_monitor, /mob/living/silicon/proc/subsystem_crew_monitor)
stacktypes = list(
/obj/item/stack/sheet/metal/cyborg = 50,
/obj/item/stack/cable_coil/cyborg = 50,
/obj/item/stack/rods/cyborg = 60,
/obj/item/stack/tile/plasteel = 20
)
/obj/item/robot_module/standard/New()
..()
modules += new /obj/item/melee/baton/loaded(src)
modules += new /obj/item/extinguisher(src)
modules += new /obj/item/wrench/cyborg(src)
// sec
modules += new /obj/item/restraints/handcuffs/cable/zipties(src)
// janitorial
modules += new /obj/item/soap/nanotrasen(src)
modules += new /obj/item/lightreplacer/cyborg(src)
// eng
modules += new /obj/item/crowbar/cyborg(src)
modules += new /obj/item/wrench/cyborg(src)
modules += new /obj/item/extinguisher(src) // for firefighting, and propulsion in space
modules += new /obj/item/weldingtool/largetank/cyborg(src)
// mining
modules += new /obj/item/pickaxe(src)
modules += new /obj/item/t_scanner/adv_mining_scanner(src)
modules += new /obj/item/storage/bag/ore/cyborg(src)
// med
modules += new /obj/item/healthanalyzer(src)
modules += new /obj/item/reagent_containers/borghypo/basic(src)
modules += new /obj/item/roller_holder(src) // for taking the injured to medbay without worsening their injuries or leaving a blood trail the whole way
emag = new /obj/item/melee/energy/sword/cyborg(src)
for(var/G in stacktypes)
var/obj/item/stack/sheet/M = new G(src)
M.amount = stacktypes[G]
modules += M
fix_modules()
/obj/item/robot_module/medical
name = "medical robot module"
module_type = "Medical"
@@ -259,6 +287,7 @@
modules += new /obj/item/mop/advanced/cyborg(src)
modules += new /obj/item/lightreplacer/cyborg(src)
modules += new /obj/item/holosign_creator(src)
modules += new /obj/item/extinguisher/mini(src)
emag = new /obj/item/reagent_containers/spray(src)
emag.reagents.add_reagent("lube", 250)
@@ -483,25 +512,44 @@
fix_modules()
/obj/item/robot_module/combat
name = "combat robot module"
/obj/item/robot_module/destroyer
name = "destroyer robot module"
module_type = "Malf"
module_actions = list(
/datum/action/innate/robot_sight/thermal,
)
/obj/item/robot_module/destroyer/New()
..()
modules += new /obj/item/gun/energy/immolator/multi/cyborg(src) // See comments on /robot_module/combat below
modules += new /obj/item/melee/baton/loaded(src) // secondary weapon, for things immune to burn, immune to ranged weapons, or for arresting low-grade threats
modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src)
modules += new /obj/item/pickaxe/drill/jackhammer(src) // for breaking walls to execute flanking moves
modules += new /obj/item/borg/destroyer/mobility(src)
emag = null
fix_modules()
/obj/item/robot_module/combat
name = "combat robot module"
module_type = "Malf"
module_actions = list()
/obj/item/robot_module/combat/New()
..()
modules += new /obj/item/gun/energy/immolator/multi/cyborg(src) // primary weapon, strong at close range (ie: against blob/terror/xeno), but consumes a lot of energy per shot.
// Borg gets 40 shots of this weapon. Gamma Sec ERT gets 10.
// So, borg has way more burst damage, but also takes way longer to recharge / get back in the fight once depleted. Has to find a borg recharger and sit in it for ages.
// Organic gamma sec ERT carries alternate weapons, including a box of flashbangs, and can load up on a huge number of guns from science. Borg cannot do either.
// Overall, gamma borg has higher skill floor but lower skill ceiling.
modules += new /obj/item/melee/baton/loaded(src) // secondary weapon, for things immune to burn, immune to ranged weapons, or for arresting low-grade threats
modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src)
modules += new /obj/item/gun/energy/gun/cyborg(src)
modules += new /obj/item/pickaxe/drill/jackhammer(src)
modules += new /obj/item/borg/combat/shield(src)
modules += new /obj/item/borg/combat/mobility(src)
modules += new /obj/item/wrench/cyborg(src)
emag = new /obj/item/gun/energy/lasercannon/cyborg(src)
modules += new /obj/item/pickaxe/drill/jackhammer(src) // for breaking walls to execute flanking moves
emag = null
fix_modules()
/obj/item/robot_module/alien/hunter
name = "alien hunter module"
module_type = "Standard"
@@ -9,7 +9,7 @@
/mob/living/silicon/robot/movement_delay()
. = ..()
. += speed
if(module_active && istype(module_active,/obj/item/borg/combat/mobility))
if(module_active && istype(module_active,/obj/item/borg/destroyer/mobility))
. -= 3
. += config.robot_delay
@@ -6,6 +6,7 @@
has_camera = FALSE
pdahide = 1
faction = list("syndicate")
bubble_icon = "syndibot"
designation = "Syndicate Assault"
modtype = "Syndicate"
req_access = list(ACCESS_SYNDICATE)
+3 -1
View File
@@ -2,7 +2,9 @@
gender = NEUTER
robot_talk_understand = 1
voice_name = "synthesized voice"
bubble_icon = "machine"
has_unlimited_silicon_privilege = 1
weather_immunities = list("ash")
var/syndicate = 0
var/const/MAIN_CHANNEL = "Main Frequency"
var/lawchannel = MAIN_CHANNEL // Default channel on which to state laws
@@ -283,7 +285,7 @@
/mob/living/silicon/proc/receive_alarm(var/datum/alarm_handler/alarm_handler, var/datum/alarm/alarm, was_raised)
if(!next_alarm_notice)
next_alarm_notice = world.time + SecondsToTicks(10)
next_alarm_notice = world.time + 10 SECONDS
var/list/alarms = queued_alarms[alarm_handler]
if(was_raised)
@@ -18,7 +18,7 @@
speak_emote = list("states")
friendly = "boops"
bubble_icon = "machine"
faction = list("neutral", "silicon")
var/obj/machinery/bot_core/bot_core = null
@@ -323,13 +323,13 @@
/mob/living/simple_animal/bot/mulebot/proc/buzz(type)
switch(type)
if(SIGH)
audible_message("[src] makes a sighing buzz.", "<span class='emote'>You hear an electronic buzzing sound.</span>")
audible_message("[src] makes a sighing buzz.")
playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
if(ANNOYED)
audible_message("[src] makes an annoyed buzzing sound.", "<span class='emote'>You hear an electronic buzzing sound.</span>")
audible_message("[src] makes an annoyed buzzing sound.")
playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0)
if(DELIGHT)
audible_message("[src] makes a delighted ping!", "<span class='emote'>You hear a ping.</span>")
audible_message("[src] makes a delighted ping!")
playsound(loc, 'sound/machines/ping.ogg', 50, 0)
@@ -601,7 +601,7 @@
/mob/living/simple_animal/bot/mulebot/proc/at_target()
if(!reached_target)
radio_channel = "Supply" //Supply channel
audible_message("[src] makes a chiming sound!", "<span class='emote'>You hear a chime.</span>")
audible_message("[src] makes a chiming sound!")
playsound(loc, 'sound/machines/chime.ogg', 50, 0)
reached_target = 1
@@ -20,6 +20,7 @@
melee_damage_upper = 25
attacktext = "slashes"
speak_emote = list("hisses")
bubble_icon = "alien"
a_intent = INTENT_HARM
attack_sound = 'sound/weapons/bladeslice.ogg'
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)
@@ -127,6 +128,7 @@
icon_state = "queen_s"
icon_living = "queen_s"
icon_dead = "queen_dead"
bubble_icon = "alienroyal"
move_to_delay = 4
maxHealth = 400
health = 400
@@ -25,6 +25,7 @@
gold_core_spawnable = HOSTILE_SPAWN
loot = list(/obj/effect/decal/cleanable/blood/gibs/robot)
deathmessage = "blows apart!"
bubble_icon = "machine"
del_on_death = 1
/mob/living/simple_animal/hostile/hivebot/range
@@ -363,6 +363,7 @@
minbodytemp = 0
mob_size = MOB_SIZE_TINY
flying = 1
bubble_icon = "syndibot"
gold_core_spawnable = HOSTILE_SPAWN
del_on_death = 1
deathmessage = "is smashed into pieces!"
@@ -17,6 +17,7 @@
response_harm = "stomps on"
emote_see = list("jiggles", "bounces in place")
speak_emote = list("blorbles")
bubble_icon = "slime"
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)
+3 -5
View File
@@ -127,14 +127,12 @@
// self_message (optional) is what the src mob hears.
// deaf_message (optional) is what deaf people will see.
// hearing_distance (optional) is the range, how many tiles away the message can be heard.
/mob/audible_message(var/message, var/deaf_message, var/hearing_distance, var/self_message)
/mob/audible_message(message, deaf_message, hearing_distance)
var/range = 7
if(hearing_distance)
range = hearing_distance
var/msg = message
for(var/mob/M in get_mobs_in_view(range, src))
if(self_message && M == src)
msg = self_message
M.show_message(msg, 2, deaf_message, 1)
// based on say code
@@ -156,12 +154,12 @@
// message is the message output to anyone who can hear.
// deaf_message (optional) is what deaf people will see.
// hearing_distance (optional) is the range, how many tiles away the message can be heard.
/atom/proc/audible_message(var/message, var/deaf_message, var/hearing_distance)
/atom/proc/audible_message(message, deaf_message, hearing_distance)
var/range = 7
if(hearing_distance)
range = hearing_distance
for(var/mob/M in get_mobs_in_view(range, src))
M.show_message( message, 2, deaf_message, 1)
M.show_message(message, 2, deaf_message, 1)
/mob/proc/findname(msg)
for(var/mob/M in GLOB.mob_list)
+11 -11
View File
@@ -53,12 +53,11 @@
hud.master = src
//check if assailant is grabbed by victim as well
if(assailant.grabbed_by)
for(var/obj/item/grab/G in assailant.grabbed_by)
if(G.assailant == affecting && G.affecting == assailant)
G.dancing = 1
G.adjust_position()
dancing = 1
for(var/obj/item/grab/G in assailant.grabbed_by)
if(G.assailant == affecting && G.affecting == assailant)
G.dancing = 1
G.adjust_position()
dancing = 1
clean_grabbed_by(assailant, affecting)
adjust_position()
@@ -276,7 +275,7 @@
assailant.visible_message("<span class='warning'>[assailant] has reinforced [assailant.p_their()] grip on [affecting] (now neck)!</span>")
state = GRAB_NECK
icon_state = "grabbed+1"
assailant.setDir(get_dir(assailant, affecting))
add_attack_logs(assailant, affecting, "Neck grabbed", ATKLOG_ALL)
if(!iscarbon(assailant))
affecting.LAssailant = null
@@ -296,7 +295,7 @@
assailant.next_move = world.time + 10
if(!affecting.get_organ_slot("breathing_tube"))
affecting.AdjustLoseBreath(1)
affecting.setDir(WEST)
adjust_position()
//This is used to make sure the victim hasn't managed to yackety sax away before using the grab.
@@ -433,9 +432,10 @@
/obj/item/grab/Destroy()
if(affecting)
affecting.pixel_x = 0
affecting.pixel_y = 0 //used to be an animate, not quick enough for del'ing
affecting.layer = initial(affecting.layer)
if(!affecting.buckled)
affecting.pixel_x = 0
affecting.pixel_y = 0 //used to be an animate, not quick enough for del'ing
affecting.layer = initial(affecting.layer)
affecting.grabbed_by -= src
affecting = null
if(assailant)
+1 -20
View File
@@ -26,25 +26,6 @@
else
to_chat(usr, "<span class='danger'>This mob type cannot throw items.</span>")
/client/verb/drop_item()
set hidden = 1
if(!isrobot(mob))
mob.drop_item_v()
return
/* /client/Center()
/* No 3D movement in 2D spessman game. dir 16 is Z Up
if(isobj(mob.loc))
var/obj/O = mob.loc
if(mob.canmove)
return O.relaymove(mob, 16)
*/
return
*/
/client/proc/Move_object(direct)
if(mob && mob.control_object)
if(mob.control_object.density)
@@ -186,7 +167,7 @@
if(newdir)
direct = newdir
n = get_step(mob, direct)
. = mob.SelfMove(n, direct, delay)
mob.setDir(direct)
+1 -1
View File
@@ -114,7 +114,7 @@
return get_turf(src)
/mob/proc/say_test(var/text)
/proc/say_test(text)
var/ending = copytext(text, length(text))
if(ending == "?")
return "1"
+10 -9
View File
@@ -5,31 +5,32 @@ mob/var/typing
mob/var/last_typed
mob/var/last_typed_time
GLOBAL_DATUM(typing_indicator, /image)
GLOBAL_LIST_EMPTY(typing_indicator)
/mob/proc/set_typing_indicator(var/state)
/mob/proc/set_typing_indicator(state)
if(!GLOB.typing_indicator)
GLOB.typing_indicator = image('icons/mob/talk.dmi', null, "typing", MOB_LAYER + 1)
GLOB.typing_indicator.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
if(!GLOB.typing_indicator[bubble_icon])
GLOB.typing_indicator[bubble_icon] = image('icons/mob/talk.dmi', null, "[bubble_icon]typing", FLY_LAYER)
var/image/I = GLOB.typing_indicator[bubble_icon]
I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
if(ishuman(src))
var/mob/living/carbon/human/H = src
if((MUTE in H.mutations) || H.silent)
overlays -= GLOB.typing_indicator
overlays -= GLOB.typing_indicator[bubble_icon]
return
if(client)
if((client.prefs.toggles & SHOW_TYPING) || stat != CONSCIOUS || is_muzzled())
overlays -= GLOB.typing_indicator
overlays -= GLOB.typing_indicator[bubble_icon]
else
if(state)
if(!typing)
overlays += GLOB.typing_indicator
overlays += GLOB.typing_indicator[bubble_icon]
typing = 1
else
if(typing)
overlays -= GLOB.typing_indicator
overlays -= GLOB.typing_indicator[bubble_icon]
typing = 0
return state
+1 -1
View File
@@ -340,7 +340,7 @@
return 0
else
playsound(loc, 'sound/machines/ping.ogg', 50, 0)
atom_say("<span class='danger'>Attention: Posterior Placed on Printing Plaque!</span>")
atom_say("Attention: Posterior Placed on Printing Plaque!")
return 1
/obj/machinery/photocopier/emag_act(user as mob)
+2 -2
View File
@@ -562,13 +562,13 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor
talk_into(M, msg)
for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines)
if(T.watchers[M] == camera)
T.audible_message("<span class='game radio'><span class='name'>(Newscaster) [M]</span> says, '[msg]'", hearing_distance = 2)
T.atom_say(msg)
/obj/item/videocam/hear_message(mob/M as mob, msg)
if(camera && on)
for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines)
if(T.watchers[M] == camera)
T.audible_message("<span class='game radio'><span class='name'>(Newscaster) [M]</span> [msg]", hearing_distance = 2)
T.atom_say(msg)
///hauntings, like hallucinations but more spooky
+8 -19
View File
@@ -366,6 +366,7 @@
JaniData["user_loc"] = list("x" = cl.x, "y" = cl.y)
else
JaniData["user_loc"] = list("x" = 0, "y" = 0)
var/MopData[0]
for(var/obj/item/mop/M in GLOB.janitorial_equipment)
var/turf/ml = get_turf(M)
@@ -375,10 +376,6 @@
var/direction = get_dir(pda, M)
MopData[++MopData.len] = list ("x" = ml.x, "y" = ml.y, "dir" = uppertext(dir2text(direction)), "status" = M.reagents.total_volume ? "Wet" : "Dry")
if(!MopData.len)
MopData[++MopData.len] = list("x" = 0, "y" = 0, dir=null, status = null)
var/BucketData[0]
for(var/obj/structure/mopbucket/B in GLOB.janitorial_equipment)
var/turf/bl = get_turf(B)
@@ -386,13 +383,10 @@
if(bl.z != cl.z)
continue
var/direction = get_dir(pda,B)
BucketData[++BucketData.len] = list ("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "status" = B.reagents.total_volume/100)
if(!BucketData.len)
BucketData[++BucketData.len] = list("x" = 0, "y" = 0, dir=null, status = null)
BucketData[++BucketData.len] = list ("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "volume" = B.reagents.total_volume, "max_volume" = B.reagents.maximum_volume)
var/CbotData[0]
for(var/mob/living/simple_animal/bot/cleanbot/B in GLOB.simple_animals)
for(var/mob/living/simple_animal/bot/cleanbot/B in GLOB.bots_list)
var/turf/bl = get_turf(B)
if(bl)
if(bl.z != cl.z)
@@ -400,9 +394,6 @@
var/direction = get_dir(pda,B)
CbotData[++CbotData.len] = list("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "status" = B.on ? "Online" : "Offline")
if(!CbotData.len)
CbotData[++CbotData.len] = list("x" = 0, "y" = 0, dir=null, status = null)
var/CartData[0]
for(var/obj/structure/janitorialcart/B in GLOB.janitorial_equipment)
var/turf/bl = get_turf(B)
@@ -410,12 +401,10 @@
if(bl.z != cl.z)
continue
var/direction = get_dir(pda,B)
CartData[++CartData.len] = list("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "status" = B.reagents.total_volume/100)
if(!CartData.len)
CartData[++CartData.len] = list("x" = 0, "y" = 0, dir=null, status = null)
CartData[++CartData.len] = list("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "volume" = B.reagents.total_volume, "max_volume" = B.reagents.maximum_volume)
JaniData["mops"] = MopData
JaniData["buckets"] = BucketData
JaniData["cleanbots"] = CbotData
JaniData["carts"] = CartData
JaniData["mops"] = MopData.len ? MopData : null
JaniData["buckets"] = BucketData.len ? BucketData : null
JaniData["cleanbots"] = CbotData.len ? CbotData : null
JaniData["carts"] = CartData.len ? CartData : null
data["janitor"] = JaniData
+2 -2
View File
@@ -47,7 +47,7 @@
SSmob_hunt.connected_clients += src
connected = 1
if(pda)
pda.audible_message("[bicon(pda)] Connection established. Capture all of the mobs, [pda.owner ? pda.owner : "hunter"]!", null, 2)
pda.atom_say("Connection established. Capture all of the mobs, [pda.owner ? pda.owner : "hunter"]!")
return 1
/datum/data/pda/app/mob_hunter_game/proc/get_player()
@@ -67,7 +67,7 @@
connected = 0
//show a disconnect message if we were disconnected involuntarily (reason argument provided)
if(pda && reason)
pda.audible_message("[bicon(pda)] Disconnected from server. Reason: [reason].", null, 2)
pda.atom_say("Disconnected from server. Reason: [reason].")
/datum/data/pda/app/mob_hunter_game/program_process()
if(!SSmob_hunt || !connected)
@@ -12,7 +12,7 @@
select_name = "kill"
/obj/item/ammo_casing/energy/laser/cyborg //to balance cyborg energy cost seperately
e_cost = 250
e_cost = 250
/obj/item/ammo_casing/energy/lasergun
projectile_type = /obj/item/projectile/beam/laser
@@ -75,6 +75,10 @@
e_cost = 125
select_name = "precise"
/obj/item/ammo_casing/energy/immolator/strong/cyborg
// Used by gamma ERT borgs
e_cost = 1000 // 5x that of the standard laser, for 2.25x the damage (if 1/1 shots hit) plus ignite. Not energy-efficient, but can be used for sniping.
/obj/item/ammo_casing/energy/immolator/scatter
projectile_type = /obj/item/projectile/beam/immolator/weak
e_cost = 125
@@ -82,6 +86,10 @@
variance = 25
select_name = "scatter"
/obj/item/ammo_casing/energy/immolator/scatter/cyborg
// Used by gamma ERT borgs
e_cost = 1000 // 5x that of the standard laser, for 7.5x the damage (if 6/6 shots hit) plus ignite. Efficient only if you hit with at least 4/6 of the shots.
/obj/item/ammo_casing/energy/electrode
projectile_type = /obj/item/projectile/energy/electrode
select_name = "stun"
@@ -253,7 +253,7 @@
icon = 'icons/obj/objects.dmi'
icon_state = "modkit"
origin_tech = "programming=2;materials=2;magnets=4"
require_module = 1
require_module = TRUE
module_type = /obj/item/robot_module/miner
usesound = 'sound/items/screwdriver.ogg'
var/denied_type = null
@@ -139,6 +139,12 @@
var/append = shot.select_name
overlays += image(icon = icon, icon_state = "multilensimmolator-[append]")
/obj/item/gun/energy/immolator/multi/cyborg
name = "cyborg immolator cannon"
ammo_type = list(/obj/item/ammo_casing/energy/immolator/scatter/cyborg, /obj/item/ammo_casing/energy/immolator/strong/cyborg) // scatter is default, because it is more useful
////////Laser Tag////////////////////
/obj/item/gun/energy/laser/tag
+48 -62
View File
@@ -26,7 +26,6 @@
var/speed = 1 //Amount of deciseconds it takes for projectile to travel
var/Angle = null
var/spread = 0 //amount (in degrees) of projectile spread
var/legacy = FALSE //legacy projectile system
animate_movement = 0
var/ignore_source_check = FALSE
@@ -232,74 +231,61 @@
return 1 //Bullets don't drift in space
/obj/item/projectile/proc/fire(var/setAngle)
set waitfor = FALSE
if(setAngle)
Angle = setAngle
if(!legacy) //new projectiles
set waitfor = 0
while(loc)
if(!paused)
if((!( current ) || loc == current))
current = locate(clamp(x+xo,1,world.maxx),clamp(y+yo,1,world.maxy),z)
if(isnull(Angle))
Angle=round(Get_Angle(src,current))
if(spread)
Angle += (rand() - 0.5) * spread
var/matrix/M = new
M.Turn(Angle)
transform = M
var/Pixel_x=round(sin(Angle)+16*sin(Angle)*2)
var/Pixel_y=round(cos(Angle)+16*cos(Angle)*2)
var/pixel_x_offset = pixel_x + Pixel_x
var/pixel_y_offset = pixel_y + Pixel_y
var/new_x = x
var/new_y = y
while(!QDELETED(src))
if(!paused)
if((!current || loc == current))
current = locate(clamp(x + xo, 1, world.maxx), clamp(y + yo, 1, world.maxy), z)
if(isnull(Angle))
Angle = round(Get_Angle(src, current))
if(spread)
Angle += (rand() - 0.5) * spread
var/matrix/M = new
M.Turn(Angle)
transform = M
while(pixel_x_offset > 16)
pixel_x_offset -= 32
pixel_x -= 32
new_x++// x++
while(pixel_x_offset < -16)
pixel_x_offset += 32
pixel_x += 32
new_x--
var/Pixel_x = round(sin(Angle) + 16 * sin(Angle) * 2)
var/Pixel_y = round(cos(Angle) + 16 * cos(Angle) * 2)
var/pixel_x_offset = pixel_x + Pixel_x
var/pixel_y_offset = pixel_y + Pixel_y
var/new_x = x
var/new_y = y
while(pixel_y_offset > 16)
pixel_y_offset -= 32
pixel_y -= 32
new_y++
while(pixel_y_offset < -16)
pixel_y_offset += 32
pixel_y += 32
new_y--
while(pixel_x_offset > 16)
pixel_x_offset -= 32
pixel_x -= 32
new_x++ // x++
while(pixel_x_offset < -16)
pixel_x_offset += 32
pixel_x += 32
new_x--
speed = round(speed)
step_towards(src, locate(new_x, new_y, z))
if(speed <= 1)
pixel_x = pixel_x_offset
pixel_y = pixel_y_offset
else
animate(src, pixel_x = pixel_x_offset, pixel_y = pixel_y_offset, time = max(1, (speed <= 3 ? speed - 1 : speed)))
while(pixel_y_offset > 16)
pixel_y_offset -= 32
pixel_y -= 32
new_y++
while(pixel_y_offset < -16)
pixel_y_offset += 32
pixel_y += 32
new_y--
if(original && (original.layer>=2.75) || ismob(original))
if(loc == get_turf(original))
if(!(original in permutated))
Bump(original, 1)
Range()
sleep(max(1, speed))
else //old projectile system
set waitfor = 0
while(loc)
if(!paused)
if((!( current ) || loc == current))
current = locate(clamp(x+xo,1,world.maxx),clamp(y+yo,1,world.maxy),z)
step_towards(src, current)
if(original && (original.layer>=2.75) || ismob(original))
if(loc == get_turf(original))
if(!(original in permutated))
Bump(original, 1)
Range()
sleep(1)
speed = round(speed)
step_towards(src, locate(new_x, new_y, z))
if(speed <= 1)
pixel_x = pixel_x_offset
pixel_y = pixel_y_offset
else
animate(src, pixel_x = pixel_x_offset, pixel_y = pixel_y_offset, time = max(1, (speed <= 3 ? speed - 1 : speed)))
if(original && (original.layer >= 2.75 || ismob(original)))
if(loc == get_turf(original))
if(!(original in permutated))
Bump(original, TRUE)
Range()
sleep(max(1, speed))
obj/item/projectile/proc/reflect_back(atom/source, list/position_modifiers = list(0, 0, 0, 0, 0, -1, 1, -2, 2))
if(starting)
@@ -543,7 +543,7 @@
M.AdjustConfused(-5)
update_flags |= M.SetWeakened(0, FALSE)
if(volume >= 70 && prob(25))
if(M.reagents.has_reagent("thc") <= 20)
if(M.reagents.get_reagent_amount("thc") <= 20)
M.Drowsy(10)
if(prob(25))
update_flags |= M.adjustBruteLoss(-2, FALSE)
+3 -2
View File
@@ -61,8 +61,9 @@ obj/item/reagent_containers/proc/add_initial_reagents()
update_icon()
return
/obj/item/reagent_containers/afterattack(obj/target, mob/user , flag)
return
/obj/item/reagent_containers/attack(mob/M, mob/user, def_zone)
if(user.a_intent == INTENT_HARM)
return ..()
/obj/item/reagent_containers/wash(mob/user, atom/source)
if(is_open_container())
@@ -47,7 +47,7 @@
/obj/item/reagent_containers/borghypo/process() //Every [recharge_time] seconds, recharge some reagents for the cyborg
charge_tick++
if(charge_tick < recharge_time)
if(charge_tick < recharge_time)
return FALSE
charge_tick = 0
@@ -126,4 +126,9 @@
if(empty)
. += "<span class='notice'>It is currently empty. Allow some time for the internal syntheszier to produce more.</span>"
/obj/item/reagent_containers/borghypo/basic
name = "Basic Medical Hypospray"
desc = "A very basic medical hypospray, capable of providing simple medical treatment in emergencies."
reagent_ids = list("salglu_solution", "epinephrine")
#undef BORGHYPO_REFILL_VALUE
@@ -14,38 +14,7 @@
container_type = OPENCONTAINER
has_lid = TRUE
resistance_flags = ACID_PROOF
var/label_text = ""
// the fucking asshole who designed this can go die in a fire - Iamgoofball
var/list/can_be_placed_into = list(
/obj/machinery/chem_master/,
/obj/machinery/chem_heater/,
/obj/machinery/chem_dispenser/,
/obj/machinery/reagentgrinder,
/obj/structure/table,
/obj/structure/closet,
/obj/structure/sink,
/obj/structure/toilet,
/obj/item/storage,
/obj/machinery/atmospherics/unary/cryo_cell,
/obj/machinery/dna_scannernew,
/obj/item/grenade/chem_grenade,
/mob/living/simple_animal/bot/medbot,
/obj/item/storage/secure/safe,
/obj/machinery/iv_drip,
/obj/machinery/computer/pandemic,
/obj/machinery/disposal,
/mob/living/simple_animal/cow,
/mob/living/simple_animal/hostile/retaliate/goat,
/obj/machinery/sleeper,
/obj/machinery/smartfridge/,
/obj/machinery/biogenerator,
/obj/machinery/hydroponics,
/obj/machinery/constructable_frame,
/obj/machinery/icemachine,
/obj/item/bombcore/chemical,
/obj/machinery/vending,
/obj/machinery/fishtank)
/obj/item/reagent_containers/glass/New()
..()
@@ -98,56 +67,44 @@
reagents.reaction(M, REAGENT_INGEST, fraction)
addtimer(CALLBACK(reagents, /datum/reagents.proc/trans_to, M, 5), 5)
playsound(M.loc,'sound/items/drink.ogg', rand(10,50), 1)
else
return ..()
/obj/item/reagent_containers/glass/afterattack(obj/target, mob/user, proximity)
if(!proximity)
if((!proximity) || !check_allowed_items(target,target_self = TRUE))
return
if(!is_open_container())
return
for(var/type in can_be_placed_into)
if(istype(target, type))
if(target.is_refillable()) //Something like a glass. Player probably wants to transfer TO it.
if(!reagents.total_volume)
to_chat(user, "<span class='warning'>[src] is empty!</span>")
return
if(istype(target, /obj/structure/reagent_dispensers)) //A dispenser. Transfer FROM it TO us.
if(target.reagents && !target.reagents.total_volume)
if(target.reagents.holder_full())
to_chat(user, "<span class='warning'>[target] is full.</span>")
return
var/trans = reagents.trans_to(target, amount_per_transfer_from_this)
to_chat(user, "<span class='notice'>You transfer [trans] unit\s of the solution to [target].</span>")
else if(target.is_drainable()) //A dispenser. Transfer FROM it TO us.
if(!target.reagents.total_volume)
to_chat(user, "<span class='warning'>[target] is empty and can't be refilled!</span>")
return
if(reagents.total_volume >= reagents.maximum_volume)
to_chat(user, "<span class='notice'>[src] is full.</span>")
if(reagents.holder_full())
to_chat(user, "<span class='warning'>[src] is full.</span>")
return
var/trans = target.reagents.trans_to(src, amount_per_transfer_from_this)
to_chat(user, "<span class='notice'>You fill [src] with [trans] unit\s of the contents of [target].</span>")
else if(target.is_refillable() && is_drainable()) //Something like a glass. Player probably wants to transfer TO it.
if(!reagents.total_volume)
to_chat(user, "<span class='warning'>[src] is empty.</span>")
return
if(target.reagents.total_volume >= target.reagents.maximum_volume)
to_chat(user, "<span class='warning'>[target] is full.</span>")
return
var/trans = reagents.trans_to(target, amount_per_transfer_from_this)
to_chat(user, "<span class='notice'>You transfer [trans] units of the solution to [target].</span>")
else if(istype(target, /obj/item/reagent_containers/glass) && !target.is_open_container())
to_chat(user, "<span class='warning'>You cannot fill [target] while it is sealed.</span>")
return
else if(istype(target, /obj/effect/decal)) //stops splashing while scooping up fluids
return
else if(reagents.total_volume && user.a_intent == INTENT_HARM)
user.visible_message("<span class='danger'>[user] splashes the contents of [src] onto [target]!</span>", \
"<span class='notice'>You splash the contents of [src] onto [target].</span>")
reagents.reaction(target, REAGENT_TOUCH)
reagents.clear_reagents()
else if(reagents.total_volume)
if(user.a_intent == INTENT_HARM)
user.visible_message("<span class='danger'>[user] splashes the contents of [src] onto [target]!</span>", \
"<span class='notice'>You splash the contents of [src] onto [target].</span>")
reagents.reaction(target, REAGENT_TOUCH)
reagents.clear_reagents()
/obj/item/reagent_containers/glass/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/pen) || istype(I, /obj/item/flashlight/pen))
+2 -2
View File
@@ -794,8 +794,8 @@
return
if(T.intact && istype(T,/turf/simulated/floor)) //intact floor, pop the tile
var/turf/simulated/floor/F = T
new F.builtin_tile.type(H)
F.remove_tile(null,TRUE,FALSE)
new F.floor_tile(H)
F.remove_tile(null, TRUE, FALSE)
if(direction) // direction is specified
if(istype(T, /turf/space)) // if ended in space, then range is unlimited
@@ -1081,8 +1081,17 @@
construction_time = 120
category = list("Cyborg Upgrade Modules")
/datum/design/borg_upgrade_lavaproof
name = "Cyborg Upgrade (Lavaproof Chassis)"
id = "borg_upgrade_lavaproof"
build_type = MECHFAB
build_path = /obj/item/borg/upgrade/lavaproof
materials = list(MAT_METAL = 10000, MAT_PLASMA = 4000, MAT_TITANIUM = 5000)
construction_time = 120
category = list("Cyborg Upgrade Modules")
/datum/design/borg_syndicate_module
name = "Cyborg Upgrade (Illegal Modules)"
name = "Cyborg Upgrade (Safety Override)"
id = "borg_syndicate_module"
build_type = MECHFAB
req_tech = list("combat" = 4, "syndicate" = 2)
@@ -13,7 +13,7 @@
category = list("Subspace Telecomms")
/datum/design/telecomms_relay
name = "Machine Board (Telecommunications Core)"
name = "Machine Board (Telecommunications Relay)"
desc = "Allows for the construction of Telecommunications Relays."
id = "s-relay"
req_tech = list("programming" = 2, "engineering" = 2, "bluespace" = 2)
+2 -2
View File
@@ -103,12 +103,12 @@ GLOBAL_LIST_EMPTY(message_servers)
if(2)
if(!Console.silent)
playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1)
Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'PRIORITY Alert in [sender]'"),,5)
Console.atom_say("PRIORITY Alert in [sender]")
Console.message_log += "<B><FONT color='red'>High Priority message from <A href='?src=[Console.UID()];write=[sender]'>[sender]</A></FONT></B><BR>[authmsg]"
else
if(!Console.silent)
playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1)
Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'Message from [sender]'"),,4)
Console.atom_say("Message from [sender]")
Console.message_log += "<B>Message from <A href='?src=[Console.UID()];write=[sender]'>[sender]</A></B><BR>[authmsg]"
Console.set_light(2)
+4 -7
View File
@@ -135,8 +135,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE)
/client/proc/create_response_team(new_gender, role, turf/spawn_location)
if(role == "Cyborg")
var/cyborg_unlock = GLOB.active_team.getCyborgUnlock()
var/mob/living/silicon/robot/ert/R = new /mob/living/silicon/robot/ert(spawn_location, cyborg_unlock)
var/mob/living/silicon/robot/ert/R = new GLOB.active_team.borg_path(spawn_location)
return R
var/mob/living/carbon/human/M = new(null)
@@ -208,7 +207,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE)
var/security_outfit
var/janitor_outfit
var/paranormal_outfit
var/cyborg_unlock = 0
var/borg_path = /mob/living/silicon/robot/ert
/datum/response_team/proc/setSlots(com=1, sec=3, med=3, eng=3, jan=0, par=0, cyb=0)
slots["Commander"] = com
@@ -223,9 +222,6 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE)
slots[role]--
count++
/datum/response_team/proc/getCyborgUnlock()
return cyborg_unlock
/datum/response_team/proc/get_slot_list()
RETURN_TYPE(/list)
var/list/slots_available = list()
@@ -285,6 +281,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE)
command_outfit = /datum/outfit/job/centcom/response_team/commander/red
janitor_outfit = /datum/outfit/job/centcom/response_team/janitorial/red
paranormal_outfit = /datum/outfit/job/centcom/response_team/paranormal/red
borg_path = /mob/living/silicon/robot/ert/red
/datum/response_team/red/announce_team()
GLOB.event_announcement.Announce("Attention, [station_name()]. We are sending a code RED Emergency Response Team. Standby.", "ERT En-Route")
@@ -298,7 +295,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE)
command_outfit = /datum/outfit/job/centcom/response_team/commander/gamma
janitor_outfit = /datum/outfit/job/centcom/response_team/janitorial/gamma
paranormal_outfit = /datum/outfit/job/centcom/response_team/paranormal/gamma
cyborg_unlock = 1
borg_path = /mob/living/silicon/robot/ert/gamma
/datum/response_team/gamma/announce_team()
GLOB.event_announcement.Announce("Attention, [station_name()]. We are sending a code GAMMA elite Emergency Response Team. Standby.", "ERT En-Route")
@@ -31,15 +31,6 @@ GLOBAL_DATUM_INIT(security_announcement_down, /datum/announcement/priority/secur
// Mark down this time to prevent shuttle cheese
SSshuttle.emergency_sec_level_time = world.time
// Reset gamma borgs if the new security level is lower than Gamma.
if(level < SEC_LEVEL_GAMMA)
for(var/M in GLOB.silicon_mob_list)
if(isrobot(M))
var/mob/living/silicon/robot/R = M
if(istype(R.module, /obj/item/robot_module/combat) && !R.crisis)
R.reset_module()
to_chat(R, "<span class='warning'>Crisis mode deactivated. The combat module is no longer available and your module has been reset.</span>")
switch(level)
if(SEC_LEVEL_GREEN)
GLOB.security_announcement_down.Announce("All threats to the station have passed. All weapons need to be holstered and privacy laws are once again fully enforced.","Attention! Security level lowered to green.")
@@ -32,8 +32,6 @@
jumpto_ports += list("nav_z1" = 1)
if(access_tcomms)
jumpto_ports += list("nav_z3" = 1)
if(access_construction)
jumpto_ports += list("nav_z4" = 1)
if(access_mining)
jumpto_ports += list("nav_z5" = 1)
if(access_derelict)
+2 -2
View File
@@ -863,7 +863,7 @@
desc = "Used to control the White Ship."
circuit = /obj/item/circuitboard/white_ship
shuttleId = "whiteship"
possible_destinations = "whiteship_away;whiteship_home;whiteship_z4"
possible_destinations = "whiteship_away;whiteship_home"
/obj/machinery/computer/shuttle/engineering
name = "Engineering Shuttle Console"
@@ -913,7 +913,7 @@
desc = "Used to control the Golem Ship."
circuit = /obj/item/circuitboard/shuttle/golem_ship
shuttleId = "freegolem"
possible_destinations = "freegolem_lavaland;freegolem_z5;freegolem_z4;freegolem_z6"
possible_destinations = "freegolem_lavaland;freegolem_z5;freegolem_z6"
/obj/machinery/computer/shuttle/golem_ship/attack_hand(mob/user)
if(!isgolem(user) && !isobserver(user))
+12
View File
@@ -0,0 +1,12 @@
//include unit test files in this module in this ifdef
//Keep this sorted alphabetically
#ifdef UNIT_TESTS
#include "component_tests.dm"
#include "reagent_id_typos.dm"
#include "spawn_humans.dm"
#include "sql.dm"
#include "subsystem_init.dm"
#include "timer_sanity.dm"
#include "unit_test.dm"
#endif
@@ -0,0 +1,12 @@
/datum/unit_test/component_duping/Run()
var/list/bad_dms = list()
var/list/bad_dts = list()
for(var/t in typesof(/datum/component))
var/datum/component/comp = t
if(!isnum(initial(comp.dupe_mode)))
bad_dms += t
var/dupe_type = initial(comp.dupe_type)
if(dupe_type && !ispath(dupe_type))
bad_dts += t
if(length(bad_dms) || length(bad_dts))
Fail("Components with invalid dupe modes: ([bad_dms.Join(",")]) ||| Components with invalid dupe types: ([bad_dts.Join(",")])")
@@ -0,0 +1,11 @@
/datum/unit_test/reagent_id_typos
/datum/unit_test/reagent_id_typos/Run()
for(var/I in GLOB.chemical_reactions_list)
for(var/V in GLOB.chemical_reactions_list[I])
var/datum/chemical_reaction/R = V
for(var/id in (R.required_reagents + R.required_catalysts))
if(!GLOB.chemical_reagents_list[id])
Fail("Unknown chemical id \"[id]\" in recipe [R.type]")
+8
View File
@@ -0,0 +1,8 @@
/datum/unit_test/spawn_humans/Run()
var/locs = block(run_loc_bottom_left, run_loc_top_right)
for(var/I in 1 to 5)
new /mob/living/carbon/human(pick(locs))
// There is a 5 second delay here so that all the items on the humans have time to initialize and spawn
sleep(50)
+42
View File
@@ -0,0 +1,42 @@
// Unit test to check SQL version has been updated properly.,
/datum/unit_test/sql_version/Run()
// Check if the SQL version set in the code is equal to the travis DB config
if(config.sql_enabled && sql_version != SQL_VERSION)
Fail("SQL version error: Game is running V[SQL_VERSION] but config is V[sql_version]. You may need to update tools/travis/dbconfig.txt")
// Check if the travis DB config is up to date with the example dbconfig
// This proc is a little unclean but it works
var/example_db_version
var/list/Lines = file2list("config/example/dbconfig.txt")
for(var/t in Lines)
if(!t) continue
t = trim(t)
if(length(t) == 0)
continue
else if(copytext(t, 1, 2) == "#")
continue
var/pos = findtext(t, " ")
var/name = null
var/value = null
if(pos)
name = lowertext(copytext(t, 1, pos))
value = copytext(t, pos + 1)
else
name = lowertext(t)
if(!name)
continue
switch(name)
if("db_version")
example_db_version = text2num(value)
if(!example_db_version)
Fail("SQL version error: File config/example/dbconfig.txt does not have a valid SQL version set!")
if(example_db_version != SQL_VERSION)
Fail("SQL version error: Game is running V[SQL_VERSION] but config/example/dbconfig.txt is V[example_db_version].")
@@ -0,0 +1,7 @@
/datum/unit_test/subsystem_init/Run()
for(var/i in Master.subsystems)
var/datum/controller/subsystem/ss = i
if(ss.flags & SS_NO_INIT)
continue
if(!ss.initialized)
Fail("[ss]([ss.type]) is a subsystem meant to initialize but doesn't get set as initialized.")
+3
View File
@@ -0,0 +1,3 @@
/datum/unit_test/timer_sanity/Run()
if(SStimer.bucket_count < 0)
Fail("SStimer is going into negative bucket count from something")
+102
View File
@@ -0,0 +1,102 @@
/*
Usage:
Override /Run() to run your test code
Call Fail() to fail the test (You should specify a reason)
You may use /New() and /Destroy() for setup/teardown respectively
You can use the run_loc_bottom_left and run_loc_top_right to get turfs for testing
*/
/// VARS FOR UNIT TESTS
GLOBAL_DATUM(current_test, /datum/unit_test)
GLOBAL_VAR_INIT(failed_any_test, FALSE)
GLOBAL_VAR(test_log)
/datum/unit_test
//Bit of metadata for the future maybe
var/list/procs_tested
//usable vars
var/turf/run_loc_bottom_left
var/turf/run_loc_top_right
//internal shit
var/succeeded = TRUE
var/list/fail_reasons
/datum/unit_test/New()
run_loc_bottom_left = locate(1, 1, 1)
run_loc_top_right = locate(5, 5, 1)
/datum/unit_test/Destroy()
//clear the test area
for(var/atom/movable/AM in block(run_loc_bottom_left, run_loc_top_right))
qdel(AM)
return ..()
/datum/unit_test/proc/Run()
Fail("Run() called parent or not implemented")
/datum/unit_test/proc/Fail(reason = "No reason")
succeeded = FALSE
if(!istext(reason))
reason = "FORMATTED: [reason != null ? reason : "NULL"]"
LAZYADD(fail_reasons, reason)
/proc/RunUnitTests()
CHECK_TICK
for(var/I in subtypesof(/datum/unit_test))
var/datum/unit_test/test = new I
GLOB.current_test = test
var/duration = REALTIMEOFDAY
test.Run()
duration = REALTIMEOFDAY - duration
GLOB.current_test = null
GLOB.failed_any_test |= !test.succeeded
var/list/log_entry = list("[test.succeeded ? "PASS" : "FAIL"]: [I] [duration / 10]s")
var/list/fail_reasons = test.fail_reasons
qdel(test)
for(var/J in 1 to LAZYLEN(fail_reasons))
log_entry += "\tREASON #[J]: [fail_reasons[J]]"
log_world(log_entry.Join("\n"))
CHECK_TICK
world.Reboot("Unit Test Reboot", "end_test", "tests ended", 0)
// OTHER MISC PROCS RELATED TO UNIT TESTS //
/world/proc/HandleTestRun()
//trigger things to run the whole process
Master.sleep_offline_after_initializations = FALSE
// This will have the ticker set the game up
// Running the tests is part of the ticker's start function, because I cant think of any better place to put it
SSticker.force_start = TRUE
/world/proc/FinishTestRun()
set waitfor = FALSE
var/list/fail_reasons
if(GLOB)
if(GLOB.total_runtimes != 0)
fail_reasons = list("Total runtimes: [GLOB.total_runtimes]")
if(!GLOB.log_directory)
LAZYADD(fail_reasons, "Missing GLOB.log_directory!")
if(GLOB.failed_any_test)
LAZYADD(fail_reasons, "Unit Tests failed!")
else
fail_reasons = list("Missing GLOB!")
if(!fail_reasons)
text2file("Success!", "data/clean_run.lk")
else
log_world("Test run failed!\n[fail_reasons.Join("\n")]")
sleep(0) //yes, 0, this'll let Reboot finish and prevent byond memes
del(src) //shut it down